From b250524e09d2c12c3a767e731329cbec684f1350 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Tue, 17 Sep 2024 13:32:28 +0200 Subject: [PATCH 01/36] init rmn home --- contracts/src/v0.8/ccip/rmn/RMNHome.sol | 275 +++++++++++++----- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 175 +++++++++++ 2 files changed, 373 insertions(+), 77 deletions(-) create mode 100644 contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/rmn/RMNHome.sol index ec479bf663..bf1e22650e 100644 --- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol +++ b/contracts/src/v0.8/ccip/rmn/RMNHome.sol @@ -8,10 +8,10 @@ import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; /// @notice Stores the home configuration for RMN, that is referenced by CCIP oracles, RMN nodes, and the RMNRemote /// contracts. contract RMNHome is OwnerIsCreator, ITypeAndVersion { + error OutOfBoundsNodesLength(); error DuplicatePeerId(); error DuplicateOffchainPublicKey(); - error OutOfOrderSourceChains(); - error OutOfOrderObserverNodeIndices(); + error DuplicateSourceChain(); error OutOfBoundsObserverNodeIndex(); error MinObserversTooHigh(); @@ -19,54 +19,79 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { event ConfigRevoked(bytes32 configDigest); struct Node { - string peerId; // used for p2p communication, base58 encoded - bytes32 offchainPublicKey; // observations are signed with this public key, and are only verified offchain + bytes32 peerId; // Used for p2p communication. + bytes32 offchainPublicKey; // Observations are signed with this public key, and are only verified offchain. } struct SourceChain { - uint64 chainSelector; - uint64[] observerNodeIndices; // indices into Config.nodes, strictly increasing - uint64 minObservers; // required to agree on an observation for this source chain + uint64 chainSelector; // ─────╮ The Source chain selector. + uint64 minObservers; // ──────╯ Required number of observers to agree on an observation for this source chain. + uint256 observerNodesBitmap; // ObserverNodesBitmap & (1< 256) { + revert OutOfBoundsNodesLength(); + } + + // Ensure no peerId or offchainPublicKey is duplicated. for (uint256 i = 0; i < newConfig.nodes.length; ++i) { for (uint256 j = i + 1; j < newConfig.nodes.length; ++j) { - if (keccak256(abi.encode(newConfig.nodes[i].peerId)) == keccak256(abi.encode(newConfig.nodes[j].peerId))) { + if (newConfig.nodes[i].peerId == newConfig.nodes[j].peerId) { revert DuplicatePeerId(); } if (newConfig.nodes[i].offchainPublicKey == newConfig.nodes[j].offchainPublicKey) { @@ -75,79 +100,175 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { } } - for (uint256 i = 0; i < newConfig.sourceChains.length; ++i) { - // source chains are in strictly increasing order of chain selectors - if (i > 0 && !(newConfig.sourceChains[i - 1].chainSelector < newConfig.sourceChains[i].chainSelector)) { - revert OutOfOrderSourceChains(); + uint256 numberOfSourceChains = newConfig.sourceChains.length; + for (uint256 i = 0; i < numberOfSourceChains; ++i) { + // Ensure the source chain is unique. + for (uint256 j = i + 1; j < numberOfSourceChains; ++j) { + if (newConfig.sourceChains[i].chainSelector == newConfig.sourceChains[j].chainSelector) { + revert DuplicateSourceChain(); + } } - // all observerNodeIndices are valid - for (uint256 j = 0; j < newConfig.sourceChains[i].observerNodeIndices.length; ++j) { - if ( - j > 0 - && !(newConfig.sourceChains[i].observerNodeIndices[j - 1] < newConfig.sourceChains[i].observerNodeIndices[j]) - ) { - revert OutOfOrderObserverNodeIndices(); - } - if (!(newConfig.sourceChains[i].observerNodeIndices[j] < newConfig.nodes.length)) { - revert OutOfBoundsObserverNodeIndex(); - } + // all observer node indices are valid + uint256 bitmap = newConfig.sourceChains[i].observerNodesBitmap; + // Check if there are any bits set for indexes outside of the expected range. + if (bitmap & (type(uint256).max >> (256 - newConfig.nodes.length)) != bitmap) { + revert OutOfBoundsObserverNodeIndex(); + } + + uint256 observersCount = 0; + for (; bitmap != 0; ++observersCount) { + bitmap &= bitmap - 1; } // minObservers are tenable - if (!(newConfig.sourceChains[i].minObservers <= newConfig.sourceChains[i].observerNodeIndices.length)) { + if (newConfig.sourceChains[i].minObservers > observersCount) { revert MinObserversTooHigh(); } } } - uint256 oldConfigIndex = s_latestConfigIndex; - uint32 oldConfigCount = s_configCounts[oldConfigIndex]; - uint256 newConfigIndex = (oldConfigIndex + 1) % CONFIG_RING_BUFFER_SIZE; + uint256 newConfigIndex = (s_latestConfigIndex + 1) % CONFIG_RING_BUFFER_SIZE; - for (uint256 i = 0; i < CONFIG_RING_BUFFER_SIZE; ++i) { - if ((i == newConfigIndex || revokePastConfigs) && s_configCounts[i] > 0) { - emit ConfigRevoked(_configDigest(VersionedConfig({version: s_configCounts[i], config: s_configs[i]}))); - delete s_configCounts[i]; - } + // are we going to overwrite a config? + if (s_configVersions[newConfigIndex] > 0) { + emit ConfigRevoked(s_configDigests[newConfigIndex]); } - uint32 newConfigCount = oldConfigCount + 1; + uint32 newConfigCount = ++s_configCount; VersionedConfig memory newVersionedConfig = VersionedConfig({version: newConfigCount, config: newConfig}); - bytes32 newConfigDigest = _configDigest(newVersionedConfig); + bytes32 newConfigDigest = _getConfigDigest(newVersionedConfig); s_configs[newConfigIndex] = newConfig; - s_configCounts[newConfigIndex] = newConfigCount; - s_latestConfigIndex = newConfigIndex; - s_latestConfigDigest = newConfigDigest; + s_configVersions[newConfigIndex] = newConfigCount; + s_configDigests[newConfigIndex] = newConfigDigest; + s_latestConfigIndex = uint32(newConfigIndex); + emit ConfigSet(newConfigDigest, newVersionedConfig); } - /// @return configDigest will be zero in case no config has been set - function getLatestConfigDigestAndVersionedConfig() - external - view - returns (bytes32 configDigest, VersionedConfig memory) - { - return ( - s_latestConfigDigest, - VersionedConfig({version: s_configCounts[s_latestConfigIndex], config: s_configs[s_latestConfigIndex]}) - ); + /// @notice Revokes past configs, so that only the latest config remains. Call to promote staging to production. If + /// the latest config that was set through setConfig, was subsequently revoked through revokeConfig, this function + /// will revoke _all_ configs. + function revokeAllConfigsButLatest() external onlyOwner { + for (uint256 i = 0; i < CONFIG_RING_BUFFER_SIZE; ++i) { + // Find all configs that are not the latest. + if (s_latestConfigIndex != i && s_configVersions[i] > 0) { + emit ConfigRevoked(_getConfigDigest(VersionedConfig({version: s_configVersions[i], config: s_configs[i]}))); + // Delete only the version, as that's what's used to determine if a config is active. This means the actual + // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in + // the future. + delete s_configVersions[i]; + } + } } - /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes - /// @dev Only to be called by offchain code, efficiency is not a concern - function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { + /// @notice Revokes a specific config by digest. + /// @param configDigest The digest of the config to revoke. + function revokeConfig(bytes32 configDigest) external onlyOwner { for (uint256 i = 0; i < CONFIG_RING_BUFFER_SIZE; ++i) { - if (s_configCounts[i] == 0) { - // unset config - continue; - } - VersionedConfig memory vc = VersionedConfig({version: s_configCounts[i], config: s_configs[i]}); - if (_configDigest(vc) == configDigest) { - versionedConfig = vc; - ok = true; + if (s_configDigests[i] == configDigest && s_configVersions[i] > 0) { + emit ConfigRevoked(configDigest); + // Delete only the version, as that's what's used to determine if a config is active. This means the actual + // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in + // the future. + delete s_configVersions[i]; break; } } } + + function _getConfigDigest(VersionedConfig memory versionedConfig) internal pure returns (bytes32) { + return bytes32((PREFIX & PREFIX_MASK) | (uint256(keccak256(abi.encode(versionedConfig))) & ~PREFIX_MASK)); + } + + // ================================================================ + // │ Offchain getters | + // │ Gas is not a concern for these functions | + // ================================================================ + + /// @return configDigests ordered from oldest to latest set + function getConfigDigests() external view returns (bytes32[] memory configDigests) { + uint256 len = 0; + for (uint256 act = 0; act <= 1; ++act) { + if (act == 1) { + configDigests = new bytes32[](len); + } + + uint256 i = s_latestConfigIndex; + do { + if (s_configVersions[i] > 0) { + if (act == 0) { + ++len; + } else if (act == 1) { + configDigests[--len] = s_configDigests[i]; + } + } + if (i == 0) { + i = CONFIG_RING_BUFFER_SIZE - 1; + } else { + --i; + } + } while (i != s_latestConfigIndex); + } + return configDigests; + } + + /// @param offset setting to 0 will put the newest config in the last position of the returned array, setting to 1 + /// will put the second newest config in the last position, and so on + /// @param limit len(versionedConfigsWithDigests) <= limit, set to 1 to get just the latest config, + /// set to CONFIG_RING_BUFFER_SIZE to ensure that all configs are returned + /// @return versionedConfigsWithDigests ordered from oldest to latest set + function getVersionedConfigsWithDigests( + uint256 offset, + uint256 limit + ) external view returns (VersionedConfigWithDigest[] memory versionedConfigsWithDigests) { + uint256[2] memory ignored; + uint256[2] memory accounted; + uint256 len; // clobbered by the end of the loop + for (uint256 act = 0; act <= 1; ++act) { + if (act == 1) { + len = accounted[0]; + versionedConfigsWithDigests = new VersionedConfigWithDigest[](len); + } + + uint256 i = s_latestConfigIndex; + do { + if (accounted[act] >= limit) { + break; + } + + if (s_configVersions[i] > 0) { + if (ignored[act] < offset) { + ++ignored[act]; + } else { + ++accounted[act]; + if (act == 1) { + versionedConfigsWithDigests[--len] = VersionedConfigWithDigest({ + configDigest: s_configDigests[i], + versionedConfig: VersionedConfig({version: s_configVersions[i], config: s_configs[i]}) + }); + } + } + } + i = i == 0 ? CONFIG_RING_BUFFER_SIZE - 1 : i - 1; + } while (i != s_latestConfigIndex); + } + return versionedConfigsWithDigests; + } + + /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use + /// in case one of the configs is too large to be returnable by one of the other getters. + /// @param configDigest The digest of the config to fetch. + /// @return versionedConfig The config and its version. + /// @return ok True if the config was found, false otherwise. + function getVersionedConfig( + bytes32 configDigest + ) external view returns (VersionedConfig memory versionedConfig, bool ok) { + for (uint256 i = 0; i < CONFIG_RING_BUFFER_SIZE; ++i) { + if (s_configVersions[i] > 0 && s_configDigests[i] == configDigest) { + return (VersionedConfig({version: s_configVersions[i], config: s_configs[i]}), true); + } + } + return (versionedConfig, false); + } } diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol new file mode 100644 index 0000000000..144d0c833c --- /dev/null +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../libraries/Internal.sol"; +import {RMNHome} from "../../rmn/RMNHome.sol"; +import {BaseTest} from "../BaseTest.t.sol"; +import {Vm} from "forge-std/Vm.sol"; + +contract RMNHomeTest is BaseTest { + RMNHome public s_rmnHome; + + function setUp() public virtual override { + super.setUp(); + s_rmnHome = new RMNHome(); + + s_rmnHome.revokeAllConfigsButLatest(); + } + + function _getBaseConfig() internal pure returns (RMNHome.Config memory) { + RMNHome.Node[] memory nodes = new RMNHome.Node[](3); + nodes[0] = RMNHome.Node({peerId: keccak256("peerId_0"), offchainPublicKey: keccak256("offchainPublicKey_0")}); + nodes[1] = RMNHome.Node({peerId: keccak256("peerId_1"), offchainPublicKey: keccak256("offchainPublicKey_1")}); + nodes[2] = RMNHome.Node({peerId: keccak256("peerId_2"), offchainPublicKey: keccak256("offchainPublicKey_2")}); + + RMNHome.SourceChain[] memory sourceChains = new RMNHome.SourceChain[](2); + // Observer 0 for source chain 9000 + sourceChains[0] = RMNHome.SourceChain({chainSelector: 9000, minObservers: 1, observerNodesBitmap: 1 << 0}); + // Observers 1 and 2 for source chain 9001 + sourceChains[1] = RMNHome.SourceChain({chainSelector: 9001, minObservers: 2, observerNodesBitmap: 1 << 1 | 1 << 2}); + + return RMNHome.Config({nodes: nodes, sourceChains: sourceChains, offchainConfig: abi.encode("offchainConfig")}); + } + + uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 + uint256 private constant PREFIX = 0x000b << (256 - 16); // 0x000b00..00 + + function _getConfigDigest(RMNHome.VersionedConfig memory versionedConfig) internal pure returns (bytes32) { + return bytes32((PREFIX & PREFIX_MASK) | (uint256(keccak256(abi.encode(versionedConfig))) & ~PREFIX_MASK)); + } +} + +contract RMNHome_setConfig is RMNHomeTest { + function test_setConfig_success() public { + RMNHome.Config memory config = _getBaseConfig(); + RMNHome.VersionedConfig memory versionedConfig = RMNHome.VersionedConfig({version: 1, config: config}); + bytes32 configDigest = _getConfigDigest(versionedConfig); + + vm.expectEmit(); + emit RMNHome.ConfigSet(configDigest, versionedConfig); + + s_rmnHome.setConfig(config); + + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getVersionedConfig(configDigest); + assertTrue(ok); + assertEq(storedVersionedConfig.version, versionedConfig.version); + assertEq(storedVersionedConfig.config.nodes.length, versionedConfig.config.nodes.length); + for (uint256 i = 0; i < storedVersionedConfig.config.nodes.length; i++) { + RMNHome.Node memory storedNode = storedVersionedConfig.config.nodes[i]; + assertEq(storedNode.peerId, versionedConfig.config.nodes[i].peerId); + assertEq(storedNode.offchainPublicKey, versionedConfig.config.nodes[i].offchainPublicKey); + } + + assertEq(storedVersionedConfig.config.sourceChains.length, versionedConfig.config.sourceChains.length); + for (uint256 i = 0; i < storedVersionedConfig.config.sourceChains.length; i++) { + RMNHome.SourceChain memory storedSourceChain = storedVersionedConfig.config.sourceChains[i]; + assertEq(storedSourceChain.chainSelector, versionedConfig.config.sourceChains[i].chainSelector); + assertEq(storedSourceChain.minObservers, versionedConfig.config.sourceChains[i].minObservers); + assertEq(storedSourceChain.observerNodesBitmap, versionedConfig.config.sourceChains[i].observerNodesBitmap); + } + assertEq(storedVersionedConfig.config.offchainConfig, versionedConfig.config.offchainConfig); + } + + function test_setConfig_moreThanRingBuffer_success() public { + RMNHome.Config memory config = _getBaseConfig(); + uint256 ringBufferSize = s_rmnHome.getRingBufferSize(); + + vm.recordLogs(); + + // Insert ring buffer size + 1 configs + for (uint256 i = 0; i < ringBufferSize + 1; i++) { + s_rmnHome.setConfig(config); + } + + Vm.Log[] memory entries = vm.getRecordedLogs(); + vm.assertEq(entries.length, ringBufferSize + 2); + + for (uint256 i = 0; i < ringBufferSize; i++) { + Vm.Log memory entry = entries[i]; + vm.assertEq(entry.topics[0], bytes32(uint256(RMNHome.ConfigSet.selector))); + } + + // The last config should revoke the first config, which means it should emit a ConfigRevoked event + vm.assertEq(entries[ringBufferSize].topics[0], bytes32(uint256(RMNHome.ConfigRevoked.selector))); + vm.assertEq(entries[ringBufferSize + 1].topics[0], bytes32(uint256(RMNHome.ConfigSet.selector))); + } + + function test_setConfig_OutOfBoundsNodesLength_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + config.nodes = new RMNHome.Node[](257); + + vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); + s_rmnHome.setConfig(config); + } + + function test_setConfig_DuplicatePeerId_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + config.nodes[1].peerId = config.nodes[0].peerId; + + vm.expectRevert(RMNHome.DuplicatePeerId.selector); + s_rmnHome.setConfig(config); + } + + function test_setConfig_DuplicateOffchainPublicKey_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + config.nodes[1].offchainPublicKey = config.nodes[0].offchainPublicKey; + + vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); + s_rmnHome.setConfig(config); + } + + function test_setConfig_DuplicateSourceChain_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + config.sourceChains[1].chainSelector = config.sourceChains[0].chainSelector; + + vm.expectRevert(RMNHome.DuplicateSourceChain.selector); + s_rmnHome.setConfig(config); + } + + function test_setConfig_OutOfBoundsObserverNodeIndex_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + config.sourceChains[0].observerNodesBitmap = 1 << config.nodes.length; + + vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); + s_rmnHome.setConfig(config); + } + + function test_setConfig_MinObserversTooHigh_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + config.sourceChains[0].minObservers++; + + vm.expectRevert(RMNHome.MinObserversTooHigh.selector); + s_rmnHome.setConfig(config); + } + + function test_setConfig_OnlyOwner_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.setConfig(config); + } +} + +contract RMNHome_revokeAllConfigsButLatest is RMNHomeTest { + function test_revokeAllConfigsButLatest_success() public {} + + function test_setConfig_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.revokeAllConfigsButLatest(); + } +} + +contract RMNHome_revokeConfig is RMNHomeTest { + function test_revokeConfig_success() public {} + + function test_setConfig_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.revokeConfig(keccak256("configDigest")); + } +} From a2633a338a7e613dd321eb090f26ebf56f97bef4 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 18 Sep 2024 15:37:25 +0200 Subject: [PATCH 02/36] refactor rmn home --- contracts/src/v0.8/ccip/rmn/RMNHome.sol | 263 ++++++++---------- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 102 +++---- 2 files changed, 155 insertions(+), 210 deletions(-) diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/rmn/RMNHome.sol index bf1e22650e..cf277d536b 100644 --- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol +++ b/contracts/src/v0.8/ccip/rmn/RMNHome.sol @@ -15,6 +15,8 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { error OutOfBoundsObserverNodeIndex(); error MinObserversTooHigh(); + error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); + event ConfigSet(bytes32 configDigest, VersionedConfig versionedConfig); event ConfigRevoked(bytes32 configDigest); @@ -51,36 +53,91 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { string public constant override typeAndVersion = "RMNHome 1.6.0-dev"; /// @notice The max number of configs that can be active at the same time. - uint256 private constant CONFIG_RING_BUFFER_SIZE = 3; + uint256 private constant MAX_CONCURRENT_CONFIGS = 2; /// @notice Used for encoding the config digest prefix uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 uint256 private constant PREFIX = 0x000b << (256 - 16); // 0x000b00..00 + bytes32 private constant ZERO_DIGEST = bytes32(uint256(0)); - /// @notice This array holds just the versions of the configs, the actual configs are stored in s_configs. - /// s_configVersions[i] == 0 iff s_configs[i] is unusable, either never set or revoked. - uint32[CONFIG_RING_BUFFER_SIZE] private s_configVersions; /// @notice This array holds the digests of the configs, used for efficiency. - /// @dev Value i in this array is valid iff s_configVersions[i] != 0. - bytes32[CONFIG_RING_BUFFER_SIZE] private s_configDigests; + /// @dev Value i in this array is valid iff it's not 0. + bytes32[MAX_CONCURRENT_CONFIGS] private s_configDigests; /// @notice This array holds the configs. - /// @dev Value i in this array is valid iff s_configVersions[i] != 0. - Config[CONFIG_RING_BUFFER_SIZE] private s_configs; - /// @notice The index of the latest config in the ring buffer. Given a ring buffer of 2 this values will be 0 or 1. - /// @dev Since this value is packed with the config count, it won't flip the slot from 0 to 1 or vice versa, meaning - /// there's no gas impact in using 0 as a value. - uint32 private s_latestConfigIndex; - /// @notice The total number of configs set, used for generating the version of the configs. - uint32 private s_configCount; - - /// @notice Returns the current ring buffer size. - function getRingBufferSize() external pure returns (uint256) { - return CONFIG_RING_BUFFER_SIZE; + /// @dev Value i in this array is valid iff s_configDigests[i] != 0. + Config[MAX_CONCURRENT_CONFIGS] private s_configs; + /// @notice This array holds the versions of the configs. + /// @dev Value i in this array is valid iff s_configDigests[i] != 0. + /// @dev Since Solidity doesn't support writing complex memory structs to storage, we have to make the config calldata + /// in setConfig and then copy it to storage. This does not allow us to modify it to add the version field, so we + /// store the version separately. + uint32[MAX_CONCURRENT_CONFIGS] private s_configVersions; + + /// @notice The total number of configs ever set, used for generating the version of the configs. + uint32 private s_configCount = 0; + /// @notice The index of the primary config. + uint32 private s_primaryConfigIndex = 0; + + /// @notice Returns the current primary and secondary config digests. + /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. + /// @return primaryConfigDigest The digest of the primary config. + /// @return secondaryConfigDigest The digest of the secondary config. + function getConfigDigests() external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + return (s_configDigests[s_primaryConfigIndex], s_configDigests[s_primaryConfigIndex ^ 1]); } - /// @notice Sets a new config. - /// Setting a new config while the ring buffer is full will revoke the oldest config + /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use + /// in case one of the configs is too large to be returnable by one of the other getters. + /// @param configDigest The digest of the config to fetch. + /// @return versionedConfig The config and its version. + /// @return ok True if the config was found, false otherwise. + function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { + // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old + // config state that is invalid. + if (s_configDigests[i] == configDigest && configDigest != ZERO_DIGEST) { + return (VersionedConfig({config: s_configs[i], version: s_configVersions[i]}), true); + } + } + return (versionedConfig, false); + } + + function getAllConfigs() + external + view + returns (VersionedConfigWithDigest memory primaryConfig, VersionedConfigWithDigest memory secondaryConfig) + { + // We need to explicitly check if the digest exists, because we don't clear out revoked config state. Not doing this + // check would result in potentially returning previous configs. + uint256 primaryConfigIndex = s_primaryConfigIndex; + bytes32 primaryConfigDigest = s_configDigests[primaryConfigIndex]; + if (primaryConfigDigest != ZERO_DIGEST) { + primaryConfig = VersionedConfigWithDigest({ + configDigest: primaryConfigDigest, + versionedConfig: ( + VersionedConfig({config: s_configs[primaryConfigIndex], version: s_configVersions[primaryConfigIndex]}) + ) + }); + } + + uint256 secondaryConfigIndex = primaryConfigIndex ^ 1; + bytes32 secondaryConfigDigest = s_configDigests[secondaryConfigIndex]; + if (secondaryConfigDigest != ZERO_DIGEST) { + secondaryConfig = VersionedConfigWithDigest({ + configDigest: secondaryConfigDigest, + versionedConfig: ( + VersionedConfig({config: s_configs[secondaryConfigIndex], version: s_configVersions[secondaryConfigIndex]}) + ) + }); + } + + return (primaryConfig, secondaryConfig); + } + + /// @notice Sets a new config as the secondary config. Does not influence the primary config. /// @param newConfig The new config to set. - function setConfig(Config calldata newConfig) external onlyOwner { + /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. + /// This is done to prevent accidental overwrites. + function setSecondary(Config calldata newConfig, bytes32 digestToOverwrite) external onlyOwner { // sanity checks { // Ensure that observerNodesBitmap can be bit-encoded into a uint256. @@ -128,147 +185,71 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { } } - uint256 newConfigIndex = (s_latestConfigIndex + 1) % CONFIG_RING_BUFFER_SIZE; + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + + if (s_configDigests[secondaryConfigIndex] != digestToOverwrite) { + revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], digestToOverwrite); + } // are we going to overwrite a config? - if (s_configVersions[newConfigIndex] > 0) { - emit ConfigRevoked(s_configDigests[newConfigIndex]); + if (digestToOverwrite != ZERO_DIGEST) { + emit ConfigRevoked(digestToOverwrite); } - uint32 newConfigCount = ++s_configCount; - VersionedConfig memory newVersionedConfig = VersionedConfig({version: newConfigCount, config: newConfig}); + VersionedConfig memory newVersionedConfig = VersionedConfig({version: ++s_configCount, config: newConfig}); bytes32 newConfigDigest = _getConfigDigest(newVersionedConfig); - s_configs[newConfigIndex] = newConfig; - s_configVersions[newConfigIndex] = newConfigCount; - s_configDigests[newConfigIndex] = newConfigDigest; - s_latestConfigIndex = uint32(newConfigIndex); + s_configs[secondaryConfigIndex] = newConfig; + s_configVersions[secondaryConfigIndex] = newVersionedConfig.version; + s_configDigests[secondaryConfigIndex] = newConfigDigest; emit ConfigSet(newConfigDigest, newVersionedConfig); } - /// @notice Revokes past configs, so that only the latest config remains. Call to promote staging to production. If - /// the latest config that was set through setConfig, was subsequently revoked through revokeConfig, this function - /// will revoke _all_ configs. - function revokeAllConfigsButLatest() external onlyOwner { - for (uint256 i = 0; i < CONFIG_RING_BUFFER_SIZE; ++i) { - // Find all configs that are not the latest. - if (s_latestConfigIndex != i && s_configVersions[i] > 0) { - emit ConfigRevoked(_getConfigDigest(VersionedConfig({version: s_configVersions[i], config: s_configs[i]}))); - // Delete only the version, as that's what's used to determine if a config is active. This means the actual - // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in - // the future. - delete s_configVersions[i]; - } + /// @notice Revokes a specific config by digest. + /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. + function revokeSecondary(bytes32 configDigest) external onlyOwner { + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configDigests[secondaryConfigIndex] != configDigest) { + revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], configDigest); } + + emit ConfigRevoked(configDigest); + // Delete only the digest, as that's what's used to determine if a config is active. This means the actual + // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in + // the future. + delete s_configDigests[secondaryConfigIndex]; } - /// @notice Revokes a specific config by digest. - /// @param configDigest The digest of the config to revoke. - function revokeConfig(bytes32 configDigest) external onlyOwner { - for (uint256 i = 0; i < CONFIG_RING_BUFFER_SIZE; ++i) { - if (s_configDigests[i] == configDigest && s_configVersions[i] > 0) { - emit ConfigRevoked(configDigest); - // Delete only the version, as that's what's used to determine if a config is active. This means the actual - // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in - // the future. - delete s_configVersions[i]; - break; - } + /// @notice Promotes the secondary config to the primary config. This demotes the primary to be the secondary but does + /// not revoke it. To revoke the primary, use `promoteSecondaryAndRevokePrimary` instead. + function promoteSecondary(bytes32 digestToPromote) external onlyOwner { + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configDigests[secondaryConfigIndex] != digestToPromote) { + revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], digestToPromote); } - } - function _getConfigDigest(VersionedConfig memory versionedConfig) internal pure returns (bytes32) { - return bytes32((PREFIX & PREFIX_MASK) | (uint256(keccak256(abi.encode(versionedConfig))) & ~PREFIX_MASK)); + s_primaryConfigIndex ^= 1; } - // ================================================================ - // │ Offchain getters | - // │ Gas is not a concern for these functions | - // ================================================================ - - /// @return configDigests ordered from oldest to latest set - function getConfigDigests() external view returns (bytes32[] memory configDigests) { - uint256 len = 0; - for (uint256 act = 0; act <= 1; ++act) { - if (act == 1) { - configDigests = new bytes32[](len); - } - - uint256 i = s_latestConfigIndex; - do { - if (s_configVersions[i] > 0) { - if (act == 0) { - ++len; - } else if (act == 1) { - configDigests[--len] = s_configDigests[i]; - } - } - if (i == 0) { - i = CONFIG_RING_BUFFER_SIZE - 1; - } else { - --i; - } - } while (i != s_latestConfigIndex); + /// @notice Promotes the secondary config to the primary config and revokes the primary config. + function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configDigests[secondaryConfigIndex] != digestToPromote) { + revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], digestToPromote); } - return configDigests; - } - /// @param offset setting to 0 will put the newest config in the last position of the returned array, setting to 1 - /// will put the second newest config in the last position, and so on - /// @param limit len(versionedConfigsWithDigests) <= limit, set to 1 to get just the latest config, - /// set to CONFIG_RING_BUFFER_SIZE to ensure that all configs are returned - /// @return versionedConfigsWithDigests ordered from oldest to latest set - function getVersionedConfigsWithDigests( - uint256 offset, - uint256 limit - ) external view returns (VersionedConfigWithDigest[] memory versionedConfigsWithDigests) { - uint256[2] memory ignored; - uint256[2] memory accounted; - uint256 len; // clobbered by the end of the loop - for (uint256 act = 0; act <= 1; ++act) { - if (act == 1) { - len = accounted[0]; - versionedConfigsWithDigests = new VersionedConfigWithDigest[](len); - } + uint256 primaryConfigIndex = s_primaryConfigIndex; + if (s_configDigests[primaryConfigIndex] != digestToRevoke) { + revert ConfigDigestMismatch(s_configDigests[primaryConfigIndex], digestToRevoke); + } - uint256 i = s_latestConfigIndex; - do { - if (accounted[act] >= limit) { - break; - } + delete s_configDigests[primaryConfigIndex]; - if (s_configVersions[i] > 0) { - if (ignored[act] < offset) { - ++ignored[act]; - } else { - ++accounted[act]; - if (act == 1) { - versionedConfigsWithDigests[--len] = VersionedConfigWithDigest({ - configDigest: s_configDigests[i], - versionedConfig: VersionedConfig({version: s_configVersions[i], config: s_configs[i]}) - }); - } - } - } - i = i == 0 ? CONFIG_RING_BUFFER_SIZE - 1 : i - 1; - } while (i != s_latestConfigIndex); - } - return versionedConfigsWithDigests; + s_primaryConfigIndex ^= 1; + emit ConfigRevoked(digestToRevoke); } - /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use - /// in case one of the configs is too large to be returnable by one of the other getters. - /// @param configDigest The digest of the config to fetch. - /// @return versionedConfig The config and its version. - /// @return ok True if the config was found, false otherwise. - function getVersionedConfig( - bytes32 configDigest - ) external view returns (VersionedConfig memory versionedConfig, bool ok) { - for (uint256 i = 0; i < CONFIG_RING_BUFFER_SIZE; ++i) { - if (s_configVersions[i] > 0 && s_configDigests[i] == configDigest) { - return (VersionedConfig({version: s_configVersions[i], config: s_configs[i]}), true); - } - } - return (versionedConfig, false); + function _getConfigDigest(VersionedConfig memory versionedConfig) internal pure returns (bytes32) { + return bytes32((PREFIX & PREFIX_MASK) | (uint256(keccak256(abi.encode(versionedConfig))) & ~PREFIX_MASK)); } } diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index 144d0c833c..c9dbc87ab0 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -3,17 +3,16 @@ pragma solidity 0.8.24; import {Internal} from "../../libraries/Internal.sol"; import {RMNHome} from "../../rmn/RMNHome.sol"; -import {BaseTest} from "../BaseTest.t.sol"; +import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; -contract RMNHomeTest is BaseTest { +contract RMNHomeTest is Test { + bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); + RMNHome public s_rmnHome; - function setUp() public virtual override { - super.setUp(); + function setUp() public virtual { s_rmnHome = new RMNHome(); - - s_rmnHome.revokeAllConfigsButLatest(); } function _getBaseConfig() internal pure returns (RMNHome.Config memory) { @@ -39,8 +38,8 @@ contract RMNHomeTest is BaseTest { } } -contract RMNHome_setConfig is RMNHomeTest { - function test_setConfig_success() public { +contract RMNHome_setSecondary is RMNHomeTest { + function test_setSecondary_success() public { RMNHome.Config memory config = _getBaseConfig(); RMNHome.VersionedConfig memory versionedConfig = RMNHome.VersionedConfig({version: 1, config: config}); bytes32 configDigest = _getConfigDigest(versionedConfig); @@ -48,9 +47,9 @@ contract RMNHome_setConfig is RMNHomeTest { vm.expectEmit(); emit RMNHome.ConfigSet(configDigest, versionedConfig); - s_rmnHome.setConfig(config); + s_rmnHome.setSecondary(config, ZERO_DIGEST); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getVersionedConfig(configDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(configDigest); assertTrue(ok); assertEq(storedVersionedConfig.version, versionedConfig.version); assertEq(storedVersionedConfig.config.nodes.length, versionedConfig.config.nodes.length); @@ -70,106 +69,71 @@ contract RMNHome_setConfig is RMNHomeTest { assertEq(storedVersionedConfig.config.offchainConfig, versionedConfig.config.offchainConfig); } - function test_setConfig_moreThanRingBuffer_success() public { - RMNHome.Config memory config = _getBaseConfig(); - uint256 ringBufferSize = s_rmnHome.getRingBufferSize(); - - vm.recordLogs(); - - // Insert ring buffer size + 1 configs - for (uint256 i = 0; i < ringBufferSize + 1; i++) { - s_rmnHome.setConfig(config); - } - - Vm.Log[] memory entries = vm.getRecordedLogs(); - vm.assertEq(entries.length, ringBufferSize + 2); - - for (uint256 i = 0; i < ringBufferSize; i++) { - Vm.Log memory entry = entries[i]; - vm.assertEq(entry.topics[0], bytes32(uint256(RMNHome.ConfigSet.selector))); - } - - // The last config should revoke the first config, which means it should emit a ConfigRevoked event - vm.assertEq(entries[ringBufferSize].topics[0], bytes32(uint256(RMNHome.ConfigRevoked.selector))); - vm.assertEq(entries[ringBufferSize + 1].topics[0], bytes32(uint256(RMNHome.ConfigSet.selector))); - } - - function test_setConfig_OutOfBoundsNodesLength_reverts() public { + function test_setSecondary_OutOfBoundsNodesLength_reverts() public { RMNHome.Config memory config = _getBaseConfig(); config.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); - s_rmnHome.setConfig(config); + s_rmnHome.setSecondary(config, ZERO_DIGEST); } - function test_setConfig_DuplicatePeerId_reverts() public { + function test_setSecondary_DuplicatePeerId_reverts() public { RMNHome.Config memory config = _getBaseConfig(); config.nodes[1].peerId = config.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); - s_rmnHome.setConfig(config); + s_rmnHome.setSecondary(config, ZERO_DIGEST); } - function test_setConfig_DuplicateOffchainPublicKey_reverts() public { + function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { RMNHome.Config memory config = _getBaseConfig(); config.nodes[1].offchainPublicKey = config.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); - s_rmnHome.setConfig(config); + s_rmnHome.setSecondary(config, ZERO_DIGEST); } - function test_setConfig_DuplicateSourceChain_reverts() public { + function test_setSecondary_DuplicateSourceChain_reverts() public { RMNHome.Config memory config = _getBaseConfig(); config.sourceChains[1].chainSelector = config.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); - s_rmnHome.setConfig(config); + s_rmnHome.setSecondary(config, ZERO_DIGEST); } - function test_setConfig_OutOfBoundsObserverNodeIndex_reverts() public { + function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { RMNHome.Config memory config = _getBaseConfig(); config.sourceChains[0].observerNodesBitmap = 1 << config.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); - s_rmnHome.setConfig(config); + s_rmnHome.setSecondary(config, ZERO_DIGEST); } - function test_setConfig_MinObserversTooHigh_reverts() public { + function test_setSecondary_MinObserversTooHigh_reverts() public { RMNHome.Config memory config = _getBaseConfig(); config.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); - s_rmnHome.setConfig(config); + s_rmnHome.setSecondary(config, ZERO_DIGEST); } - function test_setConfig_OnlyOwner_reverts() public { + function test_setSecondary_OnlyOwner_reverts() public { RMNHome.Config memory config = _getBaseConfig(); vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setConfig(config); + s_rmnHome.setSecondary(config, ZERO_DIGEST); } } -contract RMNHome_revokeAllConfigsButLatest is RMNHomeTest { - function test_revokeAllConfigsButLatest_success() public {} - - function test_setConfig_OnlyOwner_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert("Only callable by owner"); - s_rmnHome.revokeAllConfigsButLatest(); - } -} - -contract RMNHome_revokeConfig is RMNHomeTest { - function test_revokeConfig_success() public {} - - function test_setConfig_OnlyOwner_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert("Only callable by owner"); - s_rmnHome.revokeConfig(keccak256("configDigest")); - } -} +//contract RMNHome_revokeConfig is RMNHomeTest { +// function test_revokeConfig_success() public {} +// +// function test_setSecondary_OnlyOwner_reverts() public { +// vm.startPrank(address(0)); +// +// vm.expectRevert("Only callable by owner"); +// s_rmnHome.revokeConfig(keccak256("configDigest")); +// } +//} From 606981c35ca57384cfaa2f5f27b09490be566b7e Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 19 Sep 2024 13:51:22 +0200 Subject: [PATCH 03/36] add dynamic/static --- contracts/src/v0.8/ccip/rmn/RMNHome.sol | 148 +++++++++++------- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 79 ++++++---- 2 files changed, 140 insertions(+), 87 deletions(-) diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/rmn/RMNHome.sol index cf277d536b..e41a73acfb 100644 --- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol +++ b/contracts/src/v0.8/ccip/rmn/RMNHome.sol @@ -14,11 +14,12 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { error DuplicateSourceChain(); error OutOfBoundsObserverNodeIndex(); error MinObserversTooHigh(); - error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); + error DigestNotFound(bytes32 configDigest); event ConfigSet(bytes32 configDigest, VersionedConfig versionedConfig); event ConfigRevoked(bytes32 configDigest); + event DynamicConfigSet(bytes32 indexed configDigest, DynamicConfig dynamicConfig); struct Node { bytes32 peerId; // Used for p2p communication. @@ -31,15 +32,24 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { uint256 observerNodesBitmap; // ObserverNodesBitmap & (1< 256) { - revert OutOfBoundsNodesLength(); - } - - // Ensure no peerId or offchainPublicKey is duplicated. - for (uint256 i = 0; i < newConfig.nodes.length; ++i) { - for (uint256 j = i + 1; j < newConfig.nodes.length; ++j) { - if (newConfig.nodes[i].peerId == newConfig.nodes[j].peerId) { - revert DuplicatePeerId(); - } - if (newConfig.nodes[i].offchainPublicKey == newConfig.nodes[j].offchainPublicKey) { - revert DuplicateOffchainPublicKey(); - } - } - } - - uint256 numberOfSourceChains = newConfig.sourceChains.length; - for (uint256 i = 0; i < numberOfSourceChains; ++i) { - // Ensure the source chain is unique. - for (uint256 j = i + 1; j < numberOfSourceChains; ++j) { - if (newConfig.sourceChains[i].chainSelector == newConfig.sourceChains[j].chainSelector) { - revert DuplicateSourceChain(); - } - } - - // all observer node indices are valid - uint256 bitmap = newConfig.sourceChains[i].observerNodesBitmap; - // Check if there are any bits set for indexes outside of the expected range. - if (bitmap & (type(uint256).max >> (256 - newConfig.nodes.length)) != bitmap) { - revert OutOfBoundsObserverNodeIndex(); - } - - uint256 observersCount = 0; - for (; bitmap != 0; ++observersCount) { - bitmap &= bitmap - 1; - } - - // minObservers are tenable - if (newConfig.sourceChains[i].minObservers > observersCount) { - revert MinObserversTooHigh(); - } - } - } + _validateStaticConfig(newConfig.staticConfig); + _validateDynamicConfig(newConfig.dynamicConfig, newConfig.staticConfig.nodes.length); uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; @@ -191,18 +157,35 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], digestToOverwrite); } - // are we going to overwrite a config? + // are we going to overwrite a config? If so, emit an event. if (digestToOverwrite != ZERO_DIGEST) { emit ConfigRevoked(digestToOverwrite); } - VersionedConfig memory newVersionedConfig = VersionedConfig({version: ++s_configCount, config: newConfig}); - bytes32 newConfigDigest = _getConfigDigest(newVersionedConfig); + uint32 newVersion = ++s_configCount; + bytes32 newConfigDigest = _getConfigDigest(newConfig.staticConfig, newVersion); s_configs[secondaryConfigIndex] = newConfig; - s_configVersions[secondaryConfigIndex] = newVersionedConfig.version; + s_configVersions[secondaryConfigIndex] = newVersion; s_configDigests[secondaryConfigIndex] = newConfigDigest; - emit ConfigSet(newConfigDigest, newVersionedConfig); + emit ConfigSet(newConfigDigest, VersionedConfig({version: newVersion, config: newConfig})); + } + + function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 digestToOverwrite) external onlyOwner { + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { + if (s_configDigests[i] == digestToOverwrite) { + Config memory currentConfig = s_configs[i]; + _validateDynamicConfig(newDynamicConfig, currentConfig.staticConfig.nodes.length); + + // Since the dynamic config doesn't change we don't have to update the digest or version. + s_configs[i].dynamicConfig = newDynamicConfig; + + emit DynamicConfigSet(digestToOverwrite, newDynamicConfig); + return; + } + } + + revert DigestNotFound(digestToOverwrite); } /// @notice Revokes a specific config by digest. @@ -249,7 +232,62 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { emit ConfigRevoked(digestToRevoke); } - function _getConfigDigest(VersionedConfig memory versionedConfig) internal pure returns (bytes32) { - return bytes32((PREFIX & PREFIX_MASK) | (uint256(keccak256(abi.encode(versionedConfig))) & ~PREFIX_MASK)); + function _validateStaticConfig(StaticConfig calldata newStaticConfig) internal pure { + // Ensure that observerNodesBitmap can be bit-encoded into a uint256. + if (newStaticConfig.nodes.length > 256) { + revert OutOfBoundsNodesLength(); + } + + // Ensure no peerId or offchainPublicKey is duplicated. + for (uint256 i = 0; i < newStaticConfig.nodes.length; ++i) { + for (uint256 j = i + 1; j < newStaticConfig.nodes.length; ++j) { + if (newStaticConfig.nodes[i].peerId == newStaticConfig.nodes[j].peerId) { + revert DuplicatePeerId(); + } + if (newStaticConfig.nodes[i].offchainPublicKey == newStaticConfig.nodes[j].offchainPublicKey) { + revert DuplicateOffchainPublicKey(); + } + } + } + } + + function _validateDynamicConfig(DynamicConfig calldata dynamicConfig, uint256 numberOfNodes) internal pure { + uint256 numberOfSourceChains = dynamicConfig.sourceChains.length; + for (uint256 i = 0; i < numberOfSourceChains; ++i) { + SourceChain memory currentSourceChain = dynamicConfig.sourceChains[i]; + // Ensure the source chain is unique. + for (uint256 j = i + 1; j < numberOfSourceChains; ++j) { + if (currentSourceChain.chainSelector == dynamicConfig.sourceChains[j].chainSelector) { + revert DuplicateSourceChain(); + } + } + + // all observer node indices are valid + uint256 bitmap = currentSourceChain.observerNodesBitmap; + // Check if there are any bits set for indexes outside of the expected range. + if (bitmap & (type(uint256).max >> (256 - numberOfNodes)) != bitmap) { + revert OutOfBoundsObserverNodeIndex(); + } + + uint256 observersCount = 0; + for (; bitmap != 0; ++observersCount) { + bitmap &= bitmap - 1; + } + + // minObservers are tenable + if (currentSourceChain.minObservers > observersCount) { + revert MinObserversTooHigh(); + } + } + } + + function _getConfigDigest(StaticConfig memory staticConfig, uint32 version) internal view returns (bytes32) { + return bytes32( + (PREFIX & PREFIX_MASK) + | ( + uint256(keccak256(abi.encode(bytes32("EVM"), block.chainid, address(this), version, staticConfig))) + & ~PREFIX_MASK + ) + ); } } diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index c9dbc87ab0..2d363caf5f 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -27,14 +27,23 @@ contract RMNHomeTest is Test { // Observers 1 and 2 for source chain 9001 sourceChains[1] = RMNHome.SourceChain({chainSelector: 9001, minObservers: 2, observerNodesBitmap: 1 << 1 | 1 << 2}); - return RMNHome.Config({nodes: nodes, sourceChains: sourceChains, offchainConfig: abi.encode("offchainConfig")}); + return RMNHome.Config({ + staticConfig: RMNHome.StaticConfig({nodes: nodes, offchainConfig: abi.encode("static_config")}), + dynamicConfig: RMNHome.DynamicConfig({sourceChains: sourceChains, offchainConfig: abi.encode("dynamic_config")}) + }); } uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 uint256 private constant PREFIX = 0x000b << (256 - 16); // 0x000b00..00 - function _getConfigDigest(RMNHome.VersionedConfig memory versionedConfig) internal pure returns (bytes32) { - return bytes32((PREFIX & PREFIX_MASK) | (uint256(keccak256(abi.encode(versionedConfig))) & ~PREFIX_MASK)); + function _getConfigDigest(RMNHome.StaticConfig memory staticConfig, uint32 version) internal view returns (bytes32) { + return bytes32( + (PREFIX & PREFIX_MASK) + | ( + uint256(keccak256(abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), version, staticConfig))) + & ~PREFIX_MASK + ) + ); } } @@ -42,7 +51,7 @@ contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_success() public { RMNHome.Config memory config = _getBaseConfig(); RMNHome.VersionedConfig memory versionedConfig = RMNHome.VersionedConfig({version: 1, config: config}); - bytes32 configDigest = _getConfigDigest(versionedConfig); + bytes32 configDigest = _getConfigDigest(versionedConfig.config.staticConfig, versionedConfig.version); vm.expectEmit(); emit RMNHome.ConfigSet(configDigest, versionedConfig); @@ -52,26 +61,32 @@ contract RMNHome_setSecondary is RMNHomeTest { (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(configDigest); assertTrue(ok); assertEq(storedVersionedConfig.version, versionedConfig.version); - assertEq(storedVersionedConfig.config.nodes.length, versionedConfig.config.nodes.length); - for (uint256 i = 0; i < storedVersionedConfig.config.nodes.length; i++) { - RMNHome.Node memory storedNode = storedVersionedConfig.config.nodes[i]; - assertEq(storedNode.peerId, versionedConfig.config.nodes[i].peerId); - assertEq(storedNode.offchainPublicKey, versionedConfig.config.nodes[i].offchainPublicKey); + RMNHome.StaticConfig memory storedStaticConfig = storedVersionedConfig.config.staticConfig; + RMNHome.DynamicConfig memory storedDynamicConfig = storedVersionedConfig.config.dynamicConfig; + + assertEq(storedStaticConfig.nodes.length, versionedConfig.config.staticConfig.nodes.length); + for (uint256 i = 0; i < storedStaticConfig.nodes.length; i++) { + RMNHome.Node memory storedNode = storedStaticConfig.nodes[i]; + assertEq(storedNode.peerId, versionedConfig.config.staticConfig.nodes[i].peerId); + assertEq(storedNode.offchainPublicKey, versionedConfig.config.staticConfig.nodes[i].offchainPublicKey); } - assertEq(storedVersionedConfig.config.sourceChains.length, versionedConfig.config.sourceChains.length); - for (uint256 i = 0; i < storedVersionedConfig.config.sourceChains.length; i++) { - RMNHome.SourceChain memory storedSourceChain = storedVersionedConfig.config.sourceChains[i]; - assertEq(storedSourceChain.chainSelector, versionedConfig.config.sourceChains[i].chainSelector); - assertEq(storedSourceChain.minObservers, versionedConfig.config.sourceChains[i].minObservers); - assertEq(storedSourceChain.observerNodesBitmap, versionedConfig.config.sourceChains[i].observerNodesBitmap); + assertEq(storedDynamicConfig.sourceChains.length, versionedConfig.config.dynamicConfig.sourceChains.length); + for (uint256 i = 0; i < storedDynamicConfig.sourceChains.length; i++) { + RMNHome.SourceChain memory storedSourceChain = storedDynamicConfig.sourceChains[i]; + assertEq(storedSourceChain.chainSelector, versionedConfig.config.dynamicConfig.sourceChains[i].chainSelector); + assertEq(storedSourceChain.minObservers, versionedConfig.config.dynamicConfig.sourceChains[i].minObservers); + assertEq( + storedSourceChain.observerNodesBitmap, versionedConfig.config.dynamicConfig.sourceChains[i].observerNodesBitmap + ); } - assertEq(storedVersionedConfig.config.offchainConfig, versionedConfig.config.offchainConfig); + assertEq(storedDynamicConfig.offchainConfig, versionedConfig.config.dynamicConfig.offchainConfig); + assertEq(storedStaticConfig.offchainConfig, versionedConfig.config.staticConfig.offchainConfig); } function test_setSecondary_OutOfBoundsNodesLength_reverts() public { RMNHome.Config memory config = _getBaseConfig(); - config.nodes = new RMNHome.Node[](257); + config.staticConfig.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); s_rmnHome.setSecondary(config, ZERO_DIGEST); @@ -79,7 +94,7 @@ contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_DuplicatePeerId_reverts() public { RMNHome.Config memory config = _getBaseConfig(); - config.nodes[1].peerId = config.nodes[0].peerId; + config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); s_rmnHome.setSecondary(config, ZERO_DIGEST); @@ -87,7 +102,7 @@ contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { RMNHome.Config memory config = _getBaseConfig(); - config.nodes[1].offchainPublicKey = config.nodes[0].offchainPublicKey; + config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); s_rmnHome.setSecondary(config, ZERO_DIGEST); @@ -95,7 +110,7 @@ contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_DuplicateSourceChain_reverts() public { RMNHome.Config memory config = _getBaseConfig(); - config.sourceChains[1].chainSelector = config.sourceChains[0].chainSelector; + config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); s_rmnHome.setSecondary(config, ZERO_DIGEST); @@ -103,7 +118,7 @@ contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { RMNHome.Config memory config = _getBaseConfig(); - config.sourceChains[0].observerNodesBitmap = 1 << config.nodes.length; + config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); s_rmnHome.setSecondary(config, ZERO_DIGEST); @@ -111,7 +126,7 @@ contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_MinObserversTooHigh_reverts() public { RMNHome.Config memory config = _getBaseConfig(); - config.sourceChains[0].minObservers++; + config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); s_rmnHome.setSecondary(config, ZERO_DIGEST); @@ -127,13 +142,13 @@ contract RMNHome_setSecondary is RMNHomeTest { } } -//contract RMNHome_revokeConfig is RMNHomeTest { -// function test_revokeConfig_success() public {} -// -// function test_setSecondary_OnlyOwner_reverts() public { -// vm.startPrank(address(0)); -// -// vm.expectRevert("Only callable by owner"); -// s_rmnHome.revokeConfig(keccak256("configDigest")); -// } -//} +contract RMNHome_revokeSecondary is RMNHomeTest { + function test_revokeSecondary_success() public {} + + function test_revokeSecondary_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.revokeSecondary(keccak256("configDigest")); + } +} From 36720d22080c88aaefc79697e4202447f022288e Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 19 Sep 2024 16:35:18 +0200 Subject: [PATCH 04/36] add more tests --- contracts/gas-snapshots/ccip.gas-snapshot | 20 +++ contracts/src/v0.8/ccip/rmn/RMNHome.sol | 20 ++- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 144 +++++++++++++++++- 3 files changed, 177 insertions(+), 7 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 9861ed236c..55a0471c09 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,6 +809,26 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) +RMNHome_promoteSecondary:test_promoteSecondary_ConfigDigestMismatch_reverts() (gas: 18909) +RMNHome_promoteSecondary:test_promoteSecondary_OnlyOwner_reverts() (gas: 10919) +RMNHome_promoteSecondary:test_promoteSecondary_success() (gas: 42700) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10935) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18865) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10943) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27886) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30074) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18798) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14093) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 108289) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16743) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16185) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22544) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22983) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15167) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73924) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22688) +RMNHome_setSecondary:test_setSecondary_success() (gas: 594559) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/rmn/RMNHome.sol index e41a73acfb..4b6d8ddda1 100644 --- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol +++ b/contracts/src/v0.8/ccip/rmn/RMNHome.sol @@ -20,6 +20,7 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { event ConfigSet(bytes32 configDigest, VersionedConfig versionedConfig); event ConfigRevoked(bytes32 configDigest); event DynamicConfigSet(bytes32 indexed configDigest, DynamicConfig dynamicConfig); + event ConfigPromoted(bytes32 configDigest); struct Node { bytes32 peerId; // Used for p2p communication. @@ -147,7 +148,10 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @param newConfig The new config to set. /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. /// This is done to prevent accidental overwrites. - function setSecondary(Config calldata newConfig, bytes32 digestToOverwrite) external onlyOwner { + function setSecondary( + Config calldata newConfig, + bytes32 digestToOverwrite + ) external onlyOwner returns (bytes32 newConfigDigest) { _validateStaticConfig(newConfig.staticConfig); _validateDynamicConfig(newConfig.dynamicConfig, newConfig.staticConfig.nodes.length); @@ -163,29 +167,31 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { } uint32 newVersion = ++s_configCount; - bytes32 newConfigDigest = _getConfigDigest(newConfig.staticConfig, newVersion); + newConfigDigest = _getConfigDigest(newConfig.staticConfig, newVersion); s_configs[secondaryConfigIndex] = newConfig; s_configVersions[secondaryConfigIndex] = newVersion; s_configDigests[secondaryConfigIndex] = newConfigDigest; emit ConfigSet(newConfigDigest, VersionedConfig({version: newVersion, config: newConfig})); + + return newConfigDigest; } - function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 digestToOverwrite) external onlyOwner { + function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 currentDigest) external onlyOwner { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - if (s_configDigests[i] == digestToOverwrite) { + if (s_configDigests[i] == currentDigest && currentDigest != ZERO_DIGEST) { Config memory currentConfig = s_configs[i]; _validateDynamicConfig(newDynamicConfig, currentConfig.staticConfig.nodes.length); // Since the dynamic config doesn't change we don't have to update the digest or version. s_configs[i].dynamicConfig = newDynamicConfig; - emit DynamicConfigSet(digestToOverwrite, newDynamicConfig); + emit DynamicConfigSet(currentDigest, newDynamicConfig); return; } } - revert DigestNotFound(digestToOverwrite); + revert DigestNotFound(currentDigest); } /// @notice Revokes a specific config by digest. @@ -212,6 +218,8 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { } s_primaryConfigIndex ^= 1; + + emit ConfigPromoted(digestToPromote); } /// @notice Promotes the secondary config to the primary config and revokes the primary config. diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index 2d363caf5f..078e37f517 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -142,8 +142,109 @@ contract RMNHome_setSecondary is RMNHomeTest { } } +contract RMNHome_setDynamicConfig is RMNHomeTest { + function setUp() public override { + super.setUp(); + s_rmnHome.setSecondary(_getBaseConfig(), ZERO_DIGEST); + } + + function test_setDynamicConfig_success() public { + (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(); + + RMNHome.Config memory config = _getBaseConfig(); + config.dynamicConfig.sourceChains[0].minObservers--; + + (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(); + + vm.expectEmit(); + emit RMNHome.DynamicConfigSet(secondaryConfigDigest, config.dynamicConfig); + + s_rmnHome.setDynamicConfig(config.dynamicConfig, secondaryConfigDigest); + + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); + assertTrue(ok); + assertEq( + storedVersionedConfig.config.dynamicConfig.sourceChains[0].minObservers, + config.dynamicConfig.sourceChains[0].minObservers + ); + + // Asser the digests don't change when updating the dynamic config + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); + assertEq(primaryDigest, priorPrimaryDigest); + assertEq(secondaryDigest, secondaryConfigDigest); + } + + // Asserts the validation function is being called + function test_setDynamicConfig_MinObserversTooHigh_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + config.dynamicConfig.sourceChains[0].minObservers++; + + vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, ZERO_DIGEST)); + s_rmnHome.setDynamicConfig(config.dynamicConfig, ZERO_DIGEST); + } + + function test_setDynamicConfig_DigestNotFound_reverts() public { + // Zero always reverts + vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, ZERO_DIGEST)); + s_rmnHome.setDynamicConfig(_getBaseConfig().dynamicConfig, ZERO_DIGEST); + + // Non-existent digest reverts + bytes32 nonExistentDigest = keccak256("nonExistentDigest"); + vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, nonExistentDigest)); + s_rmnHome.setDynamicConfig(_getBaseConfig().dynamicConfig, nonExistentDigest); + } + + function test_setDynamicConfig_OnlyOwner_reverts() public { + RMNHome.Config memory config = _getBaseConfig(); + + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.setDynamicConfig(config.dynamicConfig, keccak256("configDigest")); + } +} + contract RMNHome_revokeSecondary is RMNHomeTest { - function test_revokeSecondary_success() public {} + // Sets two configs + function setUp() public override { + super.setUp(); + bytes32 digest = s_rmnHome.setSecondary(_getBaseConfig(), ZERO_DIGEST); + s_rmnHome.promoteSecondary(digest); + + RMNHome.Config memory config = _getBaseConfig(); + config.dynamicConfig.sourceChains[0].minObservers--; + s_rmnHome.setSecondary(_getBaseConfig(), ZERO_DIGEST); + } + + function test_revokeSecondary_success() public { + (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); + + vm.expectEmit(); + emit RMNHome.ConfigRevoked(priorSecondaryDigest); + + s_rmnHome.revokeSecondary(priorSecondaryDigest); + + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorSecondaryDigest); + assertFalse(ok); + // Ensure no old data is returned, even though it's still in storage + assertEq(storedVersionedConfig.version, 0); + assertEq(storedVersionedConfig.config.staticConfig.nodes.length, 0); + assertEq(storedVersionedConfig.config.dynamicConfig.sourceChains.length, 0); + + // Asser the primary digest is unaffected but the secondary digest is set to zero + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); + assertEq(primaryDigest, priorPrimaryDigest); + assertTrue(secondaryDigest != priorSecondaryDigest); + assertEq(secondaryDigest, ZERO_DIGEST); + } + + function test_revokeSecondary_ConfigDigestMismatch_reverts() public { + (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); + + bytes32 wrongDigest = keccak256("wrong_digest"); + vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); + s_rmnHome.revokeSecondary(wrongDigest); + } function test_revokeSecondary_OnlyOwner_reverts() public { vm.startPrank(address(0)); @@ -152,3 +253,44 @@ contract RMNHome_revokeSecondary is RMNHomeTest { s_rmnHome.revokeSecondary(keccak256("configDigest")); } } + +contract RMNHome_promoteSecondary is RMNHomeTest { + function test_promoteSecondary_success() public { + (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); + + vm.expectEmit(); + emit RMNHome.ConfigPromoted(priorSecondaryDigest); + + s_rmnHome.promoteSecondary(priorSecondaryDigest); + + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); + assertEq(primaryDigest, priorSecondaryDigest); + assertEq(secondaryDigest, priorPrimaryDigest); + } + + function test_promoteSecondary_ConfigDigestMismatch_reverts() public { + (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); + + bytes32 wrongDigest = keccak256("wrong_digest"); + vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); + s_rmnHome.promoteSecondary(wrongDigest); + } + + function test_promoteSecondary_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.promoteSecondary(keccak256("configDigest")); + } +} + +contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { + function test_promoteSecondaryAndRevokePrimary_success() public {} + + function test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.promoteSecondaryAndRevokePrimary(keccak256("toPromote"), keccak256("ToRevoke")); + } +} From 21e8308f17b1b356b6639c37c1909c39e0a65405 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 10:21:57 +0200 Subject: [PATCH 05/36] rm promoteSecondary --- contracts/src/v0.8/ccip/rmn/RMNHome.sol | 13 -------- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 32 +------------------ 2 files changed, 1 insertion(+), 44 deletions(-) diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/rmn/RMNHome.sol index 4b6d8ddda1..525937ae56 100644 --- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol +++ b/contracts/src/v0.8/ccip/rmn/RMNHome.sol @@ -209,19 +209,6 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { delete s_configDigests[secondaryConfigIndex]; } - /// @notice Promotes the secondary config to the primary config. This demotes the primary to be the secondary but does - /// not revoke it. To revoke the primary, use `promoteSecondaryAndRevokePrimary` instead. - function promoteSecondary(bytes32 digestToPromote) external onlyOwner { - uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configDigests[secondaryConfigIndex] != digestToPromote) { - revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], digestToPromote); - } - - s_primaryConfigIndex ^= 1; - - emit ConfigPromoted(digestToPromote); - } - /// @notice Promotes the secondary config to the primary config and revokes the primary config. function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index 078e37f517..e7db94cf81 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -209,7 +209,7 @@ contract RMNHome_revokeSecondary is RMNHomeTest { function setUp() public override { super.setUp(); bytes32 digest = s_rmnHome.setSecondary(_getBaseConfig(), ZERO_DIGEST); - s_rmnHome.promoteSecondary(digest); + s_rmnHome.promoteSecondaryAndRevokePrimary(digest, ZERO_DIGEST); RMNHome.Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers--; @@ -254,36 +254,6 @@ contract RMNHome_revokeSecondary is RMNHomeTest { } } -contract RMNHome_promoteSecondary is RMNHomeTest { - function test_promoteSecondary_success() public { - (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); - - vm.expectEmit(); - emit RMNHome.ConfigPromoted(priorSecondaryDigest); - - s_rmnHome.promoteSecondary(priorSecondaryDigest); - - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); - assertEq(primaryDigest, priorSecondaryDigest); - assertEq(secondaryDigest, priorPrimaryDigest); - } - - function test_promoteSecondary_ConfigDigestMismatch_reverts() public { - (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); - - bytes32 wrongDigest = keccak256("wrong_digest"); - vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); - s_rmnHome.promoteSecondary(wrongDigest); - } - - function test_promoteSecondary_OnlyOwner_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert("Only callable by owner"); - s_rmnHome.promoteSecondary(keccak256("configDigest")); - } -} - contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { function test_promoteSecondaryAndRevokePrimary_success() public {} From 053aa99f0cb28e96ab0f6d64a3e036eea805485a Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 10:41:13 +0200 Subject: [PATCH 06/36] store bytes instead of struct --- contracts/gas-snapshots/ccip.gas-snapshot | 33 +++++------ contracts/src/v0.8/ccip/rmn/RMNHome.sol | 56 +++++++++++++++---- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 35 ++++++------ 3 files changed, 77 insertions(+), 47 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 55a0471c09..aa4a8e574c 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,26 +809,23 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) -RMNHome_promoteSecondary:test_promoteSecondary_ConfigDigestMismatch_reverts() (gas: 18909) -RMNHome_promoteSecondary:test_promoteSecondary_OnlyOwner_reverts() (gas: 10919) -RMNHome_promoteSecondary:test_promoteSecondary_success() (gas: 42700) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10935) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18865) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10943) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27886) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30074) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18798) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14093) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 108289) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16743) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16185) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22544) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22983) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15167) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73924) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22688) -RMNHome_setSecondary:test_setSecondary_success() (gas: 594559) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18887) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10965) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27654) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30118) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18820) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130433) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16704) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16146) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22527) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22966) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15128) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73885) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22671) +RMNHome_setSecondary:test_setSecondary_success() (gas: 821567) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/rmn/RMNHome.sol index 525937ae56..71dfec4b51 100644 --- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol +++ b/contracts/src/v0.8/ccip/rmn/RMNHome.sol @@ -51,9 +51,15 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { DynamicConfig dynamicConfig; } + struct StoredConfig { + bytes staticConfig; + bytes dynamicConfig; + } + struct VersionedConfig { uint32 version; // The version of this config, starting from 1 it increments with each new config. - Config config; + StaticConfig staticConfig; + DynamicConfig dynamicConfig; } struct VersionedConfigWithDigest { @@ -75,7 +81,7 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { bytes32[MAX_CONCURRENT_CONFIGS] private s_configDigests; /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configDigests[i] != 0. - Config[MAX_CONCURRENT_CONFIGS] private s_configs; + StoredConfig[MAX_CONCURRENT_CONFIGS] private s_configs; /// @notice This array holds the versions of the configs. /// @dev Value i in this array is valid iff s_configDigests[i] != 0. /// @dev Since Solidity doesn't support writing complex memory structs to storage, we have to make the config calldata @@ -106,7 +112,15 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old // config state that is invalid. if (s_configDigests[i] == configDigest && configDigest != ZERO_DIGEST) { - return (VersionedConfig({config: s_configs[i], version: s_configVersions[i]}), true); + StoredConfig memory config = s_configs[i]; + return ( + VersionedConfig({ + staticConfig: abi.decode(config.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)), + version: s_configVersions[i] + }), + true + ); } } return (versionedConfig, false); @@ -122,10 +136,16 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { uint256 primaryConfigIndex = s_primaryConfigIndex; bytes32 primaryConfigDigest = s_configDigests[primaryConfigIndex]; if (primaryConfigDigest != ZERO_DIGEST) { + StoredConfig memory config = s_configs[primaryConfigIndex]; + primaryConfig = VersionedConfigWithDigest({ configDigest: primaryConfigDigest, versionedConfig: ( - VersionedConfig({config: s_configs[primaryConfigIndex], version: s_configVersions[primaryConfigIndex]}) + VersionedConfig({ + staticConfig: abi.decode(config.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)), + version: s_configVersions[primaryConfigIndex] + }) ) }); } @@ -133,10 +153,16 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { uint256 secondaryConfigIndex = primaryConfigIndex ^ 1; bytes32 secondaryConfigDigest = s_configDigests[secondaryConfigIndex]; if (secondaryConfigDigest != ZERO_DIGEST) { + StoredConfig memory config = s_configs[secondaryConfigIndex]; + secondaryConfig = VersionedConfigWithDigest({ configDigest: secondaryConfigDigest, versionedConfig: ( - VersionedConfig({config: s_configs[secondaryConfigIndex], version: s_configVersions[secondaryConfigIndex]}) + VersionedConfig({ + staticConfig: abi.decode(config.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)), + version: s_configVersions[secondaryConfigIndex] + }) ) }); } @@ -168,11 +194,19 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { uint32 newVersion = ++s_configCount; newConfigDigest = _getConfigDigest(newConfig.staticConfig, newVersion); - s_configs[secondaryConfigIndex] = newConfig; + s_configs[secondaryConfigIndex] = + StoredConfig(abi.encode(newConfig.staticConfig), abi.encode(newConfig.dynamicConfig)); s_configVersions[secondaryConfigIndex] = newVersion; s_configDigests[secondaryConfigIndex] = newConfigDigest; - emit ConfigSet(newConfigDigest, VersionedConfig({version: newVersion, config: newConfig})); + emit ConfigSet( + newConfigDigest, + VersionedConfig({ + version: newVersion, + staticConfig: newConfig.staticConfig, + dynamicConfig: newConfig.dynamicConfig + }) + ); return newConfigDigest; } @@ -180,11 +214,11 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 currentDigest) external onlyOwner { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { if (s_configDigests[i] == currentDigest && currentDigest != ZERO_DIGEST) { - Config memory currentConfig = s_configs[i]; - _validateDynamicConfig(newDynamicConfig, currentConfig.staticConfig.nodes.length); + StaticConfig memory staticConfig = abi.decode(s_configs[i].staticConfig, (StaticConfig)); + _validateDynamicConfig(newDynamicConfig, staticConfig.nodes.length); - // Since the dynamic config doesn't change we don't have to update the digest or version. - s_configs[i].dynamicConfig = newDynamicConfig; + // Since the static config doesn't change we don't have to update the digest or version. + s_configs[i].dynamicConfig = abi.encode(newDynamicConfig); emit DynamicConfigSet(currentDigest, newDynamicConfig); return; diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index e7db94cf81..e02347662d 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -50,8 +50,9 @@ contract RMNHomeTest is Test { contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_success() public { RMNHome.Config memory config = _getBaseConfig(); - RMNHome.VersionedConfig memory versionedConfig = RMNHome.VersionedConfig({version: 1, config: config}); - bytes32 configDigest = _getConfigDigest(versionedConfig.config.staticConfig, versionedConfig.version); + RMNHome.VersionedConfig memory versionedConfig = + RMNHome.VersionedConfig({version: 1, staticConfig: config.staticConfig, dynamicConfig: config.dynamicConfig}); + bytes32 configDigest = _getConfigDigest(versionedConfig.staticConfig, versionedConfig.version); vm.expectEmit(); emit RMNHome.ConfigSet(configDigest, versionedConfig); @@ -61,27 +62,25 @@ contract RMNHome_setSecondary is RMNHomeTest { (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(configDigest); assertTrue(ok); assertEq(storedVersionedConfig.version, versionedConfig.version); - RMNHome.StaticConfig memory storedStaticConfig = storedVersionedConfig.config.staticConfig; - RMNHome.DynamicConfig memory storedDynamicConfig = storedVersionedConfig.config.dynamicConfig; + RMNHome.StaticConfig memory storedStaticConfig = storedVersionedConfig.staticConfig; + RMNHome.DynamicConfig memory storedDynamicConfig = storedVersionedConfig.dynamicConfig; - assertEq(storedStaticConfig.nodes.length, versionedConfig.config.staticConfig.nodes.length); + assertEq(storedStaticConfig.nodes.length, versionedConfig.staticConfig.nodes.length); for (uint256 i = 0; i < storedStaticConfig.nodes.length; i++) { RMNHome.Node memory storedNode = storedStaticConfig.nodes[i]; - assertEq(storedNode.peerId, versionedConfig.config.staticConfig.nodes[i].peerId); - assertEq(storedNode.offchainPublicKey, versionedConfig.config.staticConfig.nodes[i].offchainPublicKey); + assertEq(storedNode.peerId, versionedConfig.staticConfig.nodes[i].peerId); + assertEq(storedNode.offchainPublicKey, versionedConfig.staticConfig.nodes[i].offchainPublicKey); } - assertEq(storedDynamicConfig.sourceChains.length, versionedConfig.config.dynamicConfig.sourceChains.length); + assertEq(storedDynamicConfig.sourceChains.length, versionedConfig.dynamicConfig.sourceChains.length); for (uint256 i = 0; i < storedDynamicConfig.sourceChains.length; i++) { RMNHome.SourceChain memory storedSourceChain = storedDynamicConfig.sourceChains[i]; - assertEq(storedSourceChain.chainSelector, versionedConfig.config.dynamicConfig.sourceChains[i].chainSelector); - assertEq(storedSourceChain.minObservers, versionedConfig.config.dynamicConfig.sourceChains[i].minObservers); - assertEq( - storedSourceChain.observerNodesBitmap, versionedConfig.config.dynamicConfig.sourceChains[i].observerNodesBitmap - ); + assertEq(storedSourceChain.chainSelector, versionedConfig.dynamicConfig.sourceChains[i].chainSelector); + assertEq(storedSourceChain.minObservers, versionedConfig.dynamicConfig.sourceChains[i].minObservers); + assertEq(storedSourceChain.observerNodesBitmap, versionedConfig.dynamicConfig.sourceChains[i].observerNodesBitmap); } - assertEq(storedDynamicConfig.offchainConfig, versionedConfig.config.dynamicConfig.offchainConfig); - assertEq(storedStaticConfig.offchainConfig, versionedConfig.config.staticConfig.offchainConfig); + assertEq(storedDynamicConfig.offchainConfig, versionedConfig.dynamicConfig.offchainConfig); + assertEq(storedStaticConfig.offchainConfig, versionedConfig.staticConfig.offchainConfig); } function test_setSecondary_OutOfBoundsNodesLength_reverts() public { @@ -164,7 +163,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); assertTrue(ok); assertEq( - storedVersionedConfig.config.dynamicConfig.sourceChains[0].minObservers, + storedVersionedConfig.dynamicConfig.sourceChains[0].minObservers, config.dynamicConfig.sourceChains[0].minObservers ); @@ -228,8 +227,8 @@ contract RMNHome_revokeSecondary is RMNHomeTest { assertFalse(ok); // Ensure no old data is returned, even though it's still in storage assertEq(storedVersionedConfig.version, 0); - assertEq(storedVersionedConfig.config.staticConfig.nodes.length, 0); - assertEq(storedVersionedConfig.config.dynamicConfig.sourceChains.length, 0); + assertEq(storedVersionedConfig.staticConfig.nodes.length, 0); + assertEq(storedVersionedConfig.dynamicConfig.sourceChains.length, 0); // Asser the primary digest is unaffected but the secondary digest is set to zero (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); From e5814d0aed58b789d6f9baf8820b4e095a17374a Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 10:53:42 +0200 Subject: [PATCH 07/36] extract HomeBase --- contracts/gas-snapshots/ccip.gas-snapshot | 6 +- .../src/v0.8/ccip/capability/HomeBase.sol | 82 +++++++++++ .../v0.8/ccip/{rmn => capability}/RMNHome.sol | 130 ++++-------------- .../src/v0.8/ccip/test/rmn/RMNHome.t.sol | 2 +- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 21 +-- 5 files changed, 122 insertions(+), 119 deletions(-) create mode 100644 contracts/src/v0.8/ccip/capability/HomeBase.sol rename contracts/src/v0.8/ccip/{rmn => capability}/RMNHome.sol (64%) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index aa4a8e574c..d16bd452b5 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -813,11 +813,11 @@ RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_O RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18887) RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10965) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27654) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27741) RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30118) RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18820) RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130433) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130540) RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16704) RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16146) RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22527) @@ -825,7 +825,7 @@ RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22966 RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15128) RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73885) RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22671) -RMNHome_setSecondary:test_setSecondary_success() (gas: 821567) +RMNHome_setSecondary:test_setSecondary_success() (gas: 821725) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol new file mode 100644 index 0000000000..57e8010787 --- /dev/null +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; + +import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; + +abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { + event ConfigRevoked(bytes32 configDigest); + + error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); + + /// @notice The max number of configs that can be active at the same time. + uint256 internal constant MAX_CONCURRENT_CONFIGS = 2; + /// @notice Used for encoding the config digest prefix + uint256 internal constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 + bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); + + /// @notice This array holds the digests of the configs, used for efficiency. + /// @dev Value i in this array is valid iff it's not 0. + bytes32[MAX_CONCURRENT_CONFIGS] internal s_configDigests; + /// @notice This array holds the configs. + /// @dev Value i in this array is valid iff s_configDigests[i] != 0. + StoredConfig[MAX_CONCURRENT_CONFIGS] internal s_configs; + /// @notice This array holds the versions of the configs. + /// @dev Value i in this array is valid iff s_configDigests[i] != 0. + /// @dev Since Solidity doesn't support writing complex memory structs to storage, we have to make the config calldata + /// in setConfig and then copy it to storage. This does not allow us to modify it to add the version field, so we + /// store the version separately. + uint32[MAX_CONCURRENT_CONFIGS] internal s_configVersions; + + /// @notice The total number of configs ever set, used for generating the version of the configs. + uint32 internal s_configCount = 0; + /// @notice The index of the primary config. + uint32 internal s_primaryConfigIndex = 0; + + struct StoredConfig { + bytes staticConfig; + bytes dynamicConfig; + } + + /// @notice Returns the current primary and secondary config digests. + /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. + /// @return primaryConfigDigest The digest of the primary config. + /// @return secondaryConfigDigest The digest of the secondary config. + function getConfigDigests() external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + return (s_configDigests[s_primaryConfigIndex], s_configDigests[s_primaryConfigIndex ^ 1]); + } + + /// @notice Revokes a specific config by digest. + /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. + function revokeSecondary(bytes32 configDigest) external onlyOwner { + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configDigests[secondaryConfigIndex] != configDigest) { + revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], configDigest); + } + + emit ConfigRevoked(configDigest); + // Delete only the digest, as that's what's used to determine if a config is active. This means the actual + // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in + // the future. + delete s_configDigests[secondaryConfigIndex]; + } + + /// @notice Promotes the secondary config to the primary config and revokes the primary config. + function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configDigests[secondaryConfigIndex] != digestToPromote) { + revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], digestToPromote); + } + + uint256 primaryConfigIndex = s_primaryConfigIndex; + if (s_configDigests[primaryConfigIndex] != digestToRevoke) { + revert ConfigDigestMismatch(s_configDigests[primaryConfigIndex], digestToRevoke); + } + + delete s_configDigests[primaryConfigIndex]; + + s_primaryConfigIndex ^= 1; + emit ConfigRevoked(digestToRevoke); + } +} diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol similarity index 64% rename from contracts/src/v0.8/ccip/rmn/RMNHome.sol rename to contracts/src/v0.8/ccip/capability/RMNHome.sol index 71dfec4b51..2e85c9d764 100644 --- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -1,24 +1,20 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; - -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; +import {HomeBase} from "./HomeBase.sol"; /// @notice Stores the home configuration for RMN, that is referenced by CCIP oracles, RMN nodes, and the RMNRemote /// contracts. -contract RMNHome is OwnerIsCreator, ITypeAndVersion { +contract RMNHome is HomeBase { error OutOfBoundsNodesLength(); error DuplicatePeerId(); error DuplicateOffchainPublicKey(); error DuplicateSourceChain(); error OutOfBoundsObserverNodeIndex(); error MinObserversTooHigh(); - error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); error DigestNotFound(bytes32 configDigest); - event ConfigSet(bytes32 configDigest, VersionedConfig versionedConfig); - event ConfigRevoked(bytes32 configDigest); + event ConfigSet(VersionedConfig versionedConfig); event DynamicConfigSet(bytes32 indexed configDigest, DynamicConfig dynamicConfig); event ConfigPromoted(bytes32 configDigest); @@ -27,12 +23,6 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { bytes32 offchainPublicKey; // Observations are signed with this public key, and are only verified offchain. } - struct SourceChain { - uint64 chainSelector; // ─────╮ The Source chain selector. - uint64 minObservers; // ──────╯ Required number of observers to agree on an observation for this source chain. - uint256 observerNodesBitmap; // ObserverNodesBitmap & (1< 256) { diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol index 40cb1e4a07..e1fe3c7dc4 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; +import {RMNHome} from "../../capability/RMNHome.sol"; import {Internal} from "../../libraries/Internal.sol"; -import {RMNHome} from "../../rmn/RMNHome.sol"; import {BaseTest} from "../BaseTest.t.sol"; contract RMNHomeTest is BaseTest { diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index e02347662d..330ff716fb 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; +import {HomeBase} from "../../capability/HomeBase.sol"; +import {RMNHome} from "../../capability/RMNHome.sol"; import {Internal} from "../../libraries/Internal.sol"; -import {RMNHome} from "../../rmn/RMNHome.sol"; import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; @@ -50,16 +51,20 @@ contract RMNHomeTest is Test { contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_success() public { RMNHome.Config memory config = _getBaseConfig(); - RMNHome.VersionedConfig memory versionedConfig = - RMNHome.VersionedConfig({version: 1, staticConfig: config.staticConfig, dynamicConfig: config.dynamicConfig}); - bytes32 configDigest = _getConfigDigest(versionedConfig.staticConfig, versionedConfig.version); + RMNHome.VersionedConfig memory versionedConfig = RMNHome.VersionedConfig({ + version: 1, + staticConfig: config.staticConfig, + dynamicConfig: config.dynamicConfig, + configDigest: ZERO_DIGEST + }); + versionedConfig.configDigest = _getConfigDigest(versionedConfig.staticConfig, versionedConfig.version); vm.expectEmit(); - emit RMNHome.ConfigSet(configDigest, versionedConfig); + emit RMNHome.ConfigSet(versionedConfig); s_rmnHome.setSecondary(config, ZERO_DIGEST); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(configDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(versionedConfig.configDigest); assertTrue(ok); assertEq(storedVersionedConfig.version, versionedConfig.version); RMNHome.StaticConfig memory storedStaticConfig = storedVersionedConfig.staticConfig; @@ -219,7 +224,7 @@ contract RMNHome_revokeSecondary is RMNHomeTest { (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); vm.expectEmit(); - emit RMNHome.ConfigRevoked(priorSecondaryDigest); + emit HomeBase.ConfigRevoked(priorSecondaryDigest); s_rmnHome.revokeSecondary(priorSecondaryDigest); @@ -241,7 +246,7 @@ contract RMNHome_revokeSecondary is RMNHomeTest { (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); bytes32 wrongDigest = keccak256("wrong_digest"); - vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); + vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); s_rmnHome.revokeSecondary(wrongDigest); } From 3992dac4ee50edf1f9eafa544ed3e80ca76e8ae7 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 10:59:59 +0200 Subject: [PATCH 08/36] extract digest calc --- contracts/gas-snapshots/ccip.gas-snapshot | 6 +++--- contracts/src/v0.8/ccip/capability/HomeBase.sol | 15 +++++++++++++++ contracts/src/v0.8/ccip/capability/RMNHome.sol | 16 +++------------- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 9 +++++---- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index d16bd452b5..3a09cb2a0e 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -813,11 +813,11 @@ RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_O RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18887) RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10965) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27741) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27751) RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30118) RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18820) RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130540) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130562) RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16704) RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16146) RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22527) @@ -825,7 +825,7 @@ RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22966 RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15128) RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73885) RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22671) -RMNHome_setSecondary:test_setSecondary_success() (gas: 821725) +RMNHome_setSecondary:test_setSecondary_success() (gas: 822090) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 57e8010787..f3ba9290f7 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -79,4 +79,19 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { s_primaryConfigIndex ^= 1; emit ConfigRevoked(digestToRevoke); } + + function _calculateConfigDigest( + bytes memory staticConfig, + uint32 version, + uint256 prefix + ) internal view returns (bytes32) { + return bytes32( + (prefix & PREFIX_MASK) + | ( + uint256( + keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), version), staticConfig)) + ) & ~PREFIX_MASK + ) + ); + } } diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index 2e85c9d764..f0b453e427 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -142,9 +142,9 @@ contract RMNHome is HomeBase { } uint32 newVersion = ++s_configCount; - newConfigDigest = _getConfigDigest(newConfig.staticConfig, newVersion); - s_configs[secondaryConfigIndex] = - StoredConfig(abi.encode(newConfig.staticConfig), abi.encode(newConfig.dynamicConfig)); + bytes memory encodedStaticConfig = abi.encode(newConfig.staticConfig); + newConfigDigest = _calculateConfigDigest(encodedStaticConfig, newVersion, PREFIX); + s_configs[secondaryConfigIndex] = StoredConfig(encodedStaticConfig, abi.encode(newConfig.dynamicConfig)); s_configVersions[secondaryConfigIndex] = newVersion; s_configDigests[secondaryConfigIndex] = newConfigDigest; @@ -225,14 +225,4 @@ contract RMNHome is HomeBase { } } } - - function _getConfigDigest(StaticConfig memory staticConfig, uint32 version) internal view returns (bytes32) { - return bytes32( - (PREFIX & PREFIX_MASK) - | ( - uint256(keccak256(abi.encode(bytes32("EVM"), block.chainid, address(this), version, staticConfig))) - & ~PREFIX_MASK - ) - ); - } } diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index 330ff716fb..2846cd921e 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -37,12 +37,13 @@ contract RMNHomeTest is Test { uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 uint256 private constant PREFIX = 0x000b << (256 - 16); // 0x000b00..00 - function _getConfigDigest(RMNHome.StaticConfig memory staticConfig, uint32 version) internal view returns (bytes32) { + function _getConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { return bytes32( (PREFIX & PREFIX_MASK) | ( - uint256(keccak256(abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), version, staticConfig))) - & ~PREFIX_MASK + uint256( + keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), version), staticConfig)) + ) & ~PREFIX_MASK ) ); } @@ -57,7 +58,7 @@ contract RMNHome_setSecondary is RMNHomeTest { dynamicConfig: config.dynamicConfig, configDigest: ZERO_DIGEST }); - versionedConfig.configDigest = _getConfigDigest(versionedConfig.staticConfig, versionedConfig.version); + versionedConfig.configDigest = _getConfigDigest(abi.encode(versionedConfig.staticConfig), versionedConfig.version); vm.expectEmit(); emit RMNHome.ConfigSet(versionedConfig); From 7ddbf721399f19daccc00081dafca4953ca91c45 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 11:07:05 +0200 Subject: [PATCH 09/36] add version to storedConfig --- contracts/gas-snapshots/ccip.gas-snapshot | 4 ++-- contracts/src/v0.8/ccip/capability/HomeBase.sol | 7 +------ contracts/src/v0.8/ccip/capability/RMNHome.sol | 13 ++++++++----- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 3a09cb2a0e..cb3b2cd031 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -817,7 +817,7 @@ RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27751) RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30118) RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18820) RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130562) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130480) RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16704) RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16146) RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22527) @@ -825,7 +825,7 @@ RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22966 RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15128) RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73885) RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22671) -RMNHome_setSecondary:test_setSecondary_success() (gas: 822090) +RMNHome_setSecondary:test_setSecondary_success() (gas: 821908) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index f3ba9290f7..9703a193bd 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -22,12 +22,6 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configDigests[i] != 0. StoredConfig[MAX_CONCURRENT_CONFIGS] internal s_configs; - /// @notice This array holds the versions of the configs. - /// @dev Value i in this array is valid iff s_configDigests[i] != 0. - /// @dev Since Solidity doesn't support writing complex memory structs to storage, we have to make the config calldata - /// in setConfig and then copy it to storage. This does not allow us to modify it to add the version field, so we - /// store the version separately. - uint32[MAX_CONCURRENT_CONFIGS] internal s_configVersions; /// @notice The total number of configs ever set, used for generating the version of the configs. uint32 internal s_configCount = 0; @@ -35,6 +29,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { uint32 internal s_primaryConfigIndex = 0; struct StoredConfig { + uint32 version; bytes staticConfig; bytes dynamicConfig; } diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index f0b453e427..1b6745c101 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -71,7 +71,7 @@ contract RMNHome is HomeBase { StoredConfig memory config = s_configs[i]; return ( VersionedConfig({ - version: s_configVersions[i], + version: config.version, configDigest: configDigest, staticConfig: abi.decode(config.staticConfig, (StaticConfig)), dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)) @@ -96,7 +96,7 @@ contract RMNHome is HomeBase { StoredConfig memory config = s_configs[primaryConfigIndex]; primaryConfig = VersionedConfig({ - version: s_configVersions[primaryConfigIndex], + version: config.version, configDigest: primaryConfigDigest, staticConfig: abi.decode(config.staticConfig, (StaticConfig)), dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)) @@ -109,7 +109,7 @@ contract RMNHome is HomeBase { StoredConfig memory config = s_configs[secondaryConfigIndex]; secondaryConfig = VersionedConfig({ - version: s_configVersions[secondaryConfigIndex], + version: config.version, configDigest: secondaryConfigDigest, staticConfig: abi.decode(config.staticConfig, (StaticConfig)), dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)) @@ -144,8 +144,11 @@ contract RMNHome is HomeBase { uint32 newVersion = ++s_configCount; bytes memory encodedStaticConfig = abi.encode(newConfig.staticConfig); newConfigDigest = _calculateConfigDigest(encodedStaticConfig, newVersion, PREFIX); - s_configs[secondaryConfigIndex] = StoredConfig(encodedStaticConfig, abi.encode(newConfig.dynamicConfig)); - s_configVersions[secondaryConfigIndex] = newVersion; + s_configs[secondaryConfigIndex] = StoredConfig({ + version: newVersion, + staticConfig: encodedStaticConfig, + dynamicConfig: abi.encode(newConfig.dynamicConfig) + }); s_configDigests[secondaryConfigIndex] = newConfigDigest; emit ConfigSet( From 451a2fc904dcc117d70b445b387b602cb4b15c3e Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 11:16:51 +0200 Subject: [PATCH 10/36] add getters for configs in base --- contracts/gas-snapshots/ccip.gas-snapshot | 6 ++--- .../src/v0.8/ccip/capability/HomeBase.sol | 26 ++++++++++++++++-- .../src/v0.8/ccip/capability/RMNHome.sol | 27 +++++++++---------- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index cb3b2cd031..a5a92354f2 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -813,11 +813,11 @@ RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_O RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18887) RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10965) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27751) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27935) RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30118) RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18820) RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130480) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130686) RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16704) RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16146) RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22527) @@ -825,7 +825,7 @@ RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22966 RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15128) RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73885) RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22671) -RMNHome_setSecondary:test_setSecondary_success() (gas: 821908) +RMNHome_setSecondary:test_setSecondary_success() (gas: 822114) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 9703a193bd..50ee3573b6 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -10,10 +10,11 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); + /// @notice Used for encoding the config digest prefix + uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 /// @notice The max number of configs that can be active at the same time. uint256 internal constant MAX_CONCURRENT_CONFIGS = 2; - /// @notice Used for encoding the config digest prefix - uint256 internal constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 + /// @notice Helper to identify the zero config digest with less casting. bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); /// @notice This array holds the digests of the configs, used for efficiency. @@ -34,6 +35,27 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { bytes dynamicConfig; } + /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero + /// digest. This is done to prevent exposing old config state that is invalid. + function _getStoredConfig(bytes32 configDigest) internal view returns (StoredConfig memory storedConfig, bool ok) { + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { + // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old + // config state that is invalid. + if (s_configDigests[i] == configDigest && configDigest != ZERO_DIGEST) { + return (s_configs[i], true); + } + } + return (storedConfig, false); + } + + function _getPrimaryStoredConfig() internal view returns (StoredConfig memory primaryConfig) { + return s_configs[s_primaryConfigIndex]; + } + + function _getSecondaryStoredConfig() internal view returns (StoredConfig memory secondaryConfig) { + return s_configs[s_primaryConfigIndex ^ 1]; + } + /// @notice Returns the current primary and secondary config digests. /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. /// @return primaryConfigDigest The digest of the primary config. diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index 1b6745c101..3842d426cc 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -64,22 +64,19 @@ contract RMNHome is HomeBase { /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { - for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old - // config state that is invalid. - if (s_configDigests[i] == configDigest && configDigest != ZERO_DIGEST) { - StoredConfig memory config = s_configs[i]; - return ( - VersionedConfig({ - version: config.version, - configDigest: configDigest, - staticConfig: abi.decode(config.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)) - }), - true - ); - } + (StoredConfig memory storedConfig, bool ok) = _getStoredConfig(configDigest); + if (ok) { + return ( + VersionedConfig({ + version: storedConfig.version, + configDigest: configDigest, + staticConfig: abi.decode(storedConfig.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(storedConfig.dynamicConfig, (DynamicConfig)) + }), + true + ); } + return (versionedConfig, false); } From 3647d2506a6aa7dda5e73400891ca81fc06920d7 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 11:28:33 +0200 Subject: [PATCH 11/36] have getters use base getters --- contracts/gas-snapshots/ccip.gas-snapshot | 12 ++--- .../src/v0.8/ccip/capability/HomeBase.sol | 42 ++++++++++-------- .../src/v0.8/ccip/capability/RMNHome.sol | 44 ++++++++----------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index a5a92354f2..401a3080aa 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -811,13 +811,13 @@ PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10935) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18887) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18928) RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10965) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27935) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30118) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18820) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28018) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30150) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18836) RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130686) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130916) RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16704) RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16146) RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22527) @@ -825,7 +825,7 @@ RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22966 RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15128) RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73885) RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22671) -RMNHome_setSecondary:test_setSecondary_success() (gas: 822114) +RMNHome_setSecondary:test_setSecondary_success() (gas: 822289) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 50ee3573b6..091cd303b8 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -17,11 +17,8 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @notice Helper to identify the zero config digest with less casting. bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); - /// @notice This array holds the digests of the configs, used for efficiency. - /// @dev Value i in this array is valid iff it's not 0. - bytes32[MAX_CONCURRENT_CONFIGS] internal s_configDigests; /// @notice This array holds the configs. - /// @dev Value i in this array is valid iff s_configDigests[i] != 0. + /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. StoredConfig[MAX_CONCURRENT_CONFIGS] internal s_configs; /// @notice The total number of configs ever set, used for generating the version of the configs. @@ -30,6 +27,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { uint32 internal s_primaryConfigIndex = 0; struct StoredConfig { + bytes32 configDigest; uint32 version; bytes staticConfig; bytes dynamicConfig; @@ -41,19 +39,27 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old // config state that is invalid. - if (s_configDigests[i] == configDigest && configDigest != ZERO_DIGEST) { + if (s_configs[i].configDigest == configDigest && configDigest != ZERO_DIGEST) { return (s_configs[i], true); } } return (storedConfig, false); } - function _getPrimaryStoredConfig() internal view returns (StoredConfig memory primaryConfig) { - return s_configs[s_primaryConfigIndex]; + function _getPrimaryStoredConfig() internal view returns (StoredConfig memory primaryConfig, bool ok) { + if (s_configs[s_primaryConfigIndex].configDigest == ZERO_DIGEST) { + return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); + } + + return (s_configs[s_primaryConfigIndex], true); } - function _getSecondaryStoredConfig() internal view returns (StoredConfig memory secondaryConfig) { - return s_configs[s_primaryConfigIndex ^ 1]; + function _getSecondaryStoredConfig() internal view returns (StoredConfig memory secondaryConfig, bool ok) { + if (s_configs[s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { + return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); + } + + return (s_configs[s_primaryConfigIndex ^ 1], true); } /// @notice Returns the current primary and secondary config digests. @@ -61,37 +67,37 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @return primaryConfigDigest The digest of the primary config. /// @return secondaryConfigDigest The digest of the secondary config. function getConfigDigests() external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { - return (s_configDigests[s_primaryConfigIndex], s_configDigests[s_primaryConfigIndex ^ 1]); + return (s_configs[s_primaryConfigIndex].configDigest, s_configs[s_primaryConfigIndex ^ 1].configDigest); } /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. function revokeSecondary(bytes32 configDigest) external onlyOwner { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configDigests[secondaryConfigIndex] != configDigest) { - revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], configDigest); + if (s_configs[secondaryConfigIndex].configDigest != configDigest) { + revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, configDigest); } emit ConfigRevoked(configDigest); // Delete only the digest, as that's what's used to determine if a config is active. This means the actual // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in // the future. - delete s_configDigests[secondaryConfigIndex]; + delete s_configs[secondaryConfigIndex].configDigest; } /// @notice Promotes the secondary config to the primary config and revokes the primary config. function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configDigests[secondaryConfigIndex] != digestToPromote) { - revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], digestToPromote); + if (s_configs[secondaryConfigIndex].configDigest != digestToPromote) { + revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToPromote); } uint256 primaryConfigIndex = s_primaryConfigIndex; - if (s_configDigests[primaryConfigIndex] != digestToRevoke) { - revert ConfigDigestMismatch(s_configDigests[primaryConfigIndex], digestToRevoke); + if (s_configs[primaryConfigIndex].configDigest != digestToRevoke) { + revert ConfigDigestMismatch(s_configs[primaryConfigIndex].configDigest, digestToRevoke); } - delete s_configDigests[primaryConfigIndex]; + delete s_configs[primaryConfigIndex].configDigest; s_primaryConfigIndex ^= 1; emit ConfigRevoked(digestToRevoke); diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index 3842d426cc..f7d1cb6913 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -64,12 +64,12 @@ contract RMNHome is HomeBase { /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool ok) = _getStoredConfig(configDigest); - if (ok) { + (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(configDigest); + if (configOK) { return ( VersionedConfig({ version: storedConfig.version, - configDigest: configDigest, + configDigest: storedConfig.configDigest, staticConfig: abi.decode(storedConfig.staticConfig, (StaticConfig)), dynamicConfig: abi.decode(storedConfig.dynamicConfig, (DynamicConfig)) }), @@ -85,31 +85,25 @@ contract RMNHome is HomeBase { view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { - // We need to explicitly check if the digest exists, because we don't clear out revoked config state. Not doing this - // check would result in potentially returning previous configs. - uint256 primaryConfigIndex = s_primaryConfigIndex; - bytes32 primaryConfigDigest = s_configDigests[primaryConfigIndex]; - if (primaryConfigDigest != ZERO_DIGEST) { - StoredConfig memory config = s_configs[primaryConfigIndex]; + (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(); + if (primaryOk) { primaryConfig = VersionedConfig({ - version: config.version, - configDigest: primaryConfigDigest, - staticConfig: abi.decode(config.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)) + version: primaryStoredConfig.version, + configDigest: primaryStoredConfig.configDigest, + staticConfig: abi.decode(primaryStoredConfig.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(primaryStoredConfig.dynamicConfig, (DynamicConfig)) }); } - uint256 secondaryConfigIndex = primaryConfigIndex ^ 1; - bytes32 secondaryConfigDigest = s_configDigests[secondaryConfigIndex]; - if (secondaryConfigDigest != ZERO_DIGEST) { - StoredConfig memory config = s_configs[secondaryConfigIndex]; + (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(); + if (secondaryOk) { secondaryConfig = VersionedConfig({ - version: config.version, - configDigest: secondaryConfigDigest, - staticConfig: abi.decode(config.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(config.dynamicConfig, (DynamicConfig)) + version: secondaryStoredConfig.version, + configDigest: secondaryStoredConfig.configDigest, + staticConfig: abi.decode(secondaryStoredConfig.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(secondaryStoredConfig.dynamicConfig, (DynamicConfig)) }); } @@ -129,8 +123,8 @@ contract RMNHome is HomeBase { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configDigests[secondaryConfigIndex] != digestToOverwrite) { - revert ConfigDigestMismatch(s_configDigests[secondaryConfigIndex], digestToOverwrite); + if (s_configs[secondaryConfigIndex].configDigest != digestToOverwrite) { + revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToOverwrite); } // are we going to overwrite a config? If so, emit an event. @@ -142,11 +136,11 @@ contract RMNHome is HomeBase { bytes memory encodedStaticConfig = abi.encode(newConfig.staticConfig); newConfigDigest = _calculateConfigDigest(encodedStaticConfig, newVersion, PREFIX); s_configs[secondaryConfigIndex] = StoredConfig({ + configDigest: newConfigDigest, version: newVersion, staticConfig: encodedStaticConfig, dynamicConfig: abi.encode(newConfig.dynamicConfig) }); - s_configDigests[secondaryConfigIndex] = newConfigDigest; emit ConfigSet( VersionedConfig({ @@ -162,7 +156,7 @@ contract RMNHome is HomeBase { function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 currentDigest) external onlyOwner { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - if (s_configDigests[i] == currentDigest && currentDigest != ZERO_DIGEST) { + if (s_configs[i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { StaticConfig memory staticConfig = abi.decode(s_configs[i].staticConfig, (StaticConfig)); _validateDynamicConfig(newDynamicConfig, staticConfig.nodes.length); From 97c14713833546717779707ebf6d32268c27e0a5 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 11:50:56 +0200 Subject: [PATCH 12/36] extract validation functions --- contracts/gas-snapshots/ccip.gas-snapshot | 32 ++++++------ .../src/v0.8/ccip/capability/HomeBase.sol | 12 +++++ .../src/v0.8/ccip/capability/RMNHome.sol | 50 +++++++++++++------ 3 files changed, 62 insertions(+), 32 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 401a3080aa..663d6ed0bc 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,23 +809,23 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10935) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10913) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18928) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10965) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28018) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30150) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18836) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 130916) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 16704) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 16146) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 22527) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 22966) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15128) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 73885) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 22671) -RMNHome_setSecondary:test_setSecondary_success() (gas: 822289) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18906) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10943) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28040) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30106) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18814) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14093) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 132676) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20085) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 19879) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 23678) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24117) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15173) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 170117) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 23822) +RMNHome_setSecondary:test_setSecondary_success() (gas: 820298) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 091cd303b8..286df18695 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -33,6 +33,10 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { bytes dynamicConfig; } + function _validateStaticAndDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; + + function _validateDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; + /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero /// digest. This is done to prevent exposing old config state that is invalid. function _getStoredConfig(bytes32 configDigest) internal view returns (StoredConfig memory storedConfig, bool ok) { @@ -62,6 +66,14 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { return (s_configs[s_primaryConfigIndex ^ 1], true); } + function getPrimaryDigest() public view returns (bytes32) { + return s_configs[s_primaryConfigIndex].configDigest; + } + + function getSecondaryDigest() public view returns (bytes32) { + return s_configs[s_primaryConfigIndex ^ 1].configDigest; + } + /// @notice Returns the current primary and secondary config digests. /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. /// @return primaryConfigDigest The digest of the primary config. diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index f7d1cb6913..8d2923155b 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -118,28 +118,30 @@ contract RMNHome is HomeBase { Config calldata newConfig, bytes32 digestToOverwrite ) external onlyOwner returns (bytes32 newConfigDigest) { - _validateStaticConfig(newConfig.staticConfig); - _validateDynamicConfig(newConfig.dynamicConfig, newConfig.staticConfig.nodes.length); + bytes memory encodedStaticConfig = abi.encode(newConfig.staticConfig); + bytes memory encodedDynamicConfig = abi.encode(newConfig.dynamicConfig); - uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + _validateStaticAndDynamicConfig(encodedStaticConfig, encodedDynamicConfig); + + bytes32 secondaryConfigDigest = getSecondaryDigest(); - if (s_configs[secondaryConfigIndex].configDigest != digestToOverwrite) { - revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToOverwrite); + if (secondaryConfigDigest != digestToOverwrite) { + revert ConfigDigestMismatch(secondaryConfigDigest, digestToOverwrite); } // are we going to overwrite a config? If so, emit an event. if (digestToOverwrite != ZERO_DIGEST) { emit ConfigRevoked(digestToOverwrite); } + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; uint32 newVersion = ++s_configCount; - bytes memory encodedStaticConfig = abi.encode(newConfig.staticConfig); newConfigDigest = _calculateConfigDigest(encodedStaticConfig, newVersion, PREFIX); s_configs[secondaryConfigIndex] = StoredConfig({ configDigest: newConfigDigest, version: newVersion, staticConfig: encodedStaticConfig, - dynamicConfig: abi.encode(newConfig.dynamicConfig) + dynamicConfig: encodedDynamicConfig }); emit ConfigSet( @@ -157,8 +159,8 @@ contract RMNHome is HomeBase { function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 currentDigest) external onlyOwner { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { if (s_configs[i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { - StaticConfig memory staticConfig = abi.decode(s_configs[i].staticConfig, (StaticConfig)); - _validateDynamicConfig(newDynamicConfig, staticConfig.nodes.length); + bytes memory DynamicConfig = abi.encode(newDynamicConfig); + _validateDynamicConfig(s_configs[i].staticConfig, DynamicConfig); // Since the static config doesn't change we don't have to update the digest or version. s_configs[i].dynamicConfig = abi.encode(newDynamicConfig); @@ -171,26 +173,32 @@ contract RMNHome is HomeBase { revert DigestNotFound(currentDigest); } - function _validateStaticConfig(StaticConfig calldata newStaticConfig) internal pure { + function _validateStaticAndDynamicConfig( + bytes memory encodedStaticConfig, + bytes memory encodedDynamicConfig + ) internal view override { + StaticConfig memory staticConfig = abi.decode(encodedStaticConfig, (StaticConfig)); // Ensure that observerNodesBitmap can be bit-encoded into a uint256. - if (newStaticConfig.nodes.length > 256) { + if (staticConfig.nodes.length > 256) { revert OutOfBoundsNodesLength(); } // Ensure no peerId or offchainPublicKey is duplicated. - for (uint256 i = 0; i < newStaticConfig.nodes.length; ++i) { - for (uint256 j = i + 1; j < newStaticConfig.nodes.length; ++j) { - if (newStaticConfig.nodes[i].peerId == newStaticConfig.nodes[j].peerId) { + for (uint256 i = 0; i < staticConfig.nodes.length; ++i) { + for (uint256 j = i + 1; j < staticConfig.nodes.length; ++j) { + if (staticConfig.nodes[i].peerId == staticConfig.nodes[j].peerId) { revert DuplicatePeerId(); } - if (newStaticConfig.nodes[i].offchainPublicKey == newStaticConfig.nodes[j].offchainPublicKey) { + if (staticConfig.nodes[i].offchainPublicKey == staticConfig.nodes[j].offchainPublicKey) { revert DuplicateOffchainPublicKey(); } } } + + _validateDynamicConfigParsed(abi.decode(encodedDynamicConfig, (DynamicConfig)), staticConfig.nodes.length); } - function _validateDynamicConfig(DynamicConfig calldata dynamicConfig, uint256 numberOfNodes) internal pure { + function _validateDynamicConfigParsed(DynamicConfig memory dynamicConfig, uint256 numberOfNodes) internal pure { uint256 numberOfSourceChains = dynamicConfig.sourceChains.length; for (uint256 i = 0; i < numberOfSourceChains; ++i) { SourceChain memory currentSourceChain = dynamicConfig.sourceChains[i]; @@ -219,4 +227,14 @@ contract RMNHome is HomeBase { } } } + + function _validateDynamicConfig( + bytes memory encodedStaticConfig, + bytes memory encodedDynamicConfig + ) internal pure override { + uint256 numberOfNodes = abi.decode(encodedStaticConfig, (StaticConfig)).nodes.length; + DynamicConfig memory dynamicConfig = abi.decode(encodedDynamicConfig, (DynamicConfig)); + + _validateDynamicConfigParsed(dynamicConfig, numberOfNodes); + } } From 32a3cdac81bd5283407ae19eef51456defe5e909 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 12:22:11 +0200 Subject: [PATCH 13/36] move all setters to HomeBase --- contracts/gas-snapshots/ccip.gas-snapshot | 32 +++---- .../src/v0.8/ccip/capability/HomeBase.sol | 85 ++++++++++++++--- .../src/v0.8/ccip/capability/RMNHome.sol | 93 +++---------------- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 90 ++++++++++-------- 4 files changed, 153 insertions(+), 147 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 663d6ed0bc..ec998c4af3 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,23 +809,23 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10913) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10958) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18906) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10943) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28040) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30106) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18814) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14093) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 132676) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20085) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 19879) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 23678) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24117) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15173) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 170117) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 23822) -RMNHome_setSecondary:test_setSecondary_success() (gas: 820298) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18940) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10988) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28260) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 32866) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20299) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15559) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 131902) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20799) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20593) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24384) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24823) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18044) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186724) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24528) +RMNHome_setSecondary:test_setSecondary_success() (gas: 820004) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 286df18695..0c277cd2e6 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -6,25 +6,29 @@ import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { - event ConfigRevoked(bytes32 configDigest); + event ConfigSet(StoredConfig versionedConfig); + event ConfigRevoked(bytes32 indexed configDigest); + event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); + event ConfigPromoted(bytes32 indexed configDigest); error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); + error DigestNotFound(bytes32 configDigest); /// @notice Used for encoding the config digest prefix uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 /// @notice The max number of configs that can be active at the same time. - uint256 internal constant MAX_CONCURRENT_CONFIGS = 2; + uint256 private constant MAX_CONCURRENT_CONFIGS = 2; /// @notice Helper to identify the zero config digest with less casting. - bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); + bytes32 private constant ZERO_DIGEST = bytes32(uint256(0)); /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. - StoredConfig[MAX_CONCURRENT_CONFIGS] internal s_configs; + StoredConfig[MAX_CONCURRENT_CONFIGS] private s_configs; /// @notice The total number of configs ever set, used for generating the version of the configs. - uint32 internal s_configCount = 0; + uint32 private s_configCount = 0; /// @notice The index of the primary config. - uint32 internal s_primaryConfigIndex = 0; + uint32 private s_primaryConfigIndex = 0; struct StoredConfig { bytes32 configDigest; @@ -37,6 +41,8 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { function _validateDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; + function _getConfigDigestPrefix() internal pure virtual returns (uint256); + /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero /// digest. This is done to prevent exposing old config state that is invalid. function _getStoredConfig(bytes32 configDigest) internal view returns (StoredConfig memory storedConfig, bool ok) { @@ -82,6 +88,60 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { return (s_configs[s_primaryConfigIndex].configDigest, s_configs[s_primaryConfigIndex ^ 1].configDigest); } + /// @notice Sets a new config as the secondary config. Does not influence the primary config. + /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. + /// This is done to prevent accidental overwrites. + function setSecondary( + bytes calldata encodedStaticConfig, + bytes calldata encodedDynamicConfig, + bytes32 digestToOverwrite + ) external onlyOwner returns (bytes32 newConfigDigest) { + _validateStaticAndDynamicConfig(encodedStaticConfig, encodedDynamicConfig); + + bytes32 existingDigest = getSecondaryDigest(); + + if (existingDigest != digestToOverwrite) { + revert ConfigDigestMismatch(existingDigest, digestToOverwrite); + } + + // are we going to overwrite a config? If so, emit an event. + if (existingDigest != ZERO_DIGEST) { + emit ConfigRevoked(digestToOverwrite); + } + + uint32 newVersion = ++s_configCount; + newConfigDigest = _calculateConfigDigest(encodedStaticConfig, newVersion); + + StoredConfig memory newConfig = StoredConfig({ + configDigest: newConfigDigest, + version: newVersion, + staticConfig: encodedStaticConfig, + dynamicConfig: encodedDynamicConfig + }); + + s_configs[s_primaryConfigIndex ^ 1] = newConfig; + + emit ConfigSet(newConfig); + + return newConfigDigest; + } + + function setDynamicConfig(bytes calldata newDynamicConfig, bytes32 currentDigest) external onlyOwner { + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { + if (s_configs[i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { + _validateDynamicConfig(s_configs[i].staticConfig, newDynamicConfig); + + // Since the static config doesn't change we don't have to update the digest or version. + s_configs[i].dynamicConfig = newDynamicConfig; + + emit DynamicConfigSet(currentDigest, newDynamicConfig); + return; + } + } + + revert DigestNotFound(currentDigest); + } + /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. function revokeSecondary(bytes32 configDigest) external onlyOwner { @@ -112,16 +172,15 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { delete s_configs[primaryConfigIndex].configDigest; s_primaryConfigIndex ^= 1; - emit ConfigRevoked(digestToRevoke); + if (digestToRevoke != ZERO_DIGEST) { + emit ConfigRevoked(digestToRevoke); + } + emit ConfigPromoted(digestToPromote); } - function _calculateConfigDigest( - bytes memory staticConfig, - uint32 version, - uint256 prefix - ) internal view returns (bytes32) { + function _calculateConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { return bytes32( - (prefix & PREFIX_MASK) + (_getConfigDigestPrefix() & PREFIX_MASK) | ( uint256( keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), version), staticConfig)) diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index 8d2923155b..d531a92425 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -12,17 +12,18 @@ contract RMNHome is HomeBase { error DuplicateSourceChain(); error OutOfBoundsObserverNodeIndex(); error MinObserversTooHigh(); - error DigestNotFound(bytes32 configDigest); - - event ConfigSet(VersionedConfig versionedConfig); - event DynamicConfigSet(bytes32 indexed configDigest, DynamicConfig dynamicConfig); - event ConfigPromoted(bytes32 configDigest); struct Node { bytes32 peerId; // Used for p2p communication. bytes32 offchainPublicKey; // Observations are signed with this public key, and are only verified offchain. } + struct SourceChain { + uint64 chainSelector; // ─────╮ The Source chain selector. + uint64 minObservers; // ──────╯ Required number of observers to agree on an observation for this source chain. + uint256 observerNodesBitmap; // ObserverNodesBitmap & (1< 256) { @@ -237,4 +164,8 @@ contract RMNHome is HomeBase { _validateDynamicConfigParsed(dynamicConfig, numberOfNodes); } + + function _getConfigDigestPrefix() internal pure override returns (uint256) { + return PREFIX; + } } diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index 2846cd921e..47e98e0905 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -8,6 +8,11 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; contract RMNHomeTest is Test { + struct Config { + RMNHome.StaticConfig staticConfig; + RMNHome.DynamicConfig dynamicConfig; + } + bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); RMNHome public s_rmnHome; @@ -16,7 +21,7 @@ contract RMNHomeTest is Test { s_rmnHome = new RMNHome(); } - function _getBaseConfig() internal pure returns (RMNHome.Config memory) { + function _getBaseConfig() internal pure returns (Config memory) { RMNHome.Node[] memory nodes = new RMNHome.Node[](3); nodes[0] = RMNHome.Node({peerId: keccak256("peerId_0"), offchainPublicKey: keccak256("offchainPublicKey_0")}); nodes[1] = RMNHome.Node({peerId: keccak256("peerId_1"), offchainPublicKey: keccak256("offchainPublicKey_1")}); @@ -28,7 +33,7 @@ contract RMNHomeTest is Test { // Observers 1 and 2 for source chain 9001 sourceChains[1] = RMNHome.SourceChain({chainSelector: 9001, minObservers: 2, observerNodesBitmap: 1 << 1 | 1 << 2}); - return RMNHome.Config({ + return Config({ staticConfig: RMNHome.StaticConfig({nodes: nodes, offchainConfig: abi.encode("static_config")}), dynamicConfig: RMNHome.DynamicConfig({sourceChains: sourceChains, offchainConfig: abi.encode("dynamic_config")}) }); @@ -51,19 +56,27 @@ contract RMNHomeTest is Test { contract RMNHome_setSecondary is RMNHomeTest { function test_setSecondary_success() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); RMNHome.VersionedConfig memory versionedConfig = RMNHome.VersionedConfig({ version: 1, staticConfig: config.staticConfig, dynamicConfig: config.dynamicConfig, configDigest: ZERO_DIGEST }); - versionedConfig.configDigest = _getConfigDigest(abi.encode(versionedConfig.staticConfig), versionedConfig.version); + HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ + configDigest: versionedConfig.configDigest, + version: versionedConfig.version, + staticConfig: abi.encode(config.staticConfig), + dynamicConfig: abi.encode(config.dynamicConfig) + }); + + versionedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, versionedConfig.version); + encodedConfig.configDigest = versionedConfig.configDigest; vm.expectEmit(); - emit RMNHome.ConfigSet(versionedConfig); + emit HomeBase.ConfigSet(encodedConfig); - s_rmnHome.setSecondary(config, ZERO_DIGEST); + s_rmnHome.setSecondary(encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(versionedConfig.configDigest); assertTrue(ok); @@ -90,81 +103,83 @@ contract RMNHome_setSecondary is RMNHomeTest { } function test_setSecondary_OutOfBoundsNodesLength_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); config.staticConfig.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); - s_rmnHome.setSecondary(config, ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicatePeerId_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); - s_rmnHome.setSecondary(config, ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); - s_rmnHome.setSecondary(config, ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicateSourceChain_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); - s_rmnHome.setSecondary(config, ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); - s_rmnHome.setSecondary(config, ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_MinObserversTooHigh_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); - s_rmnHome.setSecondary(config, ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_OnlyOwner_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setSecondary(config, ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } } contract RMNHome_setDynamicConfig is RMNHomeTest { function setUp() public override { super.setUp(); - s_rmnHome.setSecondary(_getBaseConfig(), ZERO_DIGEST); + Config memory config = _getBaseConfig(); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_success() public { (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(); - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers--; (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(); + bytes memory encodedConfig = abi.encode(config.dynamicConfig); vm.expectEmit(); - emit RMNHome.DynamicConfigSet(secondaryConfigDigest, config.dynamicConfig); + emit HomeBase.DynamicConfigSet(secondaryConfigDigest, encodedConfig); - s_rmnHome.setDynamicConfig(config.dynamicConfig, secondaryConfigDigest); + s_rmnHome.setDynamicConfig(encodedConfig, secondaryConfigDigest); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); assertTrue(ok); @@ -181,31 +196,31 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { // Asserts the validation function is being called function test_setDynamicConfig_MinObserversTooHigh_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers++; - vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(config.dynamicConfig, ZERO_DIGEST); + vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); + s_rmnHome.setDynamicConfig(abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_DigestNotFound_reverts() public { // Zero always reverts - vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(_getBaseConfig().dynamicConfig, ZERO_DIGEST); + vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); + s_rmnHome.setDynamicConfig(abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); // Non-existent digest reverts bytes32 nonExistentDigest = keccak256("nonExistentDigest"); - vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, nonExistentDigest)); - s_rmnHome.setDynamicConfig(_getBaseConfig().dynamicConfig, nonExistentDigest); + vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, nonExistentDigest)); + s_rmnHome.setDynamicConfig(abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest); } function test_setDynamicConfig_OnlyOwner_reverts() public { - RMNHome.Config memory config = _getBaseConfig(); + Config memory config = _getBaseConfig(); vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setDynamicConfig(config.dynamicConfig, keccak256("configDigest")); + s_rmnHome.setDynamicConfig(abi.encode(config.dynamicConfig), keccak256("configDigest")); } } @@ -213,12 +228,13 @@ contract RMNHome_revokeSecondary is RMNHomeTest { // Sets two configs function setUp() public override { super.setUp(); - bytes32 digest = s_rmnHome.setSecondary(_getBaseConfig(), ZERO_DIGEST); + Config memory config = _getBaseConfig(); + bytes32 digest = + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); s_rmnHome.promoteSecondaryAndRevokePrimary(digest, ZERO_DIGEST); - RMNHome.Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers--; - s_rmnHome.setSecondary(_getBaseConfig(), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_revokeSecondary_success() public { @@ -239,8 +255,8 @@ contract RMNHome_revokeSecondary is RMNHomeTest { // Asser the primary digest is unaffected but the secondary digest is set to zero (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); assertEq(primaryDigest, priorPrimaryDigest); - assertTrue(secondaryDigest != priorSecondaryDigest); assertEq(secondaryDigest, ZERO_DIGEST); + assertTrue(secondaryDigest != priorSecondaryDigest); } function test_revokeSecondary_ConfigDigestMismatch_reverts() public { From 0b33d7a1af850c792347d24e9507e38c5bfb5cb3 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 13:11:28 +0200 Subject: [PATCH 14/36] add donId and pluginType to HomeBase --- contracts/gas-snapshots/ccip.gas-snapshot | 32 ++-- .../src/v0.8/ccip/capability/HomeBase.sol | 138 +++++++++++++----- .../src/v0.8/ccip/capability/RMNHome.sol | 8 +- .../ccip/test/capability/CCIPConfig.t.sol | 3 +- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 54 +++---- 5 files changed, 155 insertions(+), 80 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index ec998c4af3..d8d3ad9771 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,23 +809,23 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10958) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 11121) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18940) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10988) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28260) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 32866) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20299) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15559) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 131902) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20799) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20593) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24384) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24823) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18044) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186724) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24528) -RMNHome_setSecondary:test_setSecondary_success() (gas: 820004) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 20027) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11177) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 30125) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 33973) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20855) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15809) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 134895) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 21038) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20832) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24599) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 25038) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18283) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186977) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24743) +RMNHome_setSecondary:test_setSecondary_success() (gas: 821147) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 0c277cd2e6..ad24364096 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; +import {ICapabilityConfiguration} from "../../keystone/interfaces/ICapabilityConfiguration.sol"; import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { +abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration { event ConfigSet(StoredConfig versionedConfig); event ConfigRevoked(bytes32 indexed configDigest); event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); @@ -13,6 +14,9 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); error DigestNotFound(bytes32 configDigest); + error ZeroAddressNotAllowed(); + error OnlyCapabilitiesRegistryCanCall(); + error OnlyOwnerOrSelfCallAllowed(); /// @notice Used for encoding the config digest prefix uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 @@ -21,9 +25,12 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @notice Helper to identify the zero config digest with less casting. bytes32 private constant ZERO_DIGEST = bytes32(uint256(0)); + /// @dev The canonical capabilities registry address. + address internal immutable i_capabilitiesRegistry; + /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. - StoredConfig[MAX_CONCURRENT_CONFIGS] private s_configs; + mapping(uint32 donId => mapping(uint8 pluginType => StoredConfig[MAX_CONCURRENT_CONFIGS])) private s_configs; /// @notice The total number of configs ever set, used for generating the version of the configs. uint32 private s_configCount = 0; @@ -37,68 +44,123 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { bytes dynamicConfig; } + /// @param capabilitiesRegistry the canonical capabilities registry address. + constructor(address capabilitiesRegistry) { + if (capabilitiesRegistry == address(0)) { + revert ZeroAddressNotAllowed(); + } + i_capabilitiesRegistry = capabilitiesRegistry; + } + function _validateStaticAndDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; function _validateDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; function _getConfigDigestPrefix() internal pure virtual returns (uint256); + /// @notice Called by the registry prior to the config being set for a particular DON. + /// @dev precondition Requires destination chain config to be set + function beforeCapabilityConfigSet( + bytes32[] calldata, /* nodes */ + bytes calldata config, + uint64, /* configCount */ + uint32 donId + ) external override { + if (msg.sender != i_capabilitiesRegistry) { + revert OnlyCapabilitiesRegistryCanCall(); + } + // + // (CCIPConfigTypes.OCR3Config[] memory commitConfigs, CCIPConfigTypes.OCR3Config[] memory execConfigs) = + // _groupByPluginType(abi.decode(config, (CCIPConfigTypes.OCR3Config[]))); + // if (commitConfigs.length > 0) { + // _updatePluginConfig(donId, Internal.OCRPluginType.Commit, commitConfigs); + // } + // if (execConfigs.length > 0) { + // _updatePluginConfig(donId, Internal.OCRPluginType.Execution, execConfigs); + // } + } + + /// @inheritdoc ICapabilityConfiguration + /// @dev The CCIP capability will fetch the configuration needed directly from this contract. + /// The offchain syncer will call this function, however, so its important that it doesn't revert. + function getCapabilityConfiguration(uint32 /* donId */ ) external pure override returns (bytes memory configuration) { + return bytes(""); + } /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero /// digest. This is done to prevent exposing old config state that is invalid. - function _getStoredConfig(bytes32 configDigest) internal view returns (StoredConfig memory storedConfig, bool ok) { + + function _getStoredConfig( + uint32 donId, + uint8 pluginType, + bytes32 configDigest + ) internal view returns (StoredConfig memory storedConfig, bool ok) { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old // config state that is invalid. - if (s_configs[i].configDigest == configDigest && configDigest != ZERO_DIGEST) { - return (s_configs[i], true); + if (s_configs[donId][pluginType][i].configDigest == configDigest && configDigest != ZERO_DIGEST) { + return (s_configs[donId][pluginType][i], true); } } return (storedConfig, false); } - function _getPrimaryStoredConfig() internal view returns (StoredConfig memory primaryConfig, bool ok) { - if (s_configs[s_primaryConfigIndex].configDigest == ZERO_DIGEST) { + function _getPrimaryStoredConfig( + uint32 donId, + uint8 pluginType + ) internal view returns (StoredConfig memory primaryConfig, bool ok) { + if (s_configs[donId][pluginType][s_primaryConfigIndex].configDigest == ZERO_DIGEST) { return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } - return (s_configs[s_primaryConfigIndex], true); + return (s_configs[donId][pluginType][s_primaryConfigIndex], true); } - function _getSecondaryStoredConfig() internal view returns (StoredConfig memory secondaryConfig, bool ok) { - if (s_configs[s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { + function _getSecondaryStoredConfig( + uint32 donId, + uint8 pluginType + ) internal view returns (StoredConfig memory secondaryConfig, bool ok) { + if (s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } - return (s_configs[s_primaryConfigIndex ^ 1], true); + return (s_configs[donId][pluginType][s_primaryConfigIndex ^ 1], true); } - function getPrimaryDigest() public view returns (bytes32) { - return s_configs[s_primaryConfigIndex].configDigest; + function getPrimaryDigest(uint32 donId, uint8 pluginType) public view returns (bytes32) { + return s_configs[donId][pluginType][s_primaryConfigIndex].configDigest; } - function getSecondaryDigest() public view returns (bytes32) { - return s_configs[s_primaryConfigIndex ^ 1].configDigest; + function getSecondaryDigest(uint32 donId, uint8 pluginType) public view returns (bytes32) { + return s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest; } /// @notice Returns the current primary and secondary config digests. /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. /// @return primaryConfigDigest The digest of the primary config. /// @return secondaryConfigDigest The digest of the secondary config. - function getConfigDigests() external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { - return (s_configs[s_primaryConfigIndex].configDigest, s_configs[s_primaryConfigIndex ^ 1].configDigest); + function getConfigDigests( + uint32 donId, + uint8 pluginType + ) external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + return ( + s_configs[donId][pluginType][s_primaryConfigIndex].configDigest, + s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest + ); } /// @notice Sets a new config as the secondary config. Does not influence the primary config. /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. /// This is done to prevent accidental overwrites. function setSecondary( + uint32 donId, + uint8 pluginType, bytes calldata encodedStaticConfig, bytes calldata encodedDynamicConfig, bytes32 digestToOverwrite ) external onlyOwner returns (bytes32 newConfigDigest) { _validateStaticAndDynamicConfig(encodedStaticConfig, encodedDynamicConfig); - bytes32 existingDigest = getSecondaryDigest(); + bytes32 existingDigest = getSecondaryDigest(donId, pluginType); if (existingDigest != digestToOverwrite) { revert ConfigDigestMismatch(existingDigest, digestToOverwrite); @@ -119,20 +181,25 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { dynamicConfig: encodedDynamicConfig }); - s_configs[s_primaryConfigIndex ^ 1] = newConfig; + s_configs[donId][pluginType][s_primaryConfigIndex ^ 1] = newConfig; emit ConfigSet(newConfig); return newConfigDigest; } - function setDynamicConfig(bytes calldata newDynamicConfig, bytes32 currentDigest) external onlyOwner { + function setDynamicConfig( + uint32 donId, + uint8 pluginType, + bytes calldata newDynamicConfig, + bytes32 currentDigest + ) external onlyOwner { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - if (s_configs[i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { - _validateDynamicConfig(s_configs[i].staticConfig, newDynamicConfig); + if (s_configs[donId][pluginType][i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { + _validateDynamicConfig(s_configs[donId][pluginType][i].staticConfig, newDynamicConfig); // Since the static config doesn't change we don't have to update the digest or version. - s_configs[i].dynamicConfig = newDynamicConfig; + s_configs[donId][pluginType][i].dynamicConfig = newDynamicConfig; emit DynamicConfigSet(currentDigest, newDynamicConfig); return; @@ -144,32 +211,37 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(bytes32 configDigest) external onlyOwner { + function revokeSecondary(uint32 donId, uint8 pluginType, bytes32 configDigest) external onlyOwner { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[secondaryConfigIndex].configDigest != configDigest) { - revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, configDigest); + if (s_configs[donId][pluginType][secondaryConfigIndex].configDigest != configDigest) { + revert ConfigDigestMismatch(s_configs[donId][pluginType][secondaryConfigIndex].configDigest, configDigest); } emit ConfigRevoked(configDigest); // Delete only the digest, as that's what's used to determine if a config is active. This means the actual // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in // the future. - delete s_configs[secondaryConfigIndex].configDigest; + delete s_configs[donId][pluginType][secondaryConfigIndex].configDigest; } /// @notice Promotes the secondary config to the primary config and revokes the primary config. - function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { + function promoteSecondaryAndRevokePrimary( + uint32 donId, + uint8 pluginType, + bytes32 digestToPromote, + bytes32 digestToRevoke + ) external onlyOwner { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[secondaryConfigIndex].configDigest != digestToPromote) { - revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToPromote); + if (s_configs[donId][pluginType][secondaryConfigIndex].configDigest != digestToPromote) { + revert ConfigDigestMismatch(s_configs[donId][pluginType][secondaryConfigIndex].configDigest, digestToPromote); } uint256 primaryConfigIndex = s_primaryConfigIndex; - if (s_configs[primaryConfigIndex].configDigest != digestToRevoke) { - revert ConfigDigestMismatch(s_configs[primaryConfigIndex].configDigest, digestToRevoke); + if (s_configs[donId][pluginType][primaryConfigIndex].configDigest != digestToRevoke) { + revert ConfigDigestMismatch(s_configs[donId][pluginType][primaryConfigIndex].configDigest, digestToRevoke); } - delete s_configs[primaryConfigIndex].configDigest; + delete s_configs[donId][pluginType][primaryConfigIndex].configDigest; s_primaryConfigIndex ^= 1; if (digestToRevoke != ZERO_DIGEST) { diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index d531a92425..e5376057b6 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -48,13 +48,15 @@ contract RMNHome is HomeBase { uint256 private constant PREFIX = 0x000b << (256 - 16); // 0x000b00..00 + constructor() HomeBase(address(1)) {} + /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use /// in case one of the configs is too large to be returnable by one of the other getters. /// @param configDigest The digest of the config to fetch. /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(configDigest); + (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(0, 0, configDigest); if (configOK) { return ( VersionedConfig({ @@ -75,7 +77,7 @@ contract RMNHome is HomeBase { view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { - (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(); + (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(0, 0); if (primaryOk) { primaryConfig = VersionedConfig({ @@ -86,7 +88,7 @@ contract RMNHome is HomeBase { }); } - (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(); + (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(0, 0); if (secondaryOk) { secondaryConfig = VersionedConfig({ diff --git a/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol b/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol index a64fee8b9a..914352ea37 100644 --- a/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.24; import {CCIPConfig} from "../../capability/CCIPConfig.sol"; +import {HomeBase} from "../../capability/HomeBase.sol"; import {ICapabilitiesRegistry} from "../../capability/interfaces/ICapabilitiesRegistry.sol"; import {CCIPConfigTypes} from "../../capability/libraries/CCIPConfigTypes.sol"; import {Internal} from "../../libraries/Internal.sol"; @@ -151,7 +152,7 @@ contract CCIPConfig_constructor is Test { // Reverts. function test_constructor_ZeroAddressNotAllowed_Revert() public { - vm.expectRevert(CCIPConfig.ZeroAddressNotAllowed.selector); + vm.expectRevert(HomeBase.ZeroAddressNotAllowed.selector); new CCIPConfigHelper(address(0)); } } diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index 47e98e0905..dca08720ef 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -76,7 +76,7 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.expectEmit(); emit HomeBase.ConfigSet(encodedConfig); - s_rmnHome.setSecondary(encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(versionedConfig.configDigest); assertTrue(ok); @@ -107,7 +107,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicatePeerId_reverts() public { @@ -115,7 +115,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { @@ -123,7 +123,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicateSourceChain_reverts() public { @@ -131,7 +131,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { @@ -139,7 +139,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_MinObserversTooHigh_reverts() public { @@ -147,7 +147,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_OnlyOwner_reverts() public { @@ -156,7 +156,7 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } } @@ -164,22 +164,22 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_success() public { - (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(); + (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(0, 0); Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers--; - (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(); + (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(0, 0); bytes memory encodedConfig = abi.encode(config.dynamicConfig); vm.expectEmit(); emit HomeBase.DynamicConfigSet(secondaryConfigDigest, encodedConfig); - s_rmnHome.setDynamicConfig(encodedConfig, secondaryConfigDigest); + s_rmnHome.setDynamicConfig(0, 0, encodedConfig, secondaryConfigDigest); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); assertTrue(ok); @@ -189,7 +189,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { ); // Asser the digests don't change when updating the dynamic config - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(0, 0); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, secondaryConfigDigest); } @@ -200,18 +200,18 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setDynamicConfig(0, 0, abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_DigestNotFound_reverts() public { // Zero always reverts vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); + s_rmnHome.setDynamicConfig(0, 0, abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); // Non-existent digest reverts bytes32 nonExistentDigest = keccak256("nonExistentDigest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, nonExistentDigest)); - s_rmnHome.setDynamicConfig(abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest); + s_rmnHome.setDynamicConfig(0, 0, abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest); } function test_setDynamicConfig_OnlyOwner_reverts() public { @@ -220,7 +220,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setDynamicConfig(abi.encode(config.dynamicConfig), keccak256("configDigest")); + s_rmnHome.setDynamicConfig(0, 0, abi.encode(config.dynamicConfig), keccak256("configDigest")); } } @@ -230,20 +230,20 @@ contract RMNHome_revokeSecondary is RMNHomeTest { super.setUp(); Config memory config = _getBaseConfig(); bytes32 digest = - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); - s_rmnHome.promoteSecondaryAndRevokePrimary(digest, ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.promoteSecondaryAndRevokePrimary(0, 0, digest, ZERO_DIGEST); config.dynamicConfig.sourceChains[0].minObservers--; - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_revokeSecondary_success() public { - (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); + (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(0, 0); vm.expectEmit(); emit HomeBase.ConfigRevoked(priorSecondaryDigest); - s_rmnHome.revokeSecondary(priorSecondaryDigest); + s_rmnHome.revokeSecondary(0, 0, priorSecondaryDigest); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorSecondaryDigest); assertFalse(ok); @@ -253,25 +253,25 @@ contract RMNHome_revokeSecondary is RMNHomeTest { assertEq(storedVersionedConfig.dynamicConfig.sourceChains.length, 0); // Asser the primary digest is unaffected but the secondary digest is set to zero - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(0, 0); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, ZERO_DIGEST); assertTrue(secondaryDigest != priorSecondaryDigest); } function test_revokeSecondary_ConfigDigestMismatch_reverts() public { - (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); + (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(0, 0); bytes32 wrongDigest = keccak256("wrong_digest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); - s_rmnHome.revokeSecondary(wrongDigest); + s_rmnHome.revokeSecondary(0, 0, wrongDigest); } function test_revokeSecondary_OnlyOwner_reverts() public { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.revokeSecondary(keccak256("configDigest")); + s_rmnHome.revokeSecondary(0, 0, keccak256("configDigest")); } } @@ -282,6 +282,6 @@ contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.promoteSecondaryAndRevokePrimary(keccak256("toPromote"), keccak256("ToRevoke")); + s_rmnHome.promoteSecondaryAndRevokePrimary(0, 0, keccak256("toPromote"), keccak256("ToRevoke")); } } From 35e56e5385271bbc6bd8924d288e07de0ba5997a Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 13:48:56 +0200 Subject: [PATCH 15/36] add CR support to RMN --- contracts/gas-snapshots/ccip.gas-snapshot | 24 ++-- .../src/v0.8/ccip/capability/HomeBase.sol | 39 ++++--- .../src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 106 ++++++++++++++---- 3 files changed, 120 insertions(+), 49 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index d8d3ad9771..671db1dc9d 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,23 +809,25 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) +RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 9867) +RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 187) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 11121) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 20027) RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11177) RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 30125) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 33973) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20855) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15809) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 134895) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 21038) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20832) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24599) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 25038) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 34017) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20877) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15831) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 135045) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 21070) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20864) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24663) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 25102) RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18283) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186977) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24743) -RMNHome_setSecondary:test_setSecondary_success() (gas: 821147) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 187009) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24807) +RMNHome_setSecondary:test_setSecondary_success() (gas: 821271) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index ad24364096..c151530546 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -6,7 +6,9 @@ import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration { +import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/interfaces/IERC165.sol"; + +abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, IERC165 { event ConfigSet(StoredConfig versionedConfig); event ConfigRevoked(bytes32 indexed configDigest); event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); @@ -58,26 +60,26 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig function _getConfigDigestPrefix() internal pure virtual returns (uint256); + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + return interfaceId == type(ICapabilityConfiguration).interfaceId || interfaceId == type(IERC165).interfaceId; + } + /// @notice Called by the registry prior to the config being set for a particular DON. /// @dev precondition Requires destination chain config to be set function beforeCapabilityConfigSet( - bytes32[] calldata, /* nodes */ - bytes calldata config, - uint64, /* configCount */ - uint32 donId + bytes32[] calldata, // nodes + bytes calldata update, + uint64, // configCount + uint32 // donId ) external override { if (msg.sender != i_capabilitiesRegistry) { revert OnlyCapabilitiesRegistryCanCall(); } - // - // (CCIPConfigTypes.OCR3Config[] memory commitConfigs, CCIPConfigTypes.OCR3Config[] memory execConfigs) = - // _groupByPluginType(abi.decode(config, (CCIPConfigTypes.OCR3Config[]))); - // if (commitConfigs.length > 0) { - // _updatePluginConfig(donId, Internal.OCRPluginType.Commit, commitConfigs); - // } - // if (execConfigs.length > 0) { - // _updatePluginConfig(donId, Internal.OCRPluginType.Execution, execConfigs); - // } + (bool success, bytes memory errorData) = address(this).call(update); + if (!success) { + revert(string(errorData)); + } } /// @inheritdoc ICapabilityConfiguration @@ -157,7 +159,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig bytes calldata encodedStaticConfig, bytes calldata encodedDynamicConfig, bytes32 digestToOverwrite - ) external onlyOwner returns (bytes32 newConfigDigest) { + ) external OnlyOwnerOrSelfCall returns (bytes32 newConfigDigest) { _validateStaticAndDynamicConfig(encodedStaticConfig, encodedDynamicConfig); bytes32 existingDigest = getSecondaryDigest(donId, pluginType); @@ -260,4 +262,11 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig ) ); } + + modifier OnlyOwnerOrSelfCall() { + if (msg.sender != owner() && msg.sender != address(this)) { + revert OnlyOwnerOrSelfCallAllowed(); + } + _; + } } diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index dca08720ef..db891a106b 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -8,6 +8,9 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; contract RMNHomeTest is Test { + uint32 internal constant RMN_DON_ID = 0; + uint8 internal constant RMN_PLUGIN_TYPE = 0; + struct Config { RMNHome.StaticConfig staticConfig; RMNHome.DynamicConfig dynamicConfig; @@ -76,7 +79,9 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.expectEmit(); emit HomeBase.ConfigSet(encodedConfig); - s_rmnHome.setSecondary(0, 0, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST + ); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(versionedConfig.configDigest); assertTrue(ok); @@ -107,7 +112,9 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } function test_setSecondary_DuplicatePeerId_reverts() public { @@ -115,7 +122,9 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { @@ -123,7 +132,9 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } function test_setSecondary_DuplicateSourceChain_reverts() public { @@ -131,7 +142,9 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { @@ -139,7 +152,9 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } function test_setSecondary_MinObserversTooHigh_reverts() public { @@ -147,7 +162,9 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } function test_setSecondary_OnlyOwner_reverts() public { @@ -155,8 +172,10 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.startPrank(address(0)); - vm.expectRevert("Only callable by owner"); - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } } @@ -164,7 +183,9 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } function test_setDynamicConfig_success() public { @@ -179,7 +200,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { vm.expectEmit(); emit HomeBase.DynamicConfigSet(secondaryConfigDigest, encodedConfig); - s_rmnHome.setDynamicConfig(0, 0, encodedConfig, secondaryConfigDigest); + s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, encodedConfig, secondaryConfigDigest); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); assertTrue(ok); @@ -200,18 +221,20 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(0, 0, abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_DigestNotFound_reverts() public { // Zero always reverts vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(0, 0, abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); + s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); // Non-existent digest reverts bytes32 nonExistentDigest = keccak256("nonExistentDigest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, nonExistentDigest)); - s_rmnHome.setDynamicConfig(0, 0, abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest); + s_rmnHome.setDynamicConfig( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest + ); } function test_setDynamicConfig_OnlyOwner_reverts() public { @@ -220,7 +243,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setDynamicConfig(0, 0, abi.encode(config.dynamicConfig), keccak256("configDigest")); + s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.dynamicConfig), keccak256("configDigest")); } } @@ -229,12 +252,15 @@ contract RMNHome_revokeSecondary is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - bytes32 digest = - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); - s_rmnHome.promoteSecondaryAndRevokePrimary(0, 0, digest, ZERO_DIGEST); + bytes32 digest = s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); + s_rmnHome.promoteSecondaryAndRevokePrimary(RMN_DON_ID, RMN_PLUGIN_TYPE, digest, ZERO_DIGEST); config.dynamicConfig.sourceChains[0].minObservers--; - s_rmnHome.setSecondary(0, 0, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary( + RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST + ); } function test_revokeSecondary_success() public { @@ -243,7 +269,7 @@ contract RMNHome_revokeSecondary is RMNHomeTest { vm.expectEmit(); emit HomeBase.ConfigRevoked(priorSecondaryDigest); - s_rmnHome.revokeSecondary(0, 0, priorSecondaryDigest); + s_rmnHome.revokeSecondary(RMN_DON_ID, RMN_PLUGIN_TYPE, priorSecondaryDigest); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorSecondaryDigest); assertFalse(ok); @@ -264,14 +290,14 @@ contract RMNHome_revokeSecondary is RMNHomeTest { bytes32 wrongDigest = keccak256("wrong_digest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); - s_rmnHome.revokeSecondary(0, 0, wrongDigest); + s_rmnHome.revokeSecondary(RMN_DON_ID, RMN_PLUGIN_TYPE, wrongDigest); } function test_revokeSecondary_OnlyOwner_reverts() public { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.revokeSecondary(0, 0, keccak256("configDigest")); + s_rmnHome.revokeSecondary(RMN_DON_ID, RMN_PLUGIN_TYPE, keccak256("configDigest")); } } @@ -282,6 +308,40 @@ contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.promoteSecondaryAndRevokePrimary(0, 0, keccak256("toPromote"), keccak256("ToRevoke")); + s_rmnHome.promoteSecondaryAndRevokePrimary( + RMN_DON_ID, RMN_PLUGIN_TYPE, keccak256("toPromote"), keccak256("ToRevoke") + ); + } +} + +contract RMNHome_beforeCapabilityConfigSet is RMNHomeTest { + function test_beforeCapabilityConfigSet_success() public { + vm.startPrank(address(1)); + + Config memory config = _getBaseConfig(); + HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ + configDigest: ZERO_DIGEST, + version: 1, + staticConfig: abi.encode(config.staticConfig), + dynamicConfig: abi.encode(config.dynamicConfig) + }); + encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); + + bytes memory callPayload = abi.encodeCall( + HomeBase.setSecondary, + (RMN_DON_ID, RMN_PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) + ); + + vm.expectEmit(); + emit HomeBase.ConfigSet(encodedConfig); + + s_rmnHome.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, RMN_DON_ID); + } + + function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); + s_rmnHome.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, RMN_DON_ID); } } From 725a3d90a0b98adb2cb065964672413a7a4f5620 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 14:01:46 +0200 Subject: [PATCH 16/36] add ccipHome --- .../src/v0.8/ccip/capability/CCIPHome.sol | 268 ++++++++++++++++++ .../src/v0.8/ccip/capability/HomeBase.sol | 25 +- 2 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 contracts/src/v0.8/ccip/capability/CCIPHome.sol diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol new file mode 100644 index 0000000000..6c05aa540c --- /dev/null +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {ICapabilitiesRegistry} from "./interfaces/ICapabilitiesRegistry.sol"; + +import {Internal} from "../libraries/Internal.sol"; +import {HomeBase} from "./HomeBase.sol"; +import {CCIPConfigTypes} from "./libraries/CCIPConfigTypes.sol"; + +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; + +/// @notice Stores the home configuration for RMN, that is referenced by CCIP oracles, RMN nodes, and the RMNRemote +/// contracts. +contract RMNHome is HomeBase { + using EnumerableSet for EnumerableSet.UintSet; + + event ChainConfigRemoved(uint64 chainSelector); + /// @notice Emitted when a chain's configuration is set. + /// @param chainSelector The chain selector. + /// @param chainConfig The chain configuration. + event ChainConfigSet(uint64 chainSelector, CCIPConfigTypes.ChainConfig chainConfig); + + error OutOfBoundsNodesLength(); + error DuplicatePeerId(); + error DuplicateOffchainPublicKey(); + error DuplicateSourceChain(); + error OutOfBoundsObserverNodeIndex(); + error MinObserversTooHigh(); + error NodeNotInRegistry(bytes32 p2pId); + error ChainSelectorNotFound(uint64 chainSelector); + error FChainMustBePositive(); + error ChainSelectorNotSet(); + error InvalidPluginType(); + error OfframpAddressCannotBeZero(); + error FChainTooHigh(uint256 fChain, uint256 FRoleDON); + error TooManySigners(); + error FTooHigh(); + error InvalidNode(CCIPConfigTypes.OCR3Node node); + error NotEnoughTransmitters(uint256 got, uint256 minimum); + + struct Node { + bytes32 peerId; // Used for p2p communication. + bytes32 offchainPublicKey; // Observations are signed with this public key, and are only verified offchain. + } + + struct SourceChain { + uint64 chainSelector; // ─────╮ The Source chain selector. + uint64 minObservers; // ──────╯ Required number of observers to agree on an observation for this source chain. + uint256 observerNodesBitmap; // ObserverNodesBitmap & (1< CCIPConfigTypes.ChainConfig chainConfig) private s_chainConfigurations; + + /// @dev All chains that are configured. + EnumerableSet.UintSet private s_remoteChainSelectors; + + constructor() HomeBase(address(1)) {} + + /// @notice Returns the total number of chains configured. + /// @return The total number of chains configured. + function getNumChainConfigurations() external view returns (uint256) { + return s_remoteChainSelectors.length(); + } + + /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use + /// in case one of the configs is too large to be returnable by one of the other getters. + /// @param configDigest The digest of the config to fetch. + /// @return versionedConfig The config and its version. + /// @return ok True if the config was found, false otherwise. + function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { + (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(0, 0, configDigest); + if (configOK) { + return ( + VersionedConfig({ + version: storedConfig.version, + configDigest: storedConfig.configDigest, + staticConfig: abi.decode(storedConfig.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(storedConfig.dynamicConfig, (DynamicConfig)) + }), + true + ); + } + + return (versionedConfig, false); + } + + function getAllConfigs() + external + view + returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) + { + (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(0, 0); + + if (primaryOk) { + primaryConfig = VersionedConfig({ + version: primaryStoredConfig.version, + configDigest: primaryStoredConfig.configDigest, + staticConfig: abi.decode(primaryStoredConfig.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(primaryStoredConfig.dynamicConfig, (DynamicConfig)) + }); + } + + (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(0, 0); + + if (secondaryOk) { + secondaryConfig = VersionedConfig({ + version: secondaryStoredConfig.version, + configDigest: secondaryStoredConfig.configDigest, + staticConfig: abi.decode(secondaryStoredConfig.staticConfig, (StaticConfig)), + dynamicConfig: abi.decode(secondaryStoredConfig.dynamicConfig, (DynamicConfig)) + }); + } + + return (primaryConfig, secondaryConfig); + } + + function _validateStaticAndDynamicConfig(bytes memory encodedStaticConfig, bytes memory) internal view override { + CCIPConfigTypes.OCR3Config memory cfg = abi.decode(encodedStaticConfig, (CCIPConfigTypes.OCR3Config)); + + if (cfg.chainSelector == 0) revert ChainSelectorNotSet(); + if (cfg.pluginType != Internal.OCRPluginType.Commit && cfg.pluginType != Internal.OCRPluginType.Execution) { + revert InvalidPluginType(); + } + if (cfg.offrampAddress.length == 0 || keccak256(cfg.offrampAddress) == EMPTY_ENCODED_ADDRESS_HASH) { + revert OfframpAddressCannotBeZero(); + } + if (!s_remoteChainSelectors.contains(cfg.chainSelector)) revert ChainSelectorNotFound(cfg.chainSelector); + + // fChain cannot exceed FRoleDON, since it is a subcommittee in the larger DON + uint256 FRoleDON = cfg.FRoleDON; + uint256 fChain = s_chainConfigurations[cfg.chainSelector].fChain; + // fChain > 0 is enforced in applyChainConfigUpdates, and the presence of a chain config is checked above + // FRoleDON != 0 because FRoleDON >= fChain is enforced here + if (fChain > FRoleDON) { + revert FChainTooHigh(fChain, FRoleDON); + } + + // len(nodes) >= 3 * FRoleDON + 1 + // len(nodes) == numberOfSigners + uint256 numberOfNodes = cfg.nodes.length; + if (numberOfNodes > MAX_NUM_ORACLES) revert TooManySigners(); + if (numberOfNodes <= 3 * FRoleDON) revert FTooHigh(); + + uint256 nonZeroTransmitters = 0; + bytes32[] memory p2pIds = new bytes32[](numberOfNodes); + for (uint256 i = 0; i < numberOfNodes; ++i) { + CCIPConfigTypes.OCR3Node memory node = cfg.nodes[i]; + + // 3 * fChain + 1 <= nonZeroTransmitters <= 3 * FRoleDON + 1 + // Transmitters can be set to 0 since there can be more signers than transmitters, + if (node.transmitterKey.length != 0) { + nonZeroTransmitters++; + } + + // Signer key and p2pIds must always be present + if (node.signerKey.length == 0 || node.p2pId == bytes32(0)) { + revert InvalidNode(node); + } + + p2pIds[i] = node.p2pId; + } + + // We check for chain config presence above, so fChain here must be non-zero. fChain <= FRoleDON due to the checks above. + // There can be less transmitters than signers - so they can be set to zero (which indicates that a node is a signer, but not a transmitter). + uint256 minTransmittersLength = 3 * fChain + 1; + if (nonZeroTransmitters < minTransmittersLength) { + revert NotEnoughTransmitters(nonZeroTransmitters, minTransmittersLength); + } + + // Check that the readers are in the capabilities registry. + _ensureInRegistry(p2pIds); + } + + function _validateDynamicConfig( + bytes memory encodedStaticConfig, + bytes memory encodedDynamicConfig + ) internal pure override { + // OCR doesn't use dynamic config + } + + function _getConfigDigestPrefix() internal pure override returns (uint256) { + return PREFIX; + } + + // ================================================================ + // │ Chain Configuration │ + // ================================================================ + + /// @notice Sets and/or removes chain configurations. + /// Does not validate that fChain <= FRoleDON and relies on OCR3Configs to be changed in case fChain becomes larger than the FRoleDON value. + /// @param chainSelectorRemoves The chain configurations to remove. + /// @param chainConfigAdds The chain configurations to add. + function applyChainConfigUpdates( + uint64[] calldata chainSelectorRemoves, + CCIPConfigTypes.ChainConfigInfo[] calldata chainConfigAdds + ) external onlyOwner { + // Process removals first. + for (uint256 i = 0; i < chainSelectorRemoves.length; ++i) { + // check if the chain selector is in s_remoteChainSelectors first. + if (!s_remoteChainSelectors.contains(chainSelectorRemoves[i])) { + revert ChainSelectorNotFound(chainSelectorRemoves[i]); + } + + delete s_chainConfigurations[chainSelectorRemoves[i]]; + s_remoteChainSelectors.remove(chainSelectorRemoves[i]); + + emit ChainConfigRemoved(chainSelectorRemoves[i]); + } + + // Process additions next. + for (uint256 i = 0; i < chainConfigAdds.length; ++i) { + CCIPConfigTypes.ChainConfig memory chainConfig = chainConfigAdds[i].chainConfig; + uint64 chainSelector = chainConfigAdds[i].chainSelector; + + // Verify that the provided readers are present in the capabilities registry. + _ensureInRegistry(chainConfig.readers); + + // Verify that fChain is positive. + if (chainConfig.fChain == 0) { + revert FChainMustBePositive(); + } + + s_chainConfigurations[chainSelector] = chainConfig; + s_remoteChainSelectors.add(chainSelector); + + emit ChainConfigSet(chainSelector, chainConfig); + } + } + + /// @notice Helper function to ensure that a node is in the capabilities registry. + /// @param p2pIds The P2P IDs of the node to check. + function _ensureInRegistry(bytes32[] memory p2pIds) internal view { + for (uint256 i = 0; i < p2pIds.length; ++i) { + // TODO add a method that does the validation in the ICapabilitiesRegistry contract + if (ICapabilitiesRegistry(i_capabilitiesRegistry).getNode(p2pIds[i]).p2pId == bytes32("")) { + revert NodeNotInRegistry(p2pIds[i]); + } + } + } +} diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index c151530546..e2690f76a9 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -60,6 +60,12 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig function _getConfigDigestPrefix() internal pure virtual returns (uint256); + /// @notice Returns the capabilities registry address. + /// @return The capabilities registry address. + function getCapabilityRegistry() external view returns (address) { + return i_capabilitiesRegistry; + } + /// @inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { return interfaceId == type(ICapabilityConfiguration).interfaceId || interfaceId == type(IERC165).interfaceId; @@ -84,13 +90,13 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig /// @inheritdoc ICapabilityConfiguration /// @dev The CCIP capability will fetch the configuration needed directly from this contract. - /// The offchain syncer will call this function, however, so its important that it doesn't revert. + /// The offchain syncer will call this function, so its important that it doesn't revert. function getCapabilityConfiguration(uint32 /* donId */ ) external pure override returns (bytes memory configuration) { return bytes(""); } + /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero /// digest. This is done to prevent exposing old config state that is invalid. - function _getStoredConfig( uint32 donId, uint8 pluginType, @@ -174,7 +180,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig } uint32 newVersion = ++s_configCount; - newConfigDigest = _calculateConfigDigest(encodedStaticConfig, newVersion); + newConfigDigest = _calculateConfigDigest(donId, pluginType, encodedStaticConfig, newVersion); StoredConfig memory newConfig = StoredConfig({ configDigest: newConfigDigest, @@ -252,12 +258,21 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig emit ConfigPromoted(digestToPromote); } - function _calculateConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { + function _calculateConfigDigest( + uint32 donId, + uint8 pluginType, + bytes memory staticConfig, + uint32 version + ) internal view returns (bytes32) { return bytes32( (_getConfigDigestPrefix() & PREFIX_MASK) | ( uint256( - keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), version), staticConfig)) + keccak256( + bytes.concat( + abi.encode(bytes32("EVM"), block.chainid, address(this), donId, pluginType, version), staticConfig + ) + ) ) & ~PREFIX_MASK ) ); From 0c076138dbc802217cc7d0323c89c1e47d402316 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 14:04:39 +0200 Subject: [PATCH 17/36] fix getter types --- .../src/v0.8/ccip/capability/CCIPHome.sol | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index 6c05aa540c..129dacb01b 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -38,35 +38,32 @@ contract RMNHome is HomeBase { error InvalidNode(CCIPConfigTypes.OCR3Node node); error NotEnoughTransmitters(uint256 got, uint256 minimum); - struct Node { - bytes32 peerId; // Used for p2p communication. - bytes32 offchainPublicKey; // Observations are signed with this public key, and are only verified offchain. + /// @notice Represents an oracle node in OCR3 configs part of the role DON. + /// Every configured node should be a signer, but does not have to be a transmitter. + struct OCR3Node { + bytes32 p2pId; // Peer2Peer connection ID of the oracle + bytes signerKey; // On-chain signer public key + bytes transmitterKey; // On-chain transmitter public key. Can be set to empty bytes to represent that the node is a signer but not a transmitter. } - struct SourceChain { - uint64 chainSelector; // ─────╮ The Source chain selector. - uint64 minObservers; // ──────╯ Required number of observers to agree on an observation for this source chain. - uint256 observerNodesBitmap; // ObserverNodesBitmap & (1<= fChain, since FRoleDON represents the role DON, and fChain represents sub-committees. + /// FRoleDON values are typically identical across multiple OCR3 configs since the chains pertain to one role DON, + /// but FRoleDON values can change across OCR3 configs to indicate role DON splits. + struct OCR3Config { + Internal.OCRPluginType pluginType; // ────────╮ The plugin that the configuration is for. + uint64 chainSelector; // | The (remote) chain that the configuration is for. + uint8 FRoleDON; // | The "big F" parameter for the role DON. + uint64 offchainConfigVersion; // ─────────────╯ The version of the offchain configuration. + bytes offrampAddress; // The remote chain offramp address. + OCR3Node[] nodes; // Keys & IDs of nodes part of the role DON + bytes offchainConfig; // The offchain configuration for the OCR3 protocol. Protobuf encoded. } struct VersionedConfig { uint32 version; bytes32 configDigest; - StaticConfig staticConfig; - DynamicConfig dynamicConfig; + OCR3Config staticConfig; } string public constant override typeAndVersion = "CCIPHome 1.6.0-dev"; @@ -102,8 +99,7 @@ contract RMNHome is HomeBase { VersionedConfig({ version: storedConfig.version, configDigest: storedConfig.configDigest, - staticConfig: abi.decode(storedConfig.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(storedConfig.dynamicConfig, (DynamicConfig)) + staticConfig: abi.decode(storedConfig.staticConfig, (OCR3Config)) }), true ); @@ -123,8 +119,7 @@ contract RMNHome is HomeBase { primaryConfig = VersionedConfig({ version: primaryStoredConfig.version, configDigest: primaryStoredConfig.configDigest, - staticConfig: abi.decode(primaryStoredConfig.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(primaryStoredConfig.dynamicConfig, (DynamicConfig)) + staticConfig: abi.decode(primaryStoredConfig.staticConfig, (OCR3Config)) }); } @@ -134,8 +129,7 @@ contract RMNHome is HomeBase { secondaryConfig = VersionedConfig({ version: secondaryStoredConfig.version, configDigest: secondaryStoredConfig.configDigest, - staticConfig: abi.decode(secondaryStoredConfig.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(secondaryStoredConfig.dynamicConfig, (DynamicConfig)) + staticConfig: abi.decode(secondaryStoredConfig.staticConfig, (OCR3Config)) }); } From f1766b2502a9ab2f27d2144e4d3bef91963512c3 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 14:07:56 +0200 Subject: [PATCH 18/36] fix tests & make all config methods callable --- contracts/src/v0.8/ccip/capability/HomeBase.sol | 6 +++--- contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol | 13 +++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index e2690f76a9..43e5171a2b 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -201,7 +201,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig uint8 pluginType, bytes calldata newDynamicConfig, bytes32 currentDigest - ) external onlyOwner { + ) external OnlyOwnerOrSelfCall { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { if (s_configs[donId][pluginType][i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { _validateDynamicConfig(s_configs[donId][pluginType][i].staticConfig, newDynamicConfig); @@ -219,7 +219,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(uint32 donId, uint8 pluginType, bytes32 configDigest) external onlyOwner { + function revokeSecondary(uint32 donId, uint8 pluginType, bytes32 configDigest) external OnlyOwnerOrSelfCall { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; if (s_configs[donId][pluginType][secondaryConfigIndex].configDigest != configDigest) { revert ConfigDigestMismatch(s_configs[donId][pluginType][secondaryConfigIndex].configDigest, configDigest); @@ -238,7 +238,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig uint8 pluginType, bytes32 digestToPromote, bytes32 digestToRevoke - ) external onlyOwner { + ) external OnlyOwnerOrSelfCall { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; if (s_configs[donId][pluginType][secondaryConfigIndex].configDigest != digestToPromote) { revert ConfigDigestMismatch(s_configs[donId][pluginType][secondaryConfigIndex].configDigest, digestToPromote); diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol index db891a106b..4aed5671ff 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol @@ -50,7 +50,12 @@ contract RMNHomeTest is Test { (PREFIX & PREFIX_MASK) | ( uint256( - keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), version), staticConfig)) + keccak256( + bytes.concat( + abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), RMN_DON_ID, RMN_PLUGIN_TYPE, version), + staticConfig + ) + ) ) & ~PREFIX_MASK ) ); @@ -242,7 +247,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { vm.startPrank(address(0)); - vm.expectRevert("Only callable by owner"); + vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.dynamicConfig), keccak256("configDigest")); } } @@ -296,7 +301,7 @@ contract RMNHome_revokeSecondary is RMNHomeTest { function test_revokeSecondary_OnlyOwner_reverts() public { vm.startPrank(address(0)); - vm.expectRevert("Only callable by owner"); + vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); s_rmnHome.revokeSecondary(RMN_DON_ID, RMN_PLUGIN_TYPE, keccak256("configDigest")); } } @@ -307,7 +312,7 @@ contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { function test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() public { vm.startPrank(address(0)); - vm.expectRevert("Only callable by owner"); + vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); s_rmnHome.promoteSecondaryAndRevokePrimary( RMN_DON_ID, RMN_PLUGIN_TYPE, keccak256("toPromote"), keccak256("ToRevoke") ); From 72b088427c858afaca933b377a7cc4d71b029e13 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 14:18:42 +0200 Subject: [PATCH 19/36] add getAllChainConfigs --- .../src/v0.8/ccip/capability/CCIPHome.sol | 75 ++++++++++++++----- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index 129dacb01b..0c65957455 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -5,20 +5,14 @@ import {ICapabilitiesRegistry} from "./interfaces/ICapabilitiesRegistry.sol"; import {Internal} from "../libraries/Internal.sol"; import {HomeBase} from "./HomeBase.sol"; -import {CCIPConfigTypes} from "./libraries/CCIPConfigTypes.sol"; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; -/// @notice Stores the home configuration for RMN, that is referenced by CCIP oracles, RMN nodes, and the RMNRemote -/// contracts. -contract RMNHome is HomeBase { +contract CCIPHome is HomeBase { using EnumerableSet for EnumerableSet.UintSet; event ChainConfigRemoved(uint64 chainSelector); - /// @notice Emitted when a chain's configuration is set. - /// @param chainSelector The chain selector. - /// @param chainConfig The chain configuration. - event ChainConfigSet(uint64 chainSelector, CCIPConfigTypes.ChainConfig chainConfig); + event ChainConfigSet(uint64 chainSelector, ChainConfig chainConfig); error OutOfBoundsNodesLength(); error DuplicatePeerId(); @@ -35,7 +29,7 @@ contract RMNHome is HomeBase { error FChainTooHigh(uint256 fChain, uint256 FRoleDON); error TooManySigners(); error FTooHigh(); - error InvalidNode(CCIPConfigTypes.OCR3Node node); + error InvalidNode(OCR3Node node); error NotEnoughTransmitters(uint256 got, uint256 minimum); /// @notice Represents an oracle node in OCR3 configs part of the role DON. @@ -51,10 +45,10 @@ contract RMNHome is HomeBase { /// FRoleDON values are typically identical across multiple OCR3 configs since the chains pertain to one role DON, /// but FRoleDON values can change across OCR3 configs to indicate role DON splits. struct OCR3Config { - Internal.OCRPluginType pluginType; // ────────╮ The plugin that the configuration is for. - uint64 chainSelector; // | The (remote) chain that the configuration is for. - uint8 FRoleDON; // | The "big F" parameter for the role DON. - uint64 offchainConfigVersion; // ─────────────╯ The version of the offchain configuration. + Internal.OCRPluginType pluginType; // ─╮ The plugin that the configuration is for. + uint64 chainSelector; // | The (remote) chain that the configuration is for. + uint8 FRoleDON; // | The "big F" parameter for the role DON. + uint64 offchainConfigVersion; // ──────╯ The version of the offchain configuration. bytes offrampAddress; // The remote chain offramp address. OCR3Node[] nodes; // Keys & IDs of nodes part of the role DON bytes offchainConfig; // The offchain configuration for the OCR3 protocol. Protobuf encoded. @@ -66,6 +60,20 @@ contract RMNHome is HomeBase { OCR3Config staticConfig; } + /// @notice Chain configuration. + /// Changes to chain configuration are detected out-of-band in plugins and decoded offchain. + struct ChainConfig { + bytes32[] readers; // The P2P IDs of the readers for the chain. These IDs must be registered in the capabilities registry. + uint8 fChain; // The fault tolerance parameter of the chain. + bytes config; // The chain configuration. This is kept intentionally opaque so as to add fields in the future if needed. + } + + /// @notice Chain configuration information struct used in applyChainConfigUpdates and getAllChainConfigs. + struct ChainConfigArgs { + uint64 chainSelector; + ChainConfig chainConfig; + } + string public constant override typeAndVersion = "CCIPHome 1.6.0-dev"; uint256 private constant PREFIX = 0x000a << (256 - 16); // 0x000a00..00 @@ -74,12 +82,12 @@ contract RMNHome is HomeBase { uint256 internal constant MAX_NUM_ORACLES = 256; /// @dev chain configuration for each chain that CCIP is deployed on. - mapping(uint64 chainSelector => CCIPConfigTypes.ChainConfig chainConfig) private s_chainConfigurations; + mapping(uint64 chainSelector => ChainConfig chainConfig) private s_chainConfigurations; /// @dev All chains that are configured. EnumerableSet.UintSet private s_remoteChainSelectors; - constructor() HomeBase(address(1)) {} + constructor(address capabilitiesRegistry) HomeBase(capabilitiesRegistry) {} /// @notice Returns the total number of chains configured. /// @return The total number of chains configured. @@ -137,7 +145,7 @@ contract RMNHome is HomeBase { } function _validateStaticAndDynamicConfig(bytes memory encodedStaticConfig, bytes memory) internal view override { - CCIPConfigTypes.OCR3Config memory cfg = abi.decode(encodedStaticConfig, (CCIPConfigTypes.OCR3Config)); + OCR3Config memory cfg = abi.decode(encodedStaticConfig, (OCR3Config)); if (cfg.chainSelector == 0) revert ChainSelectorNotSet(); if (cfg.pluginType != Internal.OCRPluginType.Commit && cfg.pluginType != Internal.OCRPluginType.Execution) { @@ -166,7 +174,7 @@ contract RMNHome is HomeBase { uint256 nonZeroTransmitters = 0; bytes32[] memory p2pIds = new bytes32[](numberOfNodes); for (uint256 i = 0; i < numberOfNodes; ++i) { - CCIPConfigTypes.OCR3Node memory node = cfg.nodes[i]; + OCR3Node memory node = cfg.nodes[i]; // 3 * fChain + 1 <= nonZeroTransmitters <= 3 * FRoleDON + 1 // Transmitters can be set to 0 since there can be more signers than transmitters, @@ -208,13 +216,42 @@ contract RMNHome is HomeBase { // │ Chain Configuration │ // ================================================================ + /// @notice Returns all the chain configurations. + /// @param pageIndex The page index. + /// @param pageSize The page size. + /// @return paginatedChainConfigs chain configurations. + function getAllChainConfigs(uint256 pageIndex, uint256 pageSize) external view returns (ChainConfigArgs[] memory) { + uint256 numberOfChains = s_remoteChainSelectors.length(); + uint256 startIndex = pageIndex * pageSize; + + if (pageSize == 0 || startIndex >= numberOfChains) { + return new ChainConfigArgs[](0); // Return an empty array if pageSize is 0 or pageIndex is out of bounds + } + + uint256 endIndex = startIndex + pageSize; + if (endIndex > numberOfChains) { + endIndex = numberOfChains; + } + + ChainConfigArgs[] memory paginatedChainConfigs = new ChainConfigArgs[](endIndex - startIndex); + + uint256[] memory chainSelectors = s_remoteChainSelectors.values(); + for (uint256 i = startIndex; i < endIndex; ++i) { + uint64 chainSelector = uint64(chainSelectors[i]); + paginatedChainConfigs[i - startIndex] = + ChainConfigArgs({chainSelector: chainSelector, chainConfig: s_chainConfigurations[chainSelector]}); + } + + return paginatedChainConfigs; + } + /// @notice Sets and/or removes chain configurations. /// Does not validate that fChain <= FRoleDON and relies on OCR3Configs to be changed in case fChain becomes larger than the FRoleDON value. /// @param chainSelectorRemoves The chain configurations to remove. /// @param chainConfigAdds The chain configurations to add. function applyChainConfigUpdates( uint64[] calldata chainSelectorRemoves, - CCIPConfigTypes.ChainConfigInfo[] calldata chainConfigAdds + ChainConfigArgs[] calldata chainConfigAdds ) external onlyOwner { // Process removals first. for (uint256 i = 0; i < chainSelectorRemoves.length; ++i) { @@ -231,7 +268,7 @@ contract RMNHome is HomeBase { // Process additions next. for (uint256 i = 0; i < chainConfigAdds.length; ++i) { - CCIPConfigTypes.ChainConfig memory chainConfig = chainConfigAdds[i].chainConfig; + ChainConfig memory chainConfig = chainConfigAdds[i].chainConfig; uint64 chainSelector = chainConfigAdds[i].chainSelector; // Verify that the provided readers are present in the capabilities registry. From 4318f80eb95be9c57ca65b24848320f12d1165d2 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 14:23:57 +0200 Subject: [PATCH 20/36] add don and type to getAllConfig --- .../src/v0.8/ccip/capability/CCIPHome.sol | 18 +++++++++++------- contracts/src/v0.8/ccip/capability/RMNHome.sol | 13 ++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index 0c65957455..c76814ea80 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -8,6 +8,11 @@ import {HomeBase} from "./HomeBase.sol"; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; +/// @notice CCIPHome stores the configuration for the CCIP capability. +/// We have two classes of configuration: chain configuration and DON (in the CapabilitiesRegistry sense) configuration. +/// Each chain will have a single configuration which includes information like the router address. +/// Each CR DON will have up to four configurations: for each of (commit, exec), one blue and one green configuration. +/// This is done in order to achieve "blue-green" deployments. contract CCIPHome is HomeBase { using EnumerableSet for EnumerableSet.UintSet; @@ -116,12 +121,11 @@ contract CCIPHome is HomeBase { return (versionedConfig, false); } - function getAllConfigs() - external - view - returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) - { - (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(0, 0); + function getAllConfigs( + uint32 donId, + uint8 pluginType + ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { + (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(donId, pluginType); if (primaryOk) { primaryConfig = VersionedConfig({ @@ -131,7 +135,7 @@ contract CCIPHome is HomeBase { }); } - (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(0, 0); + (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(donId, pluginType); if (secondaryOk) { secondaryConfig = VersionedConfig({ diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index e5376057b6..8a5f83e60c 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -72,12 +72,11 @@ contract RMNHome is HomeBase { return (versionedConfig, false); } - function getAllConfigs() - external - view - returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) - { - (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(0, 0); + function getAllConfigs( + uint32 donId, + uint8 pluginType + ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { + (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(donId, pluginType); if (primaryOk) { primaryConfig = VersionedConfig({ @@ -88,7 +87,7 @@ contract RMNHome is HomeBase { }); } - (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(0, 0); + (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(donId, pluginType); if (secondaryOk) { secondaryConfig = VersionedConfig({ From ed271dccffd11ab6aa31b1fa57502b6a588e81e2 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 14:57:42 +0200 Subject: [PATCH 21/36] add DON id and plugin type to getConfig --- .../src/v0.8/ccip/capability/CCIPHome.sol | 8 ++++-- .../src/v0.8/ccip/capability/RMNHome.sol | 8 ++++-- .../{rmn => capability}/RMNHomeTest.t.sol | 25 +++++++++++-------- .../src/v0.8/ccip/test/rmn/RMNHome.t.sol | 15 ----------- 4 files changed, 26 insertions(+), 30 deletions(-) rename contracts/src/v0.8/ccip/test/{rmn => capability}/RMNHomeTest.t.sol (95%) delete mode 100644 contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index c76814ea80..9e612ea2a8 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -105,8 +105,12 @@ contract CCIPHome is HomeBase { /// @param configDigest The digest of the config to fetch. /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. - function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(0, 0, configDigest); + function getConfig( + uint32 donId, + uint8 pluginType, + bytes32 configDigest + ) external view returns (VersionedConfig memory versionedConfig, bool ok) { + (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(donId, pluginType, configDigest); if (configOK) { return ( VersionedConfig({ diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index 8a5f83e60c..f11bd5ba87 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -55,8 +55,12 @@ contract RMNHome is HomeBase { /// @param configDigest The digest of the config to fetch. /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. - function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(0, 0, configDigest); + function getConfig( + uint32 donId, + uint8 pluginType, + bytes32 configDigest + ) external view returns (VersionedConfig memory versionedConfig, bool ok) { + (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(donId, pluginType, configDigest); if (configOK) { return ( VersionedConfig({ diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol similarity index 95% rename from contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol rename to contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol index 4aed5671ff..07b7fb9c04 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol @@ -8,8 +8,8 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; contract RMNHomeTest is Test { - uint32 internal constant RMN_DON_ID = 0; - uint8 internal constant RMN_PLUGIN_TYPE = 0; + uint32 internal constant RMN_DON_ID = 593; + uint8 internal constant RMN_PLUGIN_TYPE = 244; struct Config { RMNHome.StaticConfig staticConfig; @@ -88,7 +88,8 @@ contract RMNHome_setSecondary is RMNHomeTest { RMN_DON_ID, RMN_PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST ); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(versionedConfig.configDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = + s_rmnHome.getConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, versionedConfig.configDigest); assertTrue(ok); assertEq(storedVersionedConfig.version, versionedConfig.version); RMNHome.StaticConfig memory storedStaticConfig = storedVersionedConfig.staticConfig; @@ -194,12 +195,12 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { } function test_setDynamicConfig_success() public { - (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(0, 0); + (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers--; - (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(0, 0); + (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); bytes memory encodedConfig = abi.encode(config.dynamicConfig); vm.expectEmit(); @@ -207,7 +208,8 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, encodedConfig, secondaryConfigDigest); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = + s_rmnHome.getConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, secondaryConfigDigest); assertTrue(ok); assertEq( storedVersionedConfig.dynamicConfig.sourceChains[0].minObservers, @@ -215,7 +217,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { ); // Asser the digests don't change when updating the dynamic config - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(0, 0); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, secondaryConfigDigest); } @@ -269,14 +271,15 @@ contract RMNHome_revokeSecondary is RMNHomeTest { } function test_revokeSecondary_success() public { - (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(0, 0); + (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); vm.expectEmit(); emit HomeBase.ConfigRevoked(priorSecondaryDigest); s_rmnHome.revokeSecondary(RMN_DON_ID, RMN_PLUGIN_TYPE, priorSecondaryDigest); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorSecondaryDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = + s_rmnHome.getConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, priorSecondaryDigest); assertFalse(ok); // Ensure no old data is returned, even though it's still in storage assertEq(storedVersionedConfig.version, 0); @@ -284,14 +287,14 @@ contract RMNHome_revokeSecondary is RMNHomeTest { assertEq(storedVersionedConfig.dynamicConfig.sourceChains.length, 0); // Asser the primary digest is unaffected but the secondary digest is set to zero - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(0, 0); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, ZERO_DIGEST); assertTrue(secondaryDigest != priorSecondaryDigest); } function test_revokeSecondary_ConfigDigestMismatch_reverts() public { - (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(0, 0); + (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); bytes32 wrongDigest = keccak256("wrong_digest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol deleted file mode 100644 index e1fe3c7dc4..0000000000 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {RMNHome} from "../../capability/RMNHome.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {BaseTest} from "../BaseTest.t.sol"; - -contract RMNHomeTest is BaseTest { - RMNHome public s_rmnHome; - - function setUp() public virtual override { - super.setUp(); - s_rmnHome = new RMNHome(); - } -} From d572c5cb64b49889a38ad92737ae307e60203344 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 16:22:10 +0200 Subject: [PATCH 22/36] add basic tests --- contracts/gas-snapshots/ccip.gas-snapshot | 47 ++-- .../src/v0.8/ccip/capability/HomeBase.sol | 96 +++++---- .../ccip/test/capability/HomeBaseTest.t.sol | 204 ++++++++++++++++++ .../v0.8/ccip/test/helpers/HomeBaseHelper.sol | 48 +++++ 4 files changed, 335 insertions(+), 60 deletions(-) create mode 100644 contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol create mode 100644 contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 671db1dc9d..d8ae6cde30 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,25 +809,36 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) -RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 9867) -RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 187) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 11121) +RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 9872) +RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 9901) +RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 275772) +RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 794676) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 11073) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 11119) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 20027) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11177) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 30125) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 34017) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20877) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15831) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 135045) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 21070) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20864) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24663) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 25102) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18283) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 187009) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24807) -RMNHome_setSecondary:test_setSecondary_success() (gas: 821271) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 20060) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 20108) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11085) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11129) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28514) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 30361) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 34065) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20901) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 11973) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15786) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 135276) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 64750) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 21062) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20856) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24623) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 25062) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 12873) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18238) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 187001) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24767) +RMNHome_setSecondary:test_setSecondary_success() (gas: 283462) +RMNHome_setSecondary:test_setSecondary_success() (gas: 821737) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 43e5171a2b..f182007a56 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -60,6 +60,10 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig function _getConfigDigestPrefix() internal pure virtual returns (uint256); + // ================================================================ + // │ Capability Registry │ + // ================================================================ + /// @notice Returns the capabilities registry address. /// @return The capabilities registry address. function getCapabilityRegistry() external view returns (address) { @@ -95,6 +99,32 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig return bytes(""); } + // ================================================================ + // │ Getters │ + // ================================================================ + + /// @notice Returns the current primary and secondary config digests. + /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. + /// @return primaryConfigDigest The digest of the primary config. + /// @return secondaryConfigDigest The digest of the secondary config. + function getConfigDigests( + uint32 donId, + uint8 pluginType + ) external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + return ( + s_configs[donId][pluginType][s_primaryConfigIndex].configDigest, + s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest + ); + } + + function getPrimaryDigest(uint32 donId, uint8 pluginType) public view returns (bytes32) { + return s_configs[donId][pluginType][s_primaryConfigIndex].configDigest; + } + + function getSecondaryDigest(uint32 donId, uint8 pluginType) public view returns (bytes32) { + return s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest; + } + /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero /// digest. This is done to prevent exposing old config state that is invalid. function _getStoredConfig( @@ -134,27 +164,9 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig return (s_configs[donId][pluginType][s_primaryConfigIndex ^ 1], true); } - function getPrimaryDigest(uint32 donId, uint8 pluginType) public view returns (bytes32) { - return s_configs[donId][pluginType][s_primaryConfigIndex].configDigest; - } - - function getSecondaryDigest(uint32 donId, uint8 pluginType) public view returns (bytes32) { - return s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest; - } - - /// @notice Returns the current primary and secondary config digests. - /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. - /// @return primaryConfigDigest The digest of the primary config. - /// @return secondaryConfigDigest The digest of the secondary config. - function getConfigDigests( - uint32 donId, - uint8 pluginType - ) external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { - return ( - s_configs[donId][pluginType][s_primaryConfigIndex].configDigest, - s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest - ); - } + // ================================================================ + // │ State transitions │ + // ================================================================ /// @notice Sets a new config as the secondary config. Does not influence the primary config. /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. @@ -196,27 +208,6 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig return newConfigDigest; } - function setDynamicConfig( - uint32 donId, - uint8 pluginType, - bytes calldata newDynamicConfig, - bytes32 currentDigest - ) external OnlyOwnerOrSelfCall { - for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - if (s_configs[donId][pluginType][i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { - _validateDynamicConfig(s_configs[donId][pluginType][i].staticConfig, newDynamicConfig); - - // Since the static config doesn't change we don't have to update the digest or version. - s_configs[donId][pluginType][i].dynamicConfig = newDynamicConfig; - - emit DynamicConfigSet(currentDigest, newDynamicConfig); - return; - } - } - - revert DigestNotFound(currentDigest); - } - /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. function revokeSecondary(uint32 donId, uint8 pluginType, bytes32 configDigest) external OnlyOwnerOrSelfCall { @@ -258,6 +249,27 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig emit ConfigPromoted(digestToPromote); } + function setDynamicConfig( + uint32 donId, + uint8 pluginType, + bytes calldata newDynamicConfig, + bytes32 currentDigest + ) external OnlyOwnerOrSelfCall { + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { + if (s_configs[donId][pluginType][i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { + _validateDynamicConfig(s_configs[donId][pluginType][i].staticConfig, newDynamicConfig); + + // Since the static config doesn't change we don't have to update the digest or version. + s_configs[donId][pluginType][i].dynamicConfig = newDynamicConfig; + + emit DynamicConfigSet(currentDigest, newDynamicConfig); + return; + } + } + + revert DigestNotFound(currentDigest); + } + function _calculateConfigDigest( uint32 donId, uint8 pluginType, diff --git a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol new file mode 100644 index 0000000000..6bb8e02557 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {HomeBase} from "../../capability/HomeBase.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {HomeBaseHelper} from "../helpers/HomeBaseHelper.sol"; +import {Test} from "forge-std/Test.sol"; +import {Vm} from "forge-std/Vm.sol"; + +contract HomeBaseTest is Test { + uint32 internal constant DON_ID = 593; + uint8 internal constant PLUGIN_TYPE = 244; + + bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); + + HomeBaseHelper internal s_homeBase; + address private constant CAPABILITIES_REGISTRY = address(1); + + function setUp() public virtual { + s_homeBase = new HomeBaseHelper(CAPABILITIES_REGISTRY); + } + + uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 + uint256 public constant PREFIX = 0x0c0c << (256 - 16); + + function _getConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { + return bytes32( + (PREFIX & PREFIX_MASK) + | ( + uint256( + keccak256( + bytes.concat( + abi.encode(bytes32("EVM"), block.chainid, address(s_homeBase), DON_ID, PLUGIN_TYPE, version), staticConfig + ) + ) + ) & ~PREFIX_MASK + ) + ); + } + + function _getStaticConfig() internal pure returns (bytes memory) { + return abi.encode("staticConfig"); + } + + function _getDynamicConfig() internal pure returns (bytes memory) { + return abi.encode("dynamicConfig"); + } +} + +contract RMNHome_setSecondary is HomeBaseTest { + function test_setSecondary_success() public { + HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ + configDigest: ZERO_DIGEST, + version: 1, + staticConfig: _getStaticConfig(), + dynamicConfig: _getDynamicConfig() + }); + + encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); + + vm.expectEmit(); + emit HomeBase.ConfigSet(encodedConfig); + + s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); + + (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getSecondaryStoredConfig(DON_ID, PLUGIN_TYPE); + assertTrue(ok); + assertEq(storedConfig.version, encodedConfig.version); + assertEq(storedConfig.configDigest, encodedConfig.configDigest); + assertEq(storedConfig.staticConfig, encodedConfig.staticConfig); + assertEq(storedConfig.dynamicConfig, encodedConfig.dynamicConfig); + } + + function test_setSecondary_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); + } +} + +contract RMNHome_setDynamicConfig is HomeBaseTest { + function setUp() public override { + super.setUp(); + s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); + } + + function test_setDynamicConfig_success() public { + (bytes32 priorPrimaryDigest, bytes32 secondaryConfigDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + + bytes memory newDynamicConfig = abi.encode("newDynamicConfig"); + + vm.expectEmit(); + emit HomeBase.DynamicConfigSet(secondaryConfigDigest, newDynamicConfig); + + s_homeBase.setDynamicConfig(DON_ID, PLUGIN_TYPE, newDynamicConfig, secondaryConfigDigest); + + (HomeBase.StoredConfig memory storedConfig, bool ok) = + s_homeBase.getStoredConfig(DON_ID, PLUGIN_TYPE, secondaryConfigDigest); + assertTrue(ok); + assertEq(storedConfig.dynamicConfig, newDynamicConfig); + + // Asser the digests don't change when updating the dynamic config + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + assertEq(primaryDigest, priorPrimaryDigest); + assertEq(secondaryDigest, secondaryConfigDigest); + } + + function test_setDynamicConfig_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + s_homeBase.setDynamicConfig(DON_ID, PLUGIN_TYPE, _getDynamicConfig(), keccak256("configDigest")); + } +} + +contract RMNHome_revokeSecondary is HomeBaseTest { + // Sets two configs + function setUp() public override { + super.setUp(); + bytes32 digest = s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); + s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, PLUGIN_TYPE, digest, ZERO_DIGEST); + s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); + } + + function test_revokeSecondary_success() public { + (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + + vm.expectEmit(); + emit HomeBase.ConfigRevoked(priorSecondaryDigest); + + s_homeBase.revokeSecondary(DON_ID, PLUGIN_TYPE, priorSecondaryDigest); + + (HomeBase.StoredConfig memory storedVersionedConfig, bool ok) = + s_homeBase.getStoredConfig(DON_ID, PLUGIN_TYPE, priorSecondaryDigest); + assertFalse(ok); + // Ensure no old data is returned, even though it's still in storage + assertEq(storedVersionedConfig.version, 0); + assertEq(storedVersionedConfig.staticConfig.length, 0); + assertEq(storedVersionedConfig.dynamicConfig.length, 0); + + // Asser the primary digest is unaffected but the secondary digest is set to zero + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + assertEq(primaryDigest, priorPrimaryDigest); + assertEq(secondaryDigest, ZERO_DIGEST); + assertTrue(secondaryDigest != priorSecondaryDigest); + } + + function test_revokeSecondary_ConfigDigestMismatch_reverts() public { + (, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + + bytes32 wrongDigest = keccak256("wrong_digest"); + vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); + s_homeBase.revokeSecondary(DON_ID, PLUGIN_TYPE, wrongDigest); + } + + function test_revokeSecondary_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + s_homeBase.revokeSecondary(DON_ID, PLUGIN_TYPE, keccak256("configDigest")); + } +} + +contract RMNHome_promoteSecondaryAndRevokePrimary is HomeBaseTest { + function test_promoteSecondaryAndRevokePrimary_success() public {} + + function test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, PLUGIN_TYPE, keccak256("toPromote"), keccak256("ToRevoke")); + } +} + +contract RMNHome_beforeCapabilityConfigSet is HomeBaseTest { + function test_beforeCapabilityConfigSet_success() public { + vm.startPrank(address(1)); + + HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ + configDigest: ZERO_DIGEST, + version: 1, + staticConfig: _getStaticConfig(), + dynamicConfig: _getDynamicConfig() + }); + encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); + + bytes memory callPayload = abi.encodeCall( + HomeBase.setSecondary, (DON_ID, PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) + ); + + vm.expectEmit(); + emit HomeBase.ConfigSet(encodedConfig); + + s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, DON_ID); + } + + function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); + s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, DON_ID); + } +} diff --git a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol new file mode 100644 index 0000000000..a18b9cb766 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {HomeBase} from "../../capability/HomeBase.sol"; + +contract HomeBaseHelper is HomeBase { + string public constant override typeAndVersion = "HomeBaseHelper 1.6.0-dev"; + + uint256 public constant PREFIX = 0x0c0c << (256 - 16); + + constructor(address capabilitiesRegistry) HomeBase(capabilitiesRegistry) {} + + function _validateStaticAndDynamicConfig(bytes memory, bytes memory) internal view override {} + + function _validateDynamicConfig(bytes memory, bytes memory) internal view override {} + + function _getConfigDigestPrefix() internal pure override returns (uint256) { + return PREFIX; + } + + function getStoredConfig( + uint32 donId, + uint8 pluginType, + bytes32 configDigest + ) external view returns (StoredConfig memory, bool ok) { + return _getStoredConfig(donId, pluginType, configDigest); + } + + function getPrimaryStoredConfig(uint32 donId, uint8 pluginType) external view returns (StoredConfig memory, bool ok) { + return _getPrimaryStoredConfig(donId, pluginType); + } + + function getSecondaryStoredConfig( + uint32 donId, + uint8 pluginType + ) external view returns (StoredConfig memory, bool ok) { + return _getSecondaryStoredConfig(donId, pluginType); + } + + function calculateConfigDigest( + uint32 donId, + uint8 pluginType, + bytes memory staticConfig, + uint32 version + ) external view returns (bytes32) { + return _calculateConfigDigest(donId, pluginType, staticConfig, version); + } +} From 488c5e648ab626e3197875226d7c369aed3fa95b Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 17:23:49 +0200 Subject: [PATCH 23/36] use single key to index --- contracts/gas-snapshots/ccip.gas-snapshot | 52 ++++---- .../src/v0.8/ccip/capability/CCIPHome.sol | 12 +- .../src/v0.8/ccip/capability/HomeBase.sol | 84 ++++++------ .../src/v0.8/ccip/capability/RMNHome.sol | 12 +- .../ccip/test/capability/HomeBaseTest.t.sol | 58 ++++---- .../ccip/test/capability/RMNHomeTest.t.sol | 125 +++++------------- .../v0.8/ccip/test/helpers/HomeBaseHelper.sol | 21 ++- 7 files changed, 140 insertions(+), 224 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index d8ae6cde30..2d2b83dd9d 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -810,35 +810,33 @@ PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 9872) -RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 9901) -RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 275772) -RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 794676) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 11073) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 11119) +RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 275003) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10907) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10953) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 20060) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 20108) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11085) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11129) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28514) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 30361) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 34065) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20901) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 11973) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15786) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 135276) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 64750) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 21062) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20856) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24623) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 25062) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 12873) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18238) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 187001) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24767) -RMNHome_setSecondary:test_setSecondary_success() (gas: 283462) -RMNHome_setSecondary:test_setSecondary_success() (gas: 821737) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19344) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19366) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10912) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10956) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27066) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28966) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 33266) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20422) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 11834) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15457) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 133123) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 63135) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20894) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20688) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24455) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24894) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 12661) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18070) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186826) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24599) +RMNHome_setSecondary:test_setSecondary_success() (gas: 282478) +RMNHome_setSecondary:test_setSecondary_success() (gas: 820692) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index 9e612ea2a8..c1d336c927 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -106,11 +106,10 @@ contract CCIPHome is HomeBase { /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. function getConfig( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes32 configDigest ) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(donId, pluginType, configDigest); + (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(pluginKey, configDigest); if (configOK) { return ( VersionedConfig({ @@ -126,10 +125,9 @@ contract CCIPHome is HomeBase { } function getAllConfigs( - uint32 donId, - uint8 pluginType + bytes32 pluginKey ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { - (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(donId, pluginType); + (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(pluginKey); if (primaryOk) { primaryConfig = VersionedConfig({ @@ -139,7 +137,7 @@ contract CCIPHome is HomeBase { }); } - (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(donId, pluginType); + (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(pluginKey); if (secondaryOk) { secondaryConfig = VersionedConfig({ diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index f182007a56..8c42dd8b8c 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -32,7 +32,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. - mapping(uint32 donId => mapping(uint8 pluginType => StoredConfig[MAX_CONCURRENT_CONFIGS])) private s_configs; + mapping(bytes32 pluginKey => StoredConfig[MAX_CONCURRENT_CONFIGS]) private s_configs; /// @notice The total number of configs ever set, used for generating the version of the configs. uint32 private s_configCount = 0; @@ -108,60 +108,56 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig /// @return primaryConfigDigest The digest of the primary config. /// @return secondaryConfigDigest The digest of the secondary config. function getConfigDigests( - uint32 donId, - uint8 pluginType + bytes32 pluginKey ) external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { return ( - s_configs[donId][pluginType][s_primaryConfigIndex].configDigest, - s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest + s_configs[pluginKey][s_primaryConfigIndex].configDigest, + s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest ); } - function getPrimaryDigest(uint32 donId, uint8 pluginType) public view returns (bytes32) { - return s_configs[donId][pluginType][s_primaryConfigIndex].configDigest; + function getPrimaryDigest(bytes32 pluginKey) public view returns (bytes32) { + return s_configs[pluginKey][s_primaryConfigIndex].configDigest; } - function getSecondaryDigest(uint32 donId, uint8 pluginType) public view returns (bytes32) { - return s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest; + function getSecondaryDigest(bytes32 pluginKey) public view returns (bytes32) { + return s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest; } /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero /// digest. This is done to prevent exposing old config state that is invalid. function _getStoredConfig( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes32 configDigest ) internal view returns (StoredConfig memory storedConfig, bool ok) { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old // config state that is invalid. - if (s_configs[donId][pluginType][i].configDigest == configDigest && configDigest != ZERO_DIGEST) { - return (s_configs[donId][pluginType][i], true); + if (s_configs[pluginKey][i].configDigest == configDigest && configDigest != ZERO_DIGEST) { + return (s_configs[pluginKey][i], true); } } return (storedConfig, false); } function _getPrimaryStoredConfig( - uint32 donId, - uint8 pluginType + bytes32 pluginKey ) internal view returns (StoredConfig memory primaryConfig, bool ok) { - if (s_configs[donId][pluginType][s_primaryConfigIndex].configDigest == ZERO_DIGEST) { + if (s_configs[pluginKey][s_primaryConfigIndex].configDigest == ZERO_DIGEST) { return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } - return (s_configs[donId][pluginType][s_primaryConfigIndex], true); + return (s_configs[pluginKey][s_primaryConfigIndex], true); } function _getSecondaryStoredConfig( - uint32 donId, - uint8 pluginType + bytes32 pluginKey ) internal view returns (StoredConfig memory secondaryConfig, bool ok) { - if (s_configs[donId][pluginType][s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { + if (s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } - return (s_configs[donId][pluginType][s_primaryConfigIndex ^ 1], true); + return (s_configs[pluginKey][s_primaryConfigIndex ^ 1], true); } // ================================================================ @@ -172,15 +168,14 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. /// This is done to prevent accidental overwrites. function setSecondary( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes calldata encodedStaticConfig, bytes calldata encodedDynamicConfig, bytes32 digestToOverwrite ) external OnlyOwnerOrSelfCall returns (bytes32 newConfigDigest) { _validateStaticAndDynamicConfig(encodedStaticConfig, encodedDynamicConfig); - bytes32 existingDigest = getSecondaryDigest(donId, pluginType); + bytes32 existingDigest = getSecondaryDigest(pluginKey); if (existingDigest != digestToOverwrite) { revert ConfigDigestMismatch(existingDigest, digestToOverwrite); @@ -192,7 +187,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig } uint32 newVersion = ++s_configCount; - newConfigDigest = _calculateConfigDigest(donId, pluginType, encodedStaticConfig, newVersion); + newConfigDigest = _calculateConfigDigest(pluginKey, encodedStaticConfig, newVersion); StoredConfig memory newConfig = StoredConfig({ configDigest: newConfigDigest, @@ -201,7 +196,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig dynamicConfig: encodedDynamicConfig }); - s_configs[donId][pluginType][s_primaryConfigIndex ^ 1] = newConfig; + s_configs[pluginKey][s_primaryConfigIndex ^ 1] = newConfig; emit ConfigSet(newConfig); @@ -210,37 +205,36 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(uint32 donId, uint8 pluginType, bytes32 configDigest) external OnlyOwnerOrSelfCall { + function revokeSecondary(bytes32 pluginKey, bytes32 configDigest) external OnlyOwnerOrSelfCall { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[donId][pluginType][secondaryConfigIndex].configDigest != configDigest) { - revert ConfigDigestMismatch(s_configs[donId][pluginType][secondaryConfigIndex].configDigest, configDigest); + if (s_configs[pluginKey][secondaryConfigIndex].configDigest != configDigest) { + revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, configDigest); } emit ConfigRevoked(configDigest); // Delete only the digest, as that's what's used to determine if a config is active. This means the actual // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in // the future. - delete s_configs[donId][pluginType][secondaryConfigIndex].configDigest; + delete s_configs[pluginKey][secondaryConfigIndex].configDigest; } /// @notice Promotes the secondary config to the primary config and revokes the primary config. function promoteSecondaryAndRevokePrimary( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes32 digestToPromote, bytes32 digestToRevoke ) external OnlyOwnerOrSelfCall { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[donId][pluginType][secondaryConfigIndex].configDigest != digestToPromote) { - revert ConfigDigestMismatch(s_configs[donId][pluginType][secondaryConfigIndex].configDigest, digestToPromote); + if (s_configs[pluginKey][secondaryConfigIndex].configDigest != digestToPromote) { + revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, digestToPromote); } uint256 primaryConfigIndex = s_primaryConfigIndex; - if (s_configs[donId][pluginType][primaryConfigIndex].configDigest != digestToRevoke) { - revert ConfigDigestMismatch(s_configs[donId][pluginType][primaryConfigIndex].configDigest, digestToRevoke); + if (s_configs[pluginKey][primaryConfigIndex].configDigest != digestToRevoke) { + revert ConfigDigestMismatch(s_configs[pluginKey][primaryConfigIndex].configDigest, digestToRevoke); } - delete s_configs[donId][pluginType][primaryConfigIndex].configDigest; + delete s_configs[pluginKey][primaryConfigIndex].configDigest; s_primaryConfigIndex ^= 1; if (digestToRevoke != ZERO_DIGEST) { @@ -250,17 +244,16 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig } function setDynamicConfig( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes calldata newDynamicConfig, bytes32 currentDigest ) external OnlyOwnerOrSelfCall { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - if (s_configs[donId][pluginType][i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { - _validateDynamicConfig(s_configs[donId][pluginType][i].staticConfig, newDynamicConfig); + if (s_configs[pluginKey][i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { + _validateDynamicConfig(s_configs[pluginKey][i].staticConfig, newDynamicConfig); // Since the static config doesn't change we don't have to update the digest or version. - s_configs[donId][pluginType][i].dynamicConfig = newDynamicConfig; + s_configs[pluginKey][i].dynamicConfig = newDynamicConfig; emit DynamicConfigSet(currentDigest, newDynamicConfig); return; @@ -271,8 +264,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig } function _calculateConfigDigest( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes memory staticConfig, uint32 version ) internal view returns (bytes32) { @@ -281,9 +273,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig | ( uint256( keccak256( - bytes.concat( - abi.encode(bytes32("EVM"), block.chainid, address(this), donId, pluginType, version), staticConfig - ) + bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), pluginKey, version), staticConfig) ) ) & ~PREFIX_MASK ) diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index f11bd5ba87..a4f3c839d2 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -56,11 +56,10 @@ contract RMNHome is HomeBase { /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. function getConfig( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes32 configDigest ) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(donId, pluginType, configDigest); + (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(pluginKey, configDigest); if (configOK) { return ( VersionedConfig({ @@ -77,10 +76,9 @@ contract RMNHome is HomeBase { } function getAllConfigs( - uint32 donId, - uint8 pluginType + bytes32 pluginKey ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { - (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(donId, pluginType); + (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(pluginKey); if (primaryOk) { primaryConfig = VersionedConfig({ @@ -91,7 +89,7 @@ contract RMNHome is HomeBase { }); } - (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(donId, pluginType); + (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(pluginKey); if (secondaryOk) { secondaryConfig = VersionedConfig({ diff --git a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol index 6bb8e02557..f33d146aba 100644 --- a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol @@ -8,13 +8,12 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; contract HomeBaseTest is Test { - uint32 internal constant DON_ID = 593; - uint8 internal constant PLUGIN_TYPE = 244; + bytes32 internal constant DON_ID = bytes32(uint256(0x87654321eabc)); bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); HomeBaseHelper internal s_homeBase; - address private constant CAPABILITIES_REGISTRY = address(1); + address internal constant CAPABILITIES_REGISTRY = address(1); function setUp() public virtual { s_homeBase = new HomeBaseHelper(CAPABILITIES_REGISTRY); @@ -29,9 +28,7 @@ contract HomeBaseTest is Test { | ( uint256( keccak256( - bytes.concat( - abi.encode(bytes32("EVM"), block.chainid, address(s_homeBase), DON_ID, PLUGIN_TYPE, version), staticConfig - ) + bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(s_homeBase), DON_ID, version), staticConfig) ) ) & ~PREFIX_MASK ) @@ -61,9 +58,9 @@ contract RMNHome_setSecondary is HomeBaseTest { vm.expectEmit(); emit HomeBase.ConfigSet(encodedConfig); - s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); + s_homeBase.setSecondary(DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); - (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getSecondaryStoredConfig(DON_ID, PLUGIN_TYPE); + (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getSecondaryStoredConfig(DON_ID); assertTrue(ok); assertEq(storedConfig.version, encodedConfig.version); assertEq(storedConfig.configDigest, encodedConfig.configDigest); @@ -75,33 +72,32 @@ contract RMNHome_setSecondary is HomeBaseTest { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); - s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); + s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); } } contract RMNHome_setDynamicConfig is HomeBaseTest { function setUp() public override { super.setUp(); - s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); + s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); } function test_setDynamicConfig_success() public { - (bytes32 priorPrimaryDigest, bytes32 secondaryConfigDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + (bytes32 priorPrimaryDigest, bytes32 secondaryConfigDigest) = s_homeBase.getConfigDigests(DON_ID); bytes memory newDynamicConfig = abi.encode("newDynamicConfig"); vm.expectEmit(); emit HomeBase.DynamicConfigSet(secondaryConfigDigest, newDynamicConfig); - s_homeBase.setDynamicConfig(DON_ID, PLUGIN_TYPE, newDynamicConfig, secondaryConfigDigest); + s_homeBase.setDynamicConfig(DON_ID, newDynamicConfig, secondaryConfigDigest); - (HomeBase.StoredConfig memory storedConfig, bool ok) = - s_homeBase.getStoredConfig(DON_ID, PLUGIN_TYPE, secondaryConfigDigest); + (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getStoredConfig(DON_ID, secondaryConfigDigest); assertTrue(ok); assertEq(storedConfig.dynamicConfig, newDynamicConfig); // Asser the digests don't change when updating the dynamic config - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, secondaryConfigDigest); } @@ -110,7 +106,7 @@ contract RMNHome_setDynamicConfig is HomeBaseTest { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); - s_homeBase.setDynamicConfig(DON_ID, PLUGIN_TYPE, _getDynamicConfig(), keccak256("configDigest")); + s_homeBase.setDynamicConfig(DON_ID, _getDynamicConfig(), keccak256("configDigest")); } } @@ -118,21 +114,21 @@ contract RMNHome_revokeSecondary is HomeBaseTest { // Sets two configs function setUp() public override { super.setUp(); - bytes32 digest = s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); - s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, PLUGIN_TYPE, digest, ZERO_DIGEST); - s_homeBase.setSecondary(DON_ID, PLUGIN_TYPE, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); + bytes32 digest = s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); + s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, digest, ZERO_DIGEST); + s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); } function test_revokeSecondary_success() public { - (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID); vm.expectEmit(); emit HomeBase.ConfigRevoked(priorSecondaryDigest); - s_homeBase.revokeSecondary(DON_ID, PLUGIN_TYPE, priorSecondaryDigest); + s_homeBase.revokeSecondary(DON_ID, priorSecondaryDigest); (HomeBase.StoredConfig memory storedVersionedConfig, bool ok) = - s_homeBase.getStoredConfig(DON_ID, PLUGIN_TYPE, priorSecondaryDigest); + s_homeBase.getStoredConfig(DON_ID, priorSecondaryDigest); assertFalse(ok); // Ensure no old data is returned, even though it's still in storage assertEq(storedVersionedConfig.version, 0); @@ -140,25 +136,25 @@ contract RMNHome_revokeSecondary is HomeBaseTest { assertEq(storedVersionedConfig.dynamicConfig.length, 0); // Asser the primary digest is unaffected but the secondary digest is set to zero - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, ZERO_DIGEST); assertTrue(secondaryDigest != priorSecondaryDigest); } function test_revokeSecondary_ConfigDigestMismatch_reverts() public { - (, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID, PLUGIN_TYPE); + (, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID); bytes32 wrongDigest = keccak256("wrong_digest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); - s_homeBase.revokeSecondary(DON_ID, PLUGIN_TYPE, wrongDigest); + s_homeBase.revokeSecondary(DON_ID, wrongDigest); } function test_revokeSecondary_OnlyOwner_reverts() public { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); - s_homeBase.revokeSecondary(DON_ID, PLUGIN_TYPE, keccak256("configDigest")); + s_homeBase.revokeSecondary(DON_ID, keccak256("configDigest")); } } @@ -169,13 +165,13 @@ contract RMNHome_promoteSecondaryAndRevokePrimary is HomeBaseTest { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); - s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, PLUGIN_TYPE, keccak256("toPromote"), keccak256("ToRevoke")); + s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, keccak256("toPromote"), keccak256("ToRevoke")); } } contract RMNHome_beforeCapabilityConfigSet is HomeBaseTest { function test_beforeCapabilityConfigSet_success() public { - vm.startPrank(address(1)); + vm.startPrank(CAPABILITIES_REGISTRY); HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ configDigest: ZERO_DIGEST, @@ -186,19 +182,19 @@ contract RMNHome_beforeCapabilityConfigSet is HomeBaseTest { encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); bytes memory callPayload = abi.encodeCall( - HomeBase.setSecondary, (DON_ID, PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) + HomeBase.setSecondary, (DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) ); vm.expectEmit(); emit HomeBase.ConfigSet(encodedConfig); - s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, DON_ID); + s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, 0); } function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); - s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, DON_ID); + s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, 0); } } diff --git a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol index 07b7fb9c04..63b4e5dffc 100644 --- a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol @@ -8,8 +8,7 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; contract RMNHomeTest is Test { - uint32 internal constant RMN_DON_ID = 593; - uint8 internal constant RMN_PLUGIN_TYPE = 244; + bytes32 internal constant RMN_DON_ID = bytes32(uint256(0xaaabbb333eee)); struct Config { RMNHome.StaticConfig staticConfig; @@ -52,8 +51,7 @@ contract RMNHomeTest is Test { uint256( keccak256( bytes.concat( - abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), RMN_DON_ID, RMN_PLUGIN_TYPE, version), - staticConfig + abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), RMN_DON_ID, version), staticConfig ) ) ) & ~PREFIX_MASK @@ -84,12 +82,10 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.expectEmit(); emit HomeBase.ConfigSet(encodedConfig); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = - s_rmnHome.getConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, versionedConfig.configDigest); + s_rmnHome.getConfig(RMN_DON_ID, versionedConfig.configDigest); assertTrue(ok); assertEq(storedVersionedConfig.version, versionedConfig.version); RMNHome.StaticConfig memory storedStaticConfig = storedVersionedConfig.staticConfig; @@ -118,9 +114,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicatePeerId_reverts() public { @@ -128,9 +122,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { @@ -138,9 +130,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicateSourceChain_reverts() public { @@ -148,9 +138,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { @@ -158,9 +146,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_MinObserversTooHigh_reverts() public { @@ -168,9 +154,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_OnlyOwner_reverts() public { @@ -179,9 +163,7 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } } @@ -189,27 +171,25 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_success() public { - (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); + (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(RMN_DON_ID); Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers--; - (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); + (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); bytes memory encodedConfig = abi.encode(config.dynamicConfig); vm.expectEmit(); emit HomeBase.DynamicConfigSet(secondaryConfigDigest, encodedConfig); - s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, encodedConfig, secondaryConfigDigest); + s_rmnHome.setDynamicConfig(RMN_DON_ID, encodedConfig, secondaryConfigDigest); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = - s_rmnHome.getConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, secondaryConfigDigest); + s_rmnHome.getConfig(RMN_DON_ID, secondaryConfigDigest); assertTrue(ok); assertEq( storedVersionedConfig.dynamicConfig.sourceChains[0].minObservers, @@ -217,7 +197,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { ); // Asser the digests don't change when updating the dynamic config - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, secondaryConfigDigest); } @@ -228,20 +208,18 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_DigestNotFound_reverts() public { // Zero always reverts vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); + s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); // Non-existent digest reverts bytes32 nonExistentDigest = keccak256("nonExistentDigest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, nonExistentDigest)); - s_rmnHome.setDynamicConfig( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest - ); + s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest); } function test_setDynamicConfig_OnlyOwner_reverts() public { @@ -250,7 +228,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); - s_rmnHome.setDynamicConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.dynamicConfig), keccak256("configDigest")); + s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(config.dynamicConfig), keccak256("configDigest")); } } @@ -259,27 +237,24 @@ contract RMNHome_revokeSecondary is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - bytes32 digest = s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); - s_rmnHome.promoteSecondaryAndRevokePrimary(RMN_DON_ID, RMN_PLUGIN_TYPE, digest, ZERO_DIGEST); + bytes32 digest = + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.promoteSecondaryAndRevokePrimary(RMN_DON_ID, digest, ZERO_DIGEST); config.dynamicConfig.sourceChains[0].minObservers--; - s_rmnHome.setSecondary( - RMN_DON_ID, RMN_PLUGIN_TYPE, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST - ); + s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_revokeSecondary_success() public { - (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); + (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); vm.expectEmit(); emit HomeBase.ConfigRevoked(priorSecondaryDigest); - s_rmnHome.revokeSecondary(RMN_DON_ID, RMN_PLUGIN_TYPE, priorSecondaryDigest); + s_rmnHome.revokeSecondary(RMN_DON_ID, priorSecondaryDigest); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = - s_rmnHome.getConfig(RMN_DON_ID, RMN_PLUGIN_TYPE, priorSecondaryDigest); + s_rmnHome.getConfig(RMN_DON_ID, priorSecondaryDigest); assertFalse(ok); // Ensure no old data is returned, even though it's still in storage assertEq(storedVersionedConfig.version, 0); @@ -287,25 +262,25 @@ contract RMNHome_revokeSecondary is RMNHomeTest { assertEq(storedVersionedConfig.dynamicConfig.sourceChains.length, 0); // Asser the primary digest is unaffected but the secondary digest is set to zero - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, ZERO_DIGEST); assertTrue(secondaryDigest != priorSecondaryDigest); } function test_revokeSecondary_ConfigDigestMismatch_reverts() public { - (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID, RMN_PLUGIN_TYPE); + (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); bytes32 wrongDigest = keccak256("wrong_digest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); - s_rmnHome.revokeSecondary(RMN_DON_ID, RMN_PLUGIN_TYPE, wrongDigest); + s_rmnHome.revokeSecondary(RMN_DON_ID, wrongDigest); } function test_revokeSecondary_OnlyOwner_reverts() public { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); - s_rmnHome.revokeSecondary(RMN_DON_ID, RMN_PLUGIN_TYPE, keccak256("configDigest")); + s_rmnHome.revokeSecondary(RMN_DON_ID, keccak256("configDigest")); } } @@ -316,40 +291,6 @@ contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); - s_rmnHome.promoteSecondaryAndRevokePrimary( - RMN_DON_ID, RMN_PLUGIN_TYPE, keccak256("toPromote"), keccak256("ToRevoke") - ); - } -} - -contract RMNHome_beforeCapabilityConfigSet is RMNHomeTest { - function test_beforeCapabilityConfigSet_success() public { - vm.startPrank(address(1)); - - Config memory config = _getBaseConfig(); - HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ - configDigest: ZERO_DIGEST, - version: 1, - staticConfig: abi.encode(config.staticConfig), - dynamicConfig: abi.encode(config.dynamicConfig) - }); - encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); - - bytes memory callPayload = abi.encodeCall( - HomeBase.setSecondary, - (RMN_DON_ID, RMN_PLUGIN_TYPE, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) - ); - - vm.expectEmit(); - emit HomeBase.ConfigSet(encodedConfig); - - s_rmnHome.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, RMN_DON_ID); - } - - function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); - s_rmnHome.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, RMN_DON_ID); + s_rmnHome.promoteSecondaryAndRevokePrimary(RMN_DON_ID, keccak256("toPromote"), keccak256("ToRevoke")); } } diff --git a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol index a18b9cb766..4b0bc18763 100644 --- a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol @@ -19,30 +19,25 @@ contract HomeBaseHelper is HomeBase { } function getStoredConfig( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes32 configDigest ) external view returns (StoredConfig memory, bool ok) { - return _getStoredConfig(donId, pluginType, configDigest); + return _getStoredConfig(pluginKey, configDigest); } - function getPrimaryStoredConfig(uint32 donId, uint8 pluginType) external view returns (StoredConfig memory, bool ok) { - return _getPrimaryStoredConfig(donId, pluginType); + function getPrimaryStoredConfig(bytes32 pluginKey) external view returns (StoredConfig memory, bool ok) { + return _getPrimaryStoredConfig(pluginKey); } - function getSecondaryStoredConfig( - uint32 donId, - uint8 pluginType - ) external view returns (StoredConfig memory, bool ok) { - return _getSecondaryStoredConfig(donId, pluginType); + function getSecondaryStoredConfig(bytes32 pluginKey) external view returns (StoredConfig memory, bool ok) { + return _getSecondaryStoredConfig(pluginKey); } function calculateConfigDigest( - uint32 donId, - uint8 pluginType, + bytes32 pluginKey, bytes memory staticConfig, uint32 version ) external view returns (bytes32) { - return _calculateConfigDigest(donId, pluginType, staticConfig, version); + return _calculateConfigDigest(pluginKey, staticConfig, version); } } From 5309aa2f1bf9b0fc7194a7d0ad9c62ced23ce1a9 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 23 Sep 2024 17:28:32 +0200 Subject: [PATCH 24/36] move CR to CCIPHome --- contracts/gas-snapshots/ccip.gas-snapshot | 44 ++++++------ .../src/v0.8/ccip/capability/CCIPHome.sol | 69 +++++++++++++++++-- .../src/v0.8/ccip/capability/HomeBase.sol | 55 +-------------- .../src/v0.8/ccip/capability/RMNHome.sol | 2 - .../ccip/test/capability/HomeBaseTest.t.sol | 61 ++++++++-------- .../v0.8/ccip/test/helpers/HomeBaseHelper.sol | 2 - 6 files changed, 115 insertions(+), 118 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 2d2b83dd9d..b3c9e70cf6 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,34 +809,32 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) -RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 9872) -RMNHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 275003) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10907) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10953) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10885) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10997) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19344) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19366) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19344) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10890) RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10912) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10956) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27066) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28966) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 33266) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20422) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 11834) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15457) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 133123) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 63135) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20894) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20688) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24455) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24894) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27088) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28943) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 33356) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20467) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 11812) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15502) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 133259) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 63157) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20872) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20666) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24457) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24896) RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 12661) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18070) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186826) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24599) -RMNHome_setSecondary:test_setSecondary_success() (gas: 282478) -RMNHome_setSecondary:test_setSecondary_success() (gas: 820692) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18048) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186804) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24601) +RMNHome_setSecondary:test_setSecondary_success() (gas: 282455) +RMNHome_setSecondary:test_setSecondary_success() (gas: 820695) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index c1d336c927..2d8bd28c75 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -1,11 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; +import {ICapabilityConfiguration} from "../../keystone/interfaces/ICapabilityConfiguration.sol"; import {ICapabilitiesRegistry} from "./interfaces/ICapabilitiesRegistry.sol"; import {Internal} from "../libraries/Internal.sol"; import {HomeBase} from "./HomeBase.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/interfaces/IERC165.sol"; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; /// @notice CCIPHome stores the configuration for the CCIP capability. @@ -13,7 +15,7 @@ import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts /// Each chain will have a single configuration which includes information like the router address. /// Each CR DON will have up to four configurations: for each of (commit, exec), one blue and one green configuration. /// This is done in order to achieve "blue-green" deployments. -contract CCIPHome is HomeBase { +contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { using EnumerableSet for EnumerableSet.UintSet; event ChainConfigRemoved(uint64 chainSelector); @@ -86,20 +88,65 @@ contract CCIPHome is HomeBase { /// @dev 256 is the hard limit due to the bit encoding of their indexes into a uint256. uint256 internal constant MAX_NUM_ORACLES = 256; + /// @dev The canonical capabilities registry address. + address internal immutable i_capabilitiesRegistry; + /// @dev chain configuration for each chain that CCIP is deployed on. mapping(uint64 chainSelector => ChainConfig chainConfig) private s_chainConfigurations; /// @dev All chains that are configured. EnumerableSet.UintSet private s_remoteChainSelectors; - constructor(address capabilitiesRegistry) HomeBase(capabilitiesRegistry) {} + constructor(address capabilitiesRegistry) { + if (capabilitiesRegistry == address(0)) { + revert ZeroAddressNotAllowed(); + } + i_capabilitiesRegistry = capabilitiesRegistry; + } - /// @notice Returns the total number of chains configured. - /// @return The total number of chains configured. - function getNumChainConfigurations() external view returns (uint256) { - return s_remoteChainSelectors.length(); + // ================================================================ + // │ Capability Registry │ + // ================================================================ + + /// @notice Returns the capabilities registry address. + /// @return The capabilities registry address. + function getCapabilityRegistry() external view returns (address) { + return i_capabilitiesRegistry; + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + return interfaceId == type(ICapabilityConfiguration).interfaceId || interfaceId == type(IERC165).interfaceId; + } + + /// @notice Called by the registry prior to the config being set for a particular DON. + /// @dev precondition Requires destination chain config to be set + function beforeCapabilityConfigSet( + bytes32[] calldata, // nodes + bytes calldata update, + uint64, // configCount + uint32 // donId + ) external override { + if (msg.sender != i_capabilitiesRegistry) { + revert OnlyCapabilitiesRegistryCanCall(); + } + (bool success, bytes memory errorData) = address(this).call(update); + if (!success) { + revert(string(errorData)); + } + } + + /// @inheritdoc ICapabilityConfiguration + /// @dev The CCIP capability will fetch the configuration needed directly from this contract. + /// The offchain syncer will call this function, so its important that it doesn't revert. + function getCapabilityConfiguration(uint32 /* donId */ ) external pure override returns (bytes memory configuration) { + return bytes(""); } + // ================================================================ + // │ Getters │ + // ================================================================ + /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use /// in case one of the configs is too large to be returnable by one of the other getters. /// @param configDigest The digest of the config to fetch. @@ -150,6 +197,10 @@ contract CCIPHome is HomeBase { return (primaryConfig, secondaryConfig); } + // ================================================================ + // │ Validation │ + // ================================================================ + function _validateStaticAndDynamicConfig(bytes memory encodedStaticConfig, bytes memory) internal view override { OCR3Config memory cfg = abi.decode(encodedStaticConfig, (OCR3Config)); @@ -222,6 +273,12 @@ contract CCIPHome is HomeBase { // │ Chain Configuration │ // ================================================================ + /// @notice Returns the total number of chains configured. + /// @return The total number of chains configured. + function getNumChainConfigurations() external view returns (uint256) { + return s_remoteChainSelectors.length(); + } + /// @notice Returns all the chain configurations. /// @param pageIndex The page index. /// @param pageSize The page size. diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 8c42dd8b8c..53787d1582 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -1,14 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {ICapabilityConfiguration} from "../../keystone/interfaces/ICapabilityConfiguration.sol"; import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/interfaces/IERC165.sol"; - -abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, IERC165 { +abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { event ConfigSet(StoredConfig versionedConfig); event ConfigRevoked(bytes32 indexed configDigest); event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); @@ -27,9 +24,6 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig /// @notice Helper to identify the zero config digest with less casting. bytes32 private constant ZERO_DIGEST = bytes32(uint256(0)); - /// @dev The canonical capabilities registry address. - address internal immutable i_capabilitiesRegistry; - /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. mapping(bytes32 pluginKey => StoredConfig[MAX_CONCURRENT_CONFIGS]) private s_configs; @@ -46,59 +40,12 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion, ICapabilityConfig bytes dynamicConfig; } - /// @param capabilitiesRegistry the canonical capabilities registry address. - constructor(address capabilitiesRegistry) { - if (capabilitiesRegistry == address(0)) { - revert ZeroAddressNotAllowed(); - } - i_capabilitiesRegistry = capabilitiesRegistry; - } - function _validateStaticAndDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; function _validateDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; function _getConfigDigestPrefix() internal pure virtual returns (uint256); - // ================================================================ - // │ Capability Registry │ - // ================================================================ - - /// @notice Returns the capabilities registry address. - /// @return The capabilities registry address. - function getCapabilityRegistry() external view returns (address) { - return i_capabilitiesRegistry; - } - - /// @inheritdoc IERC165 - function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { - return interfaceId == type(ICapabilityConfiguration).interfaceId || interfaceId == type(IERC165).interfaceId; - } - - /// @notice Called by the registry prior to the config being set for a particular DON. - /// @dev precondition Requires destination chain config to be set - function beforeCapabilityConfigSet( - bytes32[] calldata, // nodes - bytes calldata update, - uint64, // configCount - uint32 // donId - ) external override { - if (msg.sender != i_capabilitiesRegistry) { - revert OnlyCapabilitiesRegistryCanCall(); - } - (bool success, bytes memory errorData) = address(this).call(update); - if (!success) { - revert(string(errorData)); - } - } - - /// @inheritdoc ICapabilityConfiguration - /// @dev The CCIP capability will fetch the configuration needed directly from this contract. - /// The offchain syncer will call this function, so its important that it doesn't revert. - function getCapabilityConfiguration(uint32 /* donId */ ) external pure override returns (bytes memory configuration) { - return bytes(""); - } - // ================================================================ // │ Getters │ // ================================================================ diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index a4f3c839d2..951c749af1 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -48,8 +48,6 @@ contract RMNHome is HomeBase { uint256 private constant PREFIX = 0x000b << (256 - 16); // 0x000b00..00 - constructor() HomeBase(address(1)) {} - /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use /// in case one of the configs is too large to be returnable by one of the other getters. /// @param configDigest The digest of the config to fetch. diff --git a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol index f33d146aba..fd9f91de34 100644 --- a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol @@ -13,10 +13,9 @@ contract HomeBaseTest is Test { bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); HomeBaseHelper internal s_homeBase; - address internal constant CAPABILITIES_REGISTRY = address(1); function setUp() public virtual { - s_homeBase = new HomeBaseHelper(CAPABILITIES_REGISTRY); + s_homeBase = new HomeBaseHelper(); } uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 @@ -169,32 +168,32 @@ contract RMNHome_promoteSecondaryAndRevokePrimary is HomeBaseTest { } } -contract RMNHome_beforeCapabilityConfigSet is HomeBaseTest { - function test_beforeCapabilityConfigSet_success() public { - vm.startPrank(CAPABILITIES_REGISTRY); - - HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ - configDigest: ZERO_DIGEST, - version: 1, - staticConfig: _getStaticConfig(), - dynamicConfig: _getDynamicConfig() - }); - encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); - - bytes memory callPayload = abi.encodeCall( - HomeBase.setSecondary, (DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) - ); - - vm.expectEmit(); - emit HomeBase.ConfigSet(encodedConfig); - - s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, 0); - } - - function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); - s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, 0); - } -} +//contract RMNHome_beforeCapabilityConfigSet is HomeBaseTest { +// function test_beforeCapabilityConfigSet_success() public { +// vm.startPrank(CAPABILITIES_REGISTRY); +// +// HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ +// configDigest: ZERO_DIGEST, +// version: 1, +// staticConfig: _getStaticConfig(), +// dynamicConfig: _getDynamicConfig() +// }); +// encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); +// +// bytes memory callPayload = abi.encodeCall( +// HomeBase.setSecondary, (DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) +// ); +// +// vm.expectEmit(); +// emit HomeBase.ConfigSet(encodedConfig); +// +// s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, 0); +// } +// +// function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { +// vm.startPrank(address(0)); +// +// vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); +// s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, 0); +// } +//} diff --git a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol index 4b0bc18763..70633ed3e8 100644 --- a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol @@ -8,8 +8,6 @@ contract HomeBaseHelper is HomeBase { uint256 public constant PREFIX = 0x0c0c << (256 - 16); - constructor(address capabilitiesRegistry) HomeBase(capabilitiesRegistry) {} - function _validateStaticAndDynamicConfig(bytes memory, bytes memory) internal view override {} function _validateDynamicConfig(bytes memory, bytes memory) internal view override {} From ce86ed6ef94b286db16b9f9c7ecfabb5bd7cfc12 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Tue, 24 Sep 2024 12:58:12 +0200 Subject: [PATCH 25/36] extract caller validation into implementations --- contracts/gas-snapshots/ccip.gas-snapshot | 50 +++++++++---------- .../src/v0.8/ccip/capability/CCIPHome.sol | 11 +++- .../src/v0.8/ccip/capability/HomeBase.sol | 31 ++++++------ .../src/v0.8/ccip/capability/RMNHome.sol | 4 ++ .../ccip/test/capability/HomeBaseTest.t.sol | 16 +++--- .../ccip/test/capability/RMNHomeTest.t.sol | 8 +-- .../v0.8/ccip/test/helpers/HomeBaseHelper.sol | 8 +++ 7 files changed, 73 insertions(+), 55 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index b3c9e70cf6..722b3920f8 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -809,32 +809,32 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) PingPong_plumbing:test_Pausing_Success() (gas: 17810) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10885) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10997) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_InvalidCaller_reverts() (gas: 10972) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10967) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 187) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19344) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19344) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10890) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10912) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27088) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28943) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 33356) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20467) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 11812) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15502) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 133259) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 63157) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20872) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20666) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24457) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24896) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 12661) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18048) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186804) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24601) -RMNHome_setSecondary:test_setSecondary_success() (gas: 282455) -RMNHome_setSecondary:test_setSecondary_success() (gas: 820695) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19366) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19408) +RMNHome_revokeSecondary:test_revokeSecondary_InvalidCaller_reverts() (gas: 10866) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10997) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27257) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 29136) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 33400) +RMNHome_setDynamicConfig:test_setDynamicConfig_InvalidCaller_reverts() (gas: 11766) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20489) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15584) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 133281) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 63155) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20894) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20688) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24479) +RMNHome_setSecondary:test_setSecondary_InvalidCaller_reverts() (gas: 12615) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24918) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18130) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186826) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24623) +RMNHome_setSecondary:test_setSecondary_success() (gas: 282453) +RMNHome_setSecondary:test_setSecondary_success() (gas: 820717) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index 2d8bd28c75..ef02fd169c 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -38,6 +38,7 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { error FTooHigh(); error InvalidNode(OCR3Node node); error NotEnoughTransmitters(uint256 got, uint256 minimum); + error OnlySelfCallAllowed(); /// @notice Represents an oracle node in OCR3 configs part of the role DON. /// Every configured node should be a signer, but does not have to be a transmitter. @@ -132,7 +133,9 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { } (bool success, bytes memory errorData) = address(this).call(update); if (!success) { - revert(string(errorData)); + assembly { + revert(add(errorData, 32), errorData) + } } } @@ -269,6 +272,12 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { return PREFIX; } + function _validateCaller() internal view override { + if (msg.sender != address(this)) { + revert OnlySelfCallAllowed(); + } + } + // ================================================================ // │ Chain Configuration │ // ================================================================ diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 53787d1582..0f57ed29a2 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -15,7 +15,6 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { error DigestNotFound(bytes32 configDigest); error ZeroAddressNotAllowed(); error OnlyCapabilitiesRegistryCanCall(); - error OnlyOwnerOrSelfCallAllowed(); /// @notice Used for encoding the config digest prefix uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 @@ -44,6 +43,8 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { function _validateDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; + function _validateCaller() internal view virtual; + function _getConfigDigestPrefix() internal pure virtual returns (uint256); // ================================================================ @@ -84,7 +85,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { return (s_configs[pluginKey][i], true); } } - return (storedConfig, false); + return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } function _getPrimaryStoredConfig( @@ -119,7 +120,8 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { bytes calldata encodedStaticConfig, bytes calldata encodedDynamicConfig, bytes32 digestToOverwrite - ) external OnlyOwnerOrSelfCall returns (bytes32 newConfigDigest) { + ) external returns (bytes32 newConfigDigest) { + _validateCaller(); _validateStaticAndDynamicConfig(encodedStaticConfig, encodedDynamicConfig); bytes32 existingDigest = getSecondaryDigest(pluginKey); @@ -152,7 +154,9 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(bytes32 pluginKey, bytes32 configDigest) external OnlyOwnerOrSelfCall { + function revokeSecondary(bytes32 pluginKey, bytes32 configDigest) external { + _validateCaller(); + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; if (s_configs[pluginKey][secondaryConfigIndex].configDigest != configDigest) { revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, configDigest); @@ -170,7 +174,9 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { bytes32 pluginKey, bytes32 digestToPromote, bytes32 digestToRevoke - ) external OnlyOwnerOrSelfCall { + ) external { + _validateCaller(); + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; if (s_configs[pluginKey][secondaryConfigIndex].configDigest != digestToPromote) { revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, digestToPromote); @@ -190,11 +196,9 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { emit ConfigPromoted(digestToPromote); } - function setDynamicConfig( - bytes32 pluginKey, - bytes calldata newDynamicConfig, - bytes32 currentDigest - ) external OnlyOwnerOrSelfCall { + function setDynamicConfig(bytes32 pluginKey, bytes calldata newDynamicConfig, bytes32 currentDigest) external { + _validateCaller(); + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { if (s_configs[pluginKey][i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { _validateDynamicConfig(s_configs[pluginKey][i].staticConfig, newDynamicConfig); @@ -226,11 +230,4 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { ) ); } - - modifier OnlyOwnerOrSelfCall() { - if (msg.sender != owner() && msg.sender != address(this)) { - revert OnlyOwnerOrSelfCallAllowed(); - } - _; - } } diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index 951c749af1..dda5a92fab 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -166,6 +166,10 @@ contract RMNHome is HomeBase { _validateDynamicConfigParsed(dynamicConfig, numberOfNodes); } + function _validateCaller() internal view override { + _validateOwnership(); + } + function _getConfigDigestPrefix() internal pure override returns (uint256) { return PREFIX; } diff --git a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol index fd9f91de34..87abad68b0 100644 --- a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol @@ -67,10 +67,10 @@ contract RMNHome_setSecondary is HomeBaseTest { assertEq(storedConfig.dynamicConfig, encodedConfig.dynamicConfig); } - function test_setSecondary_OnlyOwner_reverts() public { + function test_setSecondary_InvalidCaller_reverts() public { vm.startPrank(address(0)); - vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); } } @@ -101,10 +101,10 @@ contract RMNHome_setDynamicConfig is HomeBaseTest { assertEq(secondaryDigest, secondaryConfigDigest); } - function test_setDynamicConfig_OnlyOwner_reverts() public { + function test_setDynamicConfig_InvalidCaller_reverts() public { vm.startPrank(address(0)); - vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); s_homeBase.setDynamicConfig(DON_ID, _getDynamicConfig(), keccak256("configDigest")); } } @@ -149,10 +149,10 @@ contract RMNHome_revokeSecondary is HomeBaseTest { s_homeBase.revokeSecondary(DON_ID, wrongDigest); } - function test_revokeSecondary_OnlyOwner_reverts() public { + function test_revokeSecondary_InvalidCaller_reverts() public { vm.startPrank(address(0)); - vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); s_homeBase.revokeSecondary(DON_ID, keccak256("configDigest")); } } @@ -160,10 +160,10 @@ contract RMNHome_revokeSecondary is HomeBaseTest { contract RMNHome_promoteSecondaryAndRevokePrimary is HomeBaseTest { function test_promoteSecondaryAndRevokePrimary_success() public {} - function test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() public { + function test_promoteSecondaryAndRevokePrimary_InvalidCaller_reverts() public { vm.startPrank(address(0)); - vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, keccak256("toPromote"), keccak256("ToRevoke")); } } diff --git a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol index 63b4e5dffc..84a2fc7727 100644 --- a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol @@ -162,7 +162,7 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.startPrank(address(0)); - vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + vm.expectRevert("Only callable by owner"); s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } } @@ -227,7 +227,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { vm.startPrank(address(0)); - vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + vm.expectRevert("Only callable by owner"); s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(config.dynamicConfig), keccak256("configDigest")); } } @@ -279,7 +279,7 @@ contract RMNHome_revokeSecondary is RMNHomeTest { function test_revokeSecondary_OnlyOwner_reverts() public { vm.startPrank(address(0)); - vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + vm.expectRevert("Only callable by owner"); s_rmnHome.revokeSecondary(RMN_DON_ID, keccak256("configDigest")); } } @@ -290,7 +290,7 @@ contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { function test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() public { vm.startPrank(address(0)); - vm.expectRevert(HomeBase.OnlyOwnerOrSelfCallAllowed.selector); + vm.expectRevert("Only callable by owner"); s_rmnHome.promoteSecondaryAndRevokePrimary(RMN_DON_ID, keccak256("toPromote"), keccak256("ToRevoke")); } } diff --git a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol index 70633ed3e8..0ff0df1ae4 100644 --- a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.24; import {HomeBase} from "../../capability/HomeBase.sol"; contract HomeBaseHelper is HomeBase { + error InvalidCaller(); + string public constant override typeAndVersion = "HomeBaseHelper 1.6.0-dev"; uint256 public constant PREFIX = 0x0c0c << (256 - 16); @@ -16,6 +18,12 @@ contract HomeBaseHelper is HomeBase { return PREFIX; } + function _validateCaller() internal view override { + if (msg.sender != owner()) { + revert InvalidCaller(); + } + } + function getStoredConfig( bytes32 pluginKey, bytes32 configDigest From 0b64028cd623171f51e6eb0db7794c2ef771b12c Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Tue, 24 Sep 2024 14:13:51 +0200 Subject: [PATCH 26/36] improve comments, rm ccipConfig --- contracts/gas-snapshots/ccip.gas-snapshot | 61 +- .../src/v0.8/ccip/capability/CCIPConfig.sol | 564 ------- .../src/v0.8/ccip/capability/CCIPHome.sol | 27 +- .../src/v0.8/ccip/capability/HomeBase.sol | 48 +- .../ccip/test/capability/CCIPConfig.t.sol | 1484 ----------------- .../ccip/test/capability/HomeBaseTest.t.sol | 2 +- .../ccip/test/capability/RMNHomeTest.t.sol | 2 +- .../ccip/test/helpers/CCIPConfigHelper.sol | 68 - 8 files changed, 69 insertions(+), 2187 deletions(-) delete mode 100644 contracts/src/v0.8/ccip/capability/CCIPConfig.sol delete mode 100644 contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol delete mode 100644 contracts/src/v0.8/ccip/test/helpers/CCIPConfigHelper.sol diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 722b3920f8..559ccae820 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -35,63 +35,6 @@ BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244050) BurnWithFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 24170) CCIPClientExample_sanity:test_ImmutableExamples_Success() (gas: 2052431) -CCIPConfigSetup:test_getCapabilityConfiguration_Success() (gas: 9508) -CCIPConfig_ConfigStateMachine:test__computeConfigDigest_Success() (gas: 83274) -CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_InitToRunning_Success() (gas: 354656) -CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_RunningToStaging_Success() (gas: 466876) -CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_StagingToRunning_Success() (gas: 433253) -CCIPConfig_ConfigStateMachine:test__groupByPluginType_TooManyOCR3Configs_Reverts() (gas: 28063) -CCIPConfig_ConfigStateMachine:test__groupByPluginType_threeCommitConfigs_Reverts() (gas: 61002) -CCIPConfig_ConfigStateMachine:test__groupByPluginType_threeExecutionConfigs_Reverts() (gas: 60940) -CCIPConfig_ConfigStateMachine:test__stateFromConfigLength_Success() (gas: 11668) -CCIPConfig_ConfigStateMachine:test__validateConfigStateTransition_Success() (gas: 8783) -CCIPConfig_ConfigStateMachine:test__validateConfigTransition_InitToRunning_Success() (gas: 305974) -CCIPConfig_ConfigStateMachine:test__validateConfigTransition_InitToRunning_WrongConfigCount_Reverts() (gas: 46142) -CCIPConfig_ConfigStateMachine:test__validateConfigTransition_NonExistentConfigTransition_Reverts() (gas: 25040) -CCIPConfig_ConfigStateMachine:test__validateConfigTransition_RunningToStaging_Success() (gas: 362471) -CCIPConfig_ConfigStateMachine:test__validateConfigTransition_RunningToStaging_WrongConfigCount_Reverts() (gas: 110001) -CCIPConfig_ConfigStateMachine:test__validateConfigTransition_RunningToStaging_WrongConfigDigestBlueGreen_Reverts() (gas: 141606) -CCIPConfig_ConfigStateMachine:test__validateConfigTransition_StagingToRunning_Success() (gas: 362211) -CCIPConfig_ConfigStateMachine:test__validateConfigTransition_StagingToRunning_WrongConfigDigest_Reverts() (gas: 141673) -CCIPConfig_ConfigStateMachine:test_getCapabilityConfiguration_Success() (gas: 9632) -CCIPConfig_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_CommitAndExecConfig_Success() (gas: 1699166) -CCIPConfig_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_CommitConfigOnly_Success() (gas: 991503) -CCIPConfig_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_ExecConfigOnly_Success() (gas: 991534) -CCIPConfig_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_Reverts() (gas: 9665) -CCIPConfig_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_ZeroLengthConfig_Success() (gas: 16042) -CCIPConfig_beforeCapabilityConfigSet:test_getCapabilityConfiguration_Success() (gas: 9596) -CCIPConfig_chainConfig:test__applyChainConfigUpdates_FChainNotPositive_Reverts() (gas: 185139) -CCIPConfig_chainConfig:test_applyChainConfigUpdates_addChainConfigs_Success() (gas: 347170) -CCIPConfig_chainConfig:test_applyChainConfigUpdates_nodeNotInRegistry_Reverts() (gas: 20599) -CCIPConfig_chainConfig:test_applyChainConfigUpdates_removeChainConfigs_Success() (gas: 270867) -CCIPConfig_chainConfig:test_applyChainConfigUpdates_selectorNotFound_Reverts() (gas: 14943) -CCIPConfig_chainConfig:test_getCapabilityConfiguration_Success() (gas: 9617) -CCIPConfig_chainConfig:test_getPaginatedCCIPConfigs_Success() (gas: 370770) -CCIPConfig_constructor:test_constructor_Success() (gas: 3155264) -CCIPConfig_constructor:test_constructor_ZeroAddressNotAllowed_Revert() (gas: 61226) -CCIPConfig_updatePluginConfig:test__updatePluginConfig_InvalidConfigLength_Reverts() (gas: 22160) -CCIPConfig_updatePluginConfig:test__updatePluginConfig_InvalidConfigStateTransition_Reverts() (gas: 19491) -CCIPConfig_updatePluginConfig:test_getCapabilityConfiguration_Success() (gas: 9654) -CCIPConfig_updatePluginConfig:test_updatePluginConfig_InitToRunning_Success() (gas: 1039133) -CCIPConfig_updatePluginConfig:test_updatePluginConfig_RunningToStaging_Success() (gas: 1923111) -CCIPConfig_updatePluginConfig:test_updatePluginConfig_StagingToRunning_Success() (gas: 1923145) -CCIPConfig_validateConfig:test__validateConfigLessTransmittersThanSigners_Success() (gas: 333177) -CCIPConfig_validateConfig:test__validateConfigSmallerFChain_Success() (gas: 464532) -CCIPConfig_validateConfig:test__validateConfig_ABIEncodedAddress_OfframpAddressCannotBeZero_Reverts() (gas: 288463) -CCIPConfig_validateConfig:test__validateConfig_ChainSelectorNotFound_Reverts() (gas: 291264) -CCIPConfig_validateConfig:test__validateConfig_ChainSelectorNotSet_Reverts() (gas: 288096) -CCIPConfig_validateConfig:test__validateConfig_FChainTooHigh_Reverts() (gas: 335512) -CCIPConfig_validateConfig:test__validateConfig_FMustBePositive_Reverts() (gas: 289648) -CCIPConfig_validateConfig:test__validateConfig_FTooHigh_Reverts() (gas: 289087) -CCIPConfig_validateConfig:test__validateConfig_NodeNotInRegistry_Reverts() (gas: 342330) -CCIPConfig_validateConfig:test__validateConfig_NotEnoughTransmittersEmptyAddresses_Reverts() (gas: 306844) -CCIPConfig_validateConfig:test__validateConfig_NotEnoughTransmitters_Reverts() (gas: 1209806) -CCIPConfig_validateConfig:test__validateConfig_OfframpAddressCannotBeZero_Reverts() (gas: 288202) -CCIPConfig_validateConfig:test__validateConfig_Success() (gas: 299119) -CCIPConfig_validateConfig:test__validateConfig_TooManySigners_Reverts() (gas: 788872) -CCIPConfig_validateConfig:test__validateConfig_ZeroP2PId_Reverts() (gas: 292418) -CCIPConfig_validateConfig:test__validateConfig_ZeroSignerKey_Reverts() (gas: 292467) -CCIPConfig_validateConfig:test_getCapabilityConfiguration_Success() (gas: 9596) CommitStore_constructor:test_Constructor_Success() (gas: 2855567) CommitStore_isUnpausedAndRMNHealthy:test_RMN_Success() (gas: 73954) CommitStore_report:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 28739) @@ -833,8 +776,8 @@ RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24918 RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18130) RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186826) RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24623) -RMNHome_setSecondary:test_setSecondary_success() (gas: 282453) -RMNHome_setSecondary:test_setSecondary_success() (gas: 820717) +RMNHome_setSecondary:test_setSecondary_success() (gas: 283227) +RMNHome_setSecondary:test_setSecondary_success() (gas: 821491) RMNRemote_constructor:test_constructor_success() (gas: 8334) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) diff --git a/contracts/src/v0.8/ccip/capability/CCIPConfig.sol b/contracts/src/v0.8/ccip/capability/CCIPConfig.sol deleted file mode 100644 index 3414f4912f..0000000000 --- a/contracts/src/v0.8/ccip/capability/CCIPConfig.sol +++ /dev/null @@ -1,564 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ICapabilityConfiguration} from "../../keystone/interfaces/ICapabilityConfiguration.sol"; -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {ICapabilitiesRegistry} from "./interfaces/ICapabilitiesRegistry.sol"; - -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -import {Internal} from "../libraries/Internal.sol"; -import {CCIPConfigTypes} from "./libraries/CCIPConfigTypes.sol"; - -import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/interfaces/IERC165.sol"; -import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; - -/// @notice CCIPConfig stores the configuration for the CCIP capability. -/// We have two classes of configuration: chain configuration and DON (in the CapabilitiesRegistry sense) configuration. -/// Each chain will have a single configuration which includes information like the router address. -/// Each CR DON will have up to four configurations: for each of (commit, exec), one blue and one green configuration. -/// This is done in order to achieve "blue-green" deployments. -contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator, IERC165 { - using EnumerableSet for EnumerableSet.UintSet; - - /// @notice Emitted when a chain's configuration is set. - /// @param chainSelector The chain selector. - /// @param chainConfig The chain configuration. - event ChainConfigSet(uint64 chainSelector, CCIPConfigTypes.ChainConfig chainConfig); - /// @notice Emitted when a chain's configuration is removed. - /// @param chainSelector The chain selector. - event ChainConfigRemoved(uint64 chainSelector); - - error NodeNotInRegistry(bytes32 p2pId); - error OnlyCapabilitiesRegistryCanCall(); - error ChainSelectorNotFound(uint64 chainSelector); - error ChainSelectorNotSet(); - error TooManyOCR3Configs(); - error TooManySigners(); - error InvalidNode(CCIPConfigTypes.OCR3Node node); - error NotEnoughTransmitters(uint256 got, uint256 minimum); - error FChainMustBePositive(); - error FTooHigh(); - error FChainTooHigh(uint256 fChain, uint256 FRoleDON); - error InvalidPluginType(); - error OfframpAddressCannotBeZero(); - error InvalidConfigLength(uint256 length); - error InvalidConfigStateTransition( - CCIPConfigTypes.ConfigState currentState, CCIPConfigTypes.ConfigState proposedState - ); - error NonExistentConfigTransition(); - error WrongConfigCount(uint64 got, uint64 expected); - error WrongConfigDigest(bytes32 got, bytes32 expected); - error WrongConfigDigestBlueGreen(bytes32 got, bytes32 expected); - error ZeroAddressNotAllowed(); - - event ConfigSet(uint32 indexed donId, uint8 indexed pluginType, CCIPConfigTypes.OCR3ConfigWithMeta[] config); - - /// @dev Type and version override. - string public constant override typeAndVersion = "CCIPConfig 1.6.0-dev"; - - /// @dev The canonical capabilities registry address. - address internal immutable i_capabilitiesRegistry; - - uint8 internal constant MAX_OCR3_CONFIGS_PER_PLUGIN = 2; - uint8 internal constant MAX_OCR3_CONFIGS_PER_DON = 4; - uint256 internal constant CONFIG_DIGEST_PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..0 - /// @dev must be equal to libocr multi role: https://github.com/smartcontractkit/libocr/blob/ae747ca5b81236ffdbf1714318c652e923a5ff4d/offchainreporting2plus/types/config_digest.go#L28 - uint256 internal constant CONFIG_DIGEST_PREFIX = 0x000a << (256 - 16); // 0x000a00..00 - bytes32 internal constant EMPTY_ENCODED_ADDRESS_HASH = keccak256(abi.encode(address(0))); - /// @dev 256 is the hard limit due to the bit encoding of their indexes into a uint256. - uint256 internal constant MAX_NUM_ORACLES = 256; - - /// @dev chain configuration for each chain that CCIP is deployed on. - mapping(uint64 chainSelector => CCIPConfigTypes.ChainConfig chainConfig) private s_chainConfigurations; - - /// @dev All chains that are configured. - EnumerableSet.UintSet private s_remoteChainSelectors; - - /// @dev OCR3 configurations for each DON. - /// Each CR DON will have a commit and execution configuration. - /// This means that a DON can have up to 4 configurations, since we are implementing blue/green deployments. - mapping( - uint32 donId => mapping(Internal.OCRPluginType pluginType => CCIPConfigTypes.OCR3ConfigWithMeta[] ocr3Configs) - ) private s_ocr3Configs; - - /// @param capabilitiesRegistry the canonical capabilities registry address. - constructor(address capabilitiesRegistry) { - if (capabilitiesRegistry == address(0)) { - revert ZeroAddressNotAllowed(); - } - i_capabilitiesRegistry = capabilitiesRegistry; - } - - /// @inheritdoc IERC165 - function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { - return interfaceId == type(ICapabilityConfiguration).interfaceId || interfaceId == type(IERC165).interfaceId; - } - - // ================================================================ - // │ Config Getters │ - // ================================================================ - /// @notice Returns the capabilities registry address. - /// @return The capabilities registry address. - function getCapabilityRegistry() external view returns (address) { - return i_capabilitiesRegistry; - } - - /// @notice Returns the total number of chains configured. - /// @return The total number of chains configured. - function getNumChainConfigurations() external view returns (uint256) { - return s_remoteChainSelectors.length(); - } - - /// @notice Returns all the chain configurations. - /// @param pageIndex The page index. - /// @param pageSize The page size. - /// @return paginatedChainConfigs chain configurations. - function getAllChainConfigs( - uint256 pageIndex, - uint256 pageSize - ) external view returns (CCIPConfigTypes.ChainConfigInfo[] memory) { - uint256 totalItems = s_remoteChainSelectors.length(); // Total number of chain selectors - uint256 startIndex = pageIndex * pageSize; - - if (pageSize == 0 || startIndex >= totalItems) { - return new CCIPConfigTypes.ChainConfigInfo[](0); // Return an empty array if pageSize is 0 or pageIndex is out of bounds - } - - uint256 endIndex = startIndex + pageSize; - if (endIndex > totalItems) { - endIndex = totalItems; - } - - CCIPConfigTypes.ChainConfigInfo[] memory paginatedChainConfigs = - new CCIPConfigTypes.ChainConfigInfo[](endIndex - startIndex); - - uint256[] memory chainSelectors = s_remoteChainSelectors.values(); - for (uint256 i = startIndex; i < endIndex; ++i) { - uint64 chainSelector = uint64(chainSelectors[i]); - paginatedChainConfigs[i - startIndex] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: chainSelector, - chainConfig: s_chainConfigurations[chainSelector] - }); - } - - return paginatedChainConfigs; - } - - /// @notice Returns the OCR configuration for the given don ID and plugin type. - /// @param donId The DON ID. - /// @param pluginType The plugin type. - /// @return The OCR3 configurations, up to 2 (blue and green). - function getOCRConfig( - uint32 donId, - Internal.OCRPluginType pluginType - ) external view returns (CCIPConfigTypes.OCR3ConfigWithMeta[] memory) { - return s_ocr3Configs[donId][pluginType]; - } - - // ================================================================ - // │ Capability Configuration │ - // ================================================================ - - /// @inheritdoc ICapabilityConfiguration - /// @dev The CCIP capability will fetch the configuration needed directly from this contract. - /// The offchain syncer will call this function, however, so its important that it doesn't revert. - function getCapabilityConfiguration(uint32 /* donId */ ) external pure override returns (bytes memory configuration) { - return bytes(""); - } - - /// @notice Called by the registry prior to the config being set for a particular DON. - /// @dev precondition Requires destination chain config to be set - function beforeCapabilityConfigSet( - bytes32[] calldata, /* nodes */ - bytes calldata config, - uint64, /* configCount */ - uint32 donId - ) external override { - if (msg.sender != i_capabilitiesRegistry) { - revert OnlyCapabilitiesRegistryCanCall(); - } - - (CCIPConfigTypes.OCR3Config[] memory commitConfigs, CCIPConfigTypes.OCR3Config[] memory execConfigs) = - _groupByPluginType(abi.decode(config, (CCIPConfigTypes.OCR3Config[]))); - if (commitConfigs.length > 0) { - _updatePluginConfig(donId, Internal.OCRPluginType.Commit, commitConfigs); - } - if (execConfigs.length > 0) { - _updatePluginConfig(donId, Internal.OCRPluginType.Execution, execConfigs); - } - } - - /// @notice Sets a new OCR3 config for a specific plugin type for a DON. - /// @param donId The DON ID. - /// @param pluginType The plugin type. - /// @param newConfig The new configuration. - function _updatePluginConfig( - uint32 donId, - Internal.OCRPluginType pluginType, - CCIPConfigTypes.OCR3Config[] memory newConfig - ) internal { - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = s_ocr3Configs[donId][pluginType]; - - // Validate the state transition being proposed, which is implicitly defined by the combination - // of lengths of the current and new configurations. - CCIPConfigTypes.ConfigState currentState = _stateFromConfigLength(currentConfig.length); - CCIPConfigTypes.ConfigState proposedState = _stateFromConfigLength(newConfig.length); - _validateConfigStateTransition(currentState, proposedState); - - // Build the new configuration with metadata and validate that the transition is valid. - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = - _computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, proposedState); - _validateConfigTransition(currentConfig, newConfigWithMeta); - - // Update contract state with new configuration if its valid. - // We won't run out of gas from this delete since the array is at most 2 elements long. - delete s_ocr3Configs[donId][pluginType]; - for (uint256 i = 0; i < newConfigWithMeta.length; ++i) { - // Struct has to be manually copied since there is a nested OCR3Node array. Direct assignment - // will result in Unimplemented Feature issue. - CCIPConfigTypes.OCR3ConfigWithMeta storage ocr3ConfigWithMeta = s_ocr3Configs[donId][pluginType].push(); - ocr3ConfigWithMeta.configDigest = newConfigWithMeta[i].configDigest; - ocr3ConfigWithMeta.configCount = newConfigWithMeta[i].configCount; - - CCIPConfigTypes.OCR3Config storage ocr3Config = ocr3ConfigWithMeta.config; - CCIPConfigTypes.OCR3Config memory newOcr3Config = newConfigWithMeta[i].config; - ocr3Config.pluginType = newOcr3Config.pluginType; - ocr3Config.chainSelector = newOcr3Config.chainSelector; - ocr3Config.FRoleDON = newOcr3Config.FRoleDON; - ocr3Config.offchainConfigVersion = newOcr3Config.offchainConfigVersion; - ocr3Config.offrampAddress = newOcr3Config.offrampAddress; - ocr3Config.offchainConfig = newOcr3Config.offchainConfig; - - // Remove all excess nodes - while (ocr3Config.nodes.length > newOcr3Config.nodes.length) { - ocr3Config.nodes.pop(); - } - - // Assign nodes - for (uint256 j = 0; j < newOcr3Config.nodes.length; ++j) { - if (j >= ocr3Config.nodes.length) { - ocr3Config.nodes.push(newOcr3Config.nodes[j]); - } - } - } - - emit ConfigSet(donId, uint8(pluginType), newConfigWithMeta); - } - - // ================================================================ - // │ Config State Machine │ - // ================================================================ - - /// @notice Determine the config state of the configuration from the length of the config. - /// @param configLen The length of the configuration. - /// @return The config state. - function _stateFromConfigLength(uint256 configLen) internal pure returns (CCIPConfigTypes.ConfigState) { - if (configLen > 2) { - revert InvalidConfigLength(configLen); - } - return CCIPConfigTypes.ConfigState(configLen); - } - - /// @notice Validates the state transition between two config states. - /// The only valid state transitions are the following: - /// Init -> Running (first ever config) - /// Running -> Staging (blue/green proposal) - /// Staging -> Running (promotion) - /// Everything else is invalid and should revert. - /// @param currentState The current state. - /// @param newState The new state. - function _validateConfigStateTransition( - CCIPConfigTypes.ConfigState currentState, - CCIPConfigTypes.ConfigState newState - ) internal pure { - // Calculate the difference between the new state and the current state - int256 stateDiff = int256(uint256(newState)) - int256(uint256(currentState)); - - // Check if the state transition is valid: - // Valid transitions: - // 1. currentState -> newState (where stateDiff == 1) - // e.g., init -> running or running -> staging - // 2. staging -> running (where stateDiff == -1) - if (stateDiff == 1 || (stateDiff == -1 && currentState == CCIPConfigTypes.ConfigState.Staging)) { - return; - } - revert InvalidConfigStateTransition(currentState, newState); - } - - /// @notice Validates the transition between two OCR3 configurations. - /// @param currentConfig The current configuration with metadata. - /// @param newConfigWithMeta The new configuration with metadata. - function _validateConfigTransition( - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig, - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta - ) internal pure { - uint256 currentConfigLen = currentConfig.length; - uint256 newConfigLen = newConfigWithMeta.length; - if (currentConfigLen == 0 && newConfigLen == 1) { - // Config counts always must start at 1 for the first ever config. - if (newConfigWithMeta[0].configCount != 1) { - revert WrongConfigCount(newConfigWithMeta[0].configCount, 1); - } - return; - } - - if (currentConfigLen == 1 && newConfigLen == 2) { - // On a blue/green proposal: - // * the config digest of the blue config must remain unchanged. - // * the green config count must be the blue config count + 1. - if (newConfigWithMeta[0].configDigest != currentConfig[0].configDigest) { - revert WrongConfigDigestBlueGreen(newConfigWithMeta[0].configDigest, currentConfig[0].configDigest); - } - if (newConfigWithMeta[1].configCount != currentConfig[0].configCount + 1) { - revert WrongConfigCount(newConfigWithMeta[1].configCount, currentConfig[0].configCount + 1); - } - return; - } - - if (currentConfigLen == 2 && newConfigLen == 1) { - // On a promotion, the green config digest must become the blue config digest. - if (newConfigWithMeta[0].configDigest != currentConfig[1].configDigest) { - revert WrongConfigDigest(newConfigWithMeta[0].configDigest, currentConfig[1].configDigest); - } - return; - } - - revert NonExistentConfigTransition(); - } - - /// @notice Computes a new configuration with metadata based on the current configuration and the new configuration. - /// @param donId The DON ID. - /// @param currentConfig The current configuration, including metadata. - /// @param newConfig The new configuration, without metadata. - /// @param currentState The current state of the configuration. - /// @param newState The new state of the configuration. - /// @return The new configuration with metadata. - function _computeNewConfigWithMeta( - uint32 donId, - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig, - CCIPConfigTypes.OCR3Config[] memory newConfig, - CCIPConfigTypes.ConfigState currentState, - CCIPConfigTypes.ConfigState newState - ) internal view returns (CCIPConfigTypes.OCR3ConfigWithMeta[] memory) { - uint64[] memory configCounts = new uint64[](newConfig.length); - - // Set config counts based on the only valid state transitions. - // Init -> Running (first ever config) - // Running -> Staging (blue/green proposal) - // Staging -> Running (promotion) - if (currentState == CCIPConfigTypes.ConfigState.Init && newState == CCIPConfigTypes.ConfigState.Running) { - // First ever config starts with config count == 1. - configCounts[0] = 1; - } else if (currentState == CCIPConfigTypes.ConfigState.Running && newState == CCIPConfigTypes.ConfigState.Staging) { - // On a blue/green proposal, the config count of the green config is the blue config count + 1. - configCounts[0] = currentConfig[0].configCount; - configCounts[1] = currentConfig[0].configCount + 1; - } else if (currentState == CCIPConfigTypes.ConfigState.Staging && newState == CCIPConfigTypes.ConfigState.Running) { - // On a promotion, the config count of the green config becomes the blue config count. - configCounts[0] = currentConfig[1].configCount; - } else { - revert InvalidConfigStateTransition(currentState, newState); - } - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = - new CCIPConfigTypes.OCR3ConfigWithMeta[](newConfig.length); - for (uint256 i = 0; i < configCounts.length; ++i) { - _validateConfig(newConfig[i]); - newConfigWithMeta[i] = CCIPConfigTypes.OCR3ConfigWithMeta({ - config: newConfig[i], - configCount: configCounts[i], - configDigest: _computeConfigDigest(donId, configCounts[i], newConfig[i]) - }); - } - - return newConfigWithMeta; - } - - /// @notice Group the OCR3 configurations by plugin type for further processing. - /// @param ocr3Configs The OCR3 configurations to group. - /// @return commitConfigs The commit configurations. - /// @return execConfigs The execution configurations. - function _groupByPluginType( - CCIPConfigTypes.OCR3Config[] memory ocr3Configs - ) - internal - pure - returns (CCIPConfigTypes.OCR3Config[] memory commitConfigs, CCIPConfigTypes.OCR3Config[] memory execConfigs) - { - if (ocr3Configs.length > MAX_OCR3_CONFIGS_PER_DON) { - revert TooManyOCR3Configs(); - } - - // Declare with size 2 since we have a maximum of two configs per plugin type (blue, green). - // If we have less we will adjust the length later using mstore. - // If the caller provides more than 2 configs per plugin type, we will revert due to out of bounds - // access in the for loop below. - commitConfigs = new CCIPConfigTypes.OCR3Config[](MAX_OCR3_CONFIGS_PER_PLUGIN); - execConfigs = new CCIPConfigTypes.OCR3Config[](MAX_OCR3_CONFIGS_PER_PLUGIN); - uint256 commitCount = 0; - uint256 execCount = 0; - for (uint256 i = 0; i < ocr3Configs.length; ++i) { - if (ocr3Configs[i].pluginType == Internal.OCRPluginType.Commit) { - commitConfigs[commitCount] = ocr3Configs[i]; - ++commitCount; - } else { - execConfigs[execCount] = ocr3Configs[i]; - ++execCount; - } - } - - // Adjust the length of the arrays to the actual number of configs. - assembly { - mstore(commitConfigs, commitCount) - mstore(execConfigs, execCount) - } - - return (commitConfigs, execConfigs); - } - - /// @notice Validates an OCR3 configuration. - /// @param cfg The OCR3 configuration. - function _validateConfig(CCIPConfigTypes.OCR3Config memory cfg) internal view { - if (cfg.chainSelector == 0) revert ChainSelectorNotSet(); - if (cfg.pluginType != Internal.OCRPluginType.Commit && cfg.pluginType != Internal.OCRPluginType.Execution) { - revert InvalidPluginType(); - } - if (cfg.offrampAddress.length == 0 || keccak256(cfg.offrampAddress) == EMPTY_ENCODED_ADDRESS_HASH) { - revert OfframpAddressCannotBeZero(); - } - if (!s_remoteChainSelectors.contains(cfg.chainSelector)) revert ChainSelectorNotFound(cfg.chainSelector); - - // fChain cannot exceed FRoleDON, since it is a subcommittee in the larger DON - uint256 FRoleDON = cfg.FRoleDON; - uint256 fChain = s_chainConfigurations[cfg.chainSelector].fChain; - // fChain > 0 is enforced in applyChainConfigUpdates, and the presence of a chain config is checked above - // FRoleDON != 0 because FRoleDON >= fChain is enforced here - if (fChain > FRoleDON) { - revert FChainTooHigh(fChain, FRoleDON); - } - - // len(nodes) >= 3 * FRoleDON + 1 - // len(nodes) == numberOfSigners - uint256 numberOfNodes = cfg.nodes.length; - if (numberOfNodes > MAX_NUM_ORACLES) revert TooManySigners(); - if (numberOfNodes <= 3 * FRoleDON) revert FTooHigh(); - - uint256 nonZeroTransmitters = 0; - bytes32[] memory p2pIds = new bytes32[](numberOfNodes); - for (uint256 i = 0; i < numberOfNodes; ++i) { - CCIPConfigTypes.OCR3Node memory node = cfg.nodes[i]; - - // 3 * fChain + 1 <= nonZeroTransmitters <= 3 * FRoleDON + 1 - // Transmitters can be set to 0 since there can be more signers than transmitters, - if (node.transmitterKey.length != 0) { - nonZeroTransmitters++; - } - - // Signer key and p2pIds must always be present - if (node.signerKey.length == 0 || node.p2pId == bytes32(0)) { - revert InvalidNode(node); - } - - p2pIds[i] = node.p2pId; - } - - // We check for chain config presence above, so fChain here must be non-zero. fChain <= FRoleDON due to the checks above. - // There can be less transmitters than signers - so they can be set to zero (which indicates that a node is a signer, but not a transmitter). - uint256 minTransmittersLength = 3 * fChain + 1; - if (nonZeroTransmitters < minTransmittersLength) { - revert NotEnoughTransmitters(nonZeroTransmitters, minTransmittersLength); - } - - // Check that the readers are in the capabilities registry. - _ensureInRegistry(p2pIds); - } - - /// @notice Computes the digest of the provided configuration. - /// @dev In traditional OCR config digest computation, block.chainid and address(this) are used - /// in order to further domain separate the digest. We can't do that here since the digest will - /// be used on remote chains; so we use the chain selector instead of block.chainid. The don ID - /// replaces the address(this) in the traditional computation. - /// @param donId The DON ID. - /// @param configCount The configuration count. - /// @param ocr3Config The OCR3 configuration. - /// @return The computed digest. - function _computeConfigDigest( - uint32 donId, - uint64 configCount, - CCIPConfigTypes.OCR3Config memory ocr3Config - ) internal pure returns (bytes32) { - uint256 h = uint256( - keccak256( - abi.encode( - ocr3Config.chainSelector, - donId, - ocr3Config.pluginType, - ocr3Config.offrampAddress, - configCount, - ocr3Config.nodes, - ocr3Config.FRoleDON, - ocr3Config.offchainConfigVersion, - ocr3Config.offchainConfig - ) - ) - ); - - return bytes32((CONFIG_DIGEST_PREFIX & CONFIG_DIGEST_PREFIX_MASK) | (h & ~CONFIG_DIGEST_PREFIX_MASK)); - } - - // ================================================================ - // │ Chain Configuration │ - // ================================================================ - - /// @notice Sets and/or removes chain configurations. - /// Does not validate that fChain <= FRoleDON and relies on OCR3Configs to be changed in case fChain becomes larger than the FRoleDON value. - /// @param chainSelectorRemoves The chain configurations to remove. - /// @param chainConfigAdds The chain configurations to add. - function applyChainConfigUpdates( - uint64[] calldata chainSelectorRemoves, - CCIPConfigTypes.ChainConfigInfo[] calldata chainConfigAdds - ) external onlyOwner { - // Process removals first. - for (uint256 i = 0; i < chainSelectorRemoves.length; ++i) { - // check if the chain selector is in s_remoteChainSelectors first. - if (!s_remoteChainSelectors.contains(chainSelectorRemoves[i])) { - revert ChainSelectorNotFound(chainSelectorRemoves[i]); - } - - delete s_chainConfigurations[chainSelectorRemoves[i]]; - s_remoteChainSelectors.remove(chainSelectorRemoves[i]); - - emit ChainConfigRemoved(chainSelectorRemoves[i]); - } - - // Process additions next. - for (uint256 i = 0; i < chainConfigAdds.length; ++i) { - CCIPConfigTypes.ChainConfig memory chainConfig = chainConfigAdds[i].chainConfig; - uint64 chainSelector = chainConfigAdds[i].chainSelector; - - // Verify that the provided readers are present in the capabilities registry. - _ensureInRegistry(chainConfig.readers); - - // Verify that fChain is positive. - if (chainConfig.fChain == 0) { - revert FChainMustBePositive(); - } - - s_chainConfigurations[chainSelector] = chainConfig; - s_remoteChainSelectors.add(chainSelector); - - emit ChainConfigSet(chainSelector, chainConfig); - } - } - - /// @notice Helper function to ensure that a node is in the capabilities registry. - /// @param p2pIds The P2P IDs of the node to check. - function _ensureInRegistry(bytes32[] memory p2pIds) internal view { - for (uint256 i = 0; i < p2pIds.length; ++i) { - // TODO add a method that does the validation in the ICapabilitiesRegistry contract - if (ICapabilitiesRegistry(i_capabilitiesRegistry).getNode(p2pIds[i]).p2pId == bytes32("")) { - revert NodeNotInRegistry(p2pIds[i]); - } - } - } -} diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index ef02fd169c..b058264a03 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -39,6 +39,8 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { error InvalidNode(OCR3Node node); error NotEnoughTransmitters(uint256 got, uint256 minimum); error OnlySelfCallAllowed(); + error OnlyCapabilitiesRegistryCanCall(); + error ZeroAddressNotAllowed(); /// @notice Represents an oracle node in OCR3 configs part of the role DON. /// Every configured node should be a signer, but does not have to be a transmitter. @@ -84,6 +86,7 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { string public constant override typeAndVersion = "CCIPHome 1.6.0-dev"; + /// @dev A prefix added to all config digests that is unique to the implementation of the HomeBase contract. uint256 private constant PREFIX = 0x000a << (256 - 16); // 0x000a00..00 bytes32 internal constant EMPTY_ENCODED_ADDRESS_HASH = keccak256(abi.encode(address(0))); /// @dev 256 is the hard limit due to the bit encoding of their indexes into a uint256. @@ -98,6 +101,8 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { /// @dev All chains that are configured. EnumerableSet.UintSet private s_remoteChainSelectors; + /// @notice Constructor for the CCIPHome contract takes in the address of the capabilities registry. This address + /// is the only allowed caller to mutate the configuration through beforeCapabilityConfigSet. constructor(address capabilitiesRegistry) { if (capabilitiesRegistry == address(0)) { revert ZeroAddressNotAllowed(); @@ -131,8 +136,10 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { if (msg.sender != i_capabilitiesRegistry) { revert OnlyCapabilitiesRegistryCanCall(); } + // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory errorData) = address(this).call(update); if (!success) { + // re-throw the revert message from the call assembly { revert(add(errorData, 32), errorData) } @@ -152,6 +159,7 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use /// in case one of the configs is too large to be returnable by one of the other getters. + /// @param pluginKey The unique key for the DON that the configuration applies to. /// @param configDigest The digest of the config to fetch. /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. @@ -174,6 +182,10 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { return (versionedConfig, false); } + /// @notice Returns the primary and secondary configuration for a given plugin key. + /// @param pluginKey The unique key for the DON that the configuration applies to. + /// @return primaryConfig The primary configuration. + /// @return secondaryConfig The secondary configuration. function getAllConfigs( bytes32 pluginKey ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { @@ -204,6 +216,9 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { // │ Validation │ // ================================================================ + /// @inheritdoc HomeBase + /// @param encodedStaticConfig The abi encoded static configuration. + /// @dev Validates the static only as CCIPHome does not use any dynamic config. function _validateStaticAndDynamicConfig(bytes memory encodedStaticConfig, bytes memory) internal view override { OCR3Config memory cfg = abi.decode(encodedStaticConfig, (OCR3Config)); @@ -261,17 +276,17 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { _ensureInRegistry(p2pIds); } - function _validateDynamicConfig( - bytes memory encodedStaticConfig, - bytes memory encodedDynamicConfig - ) internal pure override { - // OCR doesn't use dynamic config - } + /// @inheritdoc HomeBase + /// @dev No-op as there is no dynamic config. + function _validateDynamicConfig(bytes memory, bytes memory) internal pure override {} + /// @inheritdoc HomeBase function _getConfigDigestPrefix() internal pure override returns (uint256) { return PREFIX; } + /// @inheritdoc HomeBase + /// @dev Uses ownable as caller validation method. function _validateCaller() internal view override { if (msg.sender != address(this)) { revert OnlySelfCallAllowed(); diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 0f57ed29a2..611dd50330 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -6,15 +6,13 @@ import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { - event ConfigSet(StoredConfig versionedConfig); + event ConfigSet(bytes32 indexed configDigest, StoredConfig versionedConfig); event ConfigRevoked(bytes32 indexed configDigest); event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); event ConfigPromoted(bytes32 indexed configDigest); error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); error DigestNotFound(bytes32 configDigest); - error ZeroAddressNotAllowed(); - error OnlyCapabilitiesRegistryCanCall(); /// @notice Used for encoding the config digest prefix uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 @@ -39,12 +37,21 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { bytes dynamicConfig; } + // ================================================================ + // │ Functions to override │ + // ================================================================ + + /// @notice Validates that the static and dynamic config are valid. Reverts otherwise. function _validateStaticAndDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; + /// @notice Validates that the dynamic config is valid given the static config. Reverts otherwise. function _validateDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; + /// @notice Validates that the caller is allowed to make changes to the config. function _validateCaller() internal view virtual; + /// @notice Returns the prefix used for calculating the config digest. + /// @dev This is used to ensure that the config digest is unique across different contract implementations. function _getConfigDigestPrefix() internal pure virtual returns (uint256); // ================================================================ @@ -53,6 +60,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @notice Returns the current primary and secondary config digests. /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. + /// @param pluginKey The key of the plugin to get the config digests for. /// @return primaryConfigDigest The digest of the primary config. /// @return secondaryConfigDigest The digest of the secondary config. function getConfigDigests( @@ -64,16 +72,22 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { ); } + /// @notice Returns the primary config digest for for a given key. + /// @param pluginKey The key of the plugin to get the config digests for. function getPrimaryDigest(bytes32 pluginKey) public view returns (bytes32) { return s_configs[pluginKey][s_primaryConfigIndex].configDigest; } + /// @notice Returns the secondary config digest for for a given key. + /// @param pluginKey The key of the plugin to get the config digests for. function getSecondaryDigest(bytes32 pluginKey) public view returns (bytes32) { return s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest; } /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero /// digest. This is done to prevent exposing old config state that is invalid. + /// @param pluginKey The key of the plugin to get the config for. + /// @param configDigest The digest of the config to fetch. function _getStoredConfig( bytes32 pluginKey, bytes32 configDigest @@ -88,6 +102,10 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } + /// @notice Returns the primary stored config for a given key. + /// @param pluginKey The key of the plugin to get the config for. + /// @return primaryConfig The primary stored config. + /// @return ok True if the config was found, false otherwise. function _getPrimaryStoredConfig( bytes32 pluginKey ) internal view returns (StoredConfig memory primaryConfig, bool ok) { @@ -98,6 +116,10 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { return (s_configs[pluginKey][s_primaryConfigIndex], true); } + /// @notice Returns the secondary stored config for a given key. + /// @param pluginKey The key of the plugin to get the config for. + /// @return secondaryConfig The secondary stored config. + /// @return ok True if the config was found, false otherwise. function _getSecondaryStoredConfig( bytes32 pluginKey ) internal view returns (StoredConfig memory secondaryConfig, bool ok) { @@ -113,8 +135,12 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { // ================================================================ /// @notice Sets a new config as the secondary config. Does not influence the primary config. + /// @param pluginKey The key of the plugin to set the config for. + /// @param encodedStaticConfig The static part of the config. + /// @param encodedDynamicConfig The dynamic part of the config. /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. /// This is done to prevent accidental overwrites. + /// @return newConfigDigest The digest of the new config. function setSecondary( bytes32 pluginKey, bytes calldata encodedStaticConfig, @@ -147,12 +173,13 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { s_configs[pluginKey][s_primaryConfigIndex ^ 1] = newConfig; - emit ConfigSet(newConfig); + emit ConfigSet(newConfig.configDigest, newConfig); return newConfigDigest; } /// @notice Revokes a specific config by digest. + /// @param pluginKey The key of the plugin to revoke the config for. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. function revokeSecondary(bytes32 pluginKey, bytes32 configDigest) external { _validateCaller(); @@ -170,6 +197,9 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { } /// @notice Promotes the secondary config to the primary config and revokes the primary config. + /// @param pluginKey The key of the plugin to promote the config for. + /// @param digestToPromote The digest of the config to promote. + /// @param digestToRevoke The digest of the config to revoke. function promoteSecondaryAndRevokePrimary( bytes32 pluginKey, bytes32 digestToPromote, @@ -196,6 +226,11 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { emit ConfigPromoted(digestToPromote); } + /// @notice Sets the dynamic config for a specific config. + /// @param pluginKey The key of the plugin to set the dynamic config for. + /// @param newDynamicConfig The new dynamic config. + /// @param currentDigest The digest of the config to update. + /// @dev This does not update the config digest as only the static config is part of the digest. function setDynamicConfig(bytes32 pluginKey, bytes calldata newDynamicConfig, bytes32 currentDigest) external { _validateCaller(); @@ -214,6 +249,11 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { revert DigestNotFound(currentDigest); } + /// @notice Calculates the config digest for a given plugin key, static config, and version. + /// @param pluginKey The key of the plugin to calculate the digest for. + /// @param staticConfig The static part of the config. + /// @param version The version of the config. + /// @return The calculated config digest. function _calculateConfigDigest( bytes32 pluginKey, bytes memory staticConfig, diff --git a/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol b/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol deleted file mode 100644 index 914352ea37..0000000000 --- a/contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol +++ /dev/null @@ -1,1484 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.24; - -import {CCIPConfig} from "../../capability/CCIPConfig.sol"; -import {HomeBase} from "../../capability/HomeBase.sol"; -import {ICapabilitiesRegistry} from "../../capability/interfaces/ICapabilitiesRegistry.sol"; -import {CCIPConfigTypes} from "../../capability/libraries/CCIPConfigTypes.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {CCIPConfigHelper} from "../helpers/CCIPConfigHelper.sol"; -import {Test} from "forge-std/Test.sol"; - -contract CCIPConfigSetup is Test { - address public constant OWNER = 0x82ae2B4F57CA5C1CBF8f744ADbD3697aD1a35AFe; - address public constant CAPABILITIES_REGISTRY = 0x272aF4BF7FBFc4944Ed59F914Cd864DfD912D55e; - - CCIPConfigHelper public s_ccipCC; - - function setUp() public { - changePrank(OWNER); - s_ccipCC = new CCIPConfigHelper(CAPABILITIES_REGISTRY); - } - - function _makeBytes32Array(uint256 length, uint256 seed) internal pure returns (bytes32[] memory arr) { - arr = new bytes32[](length); - for (uint256 i = 0; i < length; i++) { - arr[i] = keccak256(abi.encode(i, 1, seed)); - } - return arr; - } - - function _makeBytesArray(uint256 length, uint256 seed) internal pure returns (bytes[] memory arr) { - arr = new bytes[](length); - for (uint256 i = 0; i < length; i++) { - arr[i] = abi.encodePacked(keccak256(abi.encode(i, 1, seed))); - } - return arr; - } - - function _subset(bytes32[] memory arr, uint256 start, uint256 end) internal pure returns (bytes32[] memory) { - bytes32[] memory subset = new bytes32[](end - start); - for (uint256 i = start; i < end; i++) { - subset[i - start] = arr[i]; - } - return subset; - } - - function _addChainConfig(uint256 numNodes) internal returns (CCIPConfigTypes.OCR3Node[] memory nodes) { - return _addChainConfig(numNodes, 1); - } - - function _addChainConfig(uint256 numNodes, uint8 fChain) internal returns (CCIPConfigTypes.OCR3Node[] memory nodes) { - bytes32[] memory p2pIds = _makeBytes32Array(numNodes, 0); - bytes[] memory signers = _makeBytesArray(numNodes, 10); - bytes[] memory transmitters = _makeBytesArray(numNodes, 20); - - nodes = new CCIPConfigTypes.OCR3Node[](numNodes); - - for (uint256 i = 0; i < numNodes; i++) { - nodes[i] = CCIPConfigTypes.OCR3Node({p2pId: p2pIds[i], signerKey: signers[i], transmitterKey: transmitters[i]}); - - vm.mockCall( - CAPABILITIES_REGISTRY, - abi.encodeWithSelector(ICapabilitiesRegistry.getNode.selector, p2pIds[i]), - abi.encode( - ICapabilitiesRegistry.NodeInfo({ - nodeOperatorId: 1, - signer: bytes32(signers[i]), - p2pId: p2pIds[i], - hashedCapabilityIds: new bytes32[](0), - configCount: uint32(1), - workflowDONId: uint32(1), - capabilitiesDONIds: new uint256[](0) - }) - ) - ); - } - // Add chain selector for chain 1. - CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](1); - adds[0] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 1, - chainConfig: CCIPConfigTypes.ChainConfig({readers: p2pIds, fChain: fChain, config: bytes("config1")}) - }); - - vm.expectEmit(); - emit CCIPConfig.ChainConfigSet(1, adds[0].chainConfig); - s_ccipCC.applyChainConfigUpdates(new uint64[](0), adds); - - return nodes; - } - - function _constructNodesArray( - bytes32[] memory p2pIds, - bytes[] memory signers, - bytes[] memory transmitters - ) internal pure returns (CCIPConfigTypes.OCR3Node[] memory nodes) { - nodes = new CCIPConfigTypes.OCR3Node[](p2pIds.length); - - for (uint256 i = 0; i < p2pIds.length; i++) { - nodes[i] = CCIPConfigTypes.OCR3Node({p2pId: p2pIds[i], signerKey: signers[i], transmitterKey: transmitters[i]}); - } - - return nodes; - } - - function _assertOCR3ConfigWithMetaEqual( - CCIPConfigTypes.OCR3ConfigWithMeta[] memory a, - CCIPConfigTypes.OCR3ConfigWithMeta[] memory b - ) internal pure { - assertEq(a.length, b.length, "OCR3ConfigWithMeta lengths do no match"); - for (uint256 i = 0; i < a.length; ++i) { - _assertOCR3ConfigEqual(a[i].config, b[i].config); - assertEq(a[i].configCount, b[i].configCount, "configCount must match"); - assertEq(a[i].configDigest, b[i].configDigest, "configDigest must match"); - } - } - - function _assertOCR3ConfigEqual( - CCIPConfigTypes.OCR3Config memory a, - CCIPConfigTypes.OCR3Config memory b - ) internal pure { - assertEq(uint8(a.pluginType), uint8(b.pluginType), "pluginType must match"); - assertEq(a.chainSelector, b.chainSelector, "chainSelector must match"); - assertEq(a.FRoleDON, b.FRoleDON, "FRoleDON must match"); - assertEq(a.offchainConfigVersion, b.offchainConfigVersion, "offchainConfigVersion must match"); - assertEq(a.offrampAddress, b.offrampAddress, "offrampAddress must match"); - assertEq(a.nodes.length, b.nodes.length, "nodes length must match"); - assertEq(a.offchainConfig, b.offchainConfig, "offchainConfig must match"); - - for (uint256 i = 0; i < a.nodes.length; ++i) { - assertEq(a.nodes[i].p2pId, b.nodes[i].p2pId, "p2pId must match"); - assertEq(a.nodes[i].signerKey, b.nodes[i].signerKey, "signerKey must match"); - assertEq(a.nodes[i].transmitterKey, b.nodes[i].transmitterKey, "transmitterKey must match"); - } - } - - function test_getCapabilityConfiguration_Success() public view { - bytes memory capConfig = s_ccipCC.getCapabilityConfiguration(42 /* doesn't matter, not used */ ); - assertEq(capConfig.length, 0, "capability config length must be 0"); - } -} - -contract CCIPConfig_constructor is Test { - // Successes. - - function test_constructor_Success() public { - address capabilitiesRegistry = makeAddr("capabilitiesRegistry"); - CCIPConfigHelper ccipCC = new CCIPConfigHelper(capabilitiesRegistry); - assertEq(address(ccipCC.getCapabilityRegistry()), capabilitiesRegistry); - assertEq(ccipCC.typeAndVersion(), "CCIPConfig 1.6.0-dev"); - } - - // Reverts. - - function test_constructor_ZeroAddressNotAllowed_Revert() public { - vm.expectRevert(HomeBase.ZeroAddressNotAllowed.selector); - new CCIPConfigHelper(address(0)); - } -} - -contract CCIPConfig_chainConfig is CCIPConfigSetup { - // Successes. - function test_applyChainConfigUpdates_addChainConfigs_Success() public { - bytes32[] memory chainReaders = new bytes32[](1); - chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](2); - adds[0] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 1, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) - }); - adds[1] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 2, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config2")}) - }); - vm.mockCall( - CAPABILITIES_REGISTRY, - abi.encodeWithSelector(ICapabilitiesRegistry.getNode.selector, chainReaders[0]), - abi.encode( - ICapabilitiesRegistry.NodeInfo({ - nodeOperatorId: 1, - signer: bytes32(uint256(1)), - p2pId: chainReaders[0], - hashedCapabilityIds: new bytes32[](0), - configCount: uint32(1), - workflowDONId: uint32(1), - capabilitiesDONIds: new uint256[](0) - }) - ) - ); - vm.expectEmit(); - emit CCIPConfig.ChainConfigSet(1, adds[0].chainConfig); - vm.expectEmit(); - emit CCIPConfig.ChainConfigSet(2, adds[1].chainConfig); - s_ccipCC.applyChainConfigUpdates(new uint64[](0), adds); - - CCIPConfigTypes.ChainConfigInfo[] memory configs = s_ccipCC.getAllChainConfigs(0, 2); - assertEq(configs.length, 2, "chain configs length must be 2"); - assertEq(configs[0].chainSelector, 1, "chain selector must match"); - assertEq(configs[1].chainSelector, 2, "chain selector must match"); - assertEq(s_ccipCC.getNumChainConfigurations(), 2, "total chain configs must be 2"); - } - - function test_getPaginatedCCIPConfigs_Success() public { - bytes32[] memory chainReaders = new bytes32[](1); - chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](2); - adds[0] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 1, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) - }); - adds[1] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 2, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config2")}) - }); - vm.mockCall( - CAPABILITIES_REGISTRY, - abi.encodeWithSelector(ICapabilitiesRegistry.getNode.selector, chainReaders[0]), - abi.encode( - ICapabilitiesRegistry.NodeInfo({ - nodeOperatorId: 1, - signer: bytes32(uint256(1)), - p2pId: chainReaders[0], - hashedCapabilityIds: new bytes32[](0), - configCount: uint32(1), - workflowDONId: uint32(1), - capabilitiesDONIds: new uint256[](0) - }) - ) - ); - - s_ccipCC.applyChainConfigUpdates(new uint64[](0), adds); - - CCIPConfigTypes.ChainConfigInfo[] memory configs = s_ccipCC.getAllChainConfigs(0, 2); - assertEq(configs.length, 2, "chain configs length must be 2"); - assertEq(configs[0].chainSelector, 1, "chain selector must match"); - assertEq(configs[1].chainSelector, 2, "chain selector must match"); - - configs = s_ccipCC.getAllChainConfigs(0, 1); - assertEq(configs.length, 1, "chain configs length must be 1"); - assertEq(configs[0].chainSelector, 1, "chain selector must match"); - - configs = s_ccipCC.getAllChainConfigs(0, 10); - assertEq(configs.length, 2, "chain configs length must be 2"); - assertEq(configs[0].chainSelector, 1, "chain selector must match"); - assertEq(configs[1].chainSelector, 2, "chain selector must match"); - - configs = s_ccipCC.getAllChainConfigs(1, 1); - assertEq(configs.length, 1, "chain configs length must be 1"); - - configs = s_ccipCC.getAllChainConfigs(1, 2); - assertEq(configs.length, 0, "chain configs length must be 0"); - } - - function test_applyChainConfigUpdates_removeChainConfigs_Success() public { - bytes32[] memory chainReaders = new bytes32[](1); - chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](2); - adds[0] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 1, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) - }); - adds[1] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 2, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config2")}) - }); - - vm.mockCall( - CAPABILITIES_REGISTRY, - abi.encodeWithSelector(ICapabilitiesRegistry.getNode.selector, chainReaders[0]), - abi.encode( - ICapabilitiesRegistry.NodeInfo({ - nodeOperatorId: 1, - signer: bytes32(uint256(1)), - p2pId: chainReaders[0], - hashedCapabilityIds: new bytes32[](0), - configCount: uint32(1), - workflowDONId: uint32(1), - capabilitiesDONIds: new uint256[](0) - }) - ) - ); - - vm.expectEmit(); - emit CCIPConfig.ChainConfigSet(1, adds[0].chainConfig); - vm.expectEmit(); - emit CCIPConfig.ChainConfigSet(2, adds[1].chainConfig); - s_ccipCC.applyChainConfigUpdates(new uint64[](0), adds); - - assertEq(s_ccipCC.getNumChainConfigurations(), 2, "total chain configs must be 2"); - - uint64[] memory removes = new uint64[](1); - removes[0] = uint64(1); - - vm.expectEmit(); - emit CCIPConfig.ChainConfigRemoved(1); - s_ccipCC.applyChainConfigUpdates(removes, new CCIPConfigTypes.ChainConfigInfo[](0)); - - assertEq(s_ccipCC.getNumChainConfigurations(), 1, "total chain configs must be 1"); - } - - // Reverts. - - function test_applyChainConfigUpdates_selectorNotFound_Reverts() public { - uint64[] memory removes = new uint64[](1); - removes[0] = uint64(1); - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.ChainSelectorNotFound.selector, 1)); - s_ccipCC.applyChainConfigUpdates(removes, new CCIPConfigTypes.ChainConfigInfo[](0)); - } - - function test_applyChainConfigUpdates_nodeNotInRegistry_Reverts() public { - bytes32[] memory chainReaders = new bytes32[](1); - chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](1); - adds[0] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 1, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: abi.encode(1, 2, 3)}) - }); - - vm.mockCall( - CAPABILITIES_REGISTRY, - abi.encodeWithSelector(ICapabilitiesRegistry.getNode.selector, chainReaders[0]), - abi.encode( - ICapabilitiesRegistry.NodeInfo({ - nodeOperatorId: 0, - signer: bytes32(0), - p2pId: bytes32(uint256(0)), - hashedCapabilityIds: new bytes32[](0), - configCount: uint32(1), - workflowDONId: uint32(1), - capabilitiesDONIds: new uint256[](0) - }) - ) - ); - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.NodeNotInRegistry.selector, chainReaders[0])); - s_ccipCC.applyChainConfigUpdates(new uint64[](0), adds); - } - - function test__applyChainConfigUpdates_FChainNotPositive_Reverts() public { - bytes32[] memory chainReaders = new bytes32[](1); - chainReaders[0] = keccak256(abi.encode(1)); - CCIPConfigTypes.ChainConfigInfo[] memory adds = new CCIPConfigTypes.ChainConfigInfo[](2); - adds[0] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 1, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config1")}) - }); - adds[1] = CCIPConfigTypes.ChainConfigInfo({ - chainSelector: 2, - chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 0, config: bytes("config2")}) // bad fChain - }); - - vm.mockCall( - CAPABILITIES_REGISTRY, - abi.encodeWithSelector(ICapabilitiesRegistry.getNode.selector, chainReaders[0]), - abi.encode( - ICapabilitiesRegistry.NodeInfo({ - nodeOperatorId: 1, - signer: bytes32(uint256(1)), - p2pId: chainReaders[0], - hashedCapabilityIds: new bytes32[](0), - configCount: uint32(1), - workflowDONId: uint32(1), - capabilitiesDONIds: new uint256[](0) - }) - ) - ); - - vm.expectRevert(CCIPConfig.FChainMustBePositive.selector); - s_ccipCC.applyChainConfigUpdates(new uint64[](0), adds); - } -} - -contract CCIPConfig_validateConfig is CCIPConfigSetup { - function _getCorrectOCR3Config(uint8 numNodes, uint8 FRoleDON) internal returns (CCIPConfigTypes.OCR3Config memory) { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(numNodes); - - return CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: FRoleDON, - offchainConfigVersion: 30, - offchainConfig: bytes("offchainConfig") - }); - } - - function _getCorrectOCR3Config() internal returns (CCIPConfigTypes.OCR3Config memory) { - return _getCorrectOCR3Config(4, 1); - } - - // Successes. - - function test__validateConfig_Success() public { - s_ccipCC.validateConfig(_getCorrectOCR3Config()); - } - - function test__validateConfigLessTransmittersThanSigners_Success() public { - // fChain is 1, so there should be at least 4 transmitters. - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(5, 1); - config.nodes[1].transmitterKey = bytes(""); - - s_ccipCC.validateConfig(config); - } - - function test__validateConfigSmallerFChain_Success() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(11, 3); - - // Set fChain to 2 - _addChainConfig(4, 2); - - s_ccipCC.validateConfig(config); - } - - // Reverts. - - function test__validateConfig_ChainSelectorNotSet_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.chainSelector = 0; // invalid - - vm.expectRevert(CCIPConfig.ChainSelectorNotSet.selector); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_OfframpAddressCannotBeZero_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.offrampAddress = ""; // invalid - - vm.expectRevert(CCIPConfig.OfframpAddressCannotBeZero.selector); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_ABIEncodedAddress_OfframpAddressCannotBeZero_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.offrampAddress = abi.encode(address(0)); // invalid - - vm.expectRevert(CCIPConfig.OfframpAddressCannotBeZero.selector); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_ChainSelectorNotFound_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.chainSelector = 2; // not set - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.ChainSelectorNotFound.selector, 2)); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_NotEnoughTransmitters_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - uint256 numberOfTransmitters = 3; - - // 32 > 31 (max num oracles) - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(31); - - // truncate transmitters to < 3 * fChain + 1 - // since fChain is 1 in this case, we need to truncate to 3 transmitters. - for (uint256 i = numberOfTransmitters; i < nodes.length; ++i) { - nodes[i].transmitterKey = bytes(""); - } - - config.nodes = nodes; - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.NotEnoughTransmitters.selector, numberOfTransmitters, 4)); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_NotEnoughTransmittersEmptyAddresses_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.nodes[0].transmitterKey = bytes(""); - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.NotEnoughTransmitters.selector, 3, 4)); - s_ccipCC.validateConfig(config); - - // Zero out remaining transmitters to verify error changes - for (uint256 i = 1; i < config.nodes.length; ++i) { - config.nodes[i].transmitterKey = bytes(""); - } - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.NotEnoughTransmitters.selector, 0, 4)); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_TooManySigners_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.nodes = new CCIPConfigTypes.OCR3Node[](257); - - vm.expectRevert(CCIPConfig.TooManySigners.selector); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_FChainTooHigh_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.FRoleDON = 2; // too low - - // Set fChain to 3 - _addChainConfig(4, 3); - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.FChainTooHigh.selector, 3, 2)); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_FMustBePositive_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.FRoleDON = 0; // not positive - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.FChainTooHigh.selector, 1, 0)); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_FTooHigh_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.FRoleDON = 2; // too high - - vm.expectRevert(CCIPConfig.FTooHigh.selector); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_ZeroP2PId_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.nodes[1].p2pId = bytes32(0); - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.InvalidNode.selector, config.nodes[1])); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_ZeroSignerKey_Reverts() public { - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.nodes[2].signerKey = bytes(""); - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.InvalidNode.selector, config.nodes[2])); - s_ccipCC.validateConfig(config); - } - - function test__validateConfig_NodeNotInRegistry_Reverts() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - bytes32 nonExistentP2PId = keccak256("notInRegistry"); - nodes[0].p2pId = nonExistentP2PId; - - vm.mockCall( - CAPABILITIES_REGISTRY, - abi.encodeWithSelector(ICapabilitiesRegistry.getNode.selector, nonExistentP2PId), - abi.encode( - ICapabilitiesRegistry.NodeInfo({ - nodeOperatorId: 0, - signer: bytes32(0), - p2pId: bytes32(uint256(0)), - hashedCapabilityIds: new bytes32[](0), - configCount: uint32(1), - workflowDONId: uint32(1), - capabilitiesDONIds: new uint256[](0) - }) - ) - ); - CCIPConfigTypes.OCR3Config memory config = _getCorrectOCR3Config(); - config.nodes = nodes; - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.NodeNotInRegistry.selector, nonExistentP2PId)); - s_ccipCC.validateConfig(config); - } -} - -contract CCIPConfig_ConfigStateMachine is CCIPConfigSetup { - // Successful cases. - - function test__stateFromConfigLength_Success() public view { - uint256 configLen = 0; - CCIPConfigTypes.ConfigState state = s_ccipCC.stateFromConfigLength(configLen); - assertEq(uint256(state), uint256(CCIPConfigTypes.ConfigState.Init)); - - configLen = 1; - state = s_ccipCC.stateFromConfigLength(configLen); - assertEq(uint256(state), uint256(CCIPConfigTypes.ConfigState.Running)); - - configLen = 2; - state = s_ccipCC.stateFromConfigLength(configLen); - assertEq(uint256(state), uint256(CCIPConfigTypes.ConfigState.Staging)); - } - - function test__validateConfigStateTransition_Success() public view { - s_ccipCC.validateConfigStateTransition(CCIPConfigTypes.ConfigState.Init, CCIPConfigTypes.ConfigState.Running); - - s_ccipCC.validateConfigStateTransition(CCIPConfigTypes.ConfigState.Running, CCIPConfigTypes.ConfigState.Staging); - - s_ccipCC.validateConfigStateTransition(CCIPConfigTypes.ConfigState.Staging, CCIPConfigTypes.ConfigState.Running); - } - - function test__computeConfigDigest_Success() public view { - // config digest must change upon: - // - ocr config change (e.g plugin type, chain selector, etc.) - // - don id change - // - config count change - bytes32[] memory p2pIds = _makeBytes32Array(4, 0); - bytes[] memory signers = _makeBytesArray(4, 10); - bytes[] memory transmitters = _makeBytesArray(4, 20); - - CCIPConfigTypes.OCR3Node[] memory nodes = new CCIPConfigTypes.OCR3Node[](4); - for (uint256 i = 0; i < 4; ++i) { - nodes[i] = CCIPConfigTypes.OCR3Node({p2pId: p2pIds[i], signerKey: signers[i], transmitterKey: transmitters[i]}); - } - - CCIPConfigTypes.OCR3Config memory config = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("offchainConfig") - }); - uint32 donId = 1; - uint32 configCount = 1; - - bytes32 configDigest1 = s_ccipCC.computeConfigDigest(donId, configCount, config); - - donId = 2; - bytes32 configDigest2 = s_ccipCC.computeConfigDigest(donId, configCount, config); - - donId = 1; - configCount = 2; - bytes32 configDigest3 = s_ccipCC.computeConfigDigest(donId, configCount, config); - - configCount = 1; - config.pluginType = Internal.OCRPluginType.Execution; - bytes32 configDigest4 = s_ccipCC.computeConfigDigest(donId, configCount, config); - - assertNotEq(configDigest1, configDigest2, "config digests 1 and 2 must not match"); - assertNotEq(configDigest1, configDigest3, "config digests 1 and 3 must not match"); - assertNotEq(configDigest1, configDigest4, "config digests 1 and 4 must not match"); - - assertNotEq(configDigest2, configDigest3, "config digests 2 and 3 must not match"); - assertNotEq(configDigest2, configDigest4, "config digests 2 and 4 must not match"); - } - - function test_Fuzz__groupByPluginType_Success(uint256 numCommitCfgs, uint256 numExecCfgs) public view { - numCommitCfgs = bound(numCommitCfgs, 0, 2); - numExecCfgs = bound(numExecCfgs, 0, 2); - - bytes32[] memory p2pIds = _makeBytes32Array(4, 0); - bytes[] memory signers = _makeBytesArray(4, 10); - bytes[] memory transmitters = _makeBytesArray(4, 20); - CCIPConfigTypes.OCR3Node[] memory nodes = new CCIPConfigTypes.OCR3Node[](4); - for (uint256 i = 0; i < 4; ++i) { - nodes[i] = CCIPConfigTypes.OCR3Node({p2pId: p2pIds[i], signerKey: signers[i], transmitterKey: transmitters[i]}); - } - - CCIPConfigTypes.OCR3Config[] memory cfgs = new CCIPConfigTypes.OCR3Config[](numCommitCfgs + numExecCfgs); - for (uint256 i = 0; i < numCommitCfgs; i++) { - cfgs[i] = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: abi.encode("commit", i) - }); - } - for (uint256 i = 0; i < numExecCfgs; i++) { - cfgs[numCommitCfgs + i] = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Execution, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: abi.encode("exec", numCommitCfgs + i) - }); - } - (CCIPConfigTypes.OCR3Config[] memory commitCfgs, CCIPConfigTypes.OCR3Config[] memory execCfgs) = - s_ccipCC.groupByPluginType(cfgs); - - assertEq(commitCfgs.length, numCommitCfgs, "commitCfgs length must match"); - assertEq(execCfgs.length, numExecCfgs, "execCfgs length must match"); - for (uint256 i = 0; i < commitCfgs.length; i++) { - assertEq(uint8(commitCfgs[i].pluginType), uint8(Internal.OCRPluginType.Commit), "plugin type must be commit"); - assertEq(commitCfgs[i].offchainConfig, abi.encode("commit", i), "offchain config must match"); - } - for (uint256 i = 0; i < execCfgs.length; i++) { - assertEq(uint8(execCfgs[i].pluginType), uint8(Internal.OCRPluginType.Execution), "plugin type must be execution"); - assertEq(execCfgs[i].offchainConfig, abi.encode("exec", numCommitCfgs + i), "offchain config must match"); - } - } - - function test__computeNewConfigWithMeta_InitToRunning_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - uint32 donId = 1; - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](0); - CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](1); - newConfig[0] = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.ConfigState currentState = CCIPConfigTypes.ConfigState.Init; - CCIPConfigTypes.ConfigState newState = CCIPConfigTypes.ConfigState.Running; - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = - s_ccipCC.computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, newState); - assertEq(newConfigWithMeta.length, 1, "new config with meta length must be 1"); - assertEq(newConfigWithMeta[0].configCount, uint64(1), "config count must be 1"); - assertEq(uint8(newConfigWithMeta[0].config.pluginType), uint8(newConfig[0].pluginType), "plugin type must match"); - assertEq(newConfigWithMeta[0].config.offchainConfig, newConfig[0].offchainConfig, "offchain config must match"); - assertEq( - newConfigWithMeta[0].configDigest, - s_ccipCC.computeConfigDigest(donId, 1, newConfig[0]), - "config digest must match" - ); - - // This ensures that the test case is using correct inputs. - s_ccipCC.validateConfigTransition(currentConfig, newConfigWithMeta); - } - - function test__computeNewConfigWithMeta_RunningToStaging_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - - CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](2); - // existing blue config first. - newConfig[0] = blueConfig; - // green config next. - newConfig[1] = greenConfig; - - CCIPConfigTypes.ConfigState currentState = CCIPConfigTypes.ConfigState.Running; - CCIPConfigTypes.ConfigState newState = CCIPConfigTypes.ConfigState.Staging; - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = - s_ccipCC.computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, newState); - assertEq(newConfigWithMeta.length, 2, "new config with meta length must be 2"); - - assertEq(newConfigWithMeta[0].configCount, uint64(1), "config count of blue must be 1"); - assertEq( - uint8(newConfigWithMeta[0].config.pluginType), uint8(blueConfig.pluginType), "plugin type of blue must match" - ); - assertEq( - newConfigWithMeta[0].config.offchainConfig, blueConfig.offchainConfig, "offchain config of blue must match" - ); - assertEq( - newConfigWithMeta[0].configDigest, - s_ccipCC.computeConfigDigest(donId, 1, blueConfig), - "config digest of blue must match" - ); - - assertEq(newConfigWithMeta[1].configCount, uint64(2), "config count of green must be 2"); - assertEq( - uint8(newConfigWithMeta[1].config.pluginType), uint8(greenConfig.pluginType), "plugin type of green must match" - ); - assertEq( - newConfigWithMeta[1].config.offchainConfig, greenConfig.offchainConfig, "offchain config of green must match" - ); - assertEq( - newConfigWithMeta[1].configDigest, - s_ccipCC.computeConfigDigest(donId, 2, greenConfig), - "config digest of green must match" - ); - - // This ensures that the test case is using correct inputs. - s_ccipCC.validateConfigTransition(currentConfig, newConfigWithMeta); - } - - function test__computeNewConfigWithMeta_StagingToRunning_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); - currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - currentConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 2, - config: greenConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - }); - CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](1); - newConfig[0] = greenConfig; - - CCIPConfigTypes.ConfigState currentState = CCIPConfigTypes.ConfigState.Staging; - CCIPConfigTypes.ConfigState newState = CCIPConfigTypes.ConfigState.Running; - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta = - s_ccipCC.computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, newState); - - assertEq(newConfigWithMeta.length, 1, "new config with meta length must be 1"); - assertEq(newConfigWithMeta[0].configCount, uint64(2), "config count must be 2"); - assertEq(uint8(newConfigWithMeta[0].config.pluginType), uint8(greenConfig.pluginType), "plugin type must match"); - assertEq(newConfigWithMeta[0].config.offchainConfig, greenConfig.offchainConfig, "offchain config must match"); - assertEq( - newConfigWithMeta[0].configDigest, s_ccipCC.computeConfigDigest(donId, 2, greenConfig), "config digest must match" - ); - - // This ensures that the test case is using correct inputs. - s_ccipCC.validateConfigTransition(currentConfig, newConfigWithMeta); - } - - function test__validateConfigTransition_InitToRunning_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](0); - - s_ccipCC.validateConfigTransition(currentConfig, newConfig); - } - - function test__validateConfigTransition_RunningToStaging_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); - newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - newConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 2, - config: greenConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - - s_ccipCC.validateConfigTransition(currentConfig, newConfig); - } - - function test__validateConfigTransition_StagingToRunning_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); - currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - currentConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 2, - config: greenConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 2, - config: greenConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - }); - - s_ccipCC.validateConfigTransition(currentConfig, newConfig); - } - - // Reverts. - - function test_Fuzz__stateFromConfigLength_Reverts(uint256 configLen) public { - vm.assume(configLen > 2); - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.InvalidConfigLength.selector, configLen)); - s_ccipCC.stateFromConfigLength(configLen); - } - - function test__groupByPluginType_threeCommitConfigs_Reverts() public { - bytes32[] memory p2pIds = _makeBytes32Array(4, 0); - bytes[] memory signers = _makeBytesArray(4, 10); - bytes[] memory transmitters = _makeBytesArray(4, 20); - CCIPConfigTypes.OCR3Config[] memory cfgs = new CCIPConfigTypes.OCR3Config[](3); - for (uint256 i = 0; i < 3; i++) { - cfgs[i] = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(p2pIds, signers, transmitters), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: abi.encode("commit", i) - }); - } - vm.expectRevert(); - s_ccipCC.groupByPluginType(cfgs); - } - - function test__groupByPluginType_threeExecutionConfigs_Reverts() public { - bytes32[] memory p2pIds = _makeBytes32Array(4, 0); - bytes[] memory signers = _makeBytesArray(4, 10); - bytes[] memory transmitters = _makeBytesArray(4, 20); - CCIPConfigTypes.OCR3Config[] memory cfgs = new CCIPConfigTypes.OCR3Config[](3); - for (uint256 i = 0; i < 3; i++) { - cfgs[i] = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Execution, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(p2pIds, signers, transmitters), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: abi.encode("exec", i) - }); - } - vm.expectRevert(); - s_ccipCC.groupByPluginType(cfgs); - } - - function test__groupByPluginType_TooManyOCR3Configs_Reverts() public { - CCIPConfigTypes.OCR3Config[] memory cfgs = new CCIPConfigTypes.OCR3Config[](5); - vm.expectRevert(CCIPConfig.TooManyOCR3Configs.selector); - s_ccipCC.groupByPluginType(cfgs); - } - - function test__validateConfigTransition_InitToRunning_WrongConfigCount_Reverts() public { - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(_makeBytes32Array(4, 0), _makeBytesArray(4, 10), _makeBytesArray(4, 20)), - // p2pIds: _makeBytes32Array(4, 0), - // signers: _makeBytesArray(4, 10), - // transmitters: _makeBytesArray(4, 20), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 0, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](0); - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.WrongConfigCount.selector, 0, 1)); - s_ccipCC.validateConfigTransition(currentConfig, newConfig); - } - - function test__validateConfigTransition_RunningToStaging_WrongConfigDigestBlueGreen_Reverts() public { - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(_makeBytes32Array(4, 0), _makeBytesArray(4, 10), _makeBytesArray(4, 20)), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(_makeBytes32Array(4, 0), _makeBytesArray(4, 10), _makeBytesArray(4, 20)), - // p2pIds: _makeBytes32Array(4, 0), - // signers: _makeBytesArray(4, 10), - // transmitters: _makeBytesArray(4, 20), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); - newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 3, blueConfig) // wrong config digest (due to diff config count) - }); - newConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 2, - config: greenConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - }); - - vm.expectRevert( - abi.encodeWithSelector( - CCIPConfig.WrongConfigDigestBlueGreen.selector, - s_ccipCC.computeConfigDigest(donId, 3, blueConfig), - s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - ) - ); - s_ccipCC.validateConfigTransition(currentConfig, newConfig); - } - - function test__validateConfigTransition_RunningToStaging_WrongConfigCount_Reverts() public { - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(_makeBytes32Array(4, 0), _makeBytesArray(4, 10), _makeBytesArray(4, 20)), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(_makeBytes32Array(4, 0), _makeBytesArray(4, 10), _makeBytesArray(4, 20)), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); - newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - newConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 3, // wrong config count - config: greenConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 3, greenConfig) - }); - - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.WrongConfigCount.selector, 3, 2)); - s_ccipCC.validateConfigTransition(currentConfig, newConfig); - } - - function test__validateConfigTransition_StagingToRunning_WrongConfigDigest_Reverts() public { - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(_makeBytes32Array(4, 0), _makeBytesArray(4, 10), _makeBytesArray(4, 20)), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: _constructNodesArray(_makeBytes32Array(4, 0), _makeBytesArray(4, 10), _makeBytesArray(4, 20)), - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); - currentConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 1, - config: blueConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - currentConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 2, - config: greenConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - }); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - newConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - configCount: 2, - config: greenConfig, - configDigest: s_ccipCC.computeConfigDigest(donId, 3, greenConfig) // wrong config digest - }); - - vm.expectRevert( - abi.encodeWithSelector( - CCIPConfig.WrongConfigDigest.selector, - s_ccipCC.computeConfigDigest(donId, 3, greenConfig), - s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - ) - ); - s_ccipCC.validateConfigTransition(currentConfig, newConfig); - } - - function test__validateConfigTransition_NonExistentConfigTransition_Reverts() public { - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](3); - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - vm.expectRevert(CCIPConfig.NonExistentConfigTransition.selector); - s_ccipCC.validateConfigTransition(currentConfig, newConfig); - } -} - -contract CCIPConfig_updatePluginConfig is CCIPConfigSetup { - // Successes. - - function test_updatePluginConfig_InitToRunning_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](1); - configs[0] = blueConfig; - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory expectedConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](1); - expectedConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - config: blueConfig, - configCount: 1, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - - vm.expectEmit(); - emit CCIPConfig.ConfigSet(donId, uint8(Internal.OCRPluginType.Commit), expectedConfig); - s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, configs); - - // should see the updated config in the contract state. - CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfig = - s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); - - _assertOCR3ConfigWithMetaEqual(storedConfig, expectedConfig); - } - - function test_updatePluginConfig_RunningToStaging_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - // add blue config. - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config[] memory startConfigs = new CCIPConfigTypes.OCR3Config[](1); - startConfigs[0] = blueConfig; - - // add blue AND green config to indicate an update. - s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, startConfigs); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - CCIPConfigTypes.OCR3Config[] memory blueAndGreen = new CCIPConfigTypes.OCR3Config[](2); - blueAndGreen[0] = blueConfig; - blueAndGreen[1] = greenConfig; - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory expectedConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); - expectedConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - config: blueConfig, - configCount: 1, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - expectedConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ - config: greenConfig, - configCount: 2, - configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - }); - - vm.expectEmit(); - emit CCIPConfig.ConfigSet(donId, uint8(Internal.OCRPluginType.Commit), expectedConfig); - - s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, blueAndGreen); - - // should see the updated config in the contract state. - CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfig = - s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); - - _assertOCR3ConfigWithMetaEqual(storedConfig, expectedConfig); - } - - function test_updatePluginConfig_StagingToRunning_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - // add blue config. - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config[] memory startConfigs = new CCIPConfigTypes.OCR3Config[](1); - startConfigs[0] = blueConfig; - - // add blue AND green config to indicate an update. - s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, startConfigs); - CCIPConfigTypes.OCR3Config memory greenConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit-new") - }); - CCIPConfigTypes.OCR3Config[] memory blueAndGreen = new CCIPConfigTypes.OCR3Config[](2); - blueAndGreen[0] = blueConfig; - blueAndGreen[1] = greenConfig; - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory expectedConfig = new CCIPConfigTypes.OCR3ConfigWithMeta[](2); - expectedConfig[0] = CCIPConfigTypes.OCR3ConfigWithMeta({ - config: blueConfig, - configCount: 1, - configDigest: s_ccipCC.computeConfigDigest(donId, 1, blueConfig) - }); - expectedConfig[1] = CCIPConfigTypes.OCR3ConfigWithMeta({ - config: greenConfig, - configCount: 2, - configDigest: s_ccipCC.computeConfigDigest(donId, 2, greenConfig) - }); - - vm.expectEmit(); - emit CCIPConfig.ConfigSet(donId, uint8(Internal.OCRPluginType.Commit), expectedConfig); - - s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, blueAndGreen); - - // should see the updated config in the contract state. - CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfig = - s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); - - _assertOCR3ConfigWithMetaEqual(storedConfig, expectedConfig); - } - - // Reverts. - function test__updatePluginConfig_InvalidConfigLength_Reverts() public { - uint32 donId = 1; - CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](3); - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.InvalidConfigLength.selector, uint256(3))); - s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, newConfig); - } - - function test__updatePluginConfig_InvalidConfigStateTransition_Reverts() public { - uint32 donId = 1; - CCIPConfigTypes.OCR3Config[] memory newConfig = new CCIPConfigTypes.OCR3Config[](2); - // 0 -> 2 is an invalid state transition. - vm.expectRevert(abi.encodeWithSelector(CCIPConfig.InvalidConfigStateTransition.selector, 0, 2)); - s_ccipCC.updatePluginConfig(donId, Internal.OCRPluginType.Commit, newConfig); - } -} - -contract CCIPConfig_beforeCapabilityConfigSet is CCIPConfigSetup { - // Successes. - function test_beforeCapabilityConfigSet_ZeroLengthConfig_Success() public { - changePrank(CAPABILITIES_REGISTRY); - - CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](0); - bytes memory encodedConfigs = abi.encode(configs); - s_ccipCC.beforeCapabilityConfigSet(new bytes32[](0), encodedConfigs, 1, 1); - } - - function test_beforeCapabilityConfigSet_CommitConfigOnly_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - changePrank(CAPABILITIES_REGISTRY); - - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](1); - configs[0] = blueConfig; - - bytes memory encoded = abi.encode(configs); - s_ccipCC.beforeCapabilityConfigSet(new bytes32[](0), encoded, 1, donId); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfigs = - s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); - assertEq(storedConfigs.length, 1, "config length must be 1"); - assertEq(storedConfigs[0].configCount, uint64(1), "config count must be 1"); - assertEq( - uint256(storedConfigs[0].config.pluginType), uint256(Internal.OCRPluginType.Commit), "plugin type must be commit" - ); - } - - function test_beforeCapabilityConfigSet_ExecConfigOnly_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - changePrank(CAPABILITIES_REGISTRY); - - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Execution, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("exec") - }); - CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](1); - configs[0] = blueConfig; - - bytes memory encoded = abi.encode(configs); - s_ccipCC.beforeCapabilityConfigSet(new bytes32[](0), encoded, 1, donId); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedConfigs = - s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Execution); - assertEq(storedConfigs.length, 1, "config length must be 1"); - assertEq(storedConfigs[0].configCount, uint64(1), "config count must be 1"); - assertEq( - uint256(storedConfigs[0].config.pluginType), - uint256(Internal.OCRPluginType.Execution), - "plugin type must be execution" - ); - } - - function test_beforeCapabilityConfigSet_CommitAndExecConfig_Success() public { - CCIPConfigTypes.OCR3Node[] memory nodes = _addChainConfig(4); - changePrank(CAPABILITIES_REGISTRY); - - uint32 donId = 1; - CCIPConfigTypes.OCR3Config memory blueCommitConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Commit, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("commit") - }); - CCIPConfigTypes.OCR3Config memory blueExecConfig = CCIPConfigTypes.OCR3Config({ - pluginType: Internal.OCRPluginType.Execution, - offrampAddress: abi.encodePacked(keccak256(abi.encode("offramp"))), - chainSelector: 1, - nodes: nodes, - FRoleDON: 1, - offchainConfigVersion: 30, - offchainConfig: bytes("exec") - }); - CCIPConfigTypes.OCR3Config[] memory configs = new CCIPConfigTypes.OCR3Config[](2); - configs[0] = blueExecConfig; - configs[1] = blueCommitConfig; - - bytes memory encoded = abi.encode(configs); - s_ccipCC.beforeCapabilityConfigSet(new bytes32[](0), encoded, 1, donId); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedExecConfigs = - s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Execution); - assertEq(storedExecConfigs.length, 1, "config length must be 1"); - assertEq(storedExecConfigs[0].configCount, uint64(1), "config count must be 1"); - assertEq( - uint256(storedExecConfigs[0].config.pluginType), - uint256(Internal.OCRPluginType.Execution), - "plugin type must be execution" - ); - - CCIPConfigTypes.OCR3ConfigWithMeta[] memory storedCommitConfigs = - s_ccipCC.getOCRConfig(donId, Internal.OCRPluginType.Commit); - assertEq(storedCommitConfigs.length, 1, "config length must be 1"); - assertEq(storedCommitConfigs[0].configCount, uint64(1), "config count must be 1"); - assertEq( - uint256(storedCommitConfigs[0].config.pluginType), - uint256(Internal.OCRPluginType.Commit), - "plugin type must be commit" - ); - } - - // Reverts. - - function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_Reverts() public { - bytes32[] memory nodes = new bytes32[](0); - bytes memory config = bytes(""); - uint64 configCount = 1; - uint32 donId = 1; - vm.expectRevert(CCIPConfig.OnlyCapabilitiesRegistryCanCall.selector); - s_ccipCC.beforeCapabilityConfigSet(nodes, config, configCount, donId); - } -} diff --git a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol index 87abad68b0..9caab7baab 100644 --- a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol @@ -55,7 +55,7 @@ contract RMNHome_setSecondary is HomeBaseTest { encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); vm.expectEmit(); - emit HomeBase.ConfigSet(encodedConfig); + emit HomeBase.ConfigSet(encodedConfig.configDigest, encodedConfig); s_homeBase.setSecondary(DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); diff --git a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol index 84a2fc7727..5ff5217a4d 100644 --- a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol @@ -80,7 +80,7 @@ contract RMNHome_setSecondary is RMNHomeTest { encodedConfig.configDigest = versionedConfig.configDigest; vm.expectEmit(); - emit HomeBase.ConfigSet(encodedConfig); + emit HomeBase.ConfigSet(encodedConfig.configDigest, encodedConfig); s_rmnHome.setSecondary(RMN_DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); diff --git a/contracts/src/v0.8/ccip/test/helpers/CCIPConfigHelper.sol b/contracts/src/v0.8/ccip/test/helpers/CCIPConfigHelper.sol deleted file mode 100644 index efade5190b..0000000000 --- a/contracts/src/v0.8/ccip/test/helpers/CCIPConfigHelper.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.24; - -import {CCIPConfig} from "../../capability/CCIPConfig.sol"; -import {CCIPConfigTypes} from "../../capability/libraries/CCIPConfigTypes.sol"; -import {Internal} from "../../libraries/Internal.sol"; - -contract CCIPConfigHelper is CCIPConfig { - constructor(address capabilitiesRegistry) CCIPConfig(capabilitiesRegistry) {} - - function stateFromConfigLength(uint256 configLength) public pure returns (CCIPConfigTypes.ConfigState) { - return _stateFromConfigLength(configLength); - } - - function validateConfigStateTransition( - CCIPConfigTypes.ConfigState currentState, - CCIPConfigTypes.ConfigState newState - ) public pure { - _validateConfigStateTransition(currentState, newState); - } - - function validateConfigTransition( - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig, - CCIPConfigTypes.OCR3ConfigWithMeta[] memory newConfigWithMeta - ) public pure { - _validateConfigTransition(currentConfig, newConfigWithMeta); - } - - function computeNewConfigWithMeta( - uint32 donId, - CCIPConfigTypes.OCR3ConfigWithMeta[] memory currentConfig, - CCIPConfigTypes.OCR3Config[] memory newConfig, - CCIPConfigTypes.ConfigState currentState, - CCIPConfigTypes.ConfigState newState - ) public view returns (CCIPConfigTypes.OCR3ConfigWithMeta[] memory) { - return _computeNewConfigWithMeta(donId, currentConfig, newConfig, currentState, newState); - } - - function groupByPluginType( - CCIPConfigTypes.OCR3Config[] memory ocr3Configs - ) - public - pure - returns (CCIPConfigTypes.OCR3Config[] memory commitConfigs, CCIPConfigTypes.OCR3Config[] memory execConfigs) - { - return _groupByPluginType(ocr3Configs); - } - - function computeConfigDigest( - uint32 donId, - uint64 configCount, - CCIPConfigTypes.OCR3Config memory ocr3Config - ) public pure returns (bytes32) { - return _computeConfigDigest(donId, configCount, ocr3Config); - } - - function validateConfig(CCIPConfigTypes.OCR3Config memory cfg) public view { - _validateConfig(cfg); - } - - function updatePluginConfig( - uint32 donId, - Internal.OCRPluginType pluginType, - CCIPConfigTypes.OCR3Config[] memory newConfig - ) public { - _updatePluginConfig(donId, pluginType, newConfig); - } -} From 2d7ed93249050efbe5153f45b09d0d77739e8297 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 25 Sep 2024 12:35:02 +0200 Subject: [PATCH 27/36] put base into CCIPHome --- .../src/v0.8/ccip/capability/CCIPHome.sol | 252 ++++++++++++++++-- 1 file changed, 231 insertions(+), 21 deletions(-) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index b058264a03..605d770f96 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -2,10 +2,11 @@ pragma solidity 0.8.24; import {ICapabilityConfiguration} from "../../keystone/interfaces/ICapabilityConfiguration.sol"; +import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {ICapabilitiesRegistry} from "./interfaces/ICapabilitiesRegistry.sol"; +import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; import {Internal} from "../libraries/Internal.sol"; -import {HomeBase} from "./HomeBase.sol"; import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/interfaces/IERC165.sol"; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; @@ -15,11 +16,15 @@ import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts /// Each chain will have a single configuration which includes information like the router address. /// Each CR DON will have up to four configurations: for each of (commit, exec), one blue and one green configuration. /// This is done in order to achieve "blue-green" deployments. -contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { +contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, IERC165 { using EnumerableSet for EnumerableSet.UintSet; event ChainConfigRemoved(uint64 chainSelector); event ChainConfigSet(uint64 chainSelector, ChainConfig chainConfig); + event ConfigSet(bytes32 indexed configDigest, StoredConfig versionedConfig); + event ConfigRevoked(bytes32 indexed configDigest); + event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); + event ConfigPromoted(bytes32 indexed configDigest); error OutOfBoundsNodesLength(); error DuplicatePeerId(); @@ -41,6 +46,15 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { error OnlySelfCallAllowed(); error OnlyCapabilitiesRegistryCanCall(); error ZeroAddressNotAllowed(); + error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); + error DigestNotFound(bytes32 configDigest); + + // TODO kill + struct StoredConfig { + bytes32 configDigest; + uint32 version; + bytes config; + } /// @notice Represents an oracle node in OCR3 configs part of the role DON. /// Every configured node should be a signer, but does not have to be a transmitter. @@ -67,7 +81,7 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { struct VersionedConfig { uint32 version; bytes32 configDigest; - OCR3Config staticConfig; + OCR3Config config; } /// @notice Chain configuration. @@ -86,12 +100,19 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { string public constant override typeAndVersion = "CCIPHome 1.6.0-dev"; - /// @dev A prefix added to all config digests that is unique to the implementation of the HomeBase contract. + /// @dev A prefix added to all config digests that is unique to the implementation uint256 private constant PREFIX = 0x000a << (256 - 16); // 0x000a00..00 bytes32 internal constant EMPTY_ENCODED_ADDRESS_HASH = keccak256(abi.encode(address(0))); /// @dev 256 is the hard limit due to the bit encoding of their indexes into a uint256. uint256 internal constant MAX_NUM_ORACLES = 256; + /// @notice Used for encoding the config digest prefix + uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 + /// @notice The max number of configs that can be active at the same time. + uint256 private constant MAX_CONCURRENT_CONFIGS = 2; + /// @notice Helper to identify the zero config digest with less casting. + bytes32 private constant ZERO_DIGEST = bytes32(uint256(0)); + /// @dev The canonical capabilities registry address. address internal immutable i_capabilitiesRegistry; @@ -101,6 +122,15 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { /// @dev All chains that are configured. EnumerableSet.UintSet private s_remoteChainSelectors; + /// @notice This array holds the configs. + /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. + mapping(bytes32 pluginKey => StoredConfig[MAX_CONCURRENT_CONFIGS]) private s_configs; + + /// @notice The total number of configs ever set, used for generating the version of the configs. + uint32 private s_configCount = 0; + /// @notice The index of the primary config. + uint32 private s_primaryConfigIndex = 0; + /// @notice Constructor for the CCIPHome contract takes in the address of the capabilities registry. This address /// is the only allowed caller to mutate the configuration through beforeCapabilityConfigSet. constructor(address capabilitiesRegistry) { @@ -173,7 +203,7 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { VersionedConfig({ version: storedConfig.version, configDigest: storedConfig.configDigest, - staticConfig: abi.decode(storedConfig.staticConfig, (OCR3Config)) + config: abi.decode(storedConfig.config, (OCR3Config)) }), true ); @@ -195,7 +225,7 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { primaryConfig = VersionedConfig({ version: primaryStoredConfig.version, configDigest: primaryStoredConfig.configDigest, - staticConfig: abi.decode(primaryStoredConfig.staticConfig, (OCR3Config)) + config: abi.decode(primaryStoredConfig.config, (OCR3Config)) }); } @@ -205,7 +235,7 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { secondaryConfig = VersionedConfig({ version: secondaryStoredConfig.version, configDigest: secondaryStoredConfig.configDigest, - staticConfig: abi.decode(secondaryStoredConfig.staticConfig, (OCR3Config)) + config: abi.decode(secondaryStoredConfig.config, (OCR3Config)) }); } @@ -216,11 +246,8 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { // │ Validation │ // ================================================================ - /// @inheritdoc HomeBase - /// @param encodedStaticConfig The abi encoded static configuration. - /// @dev Validates the static only as CCIPHome does not use any dynamic config. - function _validateStaticAndDynamicConfig(bytes memory encodedStaticConfig, bytes memory) internal view override { - OCR3Config memory cfg = abi.decode(encodedStaticConfig, (OCR3Config)); + function _validateConfig(bytes memory encodedConfig) internal view { + OCR3Config memory cfg = abi.decode(encodedConfig, (OCR3Config)); if (cfg.chainSelector == 0) revert ChainSelectorNotSet(); if (cfg.pluginType != Internal.OCRPluginType.Commit && cfg.pluginType != Internal.OCRPluginType.Execution) { @@ -276,23 +303,206 @@ contract CCIPHome is HomeBase, ICapabilityConfiguration, IERC165 { _ensureInRegistry(p2pIds); } - /// @inheritdoc HomeBase /// @dev No-op as there is no dynamic config. - function _validateDynamicConfig(bytes memory, bytes memory) internal pure override {} + function _validateDynamicConfig(bytes memory, bytes memory) internal pure {} - /// @inheritdoc HomeBase - function _getConfigDigestPrefix() internal pure override returns (uint256) { - return PREFIX; - } - - /// @inheritdoc HomeBase /// @dev Uses ownable as caller validation method. - function _validateCaller() internal view override { + function _validateCaller() internal view { if (msg.sender != address(this)) { revert OnlySelfCallAllowed(); } } + // ================================================================ + // │ HomeBase │ + // ================================================================ + + // ================================================================ + // │ Getters │ + // ================================================================ + + /// @notice Returns the current primary and secondary config digests. + /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. + /// @param pluginKey The key of the plugin to get the config digests for. + /// @return primaryConfigDigest The digest of the primary config. + /// @return secondaryConfigDigest The digest of the secondary config. + function getConfigDigests( + bytes32 pluginKey + ) external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + return ( + s_configs[pluginKey][s_primaryConfigIndex].configDigest, + s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest + ); + } + + /// @notice Returns the primary config digest for for a given key. + /// @param pluginKey The key of the plugin to get the config digests for. + function getPrimaryDigest(bytes32 pluginKey) public view returns (bytes32) { + return s_configs[pluginKey][s_primaryConfigIndex].configDigest; + } + + /// @notice Returns the secondary config digest for for a given key. + /// @param pluginKey The key of the plugin to get the config digests for. + function getSecondaryDigest(bytes32 pluginKey) public view returns (bytes32) { + return s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest; + } + + /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero + /// digest. This is done to prevent exposing old config state that is invalid. + /// @param pluginKey The key of the plugin to get the config for. + /// @param configDigest The digest of the config to fetch. + function _getStoredConfig( + bytes32 pluginKey, + bytes32 configDigest + ) internal view returns (StoredConfig memory storedConfig, bool ok) { + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { + // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old + // config state that is invalid. + if (s_configs[pluginKey][i].configDigest == configDigest && configDigest != ZERO_DIGEST) { + return (s_configs[pluginKey][i], true); + } + } + return (StoredConfig(ZERO_DIGEST, 0, ""), false); + } + + /// @notice Returns the primary stored config for a given key. + /// @param pluginKey The key of the plugin to get the config for. + /// @return primaryConfig The primary stored config. + /// @return ok True if the config was found, false otherwise. + function _getPrimaryStoredConfig( + bytes32 pluginKey + ) internal view returns (StoredConfig memory primaryConfig, bool ok) { + if (s_configs[pluginKey][s_primaryConfigIndex].configDigest == ZERO_DIGEST) { + return (StoredConfig(ZERO_DIGEST, 0, ""), false); + } + + return (s_configs[pluginKey][s_primaryConfigIndex], true); + } + + /// @notice Returns the secondary stored config for a given key. + /// @param pluginKey The key of the plugin to get the config for. + /// @return secondaryConfig The secondary stored config. + /// @return ok True if the config was found, false otherwise. + function _getSecondaryStoredConfig( + bytes32 pluginKey + ) internal view returns (StoredConfig memory secondaryConfig, bool ok) { + if (s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { + return (StoredConfig(ZERO_DIGEST, 0, ""), false); + } + + return (s_configs[pluginKey][s_primaryConfigIndex ^ 1], true); + } + + // ================================================================ + // │ State transitions │ + // ================================================================ + + /// @notice Sets a new config as the secondary config. Does not influence the primary config. + /// @param pluginKey The key of the plugin to set the config for. + /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. + /// This is done to prevent accidental overwrites. + /// @return newConfigDigest The digest of the new config. + function setSecondary( + bytes32 pluginKey, + bytes calldata config, + bytes32 digestToOverwrite + ) external returns (bytes32 newConfigDigest) { + _validateCaller(); + _validateConfig(config); + + bytes32 existingDigest = getSecondaryDigest(pluginKey); + + if (existingDigest != digestToOverwrite) { + revert ConfigDigestMismatch(existingDigest, digestToOverwrite); + } + + // are we going to overwrite a config? If so, emit an event. + if (existingDigest != ZERO_DIGEST) { + emit ConfigRevoked(digestToOverwrite); + } + + uint32 newVersion = ++s_configCount; + newConfigDigest = _calculateConfigDigest(pluginKey, config, newVersion); + + StoredConfig memory newConfig = StoredConfig({configDigest: newConfigDigest, version: newVersion, config: config}); + + s_configs[pluginKey][s_primaryConfigIndex ^ 1] = newConfig; + + emit ConfigSet(newConfig.configDigest, newConfig); + + return newConfigDigest; + } + + /// @notice Revokes a specific config by digest. + /// @param pluginKey The key of the plugin to revoke the config for. + /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. + function revokeSecondary(bytes32 pluginKey, bytes32 configDigest) external { + _validateCaller(); + + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configs[pluginKey][secondaryConfigIndex].configDigest != configDigest) { + revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, configDigest); + } + + emit ConfigRevoked(configDigest); + // Delete only the digest, as that's what's used to determine if a config is active. This means the actual + // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in + // the future. + delete s_configs[pluginKey][secondaryConfigIndex].configDigest; + } + + /// @notice Promotes the secondary config to the primary config and revokes the primary config. + /// @param pluginKey The key of the plugin to promote the config for. + /// @param digestToPromote The digest of the config to promote. + /// @param digestToRevoke The digest of the config to revoke. + function promoteSecondaryAndRevokePrimary( + bytes32 pluginKey, + bytes32 digestToPromote, + bytes32 digestToRevoke + ) external { + _validateCaller(); + + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configs[pluginKey][secondaryConfigIndex].configDigest != digestToPromote) { + revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, digestToPromote); + } + + uint256 primaryConfigIndex = s_primaryConfigIndex; + if (s_configs[pluginKey][primaryConfigIndex].configDigest != digestToRevoke) { + revert ConfigDigestMismatch(s_configs[pluginKey][primaryConfigIndex].configDigest, digestToRevoke); + } + + delete s_configs[pluginKey][primaryConfigIndex].configDigest; + + s_primaryConfigIndex ^= 1; + if (digestToRevoke != ZERO_DIGEST) { + emit ConfigRevoked(digestToRevoke); + } + emit ConfigPromoted(digestToPromote); + } + + /// @notice Calculates the config digest for a given plugin key, static config, and version. + /// @param pluginKey The key of the plugin to calculate the digest for. + /// @param staticConfig The static part of the config. + /// @param version The version of the config. + /// @return The calculated config digest. + function _calculateConfigDigest( + bytes32 pluginKey, + bytes memory staticConfig, + uint32 version + ) internal view returns (bytes32) { + return bytes32( + (PREFIX & PREFIX_MASK) + | ( + uint256( + keccak256( + bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), pluginKey, version), staticConfig) + ) + ) & ~PREFIX_MASK + ) + ); + } + // ================================================================ // │ Chain Configuration │ // ================================================================ From fff5d773efeff1878423a1f93000bc9d399cc571 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 25 Sep 2024 14:52:50 +0200 Subject: [PATCH 28/36] ccipHome without inheritance --- .../src/v0.8/ccip/capability/CCIPHome.sol | 349 +++++++----------- 1 file changed, 139 insertions(+), 210 deletions(-) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index 605d770f96..a987ac64c3 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -21,7 +21,7 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, event ChainConfigRemoved(uint64 chainSelector); event ChainConfigSet(uint64 chainSelector, ChainConfig chainConfig); - event ConfigSet(bytes32 indexed configDigest, StoredConfig versionedConfig); + event ConfigSet(bytes32 indexed configDigest, VersionedConfig versionedConfig); event ConfigRevoked(bytes32 indexed configDigest); event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); event ConfigPromoted(bytes32 indexed configDigest); @@ -43,18 +43,14 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, error FTooHigh(); error InvalidNode(OCR3Node node); error NotEnoughTransmitters(uint256 got, uint256 minimum); - error OnlySelfCallAllowed(); error OnlyCapabilitiesRegistryCanCall(); error ZeroAddressNotAllowed(); error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); error DigestNotFound(bytes32 configDigest); - // TODO kill - struct StoredConfig { - bytes32 configDigest; - uint32 version; - bytes config; - } + error InvalidStateTransition( + bytes32 currentPrimaryDigest, bytes32 currentSecondaryDigest, bytes32 blue, bytes32 green + ); /// @notice Represents an oracle node in OCR3 configs part of the role DON. /// Every configured node should be a signer, but does not have to be a transmitter. @@ -124,7 +120,7 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. - mapping(bytes32 pluginKey => StoredConfig[MAX_CONCURRENT_CONFIGS]) private s_configs; + mapping(bytes32 pluginKey => VersionedConfig[MAX_CONCURRENT_CONFIGS]) private s_configs; /// @notice The total number of configs ever set, used for generating the version of the configs. uint32 private s_configCount = 0; @@ -160,20 +156,46 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, function beforeCapabilityConfigSet( bytes32[] calldata, // nodes bytes calldata update, - uint64, // configCount - uint32 // donId + // Config count is unused because we don't want to invalidate a config on blue/green promotions so we keep track of + // the actual newly submitted configs instead of the number of config mutations. + uint64, // config count + uint32 donId ) external override { if (msg.sender != i_capabilitiesRegistry) { revert OnlyCapabilitiesRegistryCanCall(); } - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory errorData) = address(this).call(update); - if (!success) { - // re-throw the revert message from the call - assembly { - revert(add(errorData, 32), errorData) + + (OCR3Config memory blue, OCR3Config memory green) = abi.decode(update, (OCR3Config, OCR3Config)); + bytes32 pluginKey = bytes32(uint256(donId)); + uint32 newConfigVersion = s_configCount + 1; + + (bytes32 currentBlueDigest, bytes32 currentGreenDigest) = getConfigDigests(pluginKey); + bytes32 newBlueDigest = _calculateConfigDigest(pluginKey, abi.encode(blue), newConfigVersion); + bytes32 newGreenDigest = _calculateConfigDigest(pluginKey, abi.encode(green), newConfigVersion); + + // Check the possible steps of the state machine + // 1. promoteSecondaryAndRevokePrimary requires + // - blue digest to be the current secondary digest + // - green digest to be the zero digest + if (currentGreenDigest == newBlueDigest && newGreenDigest == ZERO_DIGEST) { + _promoteSecondaryAndRevokePrimary(pluginKey, newBlueDigest); + return; + } + // setSecondary and revokeSecondary require no changes to the blue config + if (currentBlueDigest == newBlueDigest) { + // 2. If the green config is non-zero, we call setSecondary + if (newGreenDigest != ZERO_DIGEST) { + _setSecondary(pluginKey, blue, newBlueDigest); + return; + } else { + // 3. If the green config is zero, we call revokeSecondary + _revokeSecondary(pluginKey, newGreenDigest); + return; } } + + // There are no other valid state transitions so we revert if we have not returned by now. + revert InvalidStateTransition(currentBlueDigest, currentGreenDigest, newBlueDigest, newGreenDigest); } /// @inheritdoc ICapabilityConfiguration @@ -187,140 +209,6 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, // │ Getters │ // ================================================================ - /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use - /// in case one of the configs is too large to be returnable by one of the other getters. - /// @param pluginKey The unique key for the DON that the configuration applies to. - /// @param configDigest The digest of the config to fetch. - /// @return versionedConfig The config and its version. - /// @return ok True if the config was found, false otherwise. - function getConfig( - bytes32 pluginKey, - bytes32 configDigest - ) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(pluginKey, configDigest); - if (configOK) { - return ( - VersionedConfig({ - version: storedConfig.version, - configDigest: storedConfig.configDigest, - config: abi.decode(storedConfig.config, (OCR3Config)) - }), - true - ); - } - - return (versionedConfig, false); - } - - /// @notice Returns the primary and secondary configuration for a given plugin key. - /// @param pluginKey The unique key for the DON that the configuration applies to. - /// @return primaryConfig The primary configuration. - /// @return secondaryConfig The secondary configuration. - function getAllConfigs( - bytes32 pluginKey - ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { - (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(pluginKey); - - if (primaryOk) { - primaryConfig = VersionedConfig({ - version: primaryStoredConfig.version, - configDigest: primaryStoredConfig.configDigest, - config: abi.decode(primaryStoredConfig.config, (OCR3Config)) - }); - } - - (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(pluginKey); - - if (secondaryOk) { - secondaryConfig = VersionedConfig({ - version: secondaryStoredConfig.version, - configDigest: secondaryStoredConfig.configDigest, - config: abi.decode(secondaryStoredConfig.config, (OCR3Config)) - }); - } - - return (primaryConfig, secondaryConfig); - } - - // ================================================================ - // │ Validation │ - // ================================================================ - - function _validateConfig(bytes memory encodedConfig) internal view { - OCR3Config memory cfg = abi.decode(encodedConfig, (OCR3Config)); - - if (cfg.chainSelector == 0) revert ChainSelectorNotSet(); - if (cfg.pluginType != Internal.OCRPluginType.Commit && cfg.pluginType != Internal.OCRPluginType.Execution) { - revert InvalidPluginType(); - } - if (cfg.offrampAddress.length == 0 || keccak256(cfg.offrampAddress) == EMPTY_ENCODED_ADDRESS_HASH) { - revert OfframpAddressCannotBeZero(); - } - if (!s_remoteChainSelectors.contains(cfg.chainSelector)) revert ChainSelectorNotFound(cfg.chainSelector); - - // fChain cannot exceed FRoleDON, since it is a subcommittee in the larger DON - uint256 FRoleDON = cfg.FRoleDON; - uint256 fChain = s_chainConfigurations[cfg.chainSelector].fChain; - // fChain > 0 is enforced in applyChainConfigUpdates, and the presence of a chain config is checked above - // FRoleDON != 0 because FRoleDON >= fChain is enforced here - if (fChain > FRoleDON) { - revert FChainTooHigh(fChain, FRoleDON); - } - - // len(nodes) >= 3 * FRoleDON + 1 - // len(nodes) == numberOfSigners - uint256 numberOfNodes = cfg.nodes.length; - if (numberOfNodes > MAX_NUM_ORACLES) revert TooManySigners(); - if (numberOfNodes <= 3 * FRoleDON) revert FTooHigh(); - - uint256 nonZeroTransmitters = 0; - bytes32[] memory p2pIds = new bytes32[](numberOfNodes); - for (uint256 i = 0; i < numberOfNodes; ++i) { - OCR3Node memory node = cfg.nodes[i]; - - // 3 * fChain + 1 <= nonZeroTransmitters <= 3 * FRoleDON + 1 - // Transmitters can be set to 0 since there can be more signers than transmitters, - if (node.transmitterKey.length != 0) { - nonZeroTransmitters++; - } - - // Signer key and p2pIds must always be present - if (node.signerKey.length == 0 || node.p2pId == bytes32(0)) { - revert InvalidNode(node); - } - - p2pIds[i] = node.p2pId; - } - - // We check for chain config presence above, so fChain here must be non-zero. fChain <= FRoleDON due to the checks above. - // There can be less transmitters than signers - so they can be set to zero (which indicates that a node is a signer, but not a transmitter). - uint256 minTransmittersLength = 3 * fChain + 1; - if (nonZeroTransmitters < minTransmittersLength) { - revert NotEnoughTransmitters(nonZeroTransmitters, minTransmittersLength); - } - - // Check that the readers are in the capabilities registry. - _ensureInRegistry(p2pIds); - } - - /// @dev No-op as there is no dynamic config. - function _validateDynamicConfig(bytes memory, bytes memory) internal pure {} - - /// @dev Uses ownable as caller validation method. - function _validateCaller() internal view { - if (msg.sender != address(this)) { - revert OnlySelfCallAllowed(); - } - } - - // ================================================================ - // │ HomeBase │ - // ================================================================ - - // ================================================================ - // │ Getters │ - // ================================================================ - /// @notice Returns the current primary and secondary config digests. /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. /// @param pluginKey The key of the plugin to get the config digests for. @@ -328,7 +216,7 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, /// @return secondaryConfigDigest The digest of the secondary config. function getConfigDigests( bytes32 pluginKey - ) external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + ) public view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { return ( s_configs[pluginKey][s_primaryConfigIndex].configDigest, s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest @@ -347,14 +235,16 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, return s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest; } - /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero - /// digest. This is done to prevent exposing old config state that is invalid. - /// @param pluginKey The key of the plugin to get the config for. + /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use + /// in case one of the configs is too large to be returnable by one of the other getters. + /// @param pluginKey The unique key for the DON that the configuration applies to. /// @param configDigest The digest of the config to fetch. - function _getStoredConfig( + /// @return versionedConfig The config and its version. + /// @return ok True if the config was found, false otherwise. + function getConfig( bytes32 pluginKey, bytes32 configDigest - ) internal view returns (StoredConfig memory storedConfig, bool ok) { + ) external view returns (VersionedConfig memory versionedConfig, bool ok) { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old // config state that is invalid. @@ -362,35 +252,28 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, return (s_configs[pluginKey][i], true); } } - return (StoredConfig(ZERO_DIGEST, 0, ""), false); + // versionConfig is uninitialized so it contains default values. + return (versionedConfig, false); } - /// @notice Returns the primary stored config for a given key. - /// @param pluginKey The key of the plugin to get the config for. - /// @return primaryConfig The primary stored config. - /// @return ok True if the config was found, false otherwise. - function _getPrimaryStoredConfig( + /// @notice Returns the primary and secondary configuration for a given plugin key. + /// @param pluginKey The unique key for the DON that the configuration applies to. + /// @return primaryConfig The primary configuration. + /// @return secondaryConfig The secondary configuration. + function getAllConfigs( bytes32 pluginKey - ) internal view returns (StoredConfig memory primaryConfig, bool ok) { - if (s_configs[pluginKey][s_primaryConfigIndex].configDigest == ZERO_DIGEST) { - return (StoredConfig(ZERO_DIGEST, 0, ""), false); + ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { + VersionedConfig memory storedPrimaryConfig = s_configs[pluginKey][s_primaryConfigIndex]; + if (storedPrimaryConfig.configDigest != ZERO_DIGEST) { + primaryConfig = storedPrimaryConfig; } - return (s_configs[pluginKey][s_primaryConfigIndex], true); - } - - /// @notice Returns the secondary stored config for a given key. - /// @param pluginKey The key of the plugin to get the config for. - /// @return secondaryConfig The secondary stored config. - /// @return ok True if the config was found, false otherwise. - function _getSecondaryStoredConfig( - bytes32 pluginKey - ) internal view returns (StoredConfig memory secondaryConfig, bool ok) { - if (s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { - return (StoredConfig(ZERO_DIGEST, 0, ""), false); + VersionedConfig memory storedSecondaryConfig = s_configs[pluginKey][s_primaryConfigIndex ^ 1]; + if (storedSecondaryConfig.configDigest != ZERO_DIGEST) { + secondaryConfig = storedSecondaryConfig; } - return (s_configs[pluginKey][s_primaryConfigIndex ^ 1], true); + return (primaryConfig, secondaryConfig); } // ================================================================ @@ -402,43 +285,37 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. /// This is done to prevent accidental overwrites. /// @return newConfigDigest The digest of the new config. - function setSecondary( + function _setSecondary( bytes32 pluginKey, - bytes calldata config, - bytes32 digestToOverwrite - ) external returns (bytes32 newConfigDigest) { - _validateCaller(); + OCR3Config memory config, + bytes32 newDigest + ) internal returns (bytes32 newConfigDigest) { _validateConfig(config); bytes32 existingDigest = getSecondaryDigest(pluginKey); - - if (existingDigest != digestToOverwrite) { - revert ConfigDigestMismatch(existingDigest, digestToOverwrite); - } - // are we going to overwrite a config? If so, emit an event. - if (existingDigest != ZERO_DIGEST) { - emit ConfigRevoked(digestToOverwrite); + if (getSecondaryDigest(pluginKey) != ZERO_DIGEST) { + emit ConfigRevoked(existingDigest); } uint32 newVersion = ++s_configCount; - newConfigDigest = _calculateConfigDigest(pluginKey, config, newVersion); - StoredConfig memory newConfig = StoredConfig({configDigest: newConfigDigest, version: newVersion, config: config}); + VersionedConfig memory newConfig = VersionedConfig({configDigest: newDigest, version: newVersion, config: config}); - s_configs[pluginKey][s_primaryConfigIndex ^ 1] = newConfig; + VersionedConfig storage existingConfig = s_configs[pluginKey][s_primaryConfigIndex ^ 1]; + // TODO existingConfig.config = config; + existingConfig.version = newVersion; + existingConfig.configDigest = newDigest; emit ConfigSet(newConfig.configDigest, newConfig); - return newConfigDigest; + return newDigest; } /// @notice Revokes a specific config by digest. /// @param pluginKey The key of the plugin to revoke the config for. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(bytes32 pluginKey, bytes32 configDigest) external { - _validateCaller(); - + function _revokeSecondary(bytes32 pluginKey, bytes32 configDigest) internal { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; if (s_configs[pluginKey][secondaryConfigIndex].configDigest != configDigest) { revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, configDigest); @@ -454,30 +331,23 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, /// @notice Promotes the secondary config to the primary config and revokes the primary config. /// @param pluginKey The key of the plugin to promote the config for. /// @param digestToPromote The digest of the config to promote. - /// @param digestToRevoke The digest of the config to revoke. - function promoteSecondaryAndRevokePrimary( - bytes32 pluginKey, - bytes32 digestToPromote, - bytes32 digestToRevoke - ) external { - _validateCaller(); - + function _promoteSecondaryAndRevokePrimary(bytes32 pluginKey, bytes32 digestToPromote) internal { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; if (s_configs[pluginKey][secondaryConfigIndex].configDigest != digestToPromote) { revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, digestToPromote); } uint256 primaryConfigIndex = s_primaryConfigIndex; - if (s_configs[pluginKey][primaryConfigIndex].configDigest != digestToRevoke) { - revert ConfigDigestMismatch(s_configs[pluginKey][primaryConfigIndex].configDigest, digestToRevoke); - } delete s_configs[pluginKey][primaryConfigIndex].configDigest; - s_primaryConfigIndex ^= 1; + bytes32 digestToRevoke = s_configs[pluginKey][primaryConfigIndex].configDigest; if (digestToRevoke != ZERO_DIGEST) { emit ConfigRevoked(digestToRevoke); } + + s_primaryConfigIndex ^= 1; + emit ConfigPromoted(digestToPromote); } @@ -503,6 +373,65 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, ); } + // ================================================================ + // │ Validation │ + // ================================================================ + + function _validateConfig(OCR3Config memory cfg) internal view { + if (cfg.chainSelector == 0) revert ChainSelectorNotSet(); + if (cfg.pluginType != Internal.OCRPluginType.Commit && cfg.pluginType != Internal.OCRPluginType.Execution) { + revert InvalidPluginType(); + } + if (cfg.offrampAddress.length == 0 || keccak256(cfg.offrampAddress) == EMPTY_ENCODED_ADDRESS_HASH) { + revert OfframpAddressCannotBeZero(); + } + if (!s_remoteChainSelectors.contains(cfg.chainSelector)) revert ChainSelectorNotFound(cfg.chainSelector); + + // fChain cannot exceed FRoleDON, since it is a subcommittee in the larger DON + uint256 FRoleDON = cfg.FRoleDON; + uint256 fChain = s_chainConfigurations[cfg.chainSelector].fChain; + // fChain > 0 is enforced in applyChainConfigUpdates, and the presence of a chain config is checked above + // FRoleDON != 0 because FRoleDON >= fChain is enforced here + if (fChain > FRoleDON) { + revert FChainTooHigh(fChain, FRoleDON); + } + + // len(nodes) >= 3 * FRoleDON + 1 + // len(nodes) == numberOfSigners + uint256 numberOfNodes = cfg.nodes.length; + if (numberOfNodes > MAX_NUM_ORACLES) revert TooManySigners(); + if (numberOfNodes <= 3 * FRoleDON) revert FTooHigh(); + + uint256 nonZeroTransmitters = 0; + bytes32[] memory p2pIds = new bytes32[](numberOfNodes); + for (uint256 i = 0; i < numberOfNodes; ++i) { + OCR3Node memory node = cfg.nodes[i]; + + // 3 * fChain + 1 <= nonZeroTransmitters <= 3 * FRoleDON + 1 + // Transmitters can be set to 0 since there can be more signers than transmitters, + if (node.transmitterKey.length != 0) { + nonZeroTransmitters++; + } + + // Signer key and p2pIds must always be present + if (node.signerKey.length == 0 || node.p2pId == bytes32(0)) { + revert InvalidNode(node); + } + + p2pIds[i] = node.p2pId; + } + + // We check for chain config presence above, so fChain here must be non-zero. fChain <= FRoleDON due to the checks above. + // There can be less transmitters than signers - so they can be set to zero (which indicates that a node is a signer, but not a transmitter). + uint256 minTransmittersLength = 3 * fChain + 1; + if (nonZeroTransmitters < minTransmittersLength) { + revert NotEnoughTransmitters(nonZeroTransmitters, minTransmittersLength); + } + + // Check that the readers are in the capabilities registry. + _ensureInRegistry(p2pIds); + } + // ================================================================ // │ Chain Configuration │ // ================================================================ From 68b96a7169a51d8e1e21ba6fdcbcdfba51034033 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 25 Sep 2024 14:55:52 +0200 Subject: [PATCH 29/36] rm pluginKey in rmn --- contracts/gas-snapshots/ccip.gas-snapshot | 1973 ++++++++--------- .../src/v0.8/ccip/capability/CCIPHome.sol | 2 - .../src/v0.8/ccip/capability/HomeBase.sol | 104 +- .../src/v0.8/ccip/capability/RMNHome.sol | 19 +- .../ccip/test/capability/HomeBaseTest.t.sol | 362 +-- .../ccip/test/capability/RMNHomeTest.t.sol | 69 +- .../v0.8/ccip/test/helpers/HomeBaseHelper.sol | 23 +- 7 files changed, 1246 insertions(+), 1306 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 559ccae820..7accf1d73e 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -1,992 +1,983 @@ -ARMProxyStandaloneTest:test_ARMCallEmptyContractRevert() (gas: 19675) -ARMProxyStandaloneTest:test_Constructor() (gas: 310043) -ARMProxyStandaloneTest:test_SetARM() (gas: 16587) -ARMProxyStandaloneTest:test_SetARMzero() (gas: 11297) -ARMProxyTest:test_ARMCallRevertReasonForwarded() (gas: 47898) -ARMProxyTest:test_ARMIsBlessed_Success() (gas: 36363) -ARMProxyTest:test_ARMIsCursed_Success() (gas: 49851) -AggregateTokenLimiter_constructor:test_Constructor_Success() (gas: 27118) -AggregateTokenLimiter_getTokenBucket:test_GetTokenBucket_Success() (gas: 19871) -AggregateTokenLimiter_getTokenBucket:test_Refill_Success() (gas: 41586) -AggregateTokenLimiter_getTokenBucket:test_TimeUnderflow_Revert() (gas: 15452) -AggregateTokenLimiter_getTokenLimitAdmin:test_GetTokenLimitAdmin_Success() (gas: 10537) -AggregateTokenLimiter_getTokenValue:test_GetTokenValue_Success() (gas: 17531) -AggregateTokenLimiter_getTokenValue:test_NoTokenPrice_Reverts() (gas: 21414) -AggregateTokenLimiter_rateLimitValue:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 16586) -AggregateTokenLimiter_rateLimitValue:test_RateLimitValueSuccess_gas() (gas: 18357) -AggregateTokenLimiter_setAdmin:test_OnlyOwnerOrAdmin_Revert() (gas: 13078) -AggregateTokenLimiter_setAdmin:test_Owner_Success() (gas: 19016) -AggregateTokenLimiter_setRateLimiterConfig:test_OnlyOnlyCallableByAdminOrOwner_Revert() (gas: 17546) -AggregateTokenLimiter_setRateLimiterConfig:test_Owner_Success() (gas: 30393) -AggregateTokenLimiter_setRateLimiterConfig:test_TokenLimitAdmin_Success() (gas: 32407) -BurnFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842) -BurnFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) -BurnFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244024) -BurnFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 24166) -BurnMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 27609) -BurnMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) -BurnMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 241912) -BurnMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 17851) -BurnMintTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 28805) -BurnMintTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 56253) -BurnMintTokenPool_releaseOrMint:test_PoolMint_Success() (gas: 112391) -BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842) -BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) -BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244050) -BurnWithFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 24170) -CCIPClientExample_sanity:test_ImmutableExamples_Success() (gas: 2052431) -CommitStore_constructor:test_Constructor_Success() (gas: 2855567) -CommitStore_isUnpausedAndRMNHealthy:test_RMN_Success() (gas: 73954) -CommitStore_report:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 28739) -CommitStore_report:test_InvalidInterval_Revert() (gas: 28679) -CommitStore_report:test_InvalidRootRevert() (gas: 27912) -CommitStore_report:test_OnlyGasPriceUpdates_Success() (gas: 53448) -CommitStore_report:test_OnlyPriceUpdateStaleReport_Revert() (gas: 59286) -CommitStore_report:test_OnlyTokenPriceUpdates_Success() (gas: 53446) -CommitStore_report:test_Paused_Revert() (gas: 21319) -CommitStore_report:test_ReportAndPriceUpdate_Success() (gas: 84485) -CommitStore_report:test_ReportOnlyRootSuccess_gas() (gas: 56342) -CommitStore_report:test_RootAlreadyCommitted_Revert() (gas: 64077) -CommitStore_report:test_StaleReportWithRoot_Success() (gas: 117309) -CommitStore_report:test_Unhealthy_Revert() (gas: 44823) -CommitStore_report:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 98929) -CommitStore_report:test_ZeroEpochAndRound_Revert() (gas: 27707) -CommitStore_resetUnblessedRoots:test_OnlyOwner_Revert() (gas: 11376) -CommitStore_resetUnblessedRoots:test_ResetUnblessedRoots_Success() (gas: 144186) -CommitStore_setDynamicConfig:test_InvalidCommitStoreConfig_Revert() (gas: 37314) -CommitStore_setDynamicConfig:test_OnlyOwner_Revert() (gas: 37483) -CommitStore_setDynamicConfig:test_PriceEpochCleared_Success() (gas: 129329) -CommitStore_setLatestPriceEpochAndRound:test_OnlyOwner_Revert() (gas: 11099) -CommitStore_setLatestPriceEpochAndRound:test_SetLatestPriceEpochAndRound_Success() (gas: 20690) -CommitStore_setMinSeqNr:test_OnlyOwner_Revert() (gas: 11098) -CommitStore_verify:test_Blessed_Success() (gas: 96581) -CommitStore_verify:test_NotBlessed_Success() (gas: 61473) -CommitStore_verify:test_Paused_Revert() (gas: 18568) -CommitStore_verify:test_TooManyLeaves_Revert() (gas: 36848) -DefensiveExampleTest:test_HappyPath_Success() (gas: 200200) -DefensiveExampleTest:test_Recovery() (gas: 424479) -E2E:test_E2E_3MessagesSuccess_gas() (gas: 1106985) -EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_NotACompatiblePool_Revert() (gas: 38322) -EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_Success() (gas: 104438) -EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_TokenHandlingError_transfer_Revert() (gas: 86026) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 37365) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 95013) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 40341) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 87189) -EVM2EVMOffRamp__releaseOrMintTokens:test_OverValueWithARLOff_Success() (gas: 381594) -EVM2EVMOffRamp__releaseOrMintTokens:test_PriceNotFoundForToken_Reverts() (gas: 140568) -EVM2EVMOffRamp__releaseOrMintTokens:test_RateLimitErrors_Reverts() (gas: 798833) -EVM2EVMOffRamp__releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 178400) -EVM2EVMOffRamp__releaseOrMintTokens:test__releaseOrMintTokens_NotACompatiblePool_Reverts() (gas: 29681) -EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 67146) -EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_InvalidEVMAddress_Revert() (gas: 43605) -EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 208068) -EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 219365) -EVM2EVMOffRamp__report:test_Report_Success() (gas: 127774) -EVM2EVMOffRamp__trialExecute:test_RateLimitError_Success() (gas: 237406) -EVM2EVMOffRamp__trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 246039) -EVM2EVMOffRamp__trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 329283) -EVM2EVMOffRamp__trialExecute:test_trialExecute_Success() (gas: 310166) -EVM2EVMOffRamp_ccipReceive:test_Reverts() (gas: 17048) -EVM2EVMOffRamp_constructor:test_CommitStoreAlreadyInUse_Revert() (gas: 153120) -EVM2EVMOffRamp_constructor:test_Constructor_Success() (gas: 5212732) -EVM2EVMOffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 143845) -EVM2EVMOffRamp_execute:test_EmptyReport_Revert() (gas: 21507) -EVM2EVMOffRamp_execute:test_InvalidMessageId_Revert() (gas: 36936) -EVM2EVMOffRamp_execute:test_InvalidSourceChain_Revert() (gas: 52324) -EVM2EVMOffRamp_execute:test_InvalidSourcePoolAddress_Success() (gas: 473387) -EVM2EVMOffRamp_execute:test_ManualExecutionNotYetEnabled_Revert() (gas: 48346) -EVM2EVMOffRamp_execute:test_MessageTooLarge_Revert() (gas: 153019) -EVM2EVMOffRamp_execute:test_Paused_Revert() (gas: 103946) -EVM2EVMOffRamp_execute:test_ReceiverError_Success() (gas: 165358) -EVM2EVMOffRamp_execute:test_RetryFailedMessageWithoutManualExecution_Success() (gas: 180107) -EVM2EVMOffRamp_execute:test_RootNotCommitted_Revert() (gas: 43157) -EVM2EVMOffRamp_execute:test_SingleMessageNoTokensUnordered_Success() (gas: 160119) -EVM2EVMOffRamp_execute:test_SingleMessageNoTokens_Success() (gas: 175497) -EVM2EVMOffRamp_execute:test_SingleMessageToNonCCIPReceiver_Success() (gas: 237901) -EVM2EVMOffRamp_execute:test_SingleMessagesNoTokensSuccess_gas() (gas: 115048) -EVM2EVMOffRamp_execute:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 406606) -EVM2EVMOffRamp_execute:test_SkippedIncorrectNonce_Success() (gas: 54774) -EVM2EVMOffRamp_execute:test_StrictUntouchedToSuccess_Success() (gas: 132556) -EVM2EVMOffRamp_execute:test_TokenDataMismatch_Revert() (gas: 52786) -EVM2EVMOffRamp_execute:test_TwoMessagesWithTokensAndGE_Success() (gas: 564471) -EVM2EVMOffRamp_execute:test_TwoMessagesWithTokensSuccess_gas() (gas: 494719) -EVM2EVMOffRamp_execute:test_UnexpectedTokenData_Revert() (gas: 35887) -EVM2EVMOffRamp_execute:test_Unhealthy_Revert() (gas: 546333) -EVM2EVMOffRamp_execute:test_UnsupportedNumberOfTokens_Revert() (gas: 65298) -EVM2EVMOffRamp_execute:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 124107) -EVM2EVMOffRamp_execute:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 144365) -EVM2EVMOffRamp_execute:test_execute_RouterYULCall_Success() (gas: 394187) -EVM2EVMOffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 18685) -EVM2EVMOffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 275257) -EVM2EVMOffRamp_executeSingleMessage:test_NonContract_Success() (gas: 18815) -EVM2EVMOffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 223182) -EVM2EVMOffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 48391) -EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 47823) -EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 311554) -EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_ZeroGasZeroData_Success() (gas: 70839) -EVM2EVMOffRamp_execute_upgrade:test_V2NonceNewSenderStartsAtZero_Success() (gas: 232136) -EVM2EVMOffRamp_execute_upgrade:test_V2NonceStartsAtV1Nonce_Success() (gas: 281170) -EVM2EVMOffRamp_execute_upgrade:test_V2OffRampNonceSkipsIfMsgInFlight_Success() (gas: 262488) -EVM2EVMOffRamp_execute_upgrade:test_V2SenderNoncesReadsPreviousRamp_Success() (gas: 230645) -EVM2EVMOffRamp_execute_upgrade:test_V2_Success() (gas: 132092) -EVM2EVMOffRamp_getAllRateLimitTokens:test_GetAllRateLimitTokens_Success() (gas: 38626) -EVM2EVMOffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3397486) -EVM2EVMOffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 84833) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecFailedTx_Revert() (gas: 188280) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecForkedChain_Revert() (gas: 27574) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecGasLimitMismatch_Revert() (gas: 46457) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecInvalidGasLimit_Revert() (gas: 27948) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecWithMultipleMessagesAndSourceTokens_Success() (gas: 531330) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecWithSourceTokens_Success() (gas: 344463) -EVM2EVMOffRamp_manuallyExecute:test_ManualExec_Success() (gas: 189760) -EVM2EVMOffRamp_manuallyExecute:test_ReentrancyManualExecuteFails_Success() (gas: 2195128) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 362054) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 145457) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 365283) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_LowGasLimitManualExec_Success() (gas: 450711) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 192223) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithInvalidReceiverExecutionGasOverride_Revert() (gas: 155387) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithInvalidSourceTokenDataCount_Revert() (gas: 60494) -EVM2EVMOffRamp_metadataHash:test_MetadataHash_Success() (gas: 8895) -EVM2EVMOffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 40357) -EVM2EVMOffRamp_setDynamicConfig:test_RouterZeroAddress_Revert() (gas: 38419) -EVM2EVMOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 142469) -EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_AddsAndRemoves_Success() (gas: 162818) -EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_NonOwner_Revert() (gas: 16936) -EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_Success() (gas: 197985) -EVM2EVMOnRamp_constructor:test_Constructor_Success() (gas: 5056698) -EVM2EVMOnRamp_forwardFromRouter:test_CannotSendZeroTokens_Revert() (gas: 36063) -EVM2EVMOnRamp_forwardFromRouter:test_EnforceOutOfOrder_Revert() (gas: 99010) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 114925) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 114967) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 130991) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 139431) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 130607) -EVM2EVMOnRamp_forwardFromRouter:test_InvalidAddressEncodePacked_Revert() (gas: 38647) -EVM2EVMOnRamp_forwardFromRouter:test_InvalidAddress_Revert() (gas: 38830) -EVM2EVMOnRamp_forwardFromRouter:test_InvalidChainSelector_Revert() (gas: 25726) -EVM2EVMOnRamp_forwardFromRouter:test_InvalidExtraArgsTag_Revert() (gas: 25545) -EVM2EVMOnRamp_forwardFromRouter:test_MaxCapacityExceeded_Revert() (gas: 84266) -EVM2EVMOnRamp_forwardFromRouter:test_MaxFeeBalanceReached_Revert() (gas: 36847) -EVM2EVMOnRamp_forwardFromRouter:test_MessageGasLimitTooHigh_Revert() (gas: 29327) -EVM2EVMOnRamp_forwardFromRouter:test_MessageTooLarge_Revert() (gas: 107850) -EVM2EVMOnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 22823) -EVM2EVMOnRamp_forwardFromRouter:test_OverValueWithARLOff_Success() (gas: 226568) -EVM2EVMOnRamp_forwardFromRouter:test_Paused_Revert() (gas: 53432) -EVM2EVMOnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 25757) -EVM2EVMOnRamp_forwardFromRouter:test_PriceNotFoundForToken_Revert() (gas: 57722) -EVM2EVMOnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 182247) -EVM2EVMOnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 180718) -EVM2EVMOnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 133236) -EVM2EVMOnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3573653) -EVM2EVMOnRamp_forwardFromRouter:test_TooManyTokens_Revert() (gas: 30472) -EVM2EVMOnRamp_forwardFromRouter:test_Unhealthy_Revert() (gas: 43480) -EVM2EVMOnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 110111) -EVM2EVMOnRamp_forwardFromRouter:test_ZeroAddressReceiver_Revert() (gas: 316020) -EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_ShouldStoreLinkFees_Success() (gas: 113033) -EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 72824) -EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_correctSourceTokenData_Success() (gas: 714726) -EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2NonceNewSenderStartsAtZero_Success() (gas: 148808) -EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2NonceStartsAtV1Nonce_Success() (gas: 192679) -EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2SenderNoncesReadsPreviousRamp_Success() (gas: 123243) -EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2_Success() (gas: 96028) -EVM2EVMOnRamp_getDataAvailabilityCost:test_EmptyMessageCalculatesDataAvailabilityCost_Success() (gas: 20598) -EVM2EVMOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCost_Success() (gas: 20966) -EVM2EVMOnRamp_getFee:test_EmptyMessage_Success() (gas: 74894) -EVM2EVMOnRamp_getFee:test_GetFeeOfZeroForTokenMessage_Success() (gas: 80393) -EVM2EVMOnRamp_getFee:test_HighGasMessage_Success() (gas: 230742) -EVM2EVMOnRamp_getFee:test_MessageGasLimitTooHigh_Revert() (gas: 16943) -EVM2EVMOnRamp_getFee:test_MessageTooLarge_Revert() (gas: 95505) -EVM2EVMOnRamp_getFee:test_MessageWithDataAndTokenTransfer_Success() (gas: 154010) -EVM2EVMOnRamp_getFee:test_NotAFeeToken_Revert() (gas: 24323) -EVM2EVMOnRamp_getFee:test_SingleTokenMessage_Success() (gas: 114740) -EVM2EVMOnRamp_getFee:test_TooManyTokens_Revert() (gas: 20142) -EVM2EVMOnRamp_getFee:test_ZeroDataAvailabilityMultiplier_Success() (gas: 63070) -EVM2EVMOnRamp_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10532) -EVM2EVMOnRamp_getTokenPool:test_GetTokenPool_Success() (gas: 35297) -EVM2EVMOnRamp_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 43218) -EVM2EVMOnRamp_getTokenTransferCost:test_FeeTokenBpsFee_Success() (gas: 33280) -EVM2EVMOnRamp_getTokenTransferCost:test_LargeTokenTransferChargesMaxFeeAndGas_Success() (gas: 28551) -EVM2EVMOnRamp_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 122690) -EVM2EVMOnRamp_getTokenTransferCost:test_NoTokenTransferChargesZeroFee_Success() (gas: 15403) -EVM2EVMOnRamp_getTokenTransferCost:test_SmallTokenTransferChargesMinFeeAndGas_Success() (gas: 28359) -EVM2EVMOnRamp_getTokenTransferCost:test_UnsupportedToken_Revert() (gas: 21353) -EVM2EVMOnRamp_getTokenTransferCost:test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() (gas: 28382) -EVM2EVMOnRamp_getTokenTransferCost:test_ZeroFeeConfigChargesMinFee_Success() (gas: 38899) -EVM2EVMOnRamp_getTokenTransferCost:test__getTokenTransferCost_selfServeUsesDefaults_Success() (gas: 29674) -EVM2EVMOnRamp_linkAvailableForPayment:test_InsufficientLinkBalance_Success() (gas: 32756) -EVM2EVMOnRamp_linkAvailableForPayment:test_LinkAvailableForPayment_Success() (gas: 135247) -EVM2EVMOnRamp_payNops:test_AdminPayNops_Success() (gas: 143660) -EVM2EVMOnRamp_payNops:test_InsufficientBalance_Revert() (gas: 29196) -EVM2EVMOnRamp_payNops:test_NoFeesToPay_Revert() (gas: 127718) -EVM2EVMOnRamp_payNops:test_NoNopsToPay_Revert() (gas: 133580) -EVM2EVMOnRamp_payNops:test_NopPayNops_Success() (gas: 146947) -EVM2EVMOnRamp_payNops:test_OwnerPayNops_Success() (gas: 141522) -EVM2EVMOnRamp_payNops:test_PayNopsSuccessAfterSetNops() (gas: 298719) -EVM2EVMOnRamp_payNops:test_WrongPermissions_Revert() (gas: 15378) -EVM2EVMOnRamp_setDynamicConfig:test_SetConfigInvalidConfig_Revert() (gas: 42524) -EVM2EVMOnRamp_setDynamicConfig:test_SetConfigOnlyOwner_Revert() (gas: 21426) -EVM2EVMOnRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 54301) -EVM2EVMOnRamp_setFeeTokenConfig:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 13530) -EVM2EVMOnRamp_setFeeTokenConfig:test_SetFeeTokenConfigByAdmin_Success() (gas: 16497) -EVM2EVMOnRamp_setFeeTokenConfig:test_SetFeeTokenConfig_Success() (gas: 14036) -EVM2EVMOnRamp_setNops:test_AdminCanSetNops_Success() (gas: 61872) -EVM2EVMOnRamp_setNops:test_IncludesPayment_Success() (gas: 470835) -EVM2EVMOnRamp_setNops:test_LinkTokenCannotBeNop_Revert() (gas: 57370) -EVM2EVMOnRamp_setNops:test_NonOwnerOrAdmin_Revert() (gas: 14779) -EVM2EVMOnRamp_setNops:test_NotEnoughFundsForPayout_Revert() (gas: 85200) -EVM2EVMOnRamp_setNops:test_SetNopsRemovesOldNopsCompletely_Success() (gas: 60868) -EVM2EVMOnRamp_setNops:test_SetNops_Success() (gas: 174097) -EVM2EVMOnRamp_setNops:test_TooManyNops_Revert() (gas: 193503) -EVM2EVMOnRamp_setNops:test_ZeroAddressCannotBeNop_Revert() (gas: 53711) -EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_InvalidDestBytesOverhead_Revert() (gas: 14616) -EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_OnlyCallableByOwnerOrAdmin_Revert() (gas: 14427) -EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_Success() (gas: 85487) -EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_byAdmin_Success() (gas: 17468) -EVM2EVMOnRamp_withdrawNonLinkFees:test_LinkBalanceNotSettled_Revert() (gas: 83617) -EVM2EVMOnRamp_withdrawNonLinkFees:test_NonOwnerOrAdmin_Revert() (gas: 15353) -EVM2EVMOnRamp_withdrawNonLinkFees:test_SettlingBalance_Success() (gas: 272851) -EVM2EVMOnRamp_withdrawNonLinkFees:test_WithdrawNonLinkFees_Success() (gas: 53566) -EVM2EVMOnRamp_withdrawNonLinkFees:test_WithdrawToZeroAddress_Revert() (gas: 12875) -EtherSenderReceiverTest_ccipReceive:test_ccipReceive_fallbackToWethTransfer() (gas: 96907) -EtherSenderReceiverTest_ccipReceive:test_ccipReceive_happyPath() (gas: 49775) -EtherSenderReceiverTest_ccipReceive:test_ccipReceive_wrongToken() (gas: 17435) -EtherSenderReceiverTest_ccipReceive:test_ccipReceive_wrongTokenAmount() (gas: 15728) -EtherSenderReceiverTest_ccipSend:test_ccipSend_reverts_insufficientFee_feeToken() (gas: 99909) -EtherSenderReceiverTest_ccipSend:test_ccipSend_reverts_insufficientFee_native() (gas: 76138) -EtherSenderReceiverTest_ccipSend:test_ccipSend_reverts_insufficientFee_weth() (gas: 99931) -EtherSenderReceiverTest_ccipSend:test_ccipSend_success_feeToken() (gas: 145010) -EtherSenderReceiverTest_ccipSend:test_ccipSend_success_native() (gas: 80373) -EtherSenderReceiverTest_ccipSend:test_ccipSend_success_nativeExcess() (gas: 80560) -EtherSenderReceiverTest_ccipSend:test_ccipSend_success_weth() (gas: 96064) -EtherSenderReceiverTest_constructor:test_constructor() (gas: 17553) -EtherSenderReceiverTest_getFee:test_getFee() (gas: 27346) -EtherSenderReceiverTest_validateFeeToken:test_validateFeeToken_reverts_feeToken_tokenAmountNotEqualToMsgValue() (gas: 20375) -EtherSenderReceiverTest_validateFeeToken:test_validateFeeToken_valid_feeToken() (gas: 16724) -EtherSenderReceiverTest_validateFeeToken:test_validateFeeToken_valid_native() (gas: 16657) -EtherSenderReceiverTest_validatedMessage:test_validatedMessage_dataOverwrittenToMsgSender() (gas: 25457) -EtherSenderReceiverTest_validatedMessage:test_validatedMessage_emptyDataOverwrittenToMsgSender() (gas: 25307) -EtherSenderReceiverTest_validatedMessage:test_validatedMessage_invalidTokenAmounts() (gas: 17925) -EtherSenderReceiverTest_validatedMessage:test_validatedMessage_tokenOverwrittenToWeth() (gas: 25329) -EtherSenderReceiverTest_validatedMessage:test_validatedMessage_validMessage_extraArgs() (gas: 26370) -FeeQuoter_applyDestChainConfigUpdates:test_InvalidChainFamilySelector_Revert() (gas: 16686) -FeeQuoter_applyDestChainConfigUpdates:test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() (gas: 16588) -FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero_Revert() (gas: 16630) -FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesDefaultTxGasLimitGtMaxPerMessageGasLimit_Revert() (gas: 40326) -FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesZeroIntput_Success() (gas: 12483) -FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdates_Success() (gas: 137553) -FeeQuoter_applyFeeTokensUpdates:test_ApplyFeeTokensUpdates_Success() (gas: 80348) -FeeQuoter_applyFeeTokensUpdates:test_OnlyCallableByOwner_Revert() (gas: 12687) -FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 11547) -FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesMultipleTokens_Success() (gas: 54684) -FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesSingleToken_Success() (gas: 45130) -FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesZeroInput() (gas: 12332) -FeeQuoter_applyTokenTransferFeeConfigUpdates:test_ApplyTokenTransferFeeConfig_Success() (gas: 87721) -FeeQuoter_applyTokenTransferFeeConfigUpdates:test_ApplyTokenTransferFeeZeroInput() (gas: 13233) -FeeQuoter_applyTokenTransferFeeConfigUpdates:test_InvalidDestBytesOverhead_Revert() (gas: 17278) -FeeQuoter_applyTokenTransferFeeConfigUpdates:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 12330) -FeeQuoter_constructor:test_InvalidLinkTokenEqZeroAddress_Revert() (gas: 106501) -FeeQuoter_constructor:test_InvalidMaxFeeJuelsPerMsg_Revert() (gas: 110851) -FeeQuoter_constructor:test_InvalidStalenessThreshold_Revert() (gas: 110904) -FeeQuoter_constructor:test_Setup_Success() (gas: 4972944) -FeeQuoter_convertTokenAmount:test_ConvertTokenAmount_Success() (gas: 68383) -FeeQuoter_convertTokenAmount:test_LinkTokenNotSupported_Revert() (gas: 29076) -FeeQuoter_getDataAvailabilityCost:test_EmptyMessageCalculatesDataAvailabilityCost_Success() (gas: 94781) -FeeQuoter_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCostUnsupportedDestChainSelector_Success() (gas: 14670) -FeeQuoter_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCost_Success() (gas: 20550) -FeeQuoter_getTokenAndGasPrices:test_GetFeeTokenAndGasPrices_Success() (gas: 68298) -FeeQuoter_getTokenAndGasPrices:test_StaleGasPrice_Revert() (gas: 16892) -FeeQuoter_getTokenAndGasPrices:test_UnsupportedChain_Revert() (gas: 16188) -FeeQuoter_getTokenAndGasPrices:test_ZeroGasPrice_Success() (gas: 43667) -FeeQuoter_getTokenPrice:test_GetTokenPriceFromFeed_Success() (gas: 66273) -FeeQuoter_getTokenPrices:test_GetTokenPrices_Success() (gas: 78322) -FeeQuoter_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 39244) -FeeQuoter_getTokenTransferCost:test_FeeTokenBpsFee_Success() (gas: 34880) -FeeQuoter_getTokenTransferCost:test_LargeTokenTransferChargesMaxFeeAndGas_Success() (gas: 27954) -FeeQuoter_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 97513) -FeeQuoter_getTokenTransferCost:test_NoTokenTransferChargesZeroFee_Success() (gas: 20468) -FeeQuoter_getTokenTransferCost:test_SmallTokenTransferChargesMinFeeAndGas_Success() (gas: 27829) -FeeQuoter_getTokenTransferCost:test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() (gas: 27785) -FeeQuoter_getTokenTransferCost:test_ZeroFeeConfigChargesMinFee_Success() (gas: 40376) -FeeQuoter_getTokenTransferCost:test_getTokenTransferCost_selfServeUsesDefaults_Success() (gas: 29503) -FeeQuoter_getValidatedFee:test_DestinationChainNotEnabled_Revert() (gas: 18315) -FeeQuoter_getValidatedFee:test_EmptyMessage_Success() (gas: 82344) -FeeQuoter_getValidatedFee:test_EnforceOutOfOrder_Revert() (gas: 52638) -FeeQuoter_getValidatedFee:test_HighGasMessage_Success() (gas: 238762) -FeeQuoter_getValidatedFee:test_InvalidEVMAddress_Revert() (gas: 22555) -FeeQuoter_getValidatedFee:test_MessageGasLimitTooHigh_Revert() (gas: 29847) -FeeQuoter_getValidatedFee:test_MessageTooLarge_Revert() (gas: 100292) -FeeQuoter_getValidatedFee:test_MessageWithDataAndTokenTransfer_Success() (gas: 141892) -FeeQuoter_getValidatedFee:test_NotAFeeToken_Revert() (gas: 21172) -FeeQuoter_getValidatedFee:test_SingleTokenMessage_Success() (gas: 113309) -FeeQuoter_getValidatedFee:test_TooManyTokens_Revert() (gas: 22691) -FeeQuoter_getValidatedFee:test_ZeroDataAvailabilityMultiplier_Success() (gas: 62714) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedErc20Above18Decimals_Success() (gas: 1973907) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedErc20Below18Decimals_Success() (gas: 1973865) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFeedAt0Decimals_Success() (gas: 1953984) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFeedAt18Decimals_Success() (gas: 1973639) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFlippedDecimals_Success() (gas: 1973843) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedMaxInt224Value_Success() (gas: 1973655) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedOverStalenessPeriod_Success() (gas: 64610) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeed_Success() (gas: 64490) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPrice_Success() (gas: 58894) -FeeQuoter_getValidatedTokenPrice:test_OverflowFeedPrice_Revert() (gas: 1973352) -FeeQuoter_getValidatedTokenPrice:test_StaleFeeToken_Success() (gas: 61764) -FeeQuoter_getValidatedTokenPrice:test_TokenNotSupportedFeed_Revert() (gas: 116495) -FeeQuoter_getValidatedTokenPrice:test_TokenNotSupported_Revert() (gas: 14037) -FeeQuoter_getValidatedTokenPrice:test_UnderflowFeedPrice_Revert() (gas: 1972029) -FeeQuoter_onReport:test_OnReport_StaleUpdate_Revert() (gas: 43631) -FeeQuoter_onReport:test_onReport_InvalidForwarder_Reverts() (gas: 23492) -FeeQuoter_onReport:test_onReport_Success() (gas: 80094) -FeeQuoter_onReport:test_onReport_UnsupportedToken_Reverts() (gas: 26860) -FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsDefault_Success() (gas: 17284) -FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsEnforceOutOfOrder_Revert() (gas: 21428) -FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsGasLimitTooHigh_Revert() (gas: 18516) -FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsInvalidExtraArgsTag_Revert() (gas: 18034) -FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsV1_Success() (gas: 18390) -FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsV2_Success() (gas: 18512) -FeeQuoter_processMessageArgs:test_processMessageArgs_InvalidEVMAddressDestToken_Revert() (gas: 44703) -FeeQuoter_processMessageArgs:test_processMessageArgs_InvalidExtraArgs_Revert() (gas: 19914) -FeeQuoter_processMessageArgs:test_processMessageArgs_MalformedEVMExtraArgs_Revert() (gas: 20333) -FeeQuoter_processMessageArgs:test_processMessageArgs_MessageFeeTooHigh_Revert() (gas: 17904) -FeeQuoter_processMessageArgs:test_processMessageArgs_SourceTokenDataTooLarge_Revert() (gas: 122709) -FeeQuoter_processMessageArgs:test_processMessageArgs_TokenAmountArraysMismatching_Revert() (gas: 42032) -FeeQuoter_processMessageArgs:test_processMessageArgs_WitEVMExtraArgsV2_Success() (gas: 28518) -FeeQuoter_processMessageArgs:test_processMessageArgs_WithConvertedTokenAmount_Success() (gas: 29949) -FeeQuoter_processMessageArgs:test_processMessageArgs_WithCorrectPoolReturnData_Success() (gas: 76145) -FeeQuoter_processMessageArgs:test_processMessageArgs_WithEVMExtraArgsV1_Success() (gas: 28116) -FeeQuoter_processMessageArgs:test_processMessageArgs_WithEmptyEVMExtraArgs_Success() (gas: 25987) -FeeQuoter_processMessageArgs:test_processMessageArgs_WithLinkTokenAmount_Success() (gas: 19523) -FeeQuoter_updatePrices:test_OnlyCallableByUpdater_Revert() (gas: 12176) -FeeQuoter_updatePrices:test_OnlyGasPrice_Success() (gas: 23730) -FeeQuoter_updatePrices:test_OnlyTokenPrice_Success() (gas: 28505) -FeeQuoter_updatePrices:test_UpdatableByAuthorizedCaller_Success() (gas: 74598) -FeeQuoter_updatePrices:test_UpdateMultiplePrices_Success() (gas: 145320) -FeeQuoter_updateTokenPriceFeeds:test_FeedNotUpdated() (gas: 50875) -FeeQuoter_updateTokenPriceFeeds:test_FeedUnset_Success() (gas: 63847) -FeeQuoter_updateTokenPriceFeeds:test_FeedUpdatedByNonOwner_Revert() (gas: 20142) -FeeQuoter_updateTokenPriceFeeds:test_MultipleFeedUpdate_Success() (gas: 89470) -FeeQuoter_updateTokenPriceFeeds:test_SingleFeedUpdate_Success() (gas: 51121) -FeeQuoter_updateTokenPriceFeeds:test_ZeroFeeds_Success() (gas: 12437) -FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddressEncodePacked_Revert() (gas: 10655) -FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddressPrecompiles_Revert() (gas: 4001603) -FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddress_Revert() (gas: 10839) -FeeQuoter_validateDestFamilyAddress:test_ValidEVMAddress_Success() (gas: 6731) -FeeQuoter_validateDestFamilyAddress:test_ValidNonEVMAddress_Success() (gas: 6511) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209248) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135879) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 107090) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 144586) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 214817) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 423641) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 268928) -HybridUSDCTokenPoolMigrationTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 111484) -HybridUSDCTokenPoolMigrationTests:test_burnLockedUSDC_invalidPermissions_Revert() (gas: 39362) -HybridUSDCTokenPoolMigrationTests:test_cancelExistingCCTPMigrationProposal() (gas: 33189) -HybridUSDCTokenPoolMigrationTests:test_cannotCancelANonExistentMigrationProposal() (gas: 12669) -HybridUSDCTokenPoolMigrationTests:test_cannotModifyLiquidityWithoutPermissions_Revert() (gas: 13329) -HybridUSDCTokenPoolMigrationTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 160900) -HybridUSDCTokenPoolMigrationTests:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 255982) -HybridUSDCTokenPoolMigrationTests:test_transferLiquidity_Success() (gas: 165921) -HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_destChain_Success() (gas: 154242) -HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_homeChain_Success() (gas: 463740) -HybridUSDCTokenPoolTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209230) -HybridUSDCTokenPoolTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135880) -HybridUSDCTokenPoolTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 107135) -HybridUSDCTokenPoolTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 144607) -HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 214795) -HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 423619) -HybridUSDCTokenPoolTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 268910) -HybridUSDCTokenPoolTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 111528) -HybridUSDCTokenPoolTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 160845) -HybridUSDCTokenPoolTests:test_transferLiquidity_Success() (gas: 165904) -LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Revert() (gas: 10989) -LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Success() (gas: 18028) -LockReleaseTokenPoolPoolAndProxy_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 3051552) -LockReleaseTokenPoolPoolAndProxy_provideLiquidity:test_LiquidityNotAccepted_Revert() (gas: 3047988) -LockReleaseTokenPoolPoolAndProxy_provideLiquidity:test_Unauthorized_Revert() (gas: 11489) -LockReleaseTokenPoolPoolAndProxy_supportsInterface:test_SupportsInterface_Success() (gas: 10196) -LockReleaseTokenPoolPoolAndProxy_withdrawalLiquidity:test_InsufficientLiquidity_Revert() (gas: 60187) -LockReleaseTokenPoolPoolAndProxy_withdrawalLiquidity:test_Unauthorized_Revert() (gas: 11464) -LockReleaseTokenPool_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 2836138) -LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Revert() (gas: 30062) -LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Success() (gas: 79943) -LockReleaseTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 59620) -LockReleaseTokenPool_provideLiquidity:test_LiquidityNotAccepted_Revert() (gas: 2832618) -LockReleaseTokenPool_provideLiquidity:test_Unauthorized_Revert() (gas: 11489) -LockReleaseTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 72743) -LockReleaseTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 56352) -LockReleaseTokenPool_releaseOrMint:test_ReleaseOrMint_Success() (gas: 225548) -LockReleaseTokenPool_setRebalancer:test_SetRebalancer_Revert() (gas: 11011) -LockReleaseTokenPool_setRebalancer:test_SetRebalancer_Success() (gas: 18094) -LockReleaseTokenPool_supportsInterface:test_SupportsInterface_Success() (gas: 10196) -LockReleaseTokenPool_transferLiquidity:test_transferLiquidity_Success() (gas: 83231) -LockReleaseTokenPool_transferLiquidity:test_transferLiquidity_transferTooMuch_Revert() (gas: 55953) -LockReleaseTokenPool_withdrawalLiquidity:test_InsufficientLiquidity_Revert() (gas: 60187) -LockReleaseTokenPool_withdrawalLiquidity:test_Unauthorized_Revert() (gas: 11464) -LockRelease_setRateLimitAdmin:test_SetRateLimitAdmin_Revert() (gas: 11054) -LockRelease_setRateLimitAdmin:test_SetRateLimitAdmin_Success() (gas: 35060) -MerkleMultiProofTest:test_CVE_2023_34459() (gas: 5478) -MerkleMultiProofTest:test_EmptyLeaf_Revert() (gas: 3585) -MerkleMultiProofTest:test_MerkleRoot256() (gas: 394891) -MerkleMultiProofTest:test_MerkleRootSingleLeaf_Success() (gas: 3661) -MerkleMultiProofTest:test_SpecSync_gas() (gas: 34129) -MockRouterTest:test_ccipSendWithInsufficientNativeTokens_Revert() (gas: 34037) -MockRouterTest:test_ccipSendWithInvalidMsgValue_Revert() (gas: 60842) -MockRouterTest:test_ccipSendWithLinkFeeTokenAndValidMsgValue_Success() (gas: 126576) -MockRouterTest:test_ccipSendWithLinkFeeTokenbutInsufficientAllowance_Revert() (gas: 63455) -MockRouterTest:test_ccipSendWithSufficientNativeFeeTokens_Success() (gas: 44012) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_MultipleConfigsBothLanes_Success() (gas: 133528) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_MultipleConfigs_Success() (gas: 315630) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_OnlyCallableByOwner_Revert() (gas: 17864) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_SingleConfigOutbound_Success() (gas: 76453) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_SingleConfig_Success() (gas: 76369) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_UpdateExistingConfigWithNoDifference_Success() (gas: 38736) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_UpdateExistingConfig_Success() (gas: 53869) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_ZeroChainSelector_Revert() (gas: 17109) -MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_ZeroConfigs_Success() (gas: 12436) -MultiAggregateRateLimiter_constructor:test_ConstructorNoAuthorizedCallers_Success() (gas: 1958738) -MultiAggregateRateLimiter_constructor:test_Constructor_Success() (gas: 2075046) -MultiAggregateRateLimiter_getTokenBucket:test_GetTokenBucket_Success() (gas: 30794) -MultiAggregateRateLimiter_getTokenBucket:test_Refill_Success() (gas: 48099) -MultiAggregateRateLimiter_getTokenBucket:test_TimeUnderflow_Revert() (gas: 15929) -MultiAggregateRateLimiter_getTokenValue:test_GetTokenValue_Success() (gas: 17525) -MultiAggregateRateLimiter_getTokenValue:test_NoTokenPrice_Reverts() (gas: 21408) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageFromUnauthorizedCaller_Revert() (gas: 14617) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 210107) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithDisabledRateLimitToken_Success() (gas: 58416) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 17743) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitDisabled_Success() (gas: 45162) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitExceeded_Revert() (gas: 46370) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitReset_Success() (gas: 76561) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 308233) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokens_Success() (gas: 50558) -MultiAggregateRateLimiter_onOutboundMessage:test_RateLimitValueDifferentLanes_Success() (gas: 1073669578) -MultiAggregateRateLimiter_onOutboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 19302) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageFromUnauthorizedCaller_Revert() (gas: 15913) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 209885) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDisabledRateLimitToken_Success() (gas: 60182) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitDisabled_Success() (gas: 46935) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitExceeded_Revert() (gas: 48179) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitReset_Success() (gas: 77728) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 308237) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithTokens_Success() (gas: 52346) -MultiAggregateRateLimiter_setFeeQuoter:test_OnlyOwner_Revert() (gas: 11337) -MultiAggregateRateLimiter_setFeeQuoter:test_Owner_Success() (gas: 19090) -MultiAggregateRateLimiter_setFeeQuoter:test_ZeroAddress_Revert() (gas: 10609) -MultiAggregateRateLimiter_updateRateLimitTokens:test_NonOwner_Revert() (gas: 18878) -MultiAggregateRateLimiter_updateRateLimitTokens:test_UpdateRateLimitTokensMultipleChains_Success() (gas: 280256) -MultiAggregateRateLimiter_updateRateLimitTokens:test_UpdateRateLimitTokensSingleChain_Success() (gas: 254729) -MultiAggregateRateLimiter_updateRateLimitTokens:test_UpdateRateLimitTokens_AddsAndRemoves_Success() (gas: 204595) -MultiAggregateRateLimiter_updateRateLimitTokens:test_UpdateRateLimitTokens_RemoveNonExistentToken_Success() (gas: 28826) -MultiAggregateRateLimiter_updateRateLimitTokens:test_ZeroDestToken_Revert() (gas: 18324) -MultiAggregateRateLimiter_updateRateLimitTokens:test_ZeroSourceToken_Revert() (gas: 18253) -MultiOCR3Base_setOCR3Configs:test_FMustBePositive_Revert() (gas: 59397) -MultiOCR3Base_setOCR3Configs:test_FTooHigh_Revert() (gas: 44190) -MultiOCR3Base_setOCR3Configs:test_MoreTransmittersThanSigners_Revert() (gas: 104822) -MultiOCR3Base_setOCR3Configs:test_NoTransmitters_Revert() (gas: 18886) -MultiOCR3Base_setOCR3Configs:test_RepeatSignerAddress_Revert() (gas: 283736) -MultiOCR3Base_setOCR3Configs:test_RepeatTransmitterAddress_Revert() (gas: 422361) -MultiOCR3Base_setOCR3Configs:test_SetConfigIgnoreSigners_Success() (gas: 511918) -MultiOCR3Base_setOCR3Configs:test_SetConfigWithSignersMismatchingTransmitters_Success() (gas: 680323) -MultiOCR3Base_setOCR3Configs:test_SetConfigWithSigners_Success() (gas: 828900) -MultiOCR3Base_setOCR3Configs:test_SetConfigWithoutSigners_Success() (gas: 457292) -MultiOCR3Base_setOCR3Configs:test_SetConfigsZeroInput_Success() (gas: 12481) -MultiOCR3Base_setOCR3Configs:test_SetMultipleConfigs_Success() (gas: 2141722) -MultiOCR3Base_setOCR3Configs:test_SignerCannotBeZeroAddress_Revert() (gas: 141835) -MultiOCR3Base_setOCR3Configs:test_StaticConfigChange_Revert() (gas: 807623) -MultiOCR3Base_setOCR3Configs:test_TooManySigners_Revert() (gas: 158867) -MultiOCR3Base_setOCR3Configs:test_TooManyTransmitters_Revert() (gas: 112335) -MultiOCR3Base_setOCR3Configs:test_TransmitterCannotBeZeroAddress_Revert() (gas: 254201) -MultiOCR3Base_setOCR3Configs:test_UpdateConfigSigners_Success() (gas: 861206) -MultiOCR3Base_setOCR3Configs:test_UpdateConfigTransmittersWithoutSigners_Success() (gas: 475852) -MultiOCR3Base_transmit:test_ConfigDigestMismatch_Revert() (gas: 42956) -MultiOCR3Base_transmit:test_ForkedChain_Revert() (gas: 48639) -MultiOCR3Base_transmit:test_InsufficientSignatures_Revert() (gas: 77138) -MultiOCR3Base_transmit:test_NonUniqueSignature_Revert() (gas: 65912) -MultiOCR3Base_transmit:test_SignatureOutOfRegistration_Revert() (gas: 33495) -MultiOCR3Base_transmit:test_TooManySignatures_Revert() (gas: 79795) -MultiOCR3Base_transmit:test_TransmitSigners_gas_Success() (gas: 33664) -MultiOCR3Base_transmit:test_TransmitWithExtraCalldataArgs_Revert() (gas: 47200) -MultiOCR3Base_transmit:test_TransmitWithLessCalldataArgs_Revert() (gas: 25768) -MultiOCR3Base_transmit:test_TransmitWithoutSignatureVerification_gas_Success() (gas: 18745) -MultiOCR3Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 24234) -MultiOCR3Base_transmit:test_UnauthorizedSigner_Revert() (gas: 61275) -MultiOCR3Base_transmit:test_UnconfiguredPlugin_Revert() (gas: 39933) -MultiOCR3Base_transmit:test_ZeroSignatures_Revert() (gas: 33049) -MultiOnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 233778) -MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1504663) -NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 37934) -NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 23706) -NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 38778) -NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 71901) -NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 262159) -NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 265836) -NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 329824) -NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 300784) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 249120) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 237027) -NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 153748) -NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 168959) -NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 221148) -NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 126861) -NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 107723) -NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 123207) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 43079) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 64408) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 42943) -NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 66696) -NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12070) -NonceManager_typeAndVersion:test_typeAndVersion() (gas: 9705) -OCR2BaseNoChecks_setOCR2Config:test_FMustBePositive_Revert() (gas: 12210) -OCR2BaseNoChecks_setOCR2Config:test_RepeatAddress_Revert() (gas: 42431) -OCR2BaseNoChecks_setOCR2Config:test_SetConfigSuccess_gas() (gas: 84597) -OCR2BaseNoChecks_setOCR2Config:test_TooManyTransmitter_Revert() (gas: 38177) -OCR2BaseNoChecks_setOCR2Config:test_TransmitterCannotBeZeroAddress_Revert() (gas: 24308) -OCR2BaseNoChecks_transmit:test_ConfigDigestMismatch_Revert() (gas: 17499) -OCR2BaseNoChecks_transmit:test_ForkedChain_Revert() (gas: 26798) -OCR2BaseNoChecks_transmit:test_TransmitSuccess_gas() (gas: 27499) -OCR2BaseNoChecks_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 21335) -OCR2Base_setOCR2Config:test_FMustBePositive_Revert() (gas: 12216) -OCR2Base_setOCR2Config:test_FTooHigh_Revert() (gas: 12372) -OCR2Base_setOCR2Config:test_OracleOutOfRegister_Revert() (gas: 14919) -OCR2Base_setOCR2Config:test_RepeatAddress_Revert() (gas: 45469) -OCR2Base_setOCR2Config:test_SetConfigSuccess_gas() (gas: 155220) -OCR2Base_setOCR2Config:test_SingerCannotBeZeroAddress_Revert() (gas: 24425) -OCR2Base_setOCR2Config:test_TooManySigners_Revert() (gas: 20535) -OCR2Base_setOCR2Config:test_TransmitterCannotBeZeroAddress_Revert() (gas: 47316) -OCR2Base_transmit:test_ConfigDigestMismatch_Revert() (gas: 19668) -OCR2Base_transmit:test_ForkedChain_Revert() (gas: 37749) -OCR2Base_transmit:test_NonUniqueSignature_Revert() (gas: 55360) -OCR2Base_transmit:test_SignatureOutOfRegistration_Revert() (gas: 20989) -OCR2Base_transmit:test_Transmit2SignersSuccess_gas() (gas: 51689) -OCR2Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 23511) -OCR2Base_transmit:test_UnauthorizedSigner_Revert() (gas: 39707) -OCR2Base_transmit:test_WrongNumberOfSignatures_Revert() (gas: 20584) -OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 5656596) -OffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 469391) -OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 99637) -OffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 12591) -OffRamp_applySourceChainConfigUpdates:test_InvalidOnRampUpdate_Revert() (gas: 205322) -OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Success() (gas: 101437) -OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 111307) -OffRamp_applySourceChainConfigUpdates:test_RouterAddress_Revert() (gas: 13441) -OffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 72702) -OffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 15497) -OffRamp_batchExecute:test_MultipleReportsDifferentChainsSkipCursedChain_Success() (gas: 177979) -OffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 335646) -OffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 278912) -OffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 169308) -OffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 189031) -OffRamp_batchExecute:test_SingleReport_Success() (gas: 157132) -OffRamp_batchExecute:test_Unhealthy_Success() (gas: 554208) -OffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10600) -OffRamp_ccipReceive:test_Reverts() (gas: 15385) -OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 92905) -OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 64099) -OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 68124) -OffRamp_commit:test_InvalidInterval_Revert() (gas: 64291) -OffRamp_commit:test_InvalidRootRevert() (gas: 63356) -OffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6260756) -OffRamp_commit:test_NoConfig_Revert() (gas: 5844428) -OffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 113042) -OffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 121403) -OffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 113063) -OffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 355198) -OffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 164351) -OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 139364) -OffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 146555) -OffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 59858) -OffRamp_commit:test_StaleReportWithRoot_Success() (gas: 232074) -OffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 125409) -OffRamp_commit:test_Unhealthy_Revert() (gas: 58633) -OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 206729) -OffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 51722) -OffRamp_constructor:test_Constructor_Success() (gas: 5845416) -OffRamp_constructor:test_SourceChainSelector_Revert() (gas: 135514) -OffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 102949) -OffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 100843) -OffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 161040) -OffRamp_constructor:test_ZeroRMNRemote_Revert() (gas: 100763) -OffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 100801) -OffRamp_execute:test_IncorrectArrayType_Revert() (gas: 17639) -OffRamp_execute:test_LargeBatch_Success() (gas: 3426635) -OffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 373095) -OffRamp_execute:test_MultipleReports_Success() (gas: 301009) -OffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6669661) -OffRamp_execute:test_NoConfig_Revert() (gas: 5894136) -OffRamp_execute:test_NonArray_Revert() (gas: 27562) -OffRamp_execute:test_SingleReport_Success() (gas: 176364) -OffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 148382) -OffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6672410) -OffRamp_execute:test_ZeroReports_Revert() (gas: 17361) -OffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 18511) -OffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 244057) -OffRamp_executeSingleMessage:test_NonContract_Success() (gas: 20759) -OffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 205094) -OffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 49316) -OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 48760) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 218081) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 85349) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 274194) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithVInterception_Success() (gas: 91809) -OffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 28260) -OffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 22062) -OffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 481748) -OffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 48372) -OffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 33959) -OffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 28436) -OffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 188084) -OffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 198518) -OffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 40741) -OffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 413233) -OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 249776) -OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 193590) -OffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 213624) -OffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 249506) -OffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 142151) -OffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 409289) -OffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 58293) -OffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 73868) -OffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 583401) -OffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 532115) -OffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 33717) -OffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 549738) -OffRamp_executeSingleReport:test_Unhealthy_Success() (gas: 549752) -OffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 460495) -OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 135910) -OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 165615) -OffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3868658) -OffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 120474) -OffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 89178) -OffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 81284) -OffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 74161) -OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 173244) -OffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 214189) -OffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 27152) -OffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 165098) -OffRamp_manuallyExecute:test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() (gas: 27689) -OffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 55260) -OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 498614) -OffRamp_manuallyExecute:test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() (gas: 316158) -OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails_Success() (gas: 2245360) -OffRamp_manuallyExecute:test_manuallyExecute_SourceChainSelectorMismatch_Revert() (gas: 165602) -OffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 227234) -OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 227774) -OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 781510) -OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 347431) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 37634) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 104404) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 85342) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 36752) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 94382) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 39741) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 86516) -OffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 162381) -OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 23903) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 62751) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 79790) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 174512) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_WithGasOverride_Success() (gas: 176424) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 187723) -OffRamp_setDynamicConfig:test_FeeQuoterZeroAddress_Revert() (gas: 11269) -OffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 13884) -OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor_Success() (gas: 46421) -OffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 24463) -OffRamp_trialExecute:test_RateLimitError_Success() (gas: 219355) -OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 227977) -OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 295350) -OffRamp_trialExecute:test_trialExecute_Success() (gas: 277894) -OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 390842) -OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() (gas: 18030) -OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_Revert() (gas: 67426) -OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_Success() (gas: 325124) -OnRamp_applyDestChainConfigUpdates:test_ApplyDestChainConfigUpdates_Success() (gas: 65227) -OnRamp_applyDestChainConfigUpdates:test_ApplyDestChainConfigUpdates_WithInvalidChainSelector_Revert() (gas: 13446) -OnRamp_constructor:test_Constructor_InvalidConfigChainSelectorEqZero_Revert() (gas: 95048) -OnRamp_constructor:test_Constructor_InvalidConfigNonceManagerEqAddressZero_Revert() (gas: 92990) -OnRamp_constructor:test_Constructor_InvalidConfigRMNProxyEqAddressZero_Revert() (gas: 98023) -OnRamp_constructor:test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() (gas: 93046) -OnRamp_constructor:test_Constructor_Success() (gas: 2750875) -OnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 115146) -OnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 145969) -OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 145545) -OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessEmptyExtraArgs() (gas: 143772) -OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 145763) -OnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 145139) -OnRamp_forwardFromRouter:test_ForwardFromRouter_Success_ConfigurableSourceRouter() (gas: 145974) -OnRamp_forwardFromRouter:test_InvalidExtraArgsTag_Revert() (gas: 38593) -OnRamp_forwardFromRouter:test_MessageInterceptionError_Revert() (gas: 143056) -OnRamp_forwardFromRouter:test_MesssageFeeTooHigh_Revert() (gas: 36706) -OnRamp_forwardFromRouter:test_MultiCannotSendZeroTokens_Revert() (gas: 36509) -OnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 18291) -OnRamp_forwardFromRouter:test_Paused_Revert() (gas: 38454) -OnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 23663) -OnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 186202) -OnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 212587) -OnRamp_forwardFromRouter:test_ShouldStoreLinkFees() (gas: 125127) -OnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 141767) -OnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3618195) -OnRamp_forwardFromRouter:test_UnAllowedOriginalSender_Revert() (gas: 24010) -OnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 75826) -OnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 38559) -OnRamp_forwardFromRouter:test_forwardFromRouter_WithInterception_Success() (gas: 280050) -OnRamp_getFee:test_EmptyMessage_Success() (gas: 98757) -OnRamp_getFee:test_EnforceOutOfOrder_Revert() (gas: 64779) -OnRamp_getFee:test_GetFeeOfZeroForTokenMessage_Success() (gas: 86359) -OnRamp_getFee:test_NotAFeeTokenButPricedToken_Revert() (gas: 35185) -OnRamp_getFee:test_SingleTokenMessage_Success() (gas: 113916) -OnRamp_getFee:test_Unhealthy_Revert() (gas: 17171) -OnRamp_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10510) -OnRamp_getTokenPool:test_GetTokenPool_Success() (gas: 35372) -OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigFeeAggregatorEqAddressZero_Revert() (gas: 11581) -OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigFeeQuoterEqAddressZero_Revert() (gas: 13195) -OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigInvalidConfig_Revert() (gas: 11522) -OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigOnlyOwner_Revert() (gas: 16850) -OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigReentrancyGuardEnteredEqTrue_Revert() (gas: 13265) -OnRamp_setDynamicConfig:test_setDynamicConfig_Success() (gas: 56460) -OnRamp_withdrawFeeTokens:test_WithdrawFeeTokens_Success() (gas: 97302) -PingPong_ccipReceive:test_CcipReceive_Success() (gas: 151349) -PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) -PingPong_plumbing:test_Pausing_Success() (gas: 17810) -PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) -PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_InvalidCaller_reverts() (gas: 10972) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10967) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 187) +ARMProxyStandaloneTest:test_ARMCallEmptyContractRevert() (gas: 20673) +ARMProxyStandaloneTest:test_Constructor() (gas: 543485) +ARMProxyStandaloneTest:test_SetARM() (gas: 18216) +ARMProxyStandaloneTest:test_SetARMzero() (gas: 12144) +ARMProxyTest:test_ARMCallRevertReasonForwarded() (gas: 49764) +ARMProxyTest:test_ARMIsBlessed_Success() (gas: 39781) +ARMProxyTest:test_ARMIsCursed_Success() (gas: 51846) +AggregateTokenLimiter_constructor:test_Constructor_Success() (gas: 31944) +AggregateTokenLimiter_getTokenBucket:test_GetTokenBucket_Success() (gas: 23227) +AggregateTokenLimiter_getTokenBucket:test_Refill_Success() (gas: 55032) +AggregateTokenLimiter_getTokenBucket:test_TimeUnderflow_Revert() (gas: 17640) +AggregateTokenLimiter_getTokenLimitAdmin:test_GetTokenLimitAdmin_Success() (gas: 11188) +AggregateTokenLimiter_getTokenValue:test_GetTokenValue_Success() (gas: 21368) +AggregateTokenLimiter_getTokenValue:test_NoTokenPrice_Reverts() (gas: 25888) +AggregateTokenLimiter_rateLimitValue:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 20854) +AggregateTokenLimiter_rateLimitValue:test_RateLimitValueSuccess_gas() (gas: 19835) +AggregateTokenLimiter_setAdmin:test_OnlyOwnerOrAdmin_Revert() (gas: 13880) +AggregateTokenLimiter_setAdmin:test_Owner_Success() (gas: 20448) +AggregateTokenLimiter_setRateLimiterConfig:test_OnlyOnlyCallableByAdminOrOwner_Revert() (gas: 19396) +AggregateTokenLimiter_setRateLimiterConfig:test_Owner_Success() (gas: 38776) +AggregateTokenLimiter_setRateLimiterConfig:test_TokenLimitAdmin_Success() (gas: 41521) +BurnFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 34256) +BurnFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 60604) +BurnFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 293700) +BurnFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 28203) +BurnMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 31296) +BurnMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 60604) +BurnMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 291204) +BurnMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 20610) +BurnMintTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 34256) +BurnMintTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 63377) +BurnMintTokenPool_releaseOrMint:test_PoolMint_Success() (gas: 122166) +BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 34256) +BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 60604) +BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 293726) +BurnWithFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 28207) +CCIPClientExample_sanity:test_ImmutableExamples_Success() (gas: 3189509) +CommitStore_constructor:test_Constructor_Success() (gas: 4361942) +CommitStore_isUnpausedAndRMNHealthy:test_RMN_Success() (gas: 83130) +CommitStore_report:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 32885) +CommitStore_report:test_InvalidInterval_Revert() (gas: 32781) +CommitStore_report:test_InvalidRootRevert() (gas: 31537) +CommitStore_report:test_OnlyGasPriceUpdates_Success() (gas: 61004) +CommitStore_report:test_OnlyPriceUpdateStaleReport_Revert() (gas: 70437) +CommitStore_report:test_OnlyTokenPriceUpdates_Success() (gas: 61025) +CommitStore_report:test_Paused_Revert() (gas: 22402) +CommitStore_report:test_ReportAndPriceUpdate_Success() (gas: 94701) +CommitStore_report:test_ReportOnlyRootSuccess_gas() (gas: 60020) +CommitStore_report:test_RootAlreadyCommitted_Revert() (gas: 72243) +CommitStore_report:test_StaleReportWithRoot_Success() (gas: 136763) +CommitStore_report:test_Unhealthy_Revert() (gas: 46665) +CommitStore_report:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 116937) +CommitStore_report:test_ZeroEpochAndRound_Revert() (gas: 32226) +CommitStore_resetUnblessedRoots:test_OnlyOwner_Revert() (gas: 12082) +CommitStore_resetUnblessedRoots:test_ResetUnblessedRoots_Success() (gas: 172098) +CommitStore_setDynamicConfig:test_InvalidCommitStoreConfig_Revert() (gas: 44708) +CommitStore_setDynamicConfig:test_OnlyOwner_Revert() (gas: 44671) +CommitStore_setDynamicConfig:test_PriceEpochCleared_Success() (gas: 150735) +CommitStore_setLatestPriceEpochAndRound:test_OnlyOwner_Revert() (gas: 11970) +CommitStore_setLatestPriceEpochAndRound:test_SetLatestPriceEpochAndRound_Success() (gas: 22566) +CommitStore_setMinSeqNr:test_OnlyOwner_Revert() (gas: 11969) +CommitStore_verify:test_Blessed_Success() (gas: 108814) +CommitStore_verify:test_NotBlessed_Success() (gas: 69299) +CommitStore_verify:test_Paused_Revert() (gas: 19843) +CommitStore_verify:test_TooManyLeaves_Revert() (gas: 85894) +DefensiveExampleTest:test_HappyPath_Success() (gas: 247055) +DefensiveExampleTest:test_Recovery() (gas: 483147) +E2E:test_E2E_3MessagesSuccess_gas() (gas: 1459037) +EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_NotACompatiblePool_Revert() (gas: 48827) +EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_Success() (gas: 122487) +EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_TokenHandlingError_transfer_Revert() (gas: 100439) +EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 45166) +EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 110906) +EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 47409) +EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 102213) +EVM2EVMOffRamp__releaseOrMintTokens:test_OverValueWithARLOff_Success() (gas: 452793) +EVM2EVMOffRamp__releaseOrMintTokens:test_PriceNotFoundForToken_Reverts() (gas: 163533) +EVM2EVMOffRamp__releaseOrMintTokens:test_RateLimitErrors_Reverts() (gas: 947445) +EVM2EVMOffRamp__releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 207677) +EVM2EVMOffRamp__releaseOrMintTokens:test__releaseOrMintTokens_NotACompatiblePool_Reverts() (gas: 37420) +EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 81689) +EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_InvalidEVMAddress_Revert() (gas: 52840) +EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 248387) +EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 256427) +EVM2EVMOffRamp__report:test_Report_Success() (gas: 150186) +EVM2EVMOffRamp__trialExecute:test_RateLimitError_Success() (gas: 280928) +EVM2EVMOffRamp__trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 291045) +EVM2EVMOffRamp__trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 425921) +EVM2EVMOffRamp__trialExecute:test_trialExecute_Success() (gas: 368465) +EVM2EVMOffRamp_ccipReceive:test_Reverts() (gas: 20655) +EVM2EVMOffRamp_constructor:test_CommitStoreAlreadyInUse_Revert() (gas: 162623) +EVM2EVMOffRamp_constructor:test_Constructor_Success() (gas: 7939916) +EVM2EVMOffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 152500) +EVM2EVMOffRamp_execute:test_EmptyReport_Revert() (gas: 24199) +EVM2EVMOffRamp_execute:test_InvalidMessageId_Revert() (gas: 47203) +EVM2EVMOffRamp_execute:test_InvalidSourceChain_Revert() (gas: 67004) +EVM2EVMOffRamp_execute:test_InvalidSourcePoolAddress_Success() (gas: 578501) +EVM2EVMOffRamp_execute:test_ManualExecutionNotYetEnabled_Revert() (gas: 61423) +EVM2EVMOffRamp_execute:test_MessageTooLarge_Revert() (gas: 175550) +EVM2EVMOffRamp_execute:test_Paused_Revert() (gas: 139288) +EVM2EVMOffRamp_execute:test_ReceiverError_Success() (gas: 192927) +EVM2EVMOffRamp_execute:test_RetryFailedMessageWithoutManualExecution_Success() (gas: 215419) +EVM2EVMOffRamp_execute:test_RootNotCommitted_Revert() (gas: 55720) +EVM2EVMOffRamp_execute:test_SingleMessageNoTokensUnordered_Success() (gas: 208494) +EVM2EVMOffRamp_execute:test_SingleMessageNoTokens_Success() (gas: 220462) +EVM2EVMOffRamp_execute:test_SingleMessageToNonCCIPReceiver_Success() (gas: 290662) +EVM2EVMOffRamp_execute:test_SingleMessagesNoTokensSuccess_gas() (gas: 134108) +EVM2EVMOffRamp_execute:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 490970) +EVM2EVMOffRamp_execute:test_SkippedIncorrectNonce_Success() (gas: 68824) +EVM2EVMOffRamp_execute:test_StrictUntouchedToSuccess_Success() (gas: 158417) +EVM2EVMOffRamp_execute:test_TokenDataMismatch_Revert() (gas: 65640) +EVM2EVMOffRamp_execute:test_TwoMessagesWithTokensAndGE_Success() (gas: 699587) +EVM2EVMOffRamp_execute:test_TwoMessagesWithTokensSuccess_gas() (gas: 607548) +EVM2EVMOffRamp_execute:test_UnexpectedTokenData_Revert() (gas: 45035) +EVM2EVMOffRamp_execute:test_Unhealthy_Revert() (gas: 699898) +EVM2EVMOffRamp_execute:test_UnsupportedNumberOfTokens_Revert() (gas: 91821) +EVM2EVMOffRamp_execute:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 157284) +EVM2EVMOffRamp_execute:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 175420) +EVM2EVMOffRamp_execute:test_execute_RouterYULCall_Success() (gas: 601571) +EVM2EVMOffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 23052) +EVM2EVMOffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 315290) +EVM2EVMOffRamp_executeSingleMessage:test_NonContract_Success() (gas: 23338) +EVM2EVMOffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 258353) +EVM2EVMOffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 59576) +EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 58690) +EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 368147) +EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_ZeroGasZeroData_Success() (gas: 93587) +EVM2EVMOffRamp_execute_upgrade:test_V2NonceNewSenderStartsAtZero_Success() (gas: 279051) +EVM2EVMOffRamp_execute_upgrade:test_V2NonceStartsAtV1Nonce_Success() (gas: 351951) +EVM2EVMOffRamp_execute_upgrade:test_V2OffRampNonceSkipsIfMsgInFlight_Success() (gas: 326170) +EVM2EVMOffRamp_execute_upgrade:test_V2SenderNoncesReadsPreviousRamp_Success() (gas: 303514) +EVM2EVMOffRamp_execute_upgrade:test_V2_Success() (gas: 154916) +EVM2EVMOffRamp_getAllRateLimitTokens:test_GetAllRateLimitTokens_Success() (gas: 42076) +EVM2EVMOffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 5016070) +EVM2EVMOffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 104111) +EVM2EVMOffRamp_manuallyExecute:test_ManualExecFailedTx_Revert() (gas: 234610) +EVM2EVMOffRamp_manuallyExecute:test_ManualExecForkedChain_Revert() (gas: 37344) +EVM2EVMOffRamp_manuallyExecute:test_ManualExecGasLimitMismatch_Revert() (gas: 67784) +EVM2EVMOffRamp_manuallyExecute:test_ManualExecInvalidGasLimit_Revert() (gas: 37732) +EVM2EVMOffRamp_manuallyExecute:test_ManualExecWithMultipleMessagesAndSourceTokens_Success() (gas: 673237) +EVM2EVMOffRamp_manuallyExecute:test_ManualExecWithSourceTokens_Success() (gas: 416742) +EVM2EVMOffRamp_manuallyExecute:test_ManualExec_Success() (gas: 235356) +EVM2EVMOffRamp_manuallyExecute:test_ReentrancyManualExecuteFails_Success() (gas: 3548576) +EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 442167) +EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 173700) +EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 447336) +EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_LowGasLimitManualExec_Success() (gas: 681724) +EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 239038) +EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithInvalidReceiverExecutionGasOverride_Revert() (gas: 188679) +EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithInvalidSourceTokenDataCount_Revert() (gas: 80150) +EVM2EVMOffRamp_metadataHash:test_MetadataHash_Success() (gas: 10174) +EVM2EVMOffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 47737) +EVM2EVMOffRamp_setDynamicConfig:test_RouterZeroAddress_Revert() (gas: 46206) +EVM2EVMOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 182870) +EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_AddsAndRemoves_Success() (gas: 171983) +EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_NonOwner_Revert() (gas: 25219) +EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_Success() (gas: 205572) +EVM2EVMOnRamp_constructor:test_Constructor_Success() (gas: 7995985) +EVM2EVMOnRamp_forwardFromRouter:test_CannotSendZeroTokens_Revert() (gas: 40118) +EVM2EVMOnRamp_forwardFromRouter:test_EnforceOutOfOrder_Revert() (gas: 112063) +EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 136127) +EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 136125) +EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 147732) +EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 160359) +EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 146970) +EVM2EVMOnRamp_forwardFromRouter:test_InvalidAddressEncodePacked_Revert() (gas: 43162) +EVM2EVMOnRamp_forwardFromRouter:test_InvalidAddress_Revert() (gas: 43265) +EVM2EVMOnRamp_forwardFromRouter:test_InvalidChainSelector_Revert() (gas: 28899) +EVM2EVMOnRamp_forwardFromRouter:test_InvalidExtraArgsTag_Revert() (gas: 28541) +EVM2EVMOnRamp_forwardFromRouter:test_MaxCapacityExceeded_Revert() (gas: 93166) +EVM2EVMOnRamp_forwardFromRouter:test_MaxFeeBalanceReached_Revert() (gas: 40651) +EVM2EVMOnRamp_forwardFromRouter:test_MessageGasLimitTooHigh_Revert() (gas: 33116) +EVM2EVMOnRamp_forwardFromRouter:test_MessageTooLarge_Revert() (gas: 119271) +EVM2EVMOnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 25711) +EVM2EVMOnRamp_forwardFromRouter:test_OverValueWithARLOff_Success() (gas: 263611) +EVM2EVMOnRamp_forwardFromRouter:test_Paused_Revert() (gas: 60811) +EVM2EVMOnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 28656) +EVM2EVMOnRamp_forwardFromRouter:test_PriceNotFoundForToken_Revert() (gas: 66594) +EVM2EVMOnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 246017) +EVM2EVMOnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 231059) +EVM2EVMOnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 148693) +EVM2EVMOnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 5451750) +EVM2EVMOnRamp_forwardFromRouter:test_TooManyTokens_Revert() (gas: 36372) +EVM2EVMOnRamp_forwardFromRouter:test_Unhealthy_Revert() (gas: 46651) +EVM2EVMOnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 119388) +EVM2EVMOnRamp_forwardFromRouter:test_ZeroAddressReceiver_Revert() (gas: 357179) +EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_ShouldStoreLinkFees_Success() (gas: 122802) +EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 79581) +EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_correctSourceTokenData_Success() (gas: 931861) +EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2NonceNewSenderStartsAtZero_Success() (gas: 170805) +EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2NonceStartsAtV1Nonce_Success() (gas: 233734) +EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2SenderNoncesReadsPreviousRamp_Success() (gas: 149106) +EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2_Success() (gas: 111551) +EVM2EVMOnRamp_getDataAvailabilityCost:test_EmptyMessageCalculatesDataAvailabilityCost_Success() (gas: 28790) +EVM2EVMOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCost_Success() (gas: 29664) +EVM2EVMOnRamp_getFee:test_EmptyMessage_Success() (gas: 103386) +EVM2EVMOnRamp_getFee:test_GetFeeOfZeroForTokenMessage_Success() (gas: 102775) +EVM2EVMOnRamp_getFee:test_HighGasMessage_Success() (gas: 274795) +EVM2EVMOnRamp_getFee:test_MessageGasLimitTooHigh_Revert() (gas: 19531) +EVM2EVMOnRamp_getFee:test_MessageTooLarge_Revert() (gas: 105736) +EVM2EVMOnRamp_getFee:test_MessageWithDataAndTokenTransfer_Success() (gas: 246841) +EVM2EVMOnRamp_getFee:test_NotAFeeToken_Revert() (gas: 28335) +EVM2EVMOnRamp_getFee:test_SingleTokenMessage_Success() (gas: 174801) +EVM2EVMOnRamp_getFee:test_TooManyTokens_Revert() (gas: 24852) +EVM2EVMOnRamp_getFee:test_ZeroDataAvailabilityMultiplier_Success() (gas: 81035) +EVM2EVMOnRamp_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 11166) +EVM2EVMOnRamp_getTokenPool:test_GetTokenPool_Success() (gas: 41957) +EVM2EVMOnRamp_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 54001) +EVM2EVMOnRamp_getTokenTransferCost:test_FeeTokenBpsFee_Success() (gas: 43066) +EVM2EVMOnRamp_getTokenTransferCost:test_LargeTokenTransferChargesMaxFeeAndGas_Success() (gas: 37875) +EVM2EVMOnRamp_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 190349) +EVM2EVMOnRamp_getTokenTransferCost:test_NoTokenTransferChargesZeroFee_Success() (gas: 17641) +EVM2EVMOnRamp_getTokenTransferCost:test_SmallTokenTransferChargesMinFeeAndGas_Success() (gas: 37525) +EVM2EVMOnRamp_getTokenTransferCost:test_UnsupportedToken_Revert() (gas: 24786) +EVM2EVMOnRamp_getTokenTransferCost:test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() (gas: 37548) +EVM2EVMOnRamp_getTokenTransferCost:test_ZeroFeeConfigChargesMinFee_Success() (gas: 49698) +EVM2EVMOnRamp_getTokenTransferCost:test__getTokenTransferCost_selfServeUsesDefaults_Success() (gas: 38218) +EVM2EVMOnRamp_linkAvailableForPayment:test_InsufficientLinkBalance_Success() (gas: 36639) +EVM2EVMOnRamp_linkAvailableForPayment:test_LinkAvailableForPayment_Success() (gas: 145939) +EVM2EVMOnRamp_payNops:test_AdminPayNops_Success() (gas: 157645) +EVM2EVMOnRamp_payNops:test_InsufficientBalance_Revert() (gas: 32428) +EVM2EVMOnRamp_payNops:test_NoFeesToPay_Revert() (gas: 135001) +EVM2EVMOnRamp_payNops:test_NoNopsToPay_Revert() (gas: 141695) +EVM2EVMOnRamp_payNops:test_NopPayNops_Success() (gas: 161181) +EVM2EVMOnRamp_payNops:test_OwnerPayNops_Success() (gas: 155453) +EVM2EVMOnRamp_payNops:test_PayNopsSuccessAfterSetNops() (gas: 328800) +EVM2EVMOnRamp_payNops:test_WrongPermissions_Revert() (gas: 15878) +EVM2EVMOnRamp_setDynamicConfig:test_SetConfigInvalidConfig_Revert() (gas: 49643) +EVM2EVMOnRamp_setDynamicConfig:test_SetConfigOnlyOwner_Revert() (gas: 28163) +EVM2EVMOnRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 68220) +EVM2EVMOnRamp_setFeeTokenConfig:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 14432) +EVM2EVMOnRamp_setFeeTokenConfig:test_SetFeeTokenConfigByAdmin_Success() (gas: 17635) +EVM2EVMOnRamp_setFeeTokenConfig:test_SetFeeTokenConfig_Success() (gas: 14948) +EVM2EVMOnRamp_setNops:test_AdminCanSetNops_Success() (gas: 67974) +EVM2EVMOnRamp_setNops:test_IncludesPayment_Success() (gas: 551687) +EVM2EVMOnRamp_setNops:test_LinkTokenCannotBeNop_Revert() (gas: 61776) +EVM2EVMOnRamp_setNops:test_NonOwnerOrAdmin_Revert() (gas: 16548) +EVM2EVMOnRamp_setNops:test_NotEnoughFundsForPayout_Revert() (gas: 96806) +EVM2EVMOnRamp_setNops:test_SetNopsRemovesOldNopsCompletely_Success() (gas: 64532) +EVM2EVMOnRamp_setNops:test_SetNops_Success() (gas: 185469) +EVM2EVMOnRamp_setNops:test_TooManyNops_Revert() (gas: 415502) +EVM2EVMOnRamp_setNops:test_ZeroAddressCannotBeNop_Revert() (gas: 58089) +EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_InvalidDestBytesOverhead_Revert() (gas: 17784) +EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_OnlyCallableByOwnerOrAdmin_Revert() (gas: 15545) +EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_Success() (gas: 117548) +EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_byAdmin_Success() (gas: 18795) +EVM2EVMOnRamp_withdrawNonLinkFees:test_LinkBalanceNotSettled_Revert() (gas: 92590) +EVM2EVMOnRamp_withdrawNonLinkFees:test_NonOwnerOrAdmin_Revert() (gas: 16405) +EVM2EVMOnRamp_withdrawNonLinkFees:test_SettlingBalance_Success() (gas: 326944) +EVM2EVMOnRamp_withdrawNonLinkFees:test_WithdrawNonLinkFees_Success() (gas: 58679) +EVM2EVMOnRamp_withdrawNonLinkFees:test_WithdrawToZeroAddress_Revert() (gas: 13676) +EtherSenderReceiverTest_ccipReceive:test_ccipReceive_fallbackToWethTransfer() (gas: 103814) +EtherSenderReceiverTest_ccipReceive:test_ccipReceive_happyPath() (gas: 54732) +EtherSenderReceiverTest_ccipReceive:test_ccipReceive_wrongToken() (gas: 21881) +EtherSenderReceiverTest_ccipReceive:test_ccipReceive_wrongTokenAmount() (gas: 20674) +EtherSenderReceiverTest_ccipSend:test_ccipSend_reverts_insufficientFee_feeToken() (gas: 116467) +EtherSenderReceiverTest_ccipSend:test_ccipSend_reverts_insufficientFee_native() (gas: 91360) +EtherSenderReceiverTest_ccipSend:test_ccipSend_reverts_insufficientFee_weth() (gas: 116371) +EtherSenderReceiverTest_ccipSend:test_ccipSend_success_feeToken() (gas: 167929) +EtherSenderReceiverTest_ccipSend:test_ccipSend_success_native() (gas: 98200) +EtherSenderReceiverTest_ccipSend:test_ccipSend_success_nativeExcess() (gas: 98574) +EtherSenderReceiverTest_ccipSend:test_ccipSend_success_weth() (gas: 117880) +EtherSenderReceiverTest_constructor:test_constructor() (gas: 19659) +EtherSenderReceiverTest_getFee:test_getFee() (gas: 41207) +EtherSenderReceiverTest_validateFeeToken:test_validateFeeToken_reverts_feeToken_tokenAmountNotEqualToMsgValue() (gas: 22872) +EtherSenderReceiverTest_validateFeeToken:test_validateFeeToken_valid_feeToken() (gas: 18390) +EtherSenderReceiverTest_validateFeeToken:test_validateFeeToken_valid_native() (gas: 18420) +EtherSenderReceiverTest_validatedMessage:test_validatedMessage_dataOverwrittenToMsgSender() (gas: 34950) +EtherSenderReceiverTest_validatedMessage:test_validatedMessage_emptyDataOverwrittenToMsgSender() (gas: 34697) +EtherSenderReceiverTest_validatedMessage:test_validatedMessage_invalidTokenAmounts() (gas: 23796) +EtherSenderReceiverTest_validatedMessage:test_validatedMessage_tokenOverwrittenToWeth() (gas: 34674) +EtherSenderReceiverTest_validatedMessage:test_validatedMessage_validMessage_extraArgs() (gas: 36305) +FeeQuoter_applyDestChainConfigUpdates:test_InvalidChainFamilySelector_Revert() (gas: 21287) +FeeQuoter_applyDestChainConfigUpdates:test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() (gas: 21260) +FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero_Revert() (gas: 21273) +FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesDefaultTxGasLimitGtMaxPerMessageGasLimit_Revert() (gas: 49746) +FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesZeroIntput_Success() (gas: 13305) +FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdates_Success() (gas: 168513) +FeeQuoter_applyFeeTokensUpdates:test_ApplyFeeTokensUpdates_Success() (gas: 92061) +FeeQuoter_applyFeeTokensUpdates:test_OnlyCallableByOwner_Revert() (gas: 14447) +FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 12564) +FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesMultipleTokens_Success() (gas: 61121) +FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesSingleToken_Success() (gas: 48797) +FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates:test_applyPremiumMultiplierWeiPerEthUpdatesZeroInput() (gas: 13248) +FeeQuoter_applyTokenTransferFeeConfigUpdates:test_ApplyTokenTransferFeeConfig_Success() (gas: 118031) +FeeQuoter_applyTokenTransferFeeConfigUpdates:test_ApplyTokenTransferFeeZeroInput() (gas: 14427) +FeeQuoter_applyTokenTransferFeeConfigUpdates:test_InvalidDestBytesOverhead_Revert() (gas: 21388) +FeeQuoter_applyTokenTransferFeeConfigUpdates:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 13659) +FeeQuoter_constructor:test_InvalidLinkTokenEqZeroAddress_Revert() (gas: 127807) +FeeQuoter_constructor:test_InvalidMaxFeeJuelsPerMsg_Revert() (gas: 132149) +FeeQuoter_constructor:test_InvalidStalenessThreshold_Revert() (gas: 132196) +FeeQuoter_constructor:test_Setup_Success() (gas: 7908400) +FeeQuoter_convertTokenAmount:test_ConvertTokenAmount_Success() (gas: 74951) +FeeQuoter_convertTokenAmount:test_LinkTokenNotSupported_Revert() (gas: 34055) +FeeQuoter_getDataAvailabilityCost:test_EmptyMessageCalculatesDataAvailabilityCost_Success() (gas: 124958) +FeeQuoter_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCostUnsupportedDestChainSelector_Success() (gas: 20691) +FeeQuoter_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCost_Success() (gas: 33048) +FeeQuoter_getTokenAndGasPrices:test_GetFeeTokenAndGasPrices_Success() (gas: 73952) +FeeQuoter_getTokenAndGasPrices:test_StaleGasPrice_Revert() (gas: 19223) +FeeQuoter_getTokenAndGasPrices:test_UnsupportedChain_Revert() (gas: 17728) +FeeQuoter_getTokenAndGasPrices:test_ZeroGasPrice_Success() (gas: 47642) +FeeQuoter_getTokenPrice:test_GetTokenPriceFromFeed_Success() (gas: 74567) +FeeQuoter_getTokenPrices:test_GetTokenPrices_Success() (gas: 87892) +FeeQuoter_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 51274) +FeeQuoter_getTokenTransferCost:test_FeeTokenBpsFee_Success() (gas: 46576) +FeeQuoter_getTokenTransferCost:test_LargeTokenTransferChargesMaxFeeAndGas_Success() (gas: 39182) +FeeQuoter_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 161432) +FeeQuoter_getTokenTransferCost:test_NoTokenTransferChargesZeroFee_Success() (gas: 25553) +FeeQuoter_getTokenTransferCost:test_SmallTokenTransferChargesMinFeeAndGas_Success() (gas: 38832) +FeeQuoter_getTokenTransferCost:test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() (gas: 38788) +FeeQuoter_getTokenTransferCost:test_ZeroFeeConfigChargesMinFee_Success() (gas: 53538) +FeeQuoter_getTokenTransferCost:test_getTokenTransferCost_selfServeUsesDefaults_Success() (gas: 40215) +FeeQuoter_getValidatedFee:test_DestinationChainNotEnabled_Revert() (gas: 23180) +FeeQuoter_getValidatedFee:test_EmptyMessage_Success() (gas: 126780) +FeeQuoter_getValidatedFee:test_EnforceOutOfOrder_Revert() (gas: 65331) +FeeQuoter_getValidatedFee:test_HighGasMessage_Success() (gas: 298759) +FeeQuoter_getValidatedFee:test_InvalidEVMAddress_Revert() (gas: 27952) +FeeQuoter_getValidatedFee:test_MessageGasLimitTooHigh_Revert() (gas: 37814) +FeeQuoter_getValidatedFee:test_MessageTooLarge_Revert() (gas: 113039) +FeeQuoter_getValidatedFee:test_MessageWithDataAndTokenTransfer_Success() (gas: 244565) +FeeQuoter_getValidatedFee:test_NotAFeeToken_Revert() (gas: 26700) +FeeQuoter_getValidatedFee:test_SingleTokenMessage_Success() (gas: 189634) +FeeQuoter_getValidatedFee:test_TooManyTokens_Revert() (gas: 29755) +FeeQuoter_getValidatedFee:test_ZeroDataAvailabilityMultiplier_Success() (gas: 82721) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedErc20Above18Decimals_Success() (gas: 3425326) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedErc20Below18Decimals_Success() (gas: 3425261) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFeedAt0Decimals_Success() (gas: 3405403) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFeedAt18Decimals_Success() (gas: 3425009) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFlippedDecimals_Success() (gas: 3425222) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedMaxInt224Value_Success() (gas: 3425016) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedOverStalenessPeriod_Success() (gas: 71128) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeed_Success() (gas: 70962) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPrice_Success() (gas: 63583) +FeeQuoter_getValidatedTokenPrice:test_OverflowFeedPrice_Revert() (gas: 3424704) +FeeQuoter_getValidatedTokenPrice:test_StaleFeeToken_Success() (gas: 66998) +FeeQuoter_getValidatedTokenPrice:test_TokenNotSupportedFeed_Revert() (gas: 122906) +FeeQuoter_getValidatedTokenPrice:test_TokenNotSupported_Revert() (gas: 15635) +FeeQuoter_getValidatedTokenPrice:test_UnderflowFeedPrice_Revert() (gas: 3422488) +FeeQuoter_onReport:test_OnReport_StaleUpdate_Revert() (gas: 55021) +FeeQuoter_onReport:test_onReport_InvalidForwarder_Reverts() (gas: 27666) +FeeQuoter_onReport:test_onReport_Success() (gas: 98820) +FeeQuoter_onReport:test_onReport_UnsupportedToken_Reverts() (gas: 31209) +FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsDefault_Success() (gas: 23993) +FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsEnforceOutOfOrder_Revert() (gas: 28138) +FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsGasLimitTooHigh_Revert() (gas: 25276) +FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsInvalidExtraArgsTag_Revert() (gas: 24171) +FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsV1_Success() (gas: 25547) +FeeQuoter_parseEVMExtraArgsFromBytes:test_EVMExtraArgsV2_Success() (gas: 26085) +FeeQuoter_processMessageArgs:test_processMessageArgs_InvalidEVMAddressDestToken_Revert() (gas: 53875) +FeeQuoter_processMessageArgs:test_processMessageArgs_InvalidExtraArgs_Revert() (gas: 22252) +FeeQuoter_processMessageArgs:test_processMessageArgs_MalformedEVMExtraArgs_Revert() (gas: 22714) +FeeQuoter_processMessageArgs:test_processMessageArgs_MessageFeeTooHigh_Revert() (gas: 20791) +FeeQuoter_processMessageArgs:test_processMessageArgs_SourceTokenDataTooLarge_Revert() (gas: 159879) +FeeQuoter_processMessageArgs:test_processMessageArgs_TokenAmountArraysMismatching_Revert() (gas: 49920) +FeeQuoter_processMessageArgs:test_processMessageArgs_WitEVMExtraArgsV2_Success() (gas: 37697) +FeeQuoter_processMessageArgs:test_processMessageArgs_WithConvertedTokenAmount_Success() (gas: 36217) +FeeQuoter_processMessageArgs:test_processMessageArgs_WithCorrectPoolReturnData_Success() (gas: 96367) +FeeQuoter_processMessageArgs:test_processMessageArgs_WithEVMExtraArgsV1_Success() (gas: 36520) +FeeQuoter_processMessageArgs:test_processMessageArgs_WithEmptyEVMExtraArgs_Success() (gas: 33576) +FeeQuoter_processMessageArgs:test_processMessageArgs_WithLinkTokenAmount_Success() (gas: 22717) +FeeQuoter_updatePrices:test_OnlyCallableByUpdater_Revert() (gas: 13531) +FeeQuoter_updatePrices:test_OnlyGasPrice_Success() (gas: 27566) +FeeQuoter_updatePrices:test_OnlyTokenPrice_Success() (gas: 32627) +FeeQuoter_updatePrices:test_UpdatableByAuthorizedCaller_Success() (gas: 83557) +FeeQuoter_updatePrices:test_UpdateMultiplePrices_Success() (gas: 165439) +FeeQuoter_updateTokenPriceFeeds:test_FeedNotUpdated() (gas: 57411) +FeeQuoter_updateTokenPriceFeeds:test_FeedUnset_Success() (gas: 75829) +FeeQuoter_updateTokenPriceFeeds:test_FeedUpdatedByNonOwner_Revert() (gas: 22380) +FeeQuoter_updateTokenPriceFeeds:test_MultipleFeedUpdate_Success() (gas: 101508) +FeeQuoter_updateTokenPriceFeeds:test_SingleFeedUpdate_Success() (gas: 57373) +FeeQuoter_updateTokenPriceFeeds:test_ZeroFeeds_Success() (gas: 13342) +FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddressEncodePacked_Revert() (gas: 12222) +FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddressPrecompiles_Revert() (gas: 5634878) +FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddress_Revert() (gas: 12371) +FeeQuoter_validateDestFamilyAddress:test_ValidEVMAddress_Success() (gas: 7769) +FeeQuoter_validateDestFamilyAddress:test_ValidNonEVMAddress_Success() (gas: 7345) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 234768) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 150751) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 113661) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 154754) +HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 233895) +HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 509579) +HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 327065) +HybridUSDCTokenPoolMigrationTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 122081) +HybridUSDCTokenPoolMigrationTests:test_burnLockedUSDC_invalidPermissions_Revert() (gas: 41448) +HybridUSDCTokenPoolMigrationTests:test_cancelExistingCCTPMigrationProposal() (gas: 35499) +HybridUSDCTokenPoolMigrationTests:test_cannotCancelANonExistentMigrationProposal() (gas: 12889) +HybridUSDCTokenPoolMigrationTests:test_cannotModifyLiquidityWithoutPermissions_Revert() (gas: 15409) +HybridUSDCTokenPoolMigrationTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 167516) +HybridUSDCTokenPoolMigrationTests:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 283817) +HybridUSDCTokenPoolMigrationTests:test_transferLiquidity_Success() (gas: 176428) +HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_destChain_Success() (gas: 176729) +HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_homeChain_Success() (gas: 538840) +HybridUSDCTokenPoolTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 234786) +HybridUSDCTokenPoolTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 150729) +HybridUSDCTokenPoolTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 113683) +HybridUSDCTokenPoolTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 154776) +HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 233917) +HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 509534) +HybridUSDCTokenPoolTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 327020) +HybridUSDCTokenPoolTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 122147) +HybridUSDCTokenPoolTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 167494) +HybridUSDCTokenPoolTests:test_transferLiquidity_Success() (gas: 176445) +LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Revert() (gas: 11968) +LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Success() (gas: 19761) +LockReleaseTokenPoolPoolAndProxy_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 5147538) +LockReleaseTokenPoolPoolAndProxy_provideLiquidity:test_LiquidityNotAccepted_Revert() (gas: 5143388) +LockReleaseTokenPoolPoolAndProxy_provideLiquidity:test_Unauthorized_Revert() (gas: 12683) +LockReleaseTokenPoolPoolAndProxy_supportsInterface:test_SupportsInterface_Success() (gas: 11613) +LockReleaseTokenPoolPoolAndProxy_withdrawalLiquidity:test_InsufficientLiquidity_Revert() (gas: 64820) +LockReleaseTokenPoolPoolAndProxy_withdrawalLiquidity:test_Unauthorized_Revert() (gas: 12647) +LockReleaseTokenPool_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 4775338) +LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Revert() (gas: 33986) +LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Success() (gas: 91128) +LockReleaseTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 64965) +LockReleaseTokenPool_provideLiquidity:test_LiquidityNotAccepted_Revert() (gas: 4771254) +LockReleaseTokenPool_provideLiquidity:test_Unauthorized_Revert() (gas: 12661) +LockReleaseTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 81914) +LockReleaseTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 63333) +LockReleaseTokenPool_releaseOrMint:test_ReleaseOrMint_Success() (gas: 274666) +LockReleaseTokenPool_setRebalancer:test_SetRebalancer_Revert() (gas: 11990) +LockReleaseTokenPool_setRebalancer:test_SetRebalancer_Success() (gas: 19783) +LockReleaseTokenPool_supportsInterface:test_SupportsInterface_Success() (gas: 11613) +LockReleaseTokenPool_transferLiquidity:test_transferLiquidity_Success() (gas: 88490) +LockReleaseTokenPool_transferLiquidity:test_transferLiquidity_transferTooMuch_Revert() (gas: 59808) +LockReleaseTokenPool_withdrawalLiquidity:test_InsufficientLiquidity_Revert() (gas: 64803) +LockReleaseTokenPool_withdrawalLiquidity:test_Unauthorized_Revert() (gas: 12647) +LockRelease_setRateLimitAdmin:test_SetRateLimitAdmin_Revert() (gas: 12033) +LockRelease_setRateLimitAdmin:test_SetRateLimitAdmin_Success() (gas: 36728) +MerkleMultiProofTest:test_CVE_2023_34459() (gas: 6372) +MerkleMultiProofTest:test_EmptyLeaf_Revert() (gas: 4019) +MerkleMultiProofTest:test_MerkleRoot256() (gas: 668330) +MerkleMultiProofTest:test_MerkleRootSingleLeaf_Success() (gas: 4074) +MerkleMultiProofTest:test_SpecSync_gas() (gas: 49338) +MockRouterTest:test_ccipSendWithInsufficientNativeTokens_Revert() (gas: 37132) +MockRouterTest:test_ccipSendWithInvalidMsgValue_Revert() (gas: 64034) +MockRouterTest:test_ccipSendWithLinkFeeTokenAndValidMsgValue_Success() (gas: 135267) +MockRouterTest:test_ccipSendWithLinkFeeTokenbutInsufficientAllowance_Revert() (gas: 68119) +MockRouterTest:test_ccipSendWithSufficientNativeFeeTokens_Success() (gas: 48403) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_MultipleConfigsBothLanes_Success() (gas: 150584) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_MultipleConfigs_Success() (gas: 357431) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_OnlyCallableByOwner_Revert() (gas: 20693) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_SingleConfigOutbound_Success() (gas: 85456) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_SingleConfig_Success() (gas: 85382) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_UpdateExistingConfigWithNoDifference_Success() (gas: 49626) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_UpdateExistingConfig_Success() (gas: 68052) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_ZeroChainSelector_Revert() (gas: 19536) +MultiAggregateRateLimiter_applyRateLimiterConfigUpdates:test_ZeroConfigs_Success() (gas: 13331) +MultiAggregateRateLimiter_constructor:test_ConstructorNoAuthorizedCallers_Success() (gas: 3286151) +MultiAggregateRateLimiter_constructor:test_Constructor_Success() (gas: 3403578) +MultiAggregateRateLimiter_getTokenBucket:test_GetTokenBucket_Success() (gas: 38652) +MultiAggregateRateLimiter_getTokenBucket:test_Refill_Success() (gas: 63823) +MultiAggregateRateLimiter_getTokenBucket:test_TimeUnderflow_Revert() (gas: 18548) +MultiAggregateRateLimiter_getTokenValue:test_GetTokenValue_Success() (gas: 20837) +MultiAggregateRateLimiter_getTokenValue:test_NoTokenPrice_Reverts() (gas: 25361) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageFromUnauthorizedCaller_Revert() (gas: 17951) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 242409) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithDisabledRateLimitToken_Success() (gas: 68454) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 21034) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitDisabled_Success() (gas: 53968) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitExceeded_Revert() (gas: 56502) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitReset_Success() (gas: 108389) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 344604) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokens_Success() (gas: 60097) +MultiAggregateRateLimiter_onOutboundMessage:test_RateLimitValueDifferentLanes_Success() (gas: 1073657377) +MultiAggregateRateLimiter_onOutboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 21497) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageFromUnauthorizedCaller_Revert() (gas: 18167) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 240199) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDisabledRateLimitToken_Success() (gas: 69225) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitDisabled_Success() (gas: 54803) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitExceeded_Revert() (gas: 57316) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitReset_Success() (gas: 105576) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 342731) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithTokens_Success() (gas: 60912) +MultiAggregateRateLimiter_setFeeQuoter:test_OnlyOwner_Revert() (gas: 12286) +MultiAggregateRateLimiter_setFeeQuoter:test_Owner_Success() (gas: 20439) +MultiAggregateRateLimiter_setFeeQuoter:test_ZeroAddress_Revert() (gas: 11124) +MultiAggregateRateLimiter_updateRateLimitTokens:test_NonOwner_Revert() (gas: 26352) +MultiAggregateRateLimiter_updateRateLimitTokens:test_UpdateRateLimitTokensMultipleChains_Success() (gas: 292916) +MultiAggregateRateLimiter_updateRateLimitTokens:test_UpdateRateLimitTokensSingleChain_Success() (gas: 265872) +MultiAggregateRateLimiter_updateRateLimitTokens:test_UpdateRateLimitTokens_AddsAndRemoves_Success() (gas: 214939) +MultiAggregateRateLimiter_updateRateLimitTokens:test_UpdateRateLimitTokens_RemoveNonExistentToken_Success() (gas: 32607) +MultiAggregateRateLimiter_updateRateLimitTokens:test_ZeroDestToken_Revert() (gas: 21085) +MultiAggregateRateLimiter_updateRateLimitTokens:test_ZeroSourceToken_Revert() (gas: 20891) +MultiOCR3Base_setOCR3Configs:test_FMustBePositive_Revert() (gas: 72005) +MultiOCR3Base_setOCR3Configs:test_FTooHigh_Revert() (gas: 49229) +MultiOCR3Base_setOCR3Configs:test_MoreTransmittersThanSigners_Revert() (gas: 115903) +MultiOCR3Base_setOCR3Configs:test_NoTransmitters_Revert() (gas: 23219) +MultiOCR3Base_setOCR3Configs:test_RepeatSignerAddress_Revert() (gas: 296831) +MultiOCR3Base_setOCR3Configs:test_RepeatTransmitterAddress_Revert() (gas: 437346) +MultiOCR3Base_setOCR3Configs:test_SetConfigIgnoreSigners_Success() (gas: 557001) +MultiOCR3Base_setOCR3Configs:test_SetConfigWithSignersMismatchingTransmitters_Success() (gas: 719687) +MultiOCR3Base_setOCR3Configs:test_SetConfigWithSigners_Success() (gas: 874847) +MultiOCR3Base_setOCR3Configs:test_SetConfigWithoutSigners_Success() (gas: 486312) +MultiOCR3Base_setOCR3Configs:test_SetConfigsZeroInput_Success() (gas: 13373) +MultiOCR3Base_setOCR3Configs:test_SetMultipleConfigs_Success() (gas: 2266810) +MultiOCR3Base_setOCR3Configs:test_SignerCannotBeZeroAddress_Revert() (gas: 152944) +MultiOCR3Base_setOCR3Configs:test_StaticConfigChange_Revert() (gas: 841242) +MultiOCR3Base_setOCR3Configs:test_TooManySigners_Revert() (gas: 312041) +MultiOCR3Base_setOCR3Configs:test_TooManyTransmitters_Revert() (gas: 261347) +MultiOCR3Base_setOCR3Configs:test_TransmitterCannotBeZeroAddress_Revert() (gas: 266581) +MultiOCR3Base_setOCR3Configs:test_UpdateConfigSigners_Success() (gas: 930576) +MultiOCR3Base_setOCR3Configs:test_UpdateConfigTransmittersWithoutSigners_Success() (gas: 516830) +MultiOCR3Base_transmit:test_ConfigDigestMismatch_Revert() (gas: 49771) +MultiOCR3Base_transmit:test_ForkedChain_Revert() (gas: 56488) +MultiOCR3Base_transmit:test_InsufficientSignatures_Revert() (gas: 87043) +MultiOCR3Base_transmit:test_NonUniqueSignature_Revert() (gas: 75511) +MultiOCR3Base_transmit:test_SignatureOutOfRegistration_Revert() (gas: 38453) +MultiOCR3Base_transmit:test_TooManySignatures_Revert() (gas: 92076) +MultiOCR3Base_transmit:test_TransmitSigners_gas_Success() (gas: 39865) +MultiOCR3Base_transmit:test_TransmitWithExtraCalldataArgs_Revert() (gas: 52756) +MultiOCR3Base_transmit:test_TransmitWithLessCalldataArgs_Revert() (gas: 30083) +MultiOCR3Base_transmit:test_TransmitWithoutSignatureVerification_gas_Success() (gas: 21035) +MultiOCR3Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 29105) +MultiOCR3Base_transmit:test_UnauthorizedSigner_Revert() (gas: 69917) +MultiOCR3Base_transmit:test_UnconfiguredPlugin_Revert() (gas: 42537) +MultiOCR3Base_transmit:test_ZeroSignatures_Revert() (gas: 37478) +MultiOnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 304699) +MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1926302) +NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 40448) +NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 27403) +NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 41879) +NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 79874) +NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 316259) +NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 322432) +NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 417132) +NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 375380) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 327638) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 313211) +NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 183405) +NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 193460) +NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 268061) +NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 153619) +NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 125710) +NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 134448) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 47118) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 69128) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 46949) +NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 72431) +NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12892) +NonceManager_typeAndVersion:test_typeAndVersion() (gas: 10623) +OCR2BaseNoChecks_setOCR2Config:test_FMustBePositive_Revert() (gas: 15443) +OCR2BaseNoChecks_setOCR2Config:test_RepeatAddress_Revert() (gas: 48841) +OCR2BaseNoChecks_setOCR2Config:test_SetConfigSuccess_gas() (gas: 97138) +OCR2BaseNoChecks_setOCR2Config:test_TooManyTransmitter_Revert() (gas: 75615) +OCR2BaseNoChecks_setOCR2Config:test_TransmitterCannotBeZeroAddress_Revert() (gas: 32515) +OCR2BaseNoChecks_transmit:test_ConfigDigestMismatch_Revert() (gas: 20081) +OCR2BaseNoChecks_transmit:test_ForkedChain_Revert() (gas: 29888) +OCR2BaseNoChecks_transmit:test_TransmitSuccess_gas() (gas: 30530) +OCR2BaseNoChecks_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 25249) +OCR2Base_setOCR2Config:test_FMustBePositive_Revert() (gas: 15449) +OCR2Base_setOCR2Config:test_FTooHigh_Revert() (gas: 15725) +OCR2Base_setOCR2Config:test_OracleOutOfRegister_Revert() (gas: 21647) +OCR2Base_setOCR2Config:test_RepeatAddress_Revert() (gas: 56234) +OCR2Base_setOCR2Config:test_SetConfigSuccess_gas() (gas: 169648) +OCR2Base_setOCR2Config:test_SingerCannotBeZeroAddress_Revert() (gas: 32751) +OCR2Base_setOCR2Config:test_TooManySigners_Revert() (gas: 34759) +OCR2Base_setOCR2Config:test_TransmitterCannotBeZeroAddress_Revert() (gas: 55992) +OCR2Base_transmit:test_ConfigDigestMismatch_Revert() (gas: 22520) +OCR2Base_transmit:test_ForkedChain_Revert() (gas: 42561) +OCR2Base_transmit:test_NonUniqueSignature_Revert() (gas: 62252) +OCR2Base_transmit:test_SignatureOutOfRegistration_Revert() (gas: 24538) +OCR2Base_transmit:test_Transmit2SignersSuccess_gas() (gas: 58490) +OCR2Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 27448) +OCR2Base_transmit:test_UnauthorizedSigner_Revert() (gas: 45597) +OCR2Base_transmit:test_WrongNumberOfSignatures_Revert() (gas: 23593) +OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 8902529) +OffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 495132) +OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 107802) +OffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 13418) +OffRamp_applySourceChainConfigUpdates:test_InvalidOnRampUpdate_Revert() (gas: 231022) +OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Success() (gas: 110522) +OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 124535) +OffRamp_applySourceChainConfigUpdates:test_RouterAddress_Revert() (gas: 15757) +OffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 77820) +OffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 17894) +OffRamp_batchExecute:test_MultipleReportsDifferentChainsSkipCursedChain_Success() (gas: 224083) +OffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 425996) +OffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 368952) +OffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 208104) +OffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 239311) +OffRamp_batchExecute:test_SingleReport_Success() (gas: 188653) +OffRamp_batchExecute:test_Unhealthy_Success() (gas: 717329) +OffRamp_batchExecute:test_ZeroReports_Revert() (gas: 12088) +OffRamp_ccipReceive:test_Reverts() (gas: 18753) +OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 109720) +OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 77314) +OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 83071) +OffRamp_commit:test_InvalidInterval_Revert() (gas: 77116) +OffRamp_commit:test_InvalidRootRevert() (gas: 75631) +OffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 9548401) +OffRamp_commit:test_NoConfig_Revert() (gas: 9122532) +OffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 130728) +OffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 147353) +OffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 130749) +OffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 424203) +OffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 190103) +OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 161941) +OffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 176263) +OffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 72480) +OffRamp_commit:test_StaleReportWithRoot_Success() (gas: 280739) +OffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 144515) +OffRamp_commit:test_Unhealthy_Revert() (gas: 71728) +OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 250379) +OffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 61202) +OffRamp_constructor:test_Constructor_Success() (gas: 9121263) +OffRamp_constructor:test_SourceChainSelector_Revert() (gas: 148706) +OffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 112646) +OffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 110448) +OffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 174260) +OffRamp_constructor:test_ZeroRMNRemote_Revert() (gas: 110450) +OffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 110395) +OffRamp_execute:test_IncorrectArrayType_Revert() (gas: 19670) +OffRamp_execute:test_LargeBatch_Success() (gas: 4789035) +OffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 462387) +OffRamp_execute:test_MultipleReports_Success() (gas: 392392) +OffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 9972739) +OffRamp_execute:test_NoConfig_Revert() (gas: 9179658) +OffRamp_execute:test_NonArray_Revert() (gas: 34048) +OffRamp_execute:test_SingleReport_Success() (gas: 209314) +OffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 173200) +OffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 9980482) +OffRamp_execute:test_ZeroReports_Revert() (gas: 19285) +OffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 23977) +OffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 277738) +OffRamp_executeSingleMessage:test_NonContract_Success() (gas: 26476) +OffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 236582) +OffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 60990) +OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 60132) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 267137) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 98126) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 320798) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithVInterception_Success() (gas: 108714) +OffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 36766) +OffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 24545) +OffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 595814) +OffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 60468) +OffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 42455) +OffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 37212) +OffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 221890) +OffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 237935) +OffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 52346) +OffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 625018) +OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 297913) +OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 252901) +OffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 271928) +OffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 307332) +OffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 168258) +OffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 502204) +OffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 71999) +OffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 86531) +OffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 733681) +OffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 666498) +OffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 42450) +OffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 710720) +OffRamp_executeSingleReport:test_Unhealthy_Success() (gas: 710723) +OffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 574134) +OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 172776) +OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 202237) +OffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 5703276) +OffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 148004) +OffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 111520) +OffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 128026) +OffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 103971) +OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 208382) +OffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 261275) +OffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 37688) +OffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 267627) +OffRamp_manuallyExecute:test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() (gas: 38216) +OffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 73056) +OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 742494) +OffRamp_manuallyExecute:test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() (gas: 408960) +OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails_Success() (gas: 3574005) +OffRamp_manuallyExecute:test_manuallyExecute_SourceChainSelectorMismatch_Revert() (gas: 199720) +OffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 279997) +OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 280693) +OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 1033763) +OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 461923) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 47648) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 121919) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 99377) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 44074) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 109868) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 46405) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 101128) +OffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 187037) +OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 28901) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 74851) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 94066) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 206891) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_WithGasOverride_Success() (gas: 209712) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 217613) +OffRamp_setDynamicConfig:test_FeeQuoterZeroAddress_Revert() (gas: 12524) +OffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 15529) +OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor_Success() (gas: 50816) +OffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 28782) +OffRamp_trialExecute:test_RateLimitError_Success() (gas: 259624) +OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 269734) +OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 386633) +OffRamp_trialExecute:test_trialExecute_Success() (gas: 329701) +OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 508639) +OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() (gas: 21134) +OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_Revert() (gas: 80371) +OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_Success() (gas: 354874) +OnRamp_applyDestChainConfigUpdates:test_ApplyDestChainConfigUpdates_Success() (gas: 78476) +OnRamp_applyDestChainConfigUpdates:test_ApplyDestChainConfigUpdates_WithInvalidChainSelector_Revert() (gas: 15598) +OnRamp_constructor:test_Constructor_InvalidConfigChainSelectorEqZero_Revert() (gas: 102333) +OnRamp_constructor:test_Constructor_InvalidConfigNonceManagerEqAddressZero_Revert() (gas: 100239) +OnRamp_constructor:test_Constructor_InvalidConfigRMNProxyEqAddressZero_Revert() (gas: 105225) +OnRamp_constructor:test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() (gas: 100292) +OnRamp_constructor:test_Constructor_Success() (gas: 4357809) +OnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 133955) +OnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 165905) +OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 164793) +OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessEmptyExtraArgs() (gas: 162046) +OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 165055) +OnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 164074) +OnRamp_forwardFromRouter:test_ForwardFromRouter_Success_ConfigurableSourceRouter() (gas: 163767) +OnRamp_forwardFromRouter:test_InvalidExtraArgsTag_Revert() (gas: 42709) +OnRamp_forwardFromRouter:test_MessageInterceptionError_Revert() (gas: 159133) +OnRamp_forwardFromRouter:test_MesssageFeeTooHigh_Revert() (gas: 41363) +OnRamp_forwardFromRouter:test_MultiCannotSendZeroTokens_Revert() (gas: 40257) +OnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 20626) +OnRamp_forwardFromRouter:test_Paused_Revert() (gas: 44251) +OnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 26222) +OnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 247419) +OnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 274587) +OnRamp_forwardFromRouter:test_ShouldStoreLinkFees() (gas: 138011) +OnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 159128) +OnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 5511847) +OnRamp_forwardFromRouter:test_UnAllowedOriginalSender_Revert() (gas: 26986) +OnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 83006) +OnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 43332) +OnRamp_forwardFromRouter:test_forwardFromRouter_WithInterception_Success() (gas: 328778) +OnRamp_getFee:test_EmptyMessage_Success() (gas: 137801) +OnRamp_getFee:test_EnforceOutOfOrder_Revert() (gas: 78643) +OnRamp_getFee:test_GetFeeOfZeroForTokenMessage_Success() (gas: 112828) +OnRamp_getFee:test_NotAFeeTokenButPricedToken_Revert() (gas: 41569) +OnRamp_getFee:test_SingleTokenMessage_Success() (gas: 167530) +OnRamp_getFee:test_Unhealthy_Revert() (gas: 19992) +OnRamp_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 11166) +OnRamp_getTokenPool:test_GetTokenPool_Success() (gas: 42064) +OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigFeeAggregatorEqAddressZero_Revert() (gas: 13223) +OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigFeeQuoterEqAddressZero_Revert() (gas: 15545) +OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigInvalidConfig_Revert() (gas: 13277) +OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigOnlyOwner_Revert() (gas: 21138) +OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigReentrancyGuardEnteredEqTrue_Revert() (gas: 15634) +OnRamp_setDynamicConfig:test_setDynamicConfig_Success() (gas: 65013) +OnRamp_withdrawFeeTokens:test_WithdrawFeeTokens_Success() (gas: 104728) +PingPong_ccipReceive:test_CcipReceive_Success() (gas: 175023) +PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 21848) +PingPong_plumbing:test_Pausing_Success() (gas: 19077) +PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 197173) +PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 215989) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10973) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19366) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19408) -RMNHome_revokeSecondary:test_revokeSecondary_InvalidCaller_reverts() (gas: 10866) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10997) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 27257) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 29136) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 33400) -RMNHome_setDynamicConfig:test_setDynamicConfig_InvalidCaller_reverts() (gas: 11766) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20489) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15584) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 133281) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 63155) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20894) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20688) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24479) -RMNHome_setSecondary:test_setSecondary_InvalidCaller_reverts() (gas: 12615) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24918) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18130) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186826) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24623) -RMNHome_setSecondary:test_setSecondary_success() (gas: 283227) -RMNHome_setSecondary:test_setSecondary_success() (gas: 821491) -RMNRemote_constructor:test_constructor_success() (gas: 8334) -RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59165) -RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154457) -RMNRemote_curse:test_curse_calledByNonOwner_reverts() (gas: 18780) -RMNRemote_curse:test_curse_success() (gas: 149365) -RMNRemote_global_and_legacy_curses:test_global_and_legacy_curses_success() (gas: 133464) -RMNRemote_setConfig:test_setConfig_addSigner_removeSigner_success() (gas: 976479) -RMNRemote_setConfig:test_setConfig_duplicateOnChainPublicKey_reverts() (gas: 323272) -RMNRemote_setConfig:test_setConfig_invalidSignerOrder_reverts() (gas: 80138) -RMNRemote_setConfig:test_setConfig_minSignersIs0_success() (gas: 700548) -RMNRemote_setConfig:test_setConfig_minSignersTooHigh_reverts() (gas: 54024) -RMNRemote_uncurse:test_uncurse_NotCursed_duplicatedUncurseSubject_reverts() (gas: 51912) -RMNRemote_uncurse:test_uncurse_calledByNonOwner_reverts() (gas: 18748) -RMNRemote_uncurse:test_uncurse_success() (gas: 40151) -RMNRemote_verify_withConfigNotSet:test_verify_reverts() (gas: 13650) -RMNRemote_verify_withConfigSet:test_verify_InvalidSignature_reverts() (gas: 78519) -RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_duplicateSignature_reverts() (gas: 76336) -RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_not_sorted_reverts() (gas: 83399) -RMNRemote_verify_withConfigSet:test_verify_ThresholdNotMet_reverts() (gas: 153002) -RMNRemote_verify_withConfigSet:test_verify_UnexpectedSigner_reverts() (gas: 387667) -RMNRemote_verify_withConfigSet:test_verify_minSignersIsZero_success() (gas: 184524) -RMNRemote_verify_withConfigSet:test_verify_success() (gas: 68207) -RMN_constructor:test_Constructor_Success() (gas: 48994) -RMN_getRecordedCurseRelatedOps:test_OpsPostDeployment() (gas: 19732) -RMN_lazyVoteToCurseUpdate_Benchmark:test_VoteToCurseLazilyRetain3VotersUponConfigChange_gas() (gas: 152296) -RMN_ownerUnbless:test_Unbless_Success() (gas: 74936) -RMN_ownerUnvoteToCurse:test_CanBlessAndCurseAfterGlobalCurseIsLifted() (gas: 471829) -RMN_ownerUnvoteToCurse:test_IsIdempotent() (gas: 398492) -RMN_ownerUnvoteToCurse:test_NonOwner_Revert() (gas: 18723) -RMN_ownerUnvoteToCurse:test_OwnerUnvoteToCurseSuccess_gas() (gas: 358084) -RMN_ownerUnvoteToCurse:test_UnknownVoter_Revert() (gas: 33190) -RMN_ownerUnvoteToCurse_Benchmark:test_OwnerUnvoteToCurse_1Voter_LiftsCurse_gas() (gas: 262408) -RMN_permaBlessing:test_PermaBlessing() (gas: 202777) -RMN_setConfig:test_BlessVoterIsZeroAddress_Revert() (gas: 15500) -RMN_setConfig:test_EitherThresholdIsZero_Revert() (gas: 21107) -RMN_setConfig:test_NonOwner_Revert() (gas: 14725) -RMN_setConfig:test_RepeatedAddress_Revert() (gas: 18219) -RMN_setConfig:test_SetConfigSuccess_gas() (gas: 104154) -RMN_setConfig:test_TotalWeightsSmallerThanEachThreshold_Revert() (gas: 30185) -RMN_setConfig:test_VoteToBlessByEjectedVoter_Revert() (gas: 130461) -RMN_setConfig:test_VotersLengthIsZero_Revert() (gas: 12149) -RMN_setConfig:test_WeightIsZeroAddress_Revert() (gas: 15740) -RMN_setConfig_Benchmark_1:test_SetConfig_7Voters_gas() (gas: 659600) -RMN_setConfig_Benchmark_2:test_ResetConfig_7Voters_gas() (gas: 212652) -RMN_unvoteToCurse:test_InvalidCursesHash() (gas: 26430) -RMN_unvoteToCurse:test_OwnerSkips() (gas: 33831) -RMN_unvoteToCurse:test_OwnerSucceeds() (gas: 64005) -RMN_unvoteToCurse:test_UnauthorizedVoter() (gas: 47715) -RMN_unvoteToCurse:test_ValidCursesHash() (gas: 61145) -RMN_unvoteToCurse:test_VotersCantLiftCurseButOwnerCan() (gas: 629190) -RMN_voteToBless:test_Curse_Revert() (gas: 473408) -RMN_voteToBless:test_IsAlreadyBlessed_Revert() (gas: 115435) -RMN_voteToBless:test_RootSuccess() (gas: 558661) -RMN_voteToBless:test_SenderAlreadyVoted_Revert() (gas: 97234) -RMN_voteToBless:test_UnauthorizedVoter_Revert() (gas: 17126) -RMN_voteToBless_Benchmark:test_1RootSuccess_gas() (gas: 44718) -RMN_voteToBless_Benchmark:test_3RootSuccess_gas() (gas: 98694) -RMN_voteToBless_Benchmark:test_5RootSuccess_gas() (gas: 152608) -RMN_voteToBless_Blessed_Benchmark:test_1RootSuccessBecameBlessed_gas() (gas: 29682) -RMN_voteToBless_Blessed_Benchmark:test_1RootSuccess_gas() (gas: 27628) -RMN_voteToBless_Blessed_Benchmark:test_3RootSuccess_gas() (gas: 81626) -RMN_voteToBless_Blessed_Benchmark:test_5RootSuccess_gas() (gas: 135518) -RMN_voteToCurse:test_CurseOnlyWhenThresholdReached_Success() (gas: 1651170) -RMN_voteToCurse:test_EmptySubjects_Revert() (gas: 14061) -RMN_voteToCurse:test_EvenIfAlreadyCursed_Success() (gas: 535124) -RMN_voteToCurse:test_OwnerCanCurseAndUncurse() (gas: 400060) -RMN_voteToCurse:test_RepeatedSubject_Revert() (gas: 144405) -RMN_voteToCurse:test_ReusedCurseId_Revert() (gas: 146972) -RMN_voteToCurse:test_UnauthorizedVoter_Revert() (gas: 12666) -RMN_voteToCurse:test_VoteToCurse_NoCurse_Success() (gas: 187556) -RMN_voteToCurse:test_VoteToCurse_YesCurse_Success() (gas: 473079) -RMN_voteToCurse_2:test_VotesAreDroppedIfSubjectIsNotCursedDuringConfigChange() (gas: 371083) -RMN_voteToCurse_2:test_VotesAreRetainedIfSubjectIsCursedDuringConfigChange() (gas: 1154362) -RMN_voteToCurse_Benchmark_1:test_VoteToCurse_NewSubject_NewVoter_NoCurse_gas() (gas: 141118) -RMN_voteToCurse_Benchmark_1:test_VoteToCurse_NewSubject_NewVoter_YesCurse_gas() (gas: 165258) -RMN_voteToCurse_Benchmark_2:test_VoteToCurse_OldSubject_NewVoter_NoCurse_gas() (gas: 121437) -RMN_voteToCurse_Benchmark_2:test_VoteToCurse_OldSubject_OldVoter_NoCurse_gas() (gas: 98373) -RMN_voteToCurse_Benchmark_3:test_VoteToCurse_OldSubject_NewVoter_YesCurse_gas() (gas: 145784) -RateLimiter_constructor:test_Constructor_Success() (gas: 19734) -RateLimiter_consume:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 16042) -RateLimiter_consume:test_AggregateValueRateLimitReached_Revert() (gas: 22390) -RateLimiter_consume:test_ConsumeAggregateValue_Success() (gas: 31518) -RateLimiter_consume:test_ConsumeTokens_Success() (gas: 20381) -RateLimiter_consume:test_ConsumeUnlimited_Success() (gas: 40687) -RateLimiter_consume:test_ConsumingMoreThanUint128_Revert() (gas: 15822) -RateLimiter_consume:test_RateLimitReachedOverConsecutiveBlocks_Revert() (gas: 25798) -RateLimiter_consume:test_Refill_Success() (gas: 37444) -RateLimiter_consume:test_TokenMaxCapacityExceeded_Revert() (gas: 18388) -RateLimiter_consume:test_TokenRateLimitReached_Revert() (gas: 24886) -RateLimiter_currentTokenBucketState:test_CurrentTokenBucketState_Success() (gas: 38944) -RateLimiter_currentTokenBucketState:test_Refill_Success() (gas: 46849) -RateLimiter_setTokenBucketConfig:test_SetRateLimiterConfig_Success() (gas: 38506) -RegistryModuleOwnerCustom_constructor:test_constructor_Revert() (gas: 36033) -RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Revert() (gas: 19739) -RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Success() (gas: 130086) -RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Revert() (gas: 19559) -RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Success() (gas: 129905) -Router_applyRampUpdates:test_OffRampMismatch_Revert() (gas: 89366) -Router_applyRampUpdates:test_OffRampUpdatesWithRouting() (gas: 10662612) -Router_applyRampUpdates:test_OnRampDisable() (gas: 56007) -Router_applyRampUpdates:test_OnlyOwner_Revert() (gas: 12356) -Router_ccipSend:test_CCIPSendLinkFeeNoTokenSuccess_gas() (gas: 114599) -Router_ccipSend:test_CCIPSendLinkFeeOneTokenSuccess_gas() (gas: 202430) -Router_ccipSend:test_CCIPSendNativeFeeNoTokenSuccess_gas() (gas: 126968) -Router_ccipSend:test_CCIPSendNativeFeeOneTokenSuccess_gas() (gas: 214801) -Router_ccipSend:test_FeeTokenAmountTooLow_Revert() (gas: 64520) -Router_ccipSend:test_InvalidMsgValue() (gas: 32155) -Router_ccipSend:test_NativeFeeTokenInsufficientValue() (gas: 67177) -Router_ccipSend:test_NativeFeeTokenOverpay_Success() (gas: 170385) -Router_ccipSend:test_NativeFeeTokenZeroValue() (gas: 54279) -Router_ccipSend:test_NativeFeeToken_Success() (gas: 168901) -Router_ccipSend:test_NonLinkFeeToken_Success() (gas: 239227) -Router_ccipSend:test_UnsupportedDestinationChain_Revert() (gas: 24854) -Router_ccipSend:test_WhenNotHealthy_Revert() (gas: 44811) -Router_ccipSend:test_WrappedNativeFeeToken_Success() (gas: 171189) -Router_ccipSend:test_ZeroFeeAndGasPrice_Success() (gas: 241701) -Router_constructor:test_Constructor_Success() (gas: 13128) -Router_getArmProxy:test_getArmProxy() (gas: 10573) -Router_getFee:test_GetFeeSupportedChain_Success() (gas: 44673) -Router_getFee:test_UnsupportedDestinationChain_Revert() (gas: 17192) -Router_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10532) -Router_recoverTokens:test_RecoverTokensInvalidRecipient_Revert() (gas: 11334) -Router_recoverTokens:test_RecoverTokensNoFunds_Revert() (gas: 20267) -Router_recoverTokens:test_RecoverTokensNonOwner_Revert() (gas: 11171) -Router_recoverTokens:test_RecoverTokensValueReceiver_Revert() (gas: 358049) -Router_recoverTokens:test_RecoverTokens_Success() (gas: 52480) -Router_routeMessage:test_AutoExec_Success() (gas: 42816) -Router_routeMessage:test_ExecutionEvent_Success() (gas: 158520) -Router_routeMessage:test_ManualExec_Success() (gas: 35546) -Router_routeMessage:test_OnlyOffRamp_Revert() (gas: 25224) -Router_routeMessage:test_WhenNotHealthy_Revert() (gas: 44799) -Router_setWrappedNative:test_OnlyOwner_Revert() (gas: 10998) -SelfFundedPingPong_ccipReceive:test_FundingIfNotANop_Revert() (gas: 55660) -SelfFundedPingPong_ccipReceive:test_Funding_Success() (gas: 421258) -SelfFundedPingPong_setCountIncrBeforeFunding:test_setCountIncrBeforeFunding() (gas: 20181) -TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_OnlyPendingAdministrator_Revert() (gas: 51163) -TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_Success() (gas: 44004) -TokenAdminRegistry_addRegistryModule:test_addRegistryModule_OnlyOwner_Revert() (gas: 12653) -TokenAdminRegistry_addRegistryModule:test_addRegistryModule_Success() (gas: 67056) -TokenAdminRegistry_getAllConfiguredTokens:test_getAllConfiguredTokens_outOfBounds_Success() (gas: 11362) -TokenAdminRegistry_getPool:test_getPool_Success() (gas: 17602) -TokenAdminRegistry_getPools:test_getPools_Success() (gas: 39962) -TokenAdminRegistry_isAdministrator:test_isAdministrator_Success() (gas: 106006) -TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_AlreadyRegistered_Revert() (gas: 104346) -TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_OnlyRegistryModule_Revert() (gas: 15610) -TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_ZeroAddress_Revert() (gas: 15155) -TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_module_Success() (gas: 112962) -TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_owner_Success() (gas: 107965) -TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_reRegisterWhileUnclaimed_Success() (gas: 116067) -TokenAdminRegistry_removeRegistryModule:test_removeRegistryModule_OnlyOwner_Revert() (gas: 12609) -TokenAdminRegistry_removeRegistryModule:test_removeRegistryModule_Success() (gas: 54524) -TokenAdminRegistry_setPool:test_setPool_InvalidTokenPoolToken_Revert() (gas: 19316) -TokenAdminRegistry_setPool:test_setPool_OnlyAdministrator_Revert() (gas: 18137) -TokenAdminRegistry_setPool:test_setPool_Success() (gas: 36135) -TokenAdminRegistry_setPool:test_setPool_ZeroAddressRemovesPool_Success() (gas: 30842) -TokenAdminRegistry_transferAdminRole:test_transferAdminRole_OnlyAdministrator_Revert() (gas: 18103) -TokenAdminRegistry_transferAdminRole:test_transferAdminRole_Success() (gas: 49438) -TokenPoolAndProxy:test_lockOrBurn_burnMint_Success() (gas: 5586499) -TokenPoolAndProxy:test_lockOrBurn_burnWithFromMint_Success() (gas: 5617969) -TokenPoolAndProxy:test_lockOrBurn_lockRelease_Success() (gas: 5793246) -TokenPoolAndProxy:test_setPreviousPool_Success() (gas: 3070731) -TokenPoolAndProxyMigration:test_tokenPoolMigration_Success_1_2() (gas: 6434801) -TokenPoolAndProxyMigration:test_tokenPoolMigration_Success_1_4() (gas: 6634934) -TokenPoolWithAllowList_applyAllowListUpdates:test_AllowListNotEnabled_Revert() (gas: 1979943) -TokenPoolWithAllowList_applyAllowListUpdates:test_OnlyOwner_Revert() (gas: 12113) -TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowListSkipsZero_Success() (gas: 23476) -TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowList_Success() (gas: 177848) -TokenPoolWithAllowList_getAllowList:test_GetAllowList_Success() (gas: 23764) -TokenPoolWithAllowList_getAllowListEnabled:test_GetAllowListEnabled_Success() (gas: 8375) -TokenPoolWithAllowList_setRouter:test_SetRouter_Success() (gas: 24867) -TokenPool_applyChainUpdates:test_applyChainUpdates_DisabledNonZeroRateLimit_Revert() (gas: 271569) -TokenPool_applyChainUpdates:test_applyChainUpdates_InvalidRateLimitRate_Revert() (gas: 542359) -TokenPool_applyChainUpdates:test_applyChainUpdates_NonExistentChain_Revert() (gas: 18449) -TokenPool_applyChainUpdates:test_applyChainUpdates_OnlyCallableByOwner_Revert() (gas: 11469) -TokenPool_applyChainUpdates:test_applyChainUpdates_Success() (gas: 479160) -TokenPool_applyChainUpdates:test_applyChainUpdates_ZeroAddressNotAllowed_Revert() (gas: 157422) -TokenPool_constructor:test_ZeroAddressNotAllowed_Revert() (gas: 70484) -TokenPool_constructor:test_immutableFields_Success() (gas: 20586) -TokenPool_getRemotePool:test_getRemotePool_Success() (gas: 274181) -TokenPool_onlyOffRamp:test_CallerIsNotARampOnRouter_Revert() (gas: 277296) -TokenPool_onlyOffRamp:test_ChainNotAllowed_Revert() (gas: 290028) -TokenPool_onlyOffRamp:test_onlyOffRamp_Success() (gas: 350050) -TokenPool_onlyOnRamp:test_CallerIsNotARampOnRouter_Revert() (gas: 277036) -TokenPool_onlyOnRamp:test_ChainNotAllowed_Revert() (gas: 254065) -TokenPool_onlyOnRamp:test_onlyOnRamp_Success() (gas: 305106) -TokenPool_setChainRateLimiterConfig:test_NonExistentChain_Revert() (gas: 17187) -TokenPool_setChainRateLimiterConfig:test_OnlyOwnerOrRateLimitAdmin_Revert() (gas: 15227) -TokenPool_setRemotePool:test_setRemotePool_NonExistentChain_Reverts() (gas: 15671) -TokenPool_setRemotePool:test_setRemotePool_OnlyOwner_Reverts() (gas: 13219) -TokenPool_setRemotePool:test_setRemotePool_Success() (gas: 282125) -TokenProxy_ccipSend:test_CcipSendGasShouldBeZero_Revert() (gas: 17226) -TokenProxy_ccipSend:test_CcipSendInsufficientAllowance_Revert() (gas: 134605) -TokenProxy_ccipSend:test_CcipSendInvalidToken_Revert() (gas: 16000) -TokenProxy_ccipSend:test_CcipSendNative_Success() (gas: 244013) -TokenProxy_ccipSend:test_CcipSendNoDataAllowed_Revert() (gas: 16384) -TokenProxy_ccipSend:test_CcipSend_Success() (gas: 262651) -TokenProxy_constructor:test_Constructor() (gas: 13836) -TokenProxy_getFee:test_GetFeeGasShouldBeZero_Revert() (gas: 16899) -TokenProxy_getFee:test_GetFeeInvalidToken_Revert() (gas: 12706) -TokenProxy_getFee:test_GetFeeNoDataAllowed_Revert() (gas: 15885) -TokenProxy_getFee:test_GetFee_Success() (gas: 85240) -USDCTokenPool__validateMessage:test_ValidateInvalidMessage_Revert() (gas: 25704) -USDCTokenPool_lockOrBurn:test_CallerIsNotARampOnRouter_Revert() (gas: 35481) -USDCTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Revert() (gas: 30235) -USDCTokenPool_lockOrBurn:test_LockOrBurn_Success() (gas: 133508) -USDCTokenPool_lockOrBurn:test_UnknownDomain_Revert() (gas: 478182) -USDCTokenPool_releaseOrMint:test_ReleaseOrMintRealTx_Success() (gas: 268672) -USDCTokenPool_releaseOrMint:test_TokenMaxCapacityExceeded_Revert() (gas: 50952) -USDCTokenPool_releaseOrMint:test_UnlockingUSDCFailed_Revert() (gas: 98987) -USDCTokenPool_setDomains:test_InvalidDomain_Revert() (gas: 66393) -USDCTokenPool_setDomains:test_OnlyOwner_Revert() (gas: 11363) -USDCTokenPool_supportsInterface:test_SupportsInterface_Success() (gas: 10041) \ No newline at end of file +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18964) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11003) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28455) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 32914) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20323) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15574) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 131926) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20823) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20617) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24408) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24847) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18059) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186748) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24552) +RMNHome_setSecondary:test_setSecondary_success() (gas: 820802) +RMNRemote_constructor:test_constructor_success() (gas: 8853) +RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 61072) +RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 156801) +RMNRemote_curse:test_curse_calledByNonOwner_reverts() (gas: 20544) +RMNRemote_curse:test_curse_success() (gas: 157167) +RMNRemote_global_and_legacy_curses:test_global_and_legacy_curses_success() (gas: 141492) +RMNRemote_setConfig:test_setConfig_addSigner_removeSigner_success() (gas: 1097399) +RMNRemote_setConfig:test_setConfig_duplicateOnChainPublicKey_reverts() (gas: 339136) +RMNRemote_setConfig:test_setConfig_invalidSignerOrder_reverts() (gas: 91696) +RMNRemote_setConfig:test_setConfig_minSignersIs0_success() (gas: 773860) +RMNRemote_setConfig:test_setConfig_minSignersTooHigh_reverts() (gas: 64566) +RMNRemote_uncurse:test_uncurse_NotCursed_duplicatedUncurseSubject_reverts() (gas: 53942) +RMNRemote_uncurse:test_uncurse_calledByNonOwner_reverts() (gas: 20525) +RMNRemote_uncurse:test_uncurse_success() (gas: 44264) +RMNRemote_verify_withConfigNotSet:test_verify_reverts() (gas: 14954) +RMNRemote_verify_withConfigSet:test_verify_InvalidSignature_reverts() (gas: 88745) +RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_duplicateSignature_reverts() (gas: 86340) +RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_not_sorted_reverts() (gas: 93570) +RMNRemote_verify_withConfigSet:test_verify_ThresholdNotMet_reverts() (gas: 177181) +RMNRemote_verify_withConfigSet:test_verify_UnexpectedSigner_reverts() (gas: 429818) +RMNRemote_verify_withConfigSet:test_verify_minSignersIsZero_success() (gas: 230707) +RMNRemote_verify_withConfigSet:test_verify_success() (gas: 77919) +RMN_constructor:test_Constructor_Success() (gas: 63037) +RMN_getRecordedCurseRelatedOps:test_OpsPostDeployment() (gas: 24609) +RMN_lazyVoteToCurseUpdate_Benchmark:test_VoteToCurseLazilyRetain3VotersUponConfigChange_gas() (gas: 159340) +RMN_ownerUnbless:test_Unbless_Success() (gas: 103665) +RMN_ownerUnvoteToCurse:test_CanBlessAndCurseAfterGlobalCurseIsLifted() (gas: 527240) +RMN_ownerUnvoteToCurse:test_IsIdempotent() (gas: 461028) +RMN_ownerUnvoteToCurse:test_NonOwner_Revert() (gas: 25696) +RMN_ownerUnvoteToCurse:test_OwnerUnvoteToCurseSuccess_gas() (gas: 402269) +RMN_ownerUnvoteToCurse:test_UnknownVoter_Revert() (gas: 37341) +RMN_ownerUnvoteToCurse_Benchmark:test_OwnerUnvoteToCurse_1Voter_LiftsCurse_gas() (gas: 310729) +RMN_permaBlessing:test_PermaBlessing() (gas: 234032) +RMN_setConfig:test_BlessVoterIsZeroAddress_Revert() (gas: 19944) +RMN_setConfig:test_EitherThresholdIsZero_Revert() (gas: 29093) +RMN_setConfig:test_NonOwner_Revert() (gas: 19180) +RMN_setConfig:test_RepeatedAddress_Revert() (gas: 24182) +RMN_setConfig:test_SetConfigSuccess_gas() (gas: 121383) +RMN_setConfig:test_TotalWeightsSmallerThanEachThreshold_Revert() (gas: 42230) +RMN_setConfig:test_VoteToBlessByEjectedVoter_Revert() (gas: 150092) +RMN_setConfig:test_VotersLengthIsZero_Revert() (gas: 13777) +RMN_setConfig:test_WeightIsZeroAddress_Revert() (gas: 20193) +RMN_setConfig_Benchmark_1:test_SetConfig_7Voters_gas() (gas: 699447) +RMN_setConfig_Benchmark_2:test_ResetConfig_7Voters_gas() (gas: 262912) +RMN_unvoteToCurse:test_InvalidCursesHash() (gas: 29467) +RMN_unvoteToCurse:test_OwnerSkips() (gas: 38039) +RMN_unvoteToCurse:test_OwnerSucceeds() (gas: 69773) +RMN_unvoteToCurse:test_UnauthorizedVoter() (gas: 59241) +RMN_unvoteToCurse:test_ValidCursesHash() (gas: 66142) +RMN_unvoteToCurse:test_VotersCantLiftCurseButOwnerCan() (gas: 729679) +RMN_voteToBless:test_Curse_Revert() (gas: 496801) +RMN_voteToBless:test_IsAlreadyBlessed_Revert() (gas: 147850) +RMN_voteToBless:test_RootSuccess() (gas: 745652) +RMN_voteToBless:test_SenderAlreadyVoted_Revert() (gas: 123496) +RMN_voteToBless:test_UnauthorizedVoter_Revert() (gas: 19508) +RMN_voteToBless_Benchmark:test_1RootSuccess_gas() (gas: 49435) +RMN_voteToBless_Benchmark:test_3RootSuccess_gas() (gas: 110271) +RMN_voteToBless_Benchmark:test_5RootSuccess_gas() (gas: 171045) +RMN_voteToBless_Blessed_Benchmark:test_1RootSuccessBecameBlessed_gas() (gas: 34790) +RMN_voteToBless_Blessed_Benchmark:test_1RootSuccess_gas() (gas: 32338) +RMN_voteToBless_Blessed_Benchmark:test_3RootSuccess_gas() (gas: 93196) +RMN_voteToBless_Blessed_Benchmark:test_5RootSuccess_gas() (gas: 153948) +RMN_voteToCurse:test_CurseOnlyWhenThresholdReached_Success() (gas: 1748779) +RMN_voteToCurse:test_EmptySubjects_Revert() (gas: 15996) +RMN_voteToCurse:test_EvenIfAlreadyCursed_Success() (gas: 564086) +RMN_voteToCurse:test_OwnerCanCurseAndUncurse() (gas: 471086) +RMN_voteToCurse:test_RepeatedSubject_Revert() (gas: 151480) +RMN_voteToCurse:test_ReusedCurseId_Revert() (gas: 155411) +RMN_voteToCurse:test_UnauthorizedVoter_Revert() (gas: 14537) +RMN_voteToCurse:test_VoteToCurse_NoCurse_Success() (gas: 203894) +RMN_voteToCurse:test_VoteToCurse_YesCurse_Success() (gas: 495649) +RMN_voteToCurse_2:test_VotesAreDroppedIfSubjectIsNotCursedDuringConfigChange() (gas: 417003) +RMN_voteToCurse_2:test_VotesAreRetainedIfSubjectIsCursedDuringConfigChange() (gas: 1339323) +RMN_voteToCurse_Benchmark_1:test_VoteToCurse_NewSubject_NewVoter_NoCurse_gas() (gas: 146898) +RMN_voteToCurse_Benchmark_1:test_VoteToCurse_NewSubject_NewVoter_YesCurse_gas() (gas: 171152) +RMN_voteToCurse_Benchmark_2:test_VoteToCurse_OldSubject_NewVoter_NoCurse_gas() (gas: 126446) +RMN_voteToCurse_Benchmark_2:test_VoteToCurse_OldSubject_OldVoter_NoCurse_gas() (gas: 102691) +RMN_voteToCurse_Benchmark_3:test_VoteToCurse_OldSubject_NewVoter_YesCurse_gas() (gas: 151187) +RateLimiter_constructor:test_Constructor_Success() (gas: 22964) +RateLimiter_consume:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 19839) +RateLimiter_consume:test_AggregateValueRateLimitReached_Revert() (gas: 28311) +RateLimiter_consume:test_ConsumeAggregateValue_Success() (gas: 39405) +RateLimiter_consume:test_ConsumeTokens_Success() (gas: 21919) +RateLimiter_consume:test_ConsumeUnlimited_Success() (gas: 57402) +RateLimiter_consume:test_ConsumingMoreThanUint128_Revert() (gas: 19531) +RateLimiter_consume:test_RateLimitReachedOverConsecutiveBlocks_Revert() (gas: 33020) +RateLimiter_consume:test_Refill_Success() (gas: 48170) +RateLimiter_consume:test_TokenMaxCapacityExceeded_Revert() (gas: 22450) +RateLimiter_consume:test_TokenRateLimitReached_Revert() (gas: 31057) +RateLimiter_currentTokenBucketState:test_CurrentTokenBucketState_Success() (gas: 49681) +RateLimiter_currentTokenBucketState:test_Refill_Success() (gas: 63750) +RateLimiter_setTokenBucketConfig:test_SetRateLimiterConfig_Success() (gas: 48188) +RegistryModuleOwnerCustom_constructor:test_constructor_Revert() (gas: 36711) +RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Revert() (gas: 22517) +RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Success() (gas: 137341) +RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Revert() (gas: 22359) +RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Success() (gas: 137182) +Router_applyRampUpdates:test_OffRampMismatch_Revert() (gas: 93496) +Router_applyRampUpdates:test_OffRampUpdatesWithRouting() (gas: 12348645) +Router_applyRampUpdates:test_OnRampDisable() (gas: 65150) +Router_applyRampUpdates:test_OnlyOwner_Revert() (gas: 13275) +Router_ccipSend:test_CCIPSendLinkFeeNoTokenSuccess_gas() (gas: 133309) +Router_ccipSend:test_CCIPSendLinkFeeOneTokenSuccess_gas() (gas: 240131) +Router_ccipSend:test_CCIPSendNativeFeeNoTokenSuccess_gas() (gas: 147975) +Router_ccipSend:test_CCIPSendNativeFeeOneTokenSuccess_gas() (gas: 254777) +Router_ccipSend:test_FeeTokenAmountTooLow_Revert() (gas: 78020) +Router_ccipSend:test_InvalidMsgValue() (gas: 35871) +Router_ccipSend:test_NativeFeeTokenInsufficientValue() (gas: 80832) +Router_ccipSend:test_NativeFeeTokenOverpay_Success() (gas: 203317) +Router_ccipSend:test_NativeFeeTokenZeroValue() (gas: 65179) +Router_ccipSend:test_NativeFeeToken_Success() (gas: 201073) +Router_ccipSend:test_NonLinkFeeToken_Success() (gas: 266824) +Router_ccipSend:test_UnsupportedDestinationChain_Revert() (gas: 28660) +Router_ccipSend:test_WhenNotHealthy_Revert() (gas: 48532) +Router_ccipSend:test_WrappedNativeFeeToken_Success() (gas: 205616) +Router_ccipSend:test_ZeroFeeAndGasPrice_Success() (gas: 282163) +Router_constructor:test_Constructor_Success() (gas: 14515) +Router_getArmProxy:test_getArmProxy() (gas: 11328) +Router_getFee:test_GetFeeSupportedChain_Success() (gas: 55552) +Router_getFee:test_UnsupportedDestinationChain_Revert() (gas: 20945) +Router_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 11166) +Router_recoverTokens:test_RecoverTokensInvalidRecipient_Revert() (gas: 12807) +Router_recoverTokens:test_RecoverTokensNoFunds_Revert() (gas: 21382) +Router_recoverTokens:test_RecoverTokensNonOwner_Revert() (gas: 12776) +Router_recoverTokens:test_RecoverTokensValueReceiver_Revert() (gas: 603372) +Router_recoverTokens:test_RecoverTokens_Success() (gas: 59811) +Router_routeMessage:test_AutoExec_Success() (gas: 52571) +Router_routeMessage:test_ExecutionEvent_Success() (gas: 183071) +Router_routeMessage:test_ManualExec_Success() (gas: 41748) +Router_routeMessage:test_OnlyOffRamp_Revert() (gas: 28290) +Router_routeMessage:test_WhenNotHealthy_Revert() (gas: 47722) +Router_setWrappedNative:test_OnlyOwner_Revert() (gas: 11766) +SelfFundedPingPong_ccipReceive:test_FundingIfNotANop_Revert() (gas: 62294) +SelfFundedPingPong_ccipReceive:test_Funding_Success() (gas: 560135) +SelfFundedPingPong_setCountIncrBeforeFunding:test_setCountIncrBeforeFunding() (gas: 22195) +TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_OnlyPendingAdministrator_Revert() (gas: 58611) +TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_Success() (gas: 50532) +TokenAdminRegistry_addRegistryModule:test_addRegistryModule_OnlyOwner_Revert() (gas: 14159) +TokenAdminRegistry_addRegistryModule:test_addRegistryModule_Success() (gas: 70293) +TokenAdminRegistry_getAllConfiguredTokens:test_getAllConfiguredTokens_outOfBounds_Success() (gas: 12680) +TokenAdminRegistry_getPool:test_getPool_Success() (gas: 18649) +TokenAdminRegistry_getPools:test_getPools_Success() (gas: 48161) +TokenAdminRegistry_isAdministrator:test_isAdministrator_Success() (gas: 113861) +TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_AlreadyRegistered_Revert() (gas: 109470) +TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_OnlyRegistryModule_Revert() (gas: 17535) +TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_ZeroAddress_Revert() (gas: 16768) +TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_module_Success() (gas: 123148) +TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_owner_Success() (gas: 114227) +TokenAdminRegistry_proposeAdministrator:test_proposeAdministrator_reRegisterWhileUnclaimed_Success() (gas: 123889) +TokenAdminRegistry_removeRegistryModule:test_removeRegistryModule_OnlyOwner_Revert() (gas: 14115) +TokenAdminRegistry_removeRegistryModule:test_removeRegistryModule_Success() (gas: 58226) +TokenAdminRegistry_setPool:test_setPool_InvalidTokenPoolToken_Revert() (gas: 21992) +TokenAdminRegistry_setPool:test_setPool_OnlyAdministrator_Revert() (gas: 20133) +TokenAdminRegistry_setPool:test_setPool_Success() (gas: 41047) +TokenAdminRegistry_setPool:test_setPool_ZeroAddressRemovesPool_Success() (gas: 35795) +TokenAdminRegistry_transferAdminRole:test_transferAdminRole_OnlyAdministrator_Revert() (gas: 20198) +TokenAdminRegistry_transferAdminRole:test_transferAdminRole_Success() (gas: 54721) +TokenPoolAndProxy:test_lockOrBurn_burnMint_Success() (gas: 8984851) +TokenPoolAndProxy:test_lockOrBurn_burnWithFromMint_Success() (gas: 9017404) +TokenPoolAndProxy:test_lockOrBurn_lockRelease_Success() (gas: 9314221) +TokenPoolAndProxy:test_setPreviousPool_Success() (gas: 5168919) +TokenPoolAndProxyMigration:test_tokenPoolMigration_Success_1_2() (gas: 9975724) +TokenPoolAndProxyMigration:test_tokenPoolMigration_Success_1_4() (gas: 10174033) +TokenPoolWithAllowList_applyAllowListUpdates:test_AllowListNotEnabled_Revert() (gas: 3418174) +TokenPoolWithAllowList_applyAllowListUpdates:test_OnlyOwner_Revert() (gas: 13392) +TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowListSkipsZero_Success() (gas: 26742) +TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowList_Success() (gas: 189536) +TokenPoolWithAllowList_getAllowList:test_GetAllowList_Success() (gas: 25742) +TokenPoolWithAllowList_getAllowListEnabled:test_GetAllowListEnabled_Success() (gas: 8788) +TokenPoolWithAllowList_setRouter:test_SetRouter_Success() (gas: 28100) +TokenPool_applyChainUpdates:test_applyChainUpdates_DisabledNonZeroRateLimit_Revert() (gas: 282666) +TokenPool_applyChainUpdates:test_applyChainUpdates_InvalidRateLimitRate_Revert() (gas: 580050) +TokenPool_applyChainUpdates:test_applyChainUpdates_NonExistentChain_Revert() (gas: 22750) +TokenPool_applyChainUpdates:test_applyChainUpdates_OnlyCallableByOwner_Revert() (gas: 12333) +TokenPool_applyChainUpdates:test_applyChainUpdates_Success() (gas: 534354) +TokenPool_applyChainUpdates:test_applyChainUpdates_ZeroAddressNotAllowed_Revert() (gas: 165350) +TokenPool_constructor:test_ZeroAddressNotAllowed_Revert() (gas: 74247) +TokenPool_constructor:test_immutableFields_Success() (gas: 23251) +TokenPool_getRemotePool:test_getRemotePool_Success() (gas: 285082) +TokenPool_onlyOffRamp:test_CallerIsNotARampOnRouter_Revert() (gas: 287212) +TokenPool_onlyOffRamp:test_ChainNotAllowed_Revert() (gas: 305616) +TokenPool_onlyOffRamp:test_onlyOffRamp_Success() (gas: 361142) +TokenPool_onlyOnRamp:test_CallerIsNotARampOnRouter_Revert() (gas: 286591) +TokenPool_onlyOnRamp:test_ChainNotAllowed_Revert() (gas: 269389) +TokenPool_onlyOnRamp:test_onlyOnRamp_Success() (gas: 315814) +TokenPool_setChainRateLimiterConfig:test_NonExistentChain_Revert() (gas: 19578) +TokenPool_setChainRateLimiterConfig:test_OnlyOwnerOrRateLimitAdmin_Revert() (gas: 17926) +TokenPool_setRemotePool:test_setRemotePool_NonExistentChain_Reverts() (gas: 17612) +TokenPool_setRemotePool:test_setRemotePool_OnlyOwner_Reverts() (gas: 15337) +TokenPool_setRemotePool:test_setRemotePool_Success() (gas: 293732) +TokenProxy_ccipSend:test_CcipSendGasShouldBeZero_Revert() (gas: 20582) +TokenProxy_ccipSend:test_CcipSendInsufficientAllowance_Revert() (gas: 158368) +TokenProxy_ccipSend:test_CcipSendInvalidToken_Revert() (gas: 18599) +TokenProxy_ccipSend:test_CcipSendNative_Success() (gas: 288755) +TokenProxy_ccipSend:test_CcipSendNoDataAllowed_Revert() (gas: 19019) +TokenProxy_ccipSend:test_CcipSend_Success() (gas: 310931) +TokenProxy_constructor:test_Constructor() (gas: 15309) +TokenProxy_getFee:test_GetFeeGasShouldBeZero_Revert() (gas: 20176) +TokenProxy_getFee:test_GetFeeInvalidToken_Revert() (gas: 14693) +TokenProxy_getFee:test_GetFeeNoDataAllowed_Revert() (gas: 18513) +TokenProxy_getFee:test_GetFee_Success() (gas: 117905) +USDCTokenPool__validateMessage:test_ValidateInvalidMessage_Revert() (gas: 37856) +USDCTokenPool_lockOrBurn:test_CallerIsNotARampOnRouter_Revert() (gas: 40114) +USDCTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Revert() (gas: 34440) +USDCTokenPool_lockOrBurn:test_LockOrBurn_Success() (gas: 147394) +USDCTokenPool_lockOrBurn:test_UnknownDomain_Revert() (gas: 546719) +USDCTokenPool_releaseOrMint:test_ReleaseOrMintRealTx_Success() (gas: 326433) +USDCTokenPool_releaseOrMint:test_TokenMaxCapacityExceeded_Revert() (gas: 59047) +USDCTokenPool_releaseOrMint:test_UnlockingUSDCFailed_Revert() (gas: 112325) +USDCTokenPool_setDomains:test_InvalidDomain_Revert() (gas: 74539) +USDCTokenPool_setDomains:test_OnlyOwner_Revert() (gas: 12298) +USDCTokenPool_supportsInterface:test_SupportsInterface_Success() (gas: 11479) \ No newline at end of file diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index a987ac64c3..38325625f4 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -282,8 +282,6 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, /// @notice Sets a new config as the secondary config. Does not influence the primary config. /// @param pluginKey The key of the plugin to set the config for. - /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. - /// This is done to prevent accidental overwrites. /// @return newConfigDigest The digest of the new config. function _setSecondary( bytes32 pluginKey, diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol index 611dd50330..a8cb826585 100644 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ b/contracts/src/v0.8/ccip/capability/HomeBase.sol @@ -23,7 +23,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. - mapping(bytes32 pluginKey => StoredConfig[MAX_CONCURRENT_CONFIGS]) private s_configs; + StoredConfig[MAX_CONCURRENT_CONFIGS] private s_configs; /// @notice The total number of configs ever set, used for generating the version of the configs. uint32 private s_configCount = 0; @@ -60,74 +60,56 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { /// @notice Returns the current primary and secondary config digests. /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. - /// @param pluginKey The key of the plugin to get the config digests for. /// @return primaryConfigDigest The digest of the primary config. /// @return secondaryConfigDigest The digest of the secondary config. - function getConfigDigests( - bytes32 pluginKey - ) external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { - return ( - s_configs[pluginKey][s_primaryConfigIndex].configDigest, - s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest - ); + function getConfigDigests() external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + return (s_configs[s_primaryConfigIndex].configDigest, s_configs[s_primaryConfigIndex ^ 1].configDigest); } /// @notice Returns the primary config digest for for a given key. - /// @param pluginKey The key of the plugin to get the config digests for. - function getPrimaryDigest(bytes32 pluginKey) public view returns (bytes32) { - return s_configs[pluginKey][s_primaryConfigIndex].configDigest; + function getPrimaryDigest() public view returns (bytes32) { + return s_configs[s_primaryConfigIndex].configDigest; } /// @notice Returns the secondary config digest for for a given key. - /// @param pluginKey The key of the plugin to get the config digests for. - function getSecondaryDigest(bytes32 pluginKey) public view returns (bytes32) { - return s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest; + function getSecondaryDigest() public view returns (bytes32) { + return s_configs[s_primaryConfigIndex ^ 1].configDigest; } /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero /// digest. This is done to prevent exposing old config state that is invalid. - /// @param pluginKey The key of the plugin to get the config for. /// @param configDigest The digest of the config to fetch. - function _getStoredConfig( - bytes32 pluginKey, - bytes32 configDigest - ) internal view returns (StoredConfig memory storedConfig, bool ok) { + function _getStoredConfig(bytes32 configDigest) internal view returns (StoredConfig memory storedConfig, bool ok) { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old // config state that is invalid. - if (s_configs[pluginKey][i].configDigest == configDigest && configDigest != ZERO_DIGEST) { - return (s_configs[pluginKey][i], true); + if (s_configs[i].configDigest == configDigest && configDigest != ZERO_DIGEST) { + return (s_configs[i], true); } } return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } /// @notice Returns the primary stored config for a given key. - /// @param pluginKey The key of the plugin to get the config for. /// @return primaryConfig The primary stored config. /// @return ok True if the config was found, false otherwise. - function _getPrimaryStoredConfig( - bytes32 pluginKey - ) internal view returns (StoredConfig memory primaryConfig, bool ok) { - if (s_configs[pluginKey][s_primaryConfigIndex].configDigest == ZERO_DIGEST) { + function _getPrimaryStoredConfig() internal view returns (StoredConfig memory primaryConfig, bool ok) { + if (s_configs[s_primaryConfigIndex].configDigest == ZERO_DIGEST) { return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } - return (s_configs[pluginKey][s_primaryConfigIndex], true); + return (s_configs[s_primaryConfigIndex], true); } /// @notice Returns the secondary stored config for a given key. - /// @param pluginKey The key of the plugin to get the config for. /// @return secondaryConfig The secondary stored config. /// @return ok True if the config was found, false otherwise. - function _getSecondaryStoredConfig( - bytes32 pluginKey - ) internal view returns (StoredConfig memory secondaryConfig, bool ok) { - if (s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { + function _getSecondaryStoredConfig() internal view returns (StoredConfig memory secondaryConfig, bool ok) { + if (s_configs[s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); } - return (s_configs[pluginKey][s_primaryConfigIndex ^ 1], true); + return (s_configs[s_primaryConfigIndex ^ 1], true); } // ================================================================ @@ -135,14 +117,12 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { // ================================================================ /// @notice Sets a new config as the secondary config. Does not influence the primary config. - /// @param pluginKey The key of the plugin to set the config for. /// @param encodedStaticConfig The static part of the config. /// @param encodedDynamicConfig The dynamic part of the config. /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. /// This is done to prevent accidental overwrites. /// @return newConfigDigest The digest of the new config. function setSecondary( - bytes32 pluginKey, bytes calldata encodedStaticConfig, bytes calldata encodedDynamicConfig, bytes32 digestToOverwrite @@ -150,7 +130,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { _validateCaller(); _validateStaticAndDynamicConfig(encodedStaticConfig, encodedDynamicConfig); - bytes32 existingDigest = getSecondaryDigest(pluginKey); + bytes32 existingDigest = getSecondaryDigest(); if (existingDigest != digestToOverwrite) { revert ConfigDigestMismatch(existingDigest, digestToOverwrite); @@ -162,7 +142,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { } uint32 newVersion = ++s_configCount; - newConfigDigest = _calculateConfigDigest(pluginKey, encodedStaticConfig, newVersion); + newConfigDigest = _calculateConfigDigest(encodedStaticConfig, newVersion); StoredConfig memory newConfig = StoredConfig({ configDigest: newConfigDigest, @@ -171,7 +151,7 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { dynamicConfig: encodedDynamicConfig }); - s_configs[pluginKey][s_primaryConfigIndex ^ 1] = newConfig; + s_configs[s_primaryConfigIndex ^ 1] = newConfig; emit ConfigSet(newConfig.configDigest, newConfig); @@ -179,45 +159,39 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { } /// @notice Revokes a specific config by digest. - /// @param pluginKey The key of the plugin to revoke the config for. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(bytes32 pluginKey, bytes32 configDigest) external { + function revokeSecondary(bytes32 configDigest) external { _validateCaller(); uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[pluginKey][secondaryConfigIndex].configDigest != configDigest) { - revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, configDigest); + if (s_configs[secondaryConfigIndex].configDigest != configDigest) { + revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, configDigest); } emit ConfigRevoked(configDigest); // Delete only the digest, as that's what's used to determine if a config is active. This means the actual // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in // the future. - delete s_configs[pluginKey][secondaryConfigIndex].configDigest; + delete s_configs[secondaryConfigIndex].configDigest; } /// @notice Promotes the secondary config to the primary config and revokes the primary config. - /// @param pluginKey The key of the plugin to promote the config for. /// @param digestToPromote The digest of the config to promote. /// @param digestToRevoke The digest of the config to revoke. - function promoteSecondaryAndRevokePrimary( - bytes32 pluginKey, - bytes32 digestToPromote, - bytes32 digestToRevoke - ) external { + function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external { _validateCaller(); uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[pluginKey][secondaryConfigIndex].configDigest != digestToPromote) { - revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, digestToPromote); + if (s_configs[secondaryConfigIndex].configDigest != digestToPromote) { + revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToPromote); } uint256 primaryConfigIndex = s_primaryConfigIndex; - if (s_configs[pluginKey][primaryConfigIndex].configDigest != digestToRevoke) { - revert ConfigDigestMismatch(s_configs[pluginKey][primaryConfigIndex].configDigest, digestToRevoke); + if (s_configs[primaryConfigIndex].configDigest != digestToRevoke) { + revert ConfigDigestMismatch(s_configs[primaryConfigIndex].configDigest, digestToRevoke); } - delete s_configs[pluginKey][primaryConfigIndex].configDigest; + delete s_configs[primaryConfigIndex].configDigest; s_primaryConfigIndex ^= 1; if (digestToRevoke != ZERO_DIGEST) { @@ -227,19 +201,18 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { } /// @notice Sets the dynamic config for a specific config. - /// @param pluginKey The key of the plugin to set the dynamic config for. /// @param newDynamicConfig The new dynamic config. /// @param currentDigest The digest of the config to update. /// @dev This does not update the config digest as only the static config is part of the digest. - function setDynamicConfig(bytes32 pluginKey, bytes calldata newDynamicConfig, bytes32 currentDigest) external { + function setDynamicConfig(bytes calldata newDynamicConfig, bytes32 currentDigest) external { _validateCaller(); for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - if (s_configs[pluginKey][i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { - _validateDynamicConfig(s_configs[pluginKey][i].staticConfig, newDynamicConfig); + if (s_configs[i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { + _validateDynamicConfig(s_configs[i].staticConfig, newDynamicConfig); // Since the static config doesn't change we don't have to update the digest or version. - s_configs[pluginKey][i].dynamicConfig = newDynamicConfig; + s_configs[i].dynamicConfig = newDynamicConfig; emit DynamicConfigSet(currentDigest, newDynamicConfig); return; @@ -250,22 +223,15 @@ abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { } /// @notice Calculates the config digest for a given plugin key, static config, and version. - /// @param pluginKey The key of the plugin to calculate the digest for. /// @param staticConfig The static part of the config. /// @param version The version of the config. /// @return The calculated config digest. - function _calculateConfigDigest( - bytes32 pluginKey, - bytes memory staticConfig, - uint32 version - ) internal view returns (bytes32) { + function _calculateConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { return bytes32( (_getConfigDigestPrefix() & PREFIX_MASK) | ( uint256( - keccak256( - bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), pluginKey, version), staticConfig) - ) + keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), version), staticConfig)) ) & ~PREFIX_MASK ) ); diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index dda5a92fab..e447814467 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -53,11 +53,8 @@ contract RMNHome is HomeBase { /// @param configDigest The digest of the config to fetch. /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. - function getConfig( - bytes32 pluginKey, - bytes32 configDigest - ) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(pluginKey, configDigest); + function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { + (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(configDigest); if (configOK) { return ( VersionedConfig({ @@ -73,10 +70,12 @@ contract RMNHome is HomeBase { return (versionedConfig, false); } - function getAllConfigs( - bytes32 pluginKey - ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { - (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(pluginKey); + function getAllConfigs() + external + view + returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) + { + (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(); if (primaryOk) { primaryConfig = VersionedConfig({ @@ -87,7 +86,7 @@ contract RMNHome is HomeBase { }); } - (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(pluginKey); + (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(); if (secondaryOk) { secondaryConfig = VersionedConfig({ diff --git a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol index 9caab7baab..f33e693062 100644 --- a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol @@ -1,199 +1,199 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {HomeBase} from "../../capability/HomeBase.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {HomeBaseHelper} from "../helpers/HomeBaseHelper.sol"; -import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; - -contract HomeBaseTest is Test { - bytes32 internal constant DON_ID = bytes32(uint256(0x87654321eabc)); - - bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); - - HomeBaseHelper internal s_homeBase; - - function setUp() public virtual { - s_homeBase = new HomeBaseHelper(); - } - - uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 - uint256 public constant PREFIX = 0x0c0c << (256 - 16); - - function _getConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { - return bytes32( - (PREFIX & PREFIX_MASK) - | ( - uint256( - keccak256( - bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(s_homeBase), DON_ID, version), staticConfig) - ) - ) & ~PREFIX_MASK - ) - ); - } - - function _getStaticConfig() internal pure returns (bytes memory) { - return abi.encode("staticConfig"); - } - - function _getDynamicConfig() internal pure returns (bytes memory) { - return abi.encode("dynamicConfig"); - } -} - -contract RMNHome_setSecondary is HomeBaseTest { - function test_setSecondary_success() public { - HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ - configDigest: ZERO_DIGEST, - version: 1, - staticConfig: _getStaticConfig(), - dynamicConfig: _getDynamicConfig() - }); - - encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); - - vm.expectEmit(); - emit HomeBase.ConfigSet(encodedConfig.configDigest, encodedConfig); - - s_homeBase.setSecondary(DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); - - (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getSecondaryStoredConfig(DON_ID); - assertTrue(ok); - assertEq(storedConfig.version, encodedConfig.version); - assertEq(storedConfig.configDigest, encodedConfig.configDigest); - assertEq(storedConfig.staticConfig, encodedConfig.staticConfig); - assertEq(storedConfig.dynamicConfig, encodedConfig.dynamicConfig); - } - - function test_setSecondary_InvalidCaller_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); - s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); - } -} - -contract RMNHome_setDynamicConfig is HomeBaseTest { - function setUp() public override { - super.setUp(); - s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); - } - - function test_setDynamicConfig_success() public { - (bytes32 priorPrimaryDigest, bytes32 secondaryConfigDigest) = s_homeBase.getConfigDigests(DON_ID); - - bytes memory newDynamicConfig = abi.encode("newDynamicConfig"); - - vm.expectEmit(); - emit HomeBase.DynamicConfigSet(secondaryConfigDigest, newDynamicConfig); - - s_homeBase.setDynamicConfig(DON_ID, newDynamicConfig, secondaryConfigDigest); - - (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getStoredConfig(DON_ID, secondaryConfigDigest); - assertTrue(ok); - assertEq(storedConfig.dynamicConfig, newDynamicConfig); - - // Asser the digests don't change when updating the dynamic config - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID); - assertEq(primaryDigest, priorPrimaryDigest); - assertEq(secondaryDigest, secondaryConfigDigest); - } - - function test_setDynamicConfig_InvalidCaller_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); - s_homeBase.setDynamicConfig(DON_ID, _getDynamicConfig(), keccak256("configDigest")); - } -} - -contract RMNHome_revokeSecondary is HomeBaseTest { - // Sets two configs - function setUp() public override { - super.setUp(); - bytes32 digest = s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); - s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, digest, ZERO_DIGEST); - s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); - } - - function test_revokeSecondary_success() public { - (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID); - - vm.expectEmit(); - emit HomeBase.ConfigRevoked(priorSecondaryDigest); - - s_homeBase.revokeSecondary(DON_ID, priorSecondaryDigest); - - (HomeBase.StoredConfig memory storedVersionedConfig, bool ok) = - s_homeBase.getStoredConfig(DON_ID, priorSecondaryDigest); - assertFalse(ok); - // Ensure no old data is returned, even though it's still in storage - assertEq(storedVersionedConfig.version, 0); - assertEq(storedVersionedConfig.staticConfig.length, 0); - assertEq(storedVersionedConfig.dynamicConfig.length, 0); - - // Asser the primary digest is unaffected but the secondary digest is set to zero - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID); - assertEq(primaryDigest, priorPrimaryDigest); - assertEq(secondaryDigest, ZERO_DIGEST); - assertTrue(secondaryDigest != priorSecondaryDigest); - } - - function test_revokeSecondary_ConfigDigestMismatch_reverts() public { - (, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID); - - bytes32 wrongDigest = keccak256("wrong_digest"); - vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); - s_homeBase.revokeSecondary(DON_ID, wrongDigest); - } - - function test_revokeSecondary_InvalidCaller_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); - s_homeBase.revokeSecondary(DON_ID, keccak256("configDigest")); - } -} - -contract RMNHome_promoteSecondaryAndRevokePrimary is HomeBaseTest { - function test_promoteSecondaryAndRevokePrimary_success() public {} - - function test_promoteSecondaryAndRevokePrimary_InvalidCaller_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); - s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, keccak256("toPromote"), keccak256("ToRevoke")); - } -} - -//contract RMNHome_beforeCapabilityConfigSet is HomeBaseTest { -// function test_beforeCapabilityConfigSet_success() public { -// vm.startPrank(CAPABILITIES_REGISTRY); +//// SPDX-License-Identifier: BUSL-1.1 +//pragma solidity 0.8.24; // +//import {HomeBase} from "../../capability/HomeBase.sol"; +//import {Internal} from "../../libraries/Internal.sol"; +//import {HomeBaseHelper} from "../helpers/HomeBaseHelper.sol"; +//import {Test} from "forge-std/Test.sol"; +//import {Vm} from "forge-std/Vm.sol"; +// +//contract HomeBaseTest is Test { +// bytes32 internal constant DON_ID = bytes32(uint256(0x87654321eabc)); +// +// bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); +// +// HomeBaseHelper internal s_homeBase; +// +// function setUp() public virtual { +// s_homeBase = new HomeBaseHelper(); +// } +// +// uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 +// uint256 public constant PREFIX = 0x0c0c << (256 - 16); +// +// function _getConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { +// return bytes32( +// (PREFIX & PREFIX_MASK) +// | ( +// uint256( +// keccak256( +// bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(s_homeBase), DON_ID, version), staticConfig) +// ) +// ) & ~PREFIX_MASK +// ) +// ); +// } +// +// function _getStaticConfig() internal pure returns (bytes memory) { +// return abi.encode("staticConfig"); +// } +// +// function _getDynamicConfig() internal pure returns (bytes memory) { +// return abi.encode("dynamicConfig"); +// } +//} +// +//contract RMNHome_setSecondary is HomeBaseTest { +// function test_setSecondary_success() public { // HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ // configDigest: ZERO_DIGEST, // version: 1, // staticConfig: _getStaticConfig(), // dynamicConfig: _getDynamicConfig() // }); +// // encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); // -// bytes memory callPayload = abi.encodeCall( -// HomeBase.setSecondary, (DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) -// ); +// vm.expectEmit(); +// emit HomeBase.ConfigSet(encodedConfig.configDigest, encodedConfig); +// +// s_homeBase.setSecondary(DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); +// +// (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getSecondaryStoredConfig(DON_ID); +// assertTrue(ok); +// assertEq(storedConfig.version, encodedConfig.version); +// assertEq(storedConfig.configDigest, encodedConfig.configDigest); +// assertEq(storedConfig.staticConfig, encodedConfig.staticConfig); +// assertEq(storedConfig.dynamicConfig, encodedConfig.dynamicConfig); +// } +// +// function test_setSecondary_InvalidCaller_reverts() public { +// vm.startPrank(address(0)); +// +// vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); +// s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); +// } +//} +// +//contract RMNHome_setDynamicConfig is HomeBaseTest { +// function setUp() public override { +// super.setUp(); +// s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); +// } +// +// function test_setDynamicConfig_success() public { +// (bytes32 priorPrimaryDigest, bytes32 secondaryConfigDigest) = s_homeBase.getConfigDigests(DON_ID); +// +// bytes memory newDynamicConfig = abi.encode("newDynamicConfig"); +// +// vm.expectEmit(); +// emit HomeBase.DynamicConfigSet(secondaryConfigDigest, newDynamicConfig); +// +// s_homeBase.setDynamicConfig(DON_ID, newDynamicConfig, secondaryConfigDigest); +// +// (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getStoredConfig(DON_ID, secondaryConfigDigest); +// assertTrue(ok); +// assertEq(storedConfig.dynamicConfig, newDynamicConfig); +// +// // Asser the digests don't change when updating the dynamic config +// (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID); +// assertEq(primaryDigest, priorPrimaryDigest); +// assertEq(secondaryDigest, secondaryConfigDigest); +// } +// +// function test_setDynamicConfig_InvalidCaller_reverts() public { +// vm.startPrank(address(0)); +// +// vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); +// s_homeBase.setDynamicConfig(DON_ID, _getDynamicConfig(), keccak256("configDigest")); +// } +//} +// +//contract RMNHome_revokeSecondary is HomeBaseTest { +// // Sets two configs +// function setUp() public override { +// super.setUp(); +// bytes32 digest = s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); +// s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, digest, ZERO_DIGEST); +// s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); +// } +// +// function test_revokeSecondary_success() public { +// (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID); // // vm.expectEmit(); -// emit HomeBase.ConfigSet(encodedConfig); +// emit HomeBase.ConfigRevoked(priorSecondaryDigest); +// +// s_homeBase.revokeSecondary(DON_ID, priorSecondaryDigest); +// +// (HomeBase.StoredConfig memory storedVersionedConfig, bool ok) = +// s_homeBase.getStoredConfig(DON_ID, priorSecondaryDigest); +// assertFalse(ok); +// // Ensure no old data is returned, even though it's still in storage +// assertEq(storedVersionedConfig.version, 0); +// assertEq(storedVersionedConfig.staticConfig.length, 0); +// assertEq(storedVersionedConfig.dynamicConfig.length, 0); +// +// // Asser the primary digest is unaffected but the secondary digest is set to zero +// (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID); +// assertEq(primaryDigest, priorPrimaryDigest); +// assertEq(secondaryDigest, ZERO_DIGEST); +// assertTrue(secondaryDigest != priorSecondaryDigest); +// } +// +// function test_revokeSecondary_ConfigDigestMismatch_reverts() public { +// (, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID); // -// s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, 0); +// bytes32 wrongDigest = keccak256("wrong_digest"); +// vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); +// s_homeBase.revokeSecondary(DON_ID, wrongDigest); // } // -// function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { +// function test_revokeSecondary_InvalidCaller_reverts() public { // vm.startPrank(address(0)); // -// vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); -// s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, 0); +// vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); +// s_homeBase.revokeSecondary(DON_ID, keccak256("configDigest")); // } //} +// +//contract RMNHome_promoteSecondaryAndRevokePrimary is HomeBaseTest { +// function test_promoteSecondaryAndRevokePrimary_success() public {} +// +// function test_promoteSecondaryAndRevokePrimary_InvalidCaller_reverts() public { +// vm.startPrank(address(0)); +// +// vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); +// s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, keccak256("toPromote"), keccak256("ToRevoke")); +// } +//} +// +////contract RMNHome_beforeCapabilityConfigSet is HomeBaseTest { +//// function test_beforeCapabilityConfigSet_success() public { +//// vm.startPrank(CAPABILITIES_REGISTRY); +//// +//// HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ +//// configDigest: ZERO_DIGEST, +//// version: 1, +//// staticConfig: _getStaticConfig(), +//// dynamicConfig: _getDynamicConfig() +//// }); +//// encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); +//// +//// bytes memory callPayload = abi.encodeCall( +//// HomeBase.setSecondary, (DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) +//// ); +//// +//// vm.expectEmit(); +//// emit HomeBase.ConfigSet(encodedConfig); +//// +//// s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, 0); +//// } +//// +//// function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { +//// vm.startPrank(address(0)); +//// +//// vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); +//// s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, 0); +//// } +////} diff --git a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol index 5ff5217a4d..dc33a0412b 100644 --- a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol @@ -49,11 +49,7 @@ contract RMNHomeTest is Test { (PREFIX & PREFIX_MASK) | ( uint256( - keccak256( - bytes.concat( - abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), RMN_DON_ID, version), staticConfig - ) - ) + keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(s_rmnHome), version), staticConfig)) ) & ~PREFIX_MASK ) ); @@ -82,10 +78,9 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.expectEmit(); emit HomeBase.ConfigSet(encodedConfig.configDigest, encodedConfig); - s_rmnHome.setSecondary(RMN_DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setSecondary(encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = - s_rmnHome.getConfig(RMN_DON_ID, versionedConfig.configDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(versionedConfig.configDigest); assertTrue(ok); assertEq(storedVersionedConfig.version, versionedConfig.version); RMNHome.StaticConfig memory storedStaticConfig = storedVersionedConfig.staticConfig; @@ -114,7 +109,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicatePeerId_reverts() public { @@ -122,7 +117,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { @@ -130,7 +125,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_DuplicateSourceChain_reverts() public { @@ -138,7 +133,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { @@ -146,7 +141,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_MinObserversTooHigh_reverts() public { @@ -154,7 +149,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setSecondary_OnlyOwner_reverts() public { @@ -163,7 +158,7 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } } @@ -171,25 +166,24 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_success() public { - (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(RMN_DON_ID); + (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(); Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers--; - (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); + (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(); bytes memory encodedConfig = abi.encode(config.dynamicConfig); vm.expectEmit(); emit HomeBase.DynamicConfigSet(secondaryConfigDigest, encodedConfig); - s_rmnHome.setDynamicConfig(RMN_DON_ID, encodedConfig, secondaryConfigDigest); + s_rmnHome.setDynamicConfig(encodedConfig, secondaryConfigDigest); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = - s_rmnHome.getConfig(RMN_DON_ID, secondaryConfigDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); assertTrue(ok); assertEq( storedVersionedConfig.dynamicConfig.sourceChains[0].minObservers, @@ -197,7 +191,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { ); // Asser the digests don't change when updating the dynamic config - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, secondaryConfigDigest); } @@ -208,18 +202,18 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setDynamicConfig(abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_setDynamicConfig_DigestNotFound_reverts() public { // Zero always reverts vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); + s_rmnHome.setDynamicConfig(abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); // Non-existent digest reverts bytes32 nonExistentDigest = keccak256("nonExistentDigest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, nonExistentDigest)); - s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest); + s_rmnHome.setDynamicConfig(abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest); } function test_setDynamicConfig_OnlyOwner_reverts() public { @@ -228,7 +222,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setDynamicConfig(RMN_DON_ID, abi.encode(config.dynamicConfig), keccak256("configDigest")); + s_rmnHome.setDynamicConfig(abi.encode(config.dynamicConfig), keccak256("configDigest")); } } @@ -238,23 +232,22 @@ contract RMNHome_revokeSecondary is RMNHomeTest { super.setUp(); Config memory config = _getBaseConfig(); bytes32 digest = - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); - s_rmnHome.promoteSecondaryAndRevokePrimary(RMN_DON_ID, digest, ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.promoteSecondaryAndRevokePrimary(digest, ZERO_DIGEST); config.dynamicConfig.sourceChains[0].minObservers--; - s_rmnHome.setSecondary(RMN_DON_ID, abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); } function test_revokeSecondary_success() public { - (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); + (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); vm.expectEmit(); emit HomeBase.ConfigRevoked(priorSecondaryDigest); - s_rmnHome.revokeSecondary(RMN_DON_ID, priorSecondaryDigest); + s_rmnHome.revokeSecondary(priorSecondaryDigest); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = - s_rmnHome.getConfig(RMN_DON_ID, priorSecondaryDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorSecondaryDigest); assertFalse(ok); // Ensure no old data is returned, even though it's still in storage assertEq(storedVersionedConfig.version, 0); @@ -262,25 +255,25 @@ contract RMNHome_revokeSecondary is RMNHomeTest { assertEq(storedVersionedConfig.dynamicConfig.sourceChains.length, 0); // Asser the primary digest is unaffected but the secondary digest is set to zero - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); + (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); assertEq(primaryDigest, priorPrimaryDigest); assertEq(secondaryDigest, ZERO_DIGEST); assertTrue(secondaryDigest != priorSecondaryDigest); } function test_revokeSecondary_ConfigDigestMismatch_reverts() public { - (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(RMN_DON_ID); + (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); bytes32 wrongDigest = keccak256("wrong_digest"); vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); - s_rmnHome.revokeSecondary(RMN_DON_ID, wrongDigest); + s_rmnHome.revokeSecondary(wrongDigest); } function test_revokeSecondary_OnlyOwner_reverts() public { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.revokeSecondary(RMN_DON_ID, keccak256("configDigest")); + s_rmnHome.revokeSecondary(keccak256("configDigest")); } } @@ -291,6 +284,6 @@ contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.promoteSecondaryAndRevokePrimary(RMN_DON_ID, keccak256("toPromote"), keccak256("ToRevoke")); + s_rmnHome.promoteSecondaryAndRevokePrimary(keccak256("toPromote"), keccak256("ToRevoke")); } } diff --git a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol index 0ff0df1ae4..9600b02d27 100644 --- a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol @@ -24,26 +24,19 @@ contract HomeBaseHelper is HomeBase { } } - function getStoredConfig( - bytes32 pluginKey, - bytes32 configDigest - ) external view returns (StoredConfig memory, bool ok) { - return _getStoredConfig(pluginKey, configDigest); + function getStoredConfig(bytes32 configDigest) external view returns (StoredConfig memory, bool ok) { + return _getStoredConfig(configDigest); } - function getPrimaryStoredConfig(bytes32 pluginKey) external view returns (StoredConfig memory, bool ok) { - return _getPrimaryStoredConfig(pluginKey); + function getPrimaryStoredConfig() external view returns (StoredConfig memory, bool ok) { + return _getPrimaryStoredConfig(); } - function getSecondaryStoredConfig(bytes32 pluginKey) external view returns (StoredConfig memory, bool ok) { - return _getSecondaryStoredConfig(pluginKey); + function getSecondaryStoredConfig() external view returns (StoredConfig memory, bool ok) { + return _getSecondaryStoredConfig(); } - function calculateConfigDigest( - bytes32 pluginKey, - bytes memory staticConfig, - uint32 version - ) external view returns (bytes32) { - return _calculateConfigDigest(pluginKey, staticConfig, version); + function calculateConfigDigest(bytes memory staticConfig, uint32 version) external view returns (bytes32) { + return _calculateConfigDigest(staticConfig, version); } } From a445b380667d42fe3a08efa23528c2b6567b063e Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 25 Sep 2024 15:14:26 +0200 Subject: [PATCH 30/36] use correct types in RMNHome --- contracts/gas-snapshots/ccip.gas-snapshot | 30 +- .../src/v0.8/ccip/capability/HomeBase.sol | 239 ---------------- .../src/v0.8/ccip/capability/RMNHome.sol | 268 ++++++++++++++---- .../ccip/test/capability/RMNHomeTest.t.sol | 62 ++-- .../v0.8/ccip/test/helpers/HomeBaseHelper.sol | 84 +++--- 5 files changed, 301 insertions(+), 382 deletions(-) delete mode 100644 contracts/src/v0.8/ccip/capability/HomeBase.sol diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 7accf1d73e..6cc5b6c896 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -752,23 +752,23 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 21848) PingPong_plumbing:test_Pausing_Success() (gas: 19077) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 197173) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 215989) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10973) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10928) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 18964) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19056) RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11003) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28455) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 32914) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 20323) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 15574) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 131926) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 20823) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 20617) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 24408) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 24847) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 18059) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 186748) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 24552) -RMNHome_setSecondary:test_setSecondary_success() (gas: 820802) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28550) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30251) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18894) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14152) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 106995) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 18960) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 18754) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 20442) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 20881) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15291) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 137361) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 20586) +RMNHome_setSecondary:test_setSecondary_success() (gas: 588827) RMNRemote_constructor:test_constructor_success() (gas: 8853) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 61072) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 156801) diff --git a/contracts/src/v0.8/ccip/capability/HomeBase.sol b/contracts/src/v0.8/ccip/capability/HomeBase.sol deleted file mode 100644 index a8cb826585..0000000000 --- a/contracts/src/v0.8/ccip/capability/HomeBase.sol +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; - -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; - -abstract contract HomeBase is OwnerIsCreator, ITypeAndVersion { - event ConfigSet(bytes32 indexed configDigest, StoredConfig versionedConfig); - event ConfigRevoked(bytes32 indexed configDigest); - event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); - event ConfigPromoted(bytes32 indexed configDigest); - - error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); - error DigestNotFound(bytes32 configDigest); - - /// @notice Used for encoding the config digest prefix - uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 - /// @notice The max number of configs that can be active at the same time. - uint256 private constant MAX_CONCURRENT_CONFIGS = 2; - /// @notice Helper to identify the zero config digest with less casting. - bytes32 private constant ZERO_DIGEST = bytes32(uint256(0)); - - /// @notice This array holds the configs. - /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. - StoredConfig[MAX_CONCURRENT_CONFIGS] private s_configs; - - /// @notice The total number of configs ever set, used for generating the version of the configs. - uint32 private s_configCount = 0; - /// @notice The index of the primary config. - uint32 private s_primaryConfigIndex = 0; - - struct StoredConfig { - bytes32 configDigest; - uint32 version; - bytes staticConfig; - bytes dynamicConfig; - } - - // ================================================================ - // │ Functions to override │ - // ================================================================ - - /// @notice Validates that the static and dynamic config are valid. Reverts otherwise. - function _validateStaticAndDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; - - /// @notice Validates that the dynamic config is valid given the static config. Reverts otherwise. - function _validateDynamicConfig(bytes memory staticConfig, bytes memory dynamicConfig) internal view virtual; - - /// @notice Validates that the caller is allowed to make changes to the config. - function _validateCaller() internal view virtual; - - /// @notice Returns the prefix used for calculating the config digest. - /// @dev This is used to ensure that the config digest is unique across different contract implementations. - function _getConfigDigestPrefix() internal pure virtual returns (uint256); - - // ================================================================ - // │ Getters │ - // ================================================================ - - /// @notice Returns the current primary and secondary config digests. - /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. - /// @return primaryConfigDigest The digest of the primary config. - /// @return secondaryConfigDigest The digest of the secondary config. - function getConfigDigests() external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { - return (s_configs[s_primaryConfigIndex].configDigest, s_configs[s_primaryConfigIndex ^ 1].configDigest); - } - - /// @notice Returns the primary config digest for for a given key. - function getPrimaryDigest() public view returns (bytes32) { - return s_configs[s_primaryConfigIndex].configDigest; - } - - /// @notice Returns the secondary config digest for for a given key. - function getSecondaryDigest() public view returns (bytes32) { - return s_configs[s_primaryConfigIndex ^ 1].configDigest; - } - - /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero - /// digest. This is done to prevent exposing old config state that is invalid. - /// @param configDigest The digest of the config to fetch. - function _getStoredConfig(bytes32 configDigest) internal view returns (StoredConfig memory storedConfig, bool ok) { - for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old - // config state that is invalid. - if (s_configs[i].configDigest == configDigest && configDigest != ZERO_DIGEST) { - return (s_configs[i], true); - } - } - return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); - } - - /// @notice Returns the primary stored config for a given key. - /// @return primaryConfig The primary stored config. - /// @return ok True if the config was found, false otherwise. - function _getPrimaryStoredConfig() internal view returns (StoredConfig memory primaryConfig, bool ok) { - if (s_configs[s_primaryConfigIndex].configDigest == ZERO_DIGEST) { - return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); - } - - return (s_configs[s_primaryConfigIndex], true); - } - - /// @notice Returns the secondary stored config for a given key. - /// @return secondaryConfig The secondary stored config. - /// @return ok True if the config was found, false otherwise. - function _getSecondaryStoredConfig() internal view returns (StoredConfig memory secondaryConfig, bool ok) { - if (s_configs[s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { - return (StoredConfig(ZERO_DIGEST, 0, "", ""), false); - } - - return (s_configs[s_primaryConfigIndex ^ 1], true); - } - - // ================================================================ - // │ State transitions │ - // ================================================================ - - /// @notice Sets a new config as the secondary config. Does not influence the primary config. - /// @param encodedStaticConfig The static part of the config. - /// @param encodedDynamicConfig The dynamic part of the config. - /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. - /// This is done to prevent accidental overwrites. - /// @return newConfigDigest The digest of the new config. - function setSecondary( - bytes calldata encodedStaticConfig, - bytes calldata encodedDynamicConfig, - bytes32 digestToOverwrite - ) external returns (bytes32 newConfigDigest) { - _validateCaller(); - _validateStaticAndDynamicConfig(encodedStaticConfig, encodedDynamicConfig); - - bytes32 existingDigest = getSecondaryDigest(); - - if (existingDigest != digestToOverwrite) { - revert ConfigDigestMismatch(existingDigest, digestToOverwrite); - } - - // are we going to overwrite a config? If so, emit an event. - if (existingDigest != ZERO_DIGEST) { - emit ConfigRevoked(digestToOverwrite); - } - - uint32 newVersion = ++s_configCount; - newConfigDigest = _calculateConfigDigest(encodedStaticConfig, newVersion); - - StoredConfig memory newConfig = StoredConfig({ - configDigest: newConfigDigest, - version: newVersion, - staticConfig: encodedStaticConfig, - dynamicConfig: encodedDynamicConfig - }); - - s_configs[s_primaryConfigIndex ^ 1] = newConfig; - - emit ConfigSet(newConfig.configDigest, newConfig); - - return newConfigDigest; - } - - /// @notice Revokes a specific config by digest. - /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(bytes32 configDigest) external { - _validateCaller(); - - uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[secondaryConfigIndex].configDigest != configDigest) { - revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, configDigest); - } - - emit ConfigRevoked(configDigest); - // Delete only the digest, as that's what's used to determine if a config is active. This means the actual - // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in - // the future. - delete s_configs[secondaryConfigIndex].configDigest; - } - - /// @notice Promotes the secondary config to the primary config and revokes the primary config. - /// @param digestToPromote The digest of the config to promote. - /// @param digestToRevoke The digest of the config to revoke. - function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external { - _validateCaller(); - - uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[secondaryConfigIndex].configDigest != digestToPromote) { - revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToPromote); - } - - uint256 primaryConfigIndex = s_primaryConfigIndex; - if (s_configs[primaryConfigIndex].configDigest != digestToRevoke) { - revert ConfigDigestMismatch(s_configs[primaryConfigIndex].configDigest, digestToRevoke); - } - - delete s_configs[primaryConfigIndex].configDigest; - - s_primaryConfigIndex ^= 1; - if (digestToRevoke != ZERO_DIGEST) { - emit ConfigRevoked(digestToRevoke); - } - emit ConfigPromoted(digestToPromote); - } - - /// @notice Sets the dynamic config for a specific config. - /// @param newDynamicConfig The new dynamic config. - /// @param currentDigest The digest of the config to update. - /// @dev This does not update the config digest as only the static config is part of the digest. - function setDynamicConfig(bytes calldata newDynamicConfig, bytes32 currentDigest) external { - _validateCaller(); - - for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { - if (s_configs[i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { - _validateDynamicConfig(s_configs[i].staticConfig, newDynamicConfig); - - // Since the static config doesn't change we don't have to update the digest or version. - s_configs[i].dynamicConfig = newDynamicConfig; - - emit DynamicConfigSet(currentDigest, newDynamicConfig); - return; - } - } - - revert DigestNotFound(currentDigest); - } - - /// @notice Calculates the config digest for a given plugin key, static config, and version. - /// @param staticConfig The static part of the config. - /// @param version The version of the config. - /// @return The calculated config digest. - function _calculateConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { - return bytes32( - (_getConfigDigestPrefix() & PREFIX_MASK) - | ( - uint256( - keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), version), staticConfig)) - ) & ~PREFIX_MASK - ) - ); - } -} diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index e447814467..4813d1c64a 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -1,17 +1,26 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {HomeBase} from "./HomeBase.sol"; +import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; + +import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; /// @notice Stores the home configuration for RMN, that is referenced by CCIP oracles, RMN nodes, and the RMNRemote /// contracts. -contract RMNHome is HomeBase { +contract RMNHome is OwnerIsCreator, ITypeAndVersion { + event ConfigSet(bytes32 indexed configDigest, uint32 version, StaticConfig staticConfig, DynamicConfig dynamicConfig); + event ConfigRevoked(bytes32 indexed configDigest); + event DynamicConfigSet(bytes32 indexed configDigest, DynamicConfig dynamicConfig); + event ConfigPromoted(bytes32 indexed configDigest); + error OutOfBoundsNodesLength(); error DuplicatePeerId(); error DuplicateOffchainPublicKey(); error DuplicateSourceChain(); error OutOfBoundsObserverNodeIndex(); error MinObserversTooHigh(); + error ConfigDigestMismatch(bytes32 expectedConfigDigest, bytes32 gotConfigDigest); + error DigestNotFound(bytes32 configDigest); struct Node { bytes32 peerId; // Used for p2p communication. @@ -47,6 +56,21 @@ contract RMNHome is HomeBase { string public constant override typeAndVersion = "RMNHome 1.6.0-dev"; uint256 private constant PREFIX = 0x000b << (256 - 16); // 0x000b00..00 + /// @notice Used for encoding the config digest prefix + uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 + /// @notice The max number of configs that can be active at the same time. + uint256 private constant MAX_CONCURRENT_CONFIGS = 2; + /// @notice Helper to identify the zero config digest with less casting. + bytes32 private constant ZERO_DIGEST = bytes32(uint256(0)); + + /// @notice This array holds the configs. + /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. + VersionedConfig[MAX_CONCURRENT_CONFIGS] private s_configs; + + /// @notice The total number of configs ever set, used for generating the version of the configs. + uint32 private s_configCount = 0; + /// @notice The index of the primary config. + uint32 private s_primaryConfigIndex = 0; /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use /// in case one of the configs is too large to be returnable by one of the other getters. @@ -54,20 +78,7 @@ contract RMNHome is HomeBase { /// @return versionedConfig The config and its version. /// @return ok True if the config was found, false otherwise. function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { - (StoredConfig memory storedConfig, bool configOK) = _getStoredConfig(configDigest); - if (configOK) { - return ( - VersionedConfig({ - version: storedConfig.version, - configDigest: storedConfig.configDigest, - staticConfig: abi.decode(storedConfig.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(storedConfig.dynamicConfig, (DynamicConfig)) - }), - true - ); - } - - return (versionedConfig, false); + return _getStoredConfig(configDigest); } function getAllConfigs() @@ -75,36 +86,23 @@ contract RMNHome is HomeBase { view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { - (StoredConfig memory primaryStoredConfig, bool primaryOk) = _getPrimaryStoredConfig(); - - if (primaryOk) { - primaryConfig = VersionedConfig({ - version: primaryStoredConfig.version, - configDigest: primaryStoredConfig.configDigest, - staticConfig: abi.decode(primaryStoredConfig.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(primaryStoredConfig.dynamicConfig, (DynamicConfig)) - }); + VersionedConfig memory storedPrimaryConfig = s_configs[s_primaryConfigIndex]; + if (storedPrimaryConfig.configDigest != ZERO_DIGEST) { + primaryConfig = storedPrimaryConfig; } - (StoredConfig memory secondaryStoredConfig, bool secondaryOk) = _getSecondaryStoredConfig(); - - if (secondaryOk) { - secondaryConfig = VersionedConfig({ - version: secondaryStoredConfig.version, - configDigest: secondaryStoredConfig.configDigest, - staticConfig: abi.decode(secondaryStoredConfig.staticConfig, (StaticConfig)), - dynamicConfig: abi.decode(secondaryStoredConfig.dynamicConfig, (DynamicConfig)) - }); + VersionedConfig memory storedSecondaryConfig = s_configs[s_primaryConfigIndex ^ 1]; + if (storedSecondaryConfig.configDigest != ZERO_DIGEST) { + secondaryConfig = storedSecondaryConfig; } return (primaryConfig, secondaryConfig); } function _validateStaticAndDynamicConfig( - bytes memory encodedStaticConfig, - bytes memory encodedDynamicConfig - ) internal pure override { - StaticConfig memory staticConfig = abi.decode(encodedStaticConfig, (StaticConfig)); + StaticConfig memory staticConfig, + DynamicConfig memory dynamicConfig + ) internal pure { // Ensure that observerNodesBitmap can be bit-encoded into a uint256. if (staticConfig.nodes.length > 256) { revert OutOfBoundsNodesLength(); @@ -122,7 +120,7 @@ contract RMNHome is HomeBase { } } - _validateDynamicConfigParsed(abi.decode(encodedDynamicConfig, (DynamicConfig)), staticConfig.nodes.length); + _validateDynamicConfigParsed(dynamicConfig, staticConfig.nodes.length); } function _validateDynamicConfigParsed(DynamicConfig memory dynamicConfig, uint256 numberOfNodes) internal pure { @@ -155,21 +153,191 @@ contract RMNHome is HomeBase { } } - function _validateDynamicConfig( - bytes memory encodedStaticConfig, - bytes memory encodedDynamicConfig - ) internal pure override { - uint256 numberOfNodes = abi.decode(encodedStaticConfig, (StaticConfig)).nodes.length; - DynamicConfig memory dynamicConfig = abi.decode(encodedDynamicConfig, (DynamicConfig)); - - _validateDynamicConfigParsed(dynamicConfig, numberOfNodes); + function _validateDynamicConfig(StaticConfig memory staticConfig, DynamicConfig memory dynamicConfig) internal pure { + _validateDynamicConfigParsed(dynamicConfig, staticConfig.nodes.length); } - function _validateCaller() internal view override { + function _validateCaller() internal view { _validateOwnership(); } - function _getConfigDigestPrefix() internal pure override returns (uint256) { - return PREFIX; + // ================================================================ + // │ Getters │ + // ================================================================ + + /// @notice Returns the current primary and secondary config digests. + /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. + /// @return primaryConfigDigest The digest of the primary config. + /// @return secondaryConfigDigest The digest of the secondary config. + function getConfigDigests() external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + return (s_configs[s_primaryConfigIndex].configDigest, s_configs[s_primaryConfigIndex ^ 1].configDigest); + } + + /// @notice Returns the primary config digest for for a given key. + function getPrimaryDigest() public view returns (bytes32) { + return s_configs[s_primaryConfigIndex].configDigest; + } + + /// @notice Returns the secondary config digest for for a given key. + function getSecondaryDigest() public view returns (bytes32) { + return s_configs[s_primaryConfigIndex ^ 1].configDigest; + } + + /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero + /// digest. This is done to prevent exposing old config state that is invalid. + /// @param configDigest The digest of the config to fetch. + function _getStoredConfig(bytes32 configDigest) internal view returns (VersionedConfig memory storedConfig, bool ok) { + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { + // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old + // config state that is invalid. + if (s_configs[i].configDigest == configDigest && configDigest != ZERO_DIGEST) { + return (s_configs[i], true); + } + } + return (storedConfig, false); + } + + /// @notice Returns the primary stored config for a given key. + /// @return primaryConfig The primary stored config. + /// @return ok True if the config was found, false otherwise. + function _getPrimaryStoredConfig() internal view returns (VersionedConfig memory primaryConfig, bool ok) { + if (s_configs[s_primaryConfigIndex].configDigest == ZERO_DIGEST) { + return (primaryConfig, false); + } + + return (s_configs[s_primaryConfigIndex], true); + } + + /// @notice Returns the secondary stored config for a given key. + /// @return secondaryConfig The secondary stored config. + /// @return ok True if the config was found, false otherwise. + function _getSecondaryStoredConfig() internal view returns (VersionedConfig memory secondaryConfig, bool ok) { + if (s_configs[s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { + return (secondaryConfig, false); + } + + return (s_configs[s_primaryConfigIndex ^ 1], true); + } + + // ================================================================ + // │ State transitions │ + // ================================================================ + + /// @notice Sets a new config as the secondary config. Does not influence the primary config. + /// @param staticConfig The static part of the config. + /// @param dynamicConfig The dynamic part of the config. + /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. + /// This is done to prevent accidental overwrites. + /// @return newConfigDigest The digest of the new config. + function setSecondary( + StaticConfig calldata staticConfig, + DynamicConfig calldata dynamicConfig, + bytes32 digestToOverwrite + ) external returns (bytes32 newConfigDigest) { + _validateCaller(); + _validateStaticAndDynamicConfig(staticConfig, dynamicConfig); + + bytes32 existingDigest = getSecondaryDigest(); + + if (existingDigest != digestToOverwrite) { + revert ConfigDigestMismatch(existingDigest, digestToOverwrite); + } + + // are we going to overwrite a config? If so, emit an event. + if (existingDigest != ZERO_DIGEST) { + emit ConfigRevoked(digestToOverwrite); + } + + uint32 newVersion = ++s_configCount; + newConfigDigest = _calculateConfigDigest(abi.encode(staticConfig), newVersion); + + VersionedConfig storage existingConfig = s_configs[s_primaryConfigIndex ^ 1]; + existingConfig.configDigest = newConfigDigest; + existingConfig.version = newVersion; + existingConfig.staticConfig = staticConfig; + existingConfig.dynamicConfig = dynamicConfig; + + emit ConfigSet(newConfigDigest, newVersion, staticConfig, dynamicConfig); + + return newConfigDigest; + } + + /// @notice Revokes a specific config by digest. + /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. + function revokeSecondary(bytes32 configDigest) external { + _validateCaller(); + + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configs[secondaryConfigIndex].configDigest != configDigest) { + revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, configDigest); + } + + emit ConfigRevoked(configDigest); + // Delete only the digest, as that's what's used to determine if a config is active. This means the actual + // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in + // the future. + delete s_configs[secondaryConfigIndex].configDigest; + } + + /// @notice Promotes the secondary config to the primary config and revokes the primary config. + /// @param digestToPromote The digest of the config to promote. + /// @param digestToRevoke The digest of the config to revoke. + function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external { + _validateCaller(); + + uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; + if (s_configs[secondaryConfigIndex].configDigest != digestToPromote) { + revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToPromote); + } + + uint256 primaryConfigIndex = s_primaryConfigIndex; + if (s_configs[primaryConfigIndex].configDigest != digestToRevoke) { + revert ConfigDigestMismatch(s_configs[primaryConfigIndex].configDigest, digestToRevoke); + } + + delete s_configs[primaryConfigIndex].configDigest; + + s_primaryConfigIndex ^= 1; + if (digestToRevoke != ZERO_DIGEST) { + emit ConfigRevoked(digestToRevoke); + } + emit ConfigPromoted(digestToPromote); + } + + /// @notice Sets the dynamic config for a specific config. + /// @param newDynamicConfig The new dynamic config. + /// @param currentDigest The digest of the config to update. + /// @dev This does not update the config digest as only the static config is part of the digest. + function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 currentDigest) external { + _validateCaller(); + + for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { + if (s_configs[i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { + _validateDynamicConfig(s_configs[i].staticConfig, newDynamicConfig); + + // Since the static config doesn't change we don't have to update the digest or version. + s_configs[i].dynamicConfig = newDynamicConfig; + + emit DynamicConfigSet(currentDigest, newDynamicConfig); + return; + } + } + + revert DigestNotFound(currentDigest); + } + + /// @notice Calculates the config digest for a given plugin key, static config, and version. + /// @param staticConfig The static part of the config. + /// @param version The version of the config. + /// @return The calculated config digest. + function _calculateConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { + return bytes32( + (PREFIX & PREFIX_MASK) + | ( + uint256( + keccak256(bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(this), version), staticConfig)) + ) & ~PREFIX_MASK + ) + ); } } diff --git a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol index dc33a0412b..16c299a573 100644 --- a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol @@ -1,15 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {HomeBase} from "../../capability/HomeBase.sol"; import {RMNHome} from "../../capability/RMNHome.sol"; import {Internal} from "../../libraries/Internal.sol"; import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; contract RMNHomeTest is Test { - bytes32 internal constant RMN_DON_ID = bytes32(uint256(0xaaabbb333eee)); - struct Config { RMNHome.StaticConfig staticConfig; RMNHome.DynamicConfig dynamicConfig; @@ -65,20 +62,15 @@ contract RMNHome_setSecondary is RMNHomeTest { dynamicConfig: config.dynamicConfig, configDigest: ZERO_DIGEST }); - HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ - configDigest: versionedConfig.configDigest, - version: versionedConfig.version, - staticConfig: abi.encode(config.staticConfig), - dynamicConfig: abi.encode(config.dynamicConfig) - }); - versionedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, versionedConfig.version); - encodedConfig.configDigest = versionedConfig.configDigest; + versionedConfig.configDigest = _getConfigDigest(abi.encode(versionedConfig.staticConfig), versionedConfig.version); vm.expectEmit(); - emit HomeBase.ConfigSet(encodedConfig.configDigest, encodedConfig); + emit RMNHome.ConfigSet( + versionedConfig.configDigest, versionedConfig.version, versionedConfig.staticConfig, versionedConfig.dynamicConfig + ); - s_rmnHome.setSecondary(encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setSecondary(versionedConfig.staticConfig, versionedConfig.dynamicConfig, ZERO_DIGEST); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(versionedConfig.configDigest); assertTrue(ok); @@ -109,7 +101,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_setSecondary_DuplicatePeerId_reverts() public { @@ -117,7 +109,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { @@ -125,7 +117,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_setSecondary_DuplicateSourceChain_reverts() public { @@ -133,7 +125,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { @@ -141,7 +133,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_setSecondary_MinObserversTooHigh_reverts() public { @@ -149,7 +141,7 @@ contract RMNHome_setSecondary is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_setSecondary_OnlyOwner_reverts() public { @@ -158,7 +150,7 @@ contract RMNHome_setSecondary is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } } @@ -166,7 +158,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_setDynamicConfig_success() public { @@ -176,12 +168,11 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { config.dynamicConfig.sourceChains[0].minObservers--; (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(); - bytes memory encodedConfig = abi.encode(config.dynamicConfig); vm.expectEmit(); - emit HomeBase.DynamicConfigSet(secondaryConfigDigest, encodedConfig); + emit RMNHome.DynamicConfigSet(secondaryConfigDigest, config.dynamicConfig); - s_rmnHome.setDynamicConfig(encodedConfig, secondaryConfigDigest); + s_rmnHome.setDynamicConfig(config.dynamicConfig, secondaryConfigDigest); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); assertTrue(ok); @@ -201,19 +192,19 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers++; - vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(abi.encode(config.dynamicConfig), ZERO_DIGEST); + vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, ZERO_DIGEST)); + s_rmnHome.setDynamicConfig(config.dynamicConfig, ZERO_DIGEST); } function test_setDynamicConfig_DigestNotFound_reverts() public { // Zero always reverts - vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, ZERO_DIGEST)); - s_rmnHome.setDynamicConfig(abi.encode(_getBaseConfig().dynamicConfig), ZERO_DIGEST); + vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, ZERO_DIGEST)); + s_rmnHome.setDynamicConfig(_getBaseConfig().dynamicConfig, ZERO_DIGEST); // Non-existent digest reverts bytes32 nonExistentDigest = keccak256("nonExistentDigest"); - vm.expectRevert(abi.encodeWithSelector(HomeBase.DigestNotFound.selector, nonExistentDigest)); - s_rmnHome.setDynamicConfig(abi.encode(_getBaseConfig().dynamicConfig), nonExistentDigest); + vm.expectRevert(abi.encodeWithSelector(RMNHome.DigestNotFound.selector, nonExistentDigest)); + s_rmnHome.setDynamicConfig(_getBaseConfig().dynamicConfig, nonExistentDigest); } function test_setDynamicConfig_OnlyOwner_reverts() public { @@ -222,7 +213,7 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setDynamicConfig(abi.encode(config.dynamicConfig), keccak256("configDigest")); + s_rmnHome.setDynamicConfig(config.dynamicConfig, keccak256("configDigest")); } } @@ -231,19 +222,18 @@ contract RMNHome_revokeSecondary is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - bytes32 digest = - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + bytes32 digest = s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); s_rmnHome.promoteSecondaryAndRevokePrimary(digest, ZERO_DIGEST); config.dynamicConfig.sourceChains[0].minObservers--; - s_rmnHome.setSecondary(abi.encode(config.staticConfig), abi.encode(config.dynamicConfig), ZERO_DIGEST); + s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_revokeSecondary_success() public { (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); vm.expectEmit(); - emit HomeBase.ConfigRevoked(priorSecondaryDigest); + emit RMNHome.ConfigRevoked(priorSecondaryDigest); s_rmnHome.revokeSecondary(priorSecondaryDigest); @@ -265,7 +255,7 @@ contract RMNHome_revokeSecondary is RMNHomeTest { (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); bytes32 wrongDigest = keccak256("wrong_digest"); - vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); + vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); s_rmnHome.revokeSecondary(wrongDigest); } diff --git a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol index 9600b02d27..e56dd6cec8 100644 --- a/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/HomeBaseHelper.sol @@ -1,42 +1,42 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {HomeBase} from "../../capability/HomeBase.sol"; - -contract HomeBaseHelper is HomeBase { - error InvalidCaller(); - - string public constant override typeAndVersion = "HomeBaseHelper 1.6.0-dev"; - - uint256 public constant PREFIX = 0x0c0c << (256 - 16); - - function _validateStaticAndDynamicConfig(bytes memory, bytes memory) internal view override {} - - function _validateDynamicConfig(bytes memory, bytes memory) internal view override {} - - function _getConfigDigestPrefix() internal pure override returns (uint256) { - return PREFIX; - } - - function _validateCaller() internal view override { - if (msg.sender != owner()) { - revert InvalidCaller(); - } - } - - function getStoredConfig(bytes32 configDigest) external view returns (StoredConfig memory, bool ok) { - return _getStoredConfig(configDigest); - } - - function getPrimaryStoredConfig() external view returns (StoredConfig memory, bool ok) { - return _getPrimaryStoredConfig(); - } - - function getSecondaryStoredConfig() external view returns (StoredConfig memory, bool ok) { - return _getSecondaryStoredConfig(); - } - - function calculateConfigDigest(bytes memory staticConfig, uint32 version) external view returns (bytes32) { - return _calculateConfigDigest(staticConfig, version); - } -} +//// SPDX-License-Identifier: BUSL-1.1 +//pragma solidity 0.8.24; +// +//import {HomeBase} from "../../capability/HomeBase.sol"; +// +//contract HomeBaseHelper is HomeBase { +// error InvalidCaller(); +// +// string public constant override typeAndVersion = "HomeBaseHelper 1.6.0-dev"; +// +// uint256 public constant PREFIX = 0x0c0c << (256 - 16); +// +// function _validateStaticAndDynamicConfig(bytes memory, bytes memory) internal view override {} +// +// function _validateDynamicConfig(bytes memory, bytes memory) internal view override {} +// +// function _getConfigDigestPrefix() internal pure override returns (uint256) { +// return PREFIX; +// } +// +// function _validateCaller() internal view override { +// if (msg.sender != owner()) { +// revert InvalidCaller(); +// } +// } +// +// function getStoredConfig(bytes32 configDigest) external view returns (StoredConfig memory, bool ok) { +// return _getStoredConfig(configDigest); +// } +// +// function getPrimaryStoredConfig() external view returns (StoredConfig memory, bool ok) { +// return _getPrimaryStoredConfig(); +// } +// +// function getSecondaryStoredConfig() external view returns (StoredConfig memory, bool ok) { +// return _getSecondaryStoredConfig(); +// } +// +// function calculateConfigDigest(bytes memory staticConfig, uint32 version) external view returns (bytes32) { +// return _calculateConfigDigest(staticConfig, version); +// } +//} From 17b57ab72381f999b5d1f3fb4d0d9dc68c03a71e Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 25 Sep 2024 15:20:40 +0200 Subject: [PATCH 31/36] cleanup --- contracts/gas-snapshots/ccip.gas-snapshot | 32 +-- .../src/v0.8/ccip/capability/RMNHome.sol | 207 +++++++----------- 2 files changed, 99 insertions(+), 140 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 6cc5b6c896..b2f97021c8 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -752,23 +752,23 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 21848) PingPong_plumbing:test_Pausing_Success() (gas: 19077) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 197173) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 215989) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10928) +RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10913) RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19056) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 11003) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28550) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30251) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18894) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14152) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 106995) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 18960) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 18754) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 20442) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 20881) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15291) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 137361) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 20586) -RMNHome_setSecondary:test_setSecondary_success() (gas: 588827) +RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19032) +RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10988) +RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28209) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30203) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18870) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14137) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 104105) +RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 18936) +RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 18730) +RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 20418) +RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 20857) +RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15276) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 137337) +RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 20562) +RMNHome_setSecondary:test_setSecondary_success() (gas: 588484) RMNRemote_constructor:test_constructor_success() (gas: 8853) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 61072) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 156801) diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index 4813d1c64a..c8d554cb4b 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -72,95 +72,6 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @notice The index of the primary config. uint32 private s_primaryConfigIndex = 0; - /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use - /// in case one of the configs is too large to be returnable by one of the other getters. - /// @param configDigest The digest of the config to fetch. - /// @return versionedConfig The config and its version. - /// @return ok True if the config was found, false otherwise. - function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { - return _getStoredConfig(configDigest); - } - - function getAllConfigs() - external - view - returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) - { - VersionedConfig memory storedPrimaryConfig = s_configs[s_primaryConfigIndex]; - if (storedPrimaryConfig.configDigest != ZERO_DIGEST) { - primaryConfig = storedPrimaryConfig; - } - - VersionedConfig memory storedSecondaryConfig = s_configs[s_primaryConfigIndex ^ 1]; - if (storedSecondaryConfig.configDigest != ZERO_DIGEST) { - secondaryConfig = storedSecondaryConfig; - } - - return (primaryConfig, secondaryConfig); - } - - function _validateStaticAndDynamicConfig( - StaticConfig memory staticConfig, - DynamicConfig memory dynamicConfig - ) internal pure { - // Ensure that observerNodesBitmap can be bit-encoded into a uint256. - if (staticConfig.nodes.length > 256) { - revert OutOfBoundsNodesLength(); - } - - // Ensure no peerId or offchainPublicKey is duplicated. - for (uint256 i = 0; i < staticConfig.nodes.length; ++i) { - for (uint256 j = i + 1; j < staticConfig.nodes.length; ++j) { - if (staticConfig.nodes[i].peerId == staticConfig.nodes[j].peerId) { - revert DuplicatePeerId(); - } - if (staticConfig.nodes[i].offchainPublicKey == staticConfig.nodes[j].offchainPublicKey) { - revert DuplicateOffchainPublicKey(); - } - } - } - - _validateDynamicConfigParsed(dynamicConfig, staticConfig.nodes.length); - } - - function _validateDynamicConfigParsed(DynamicConfig memory dynamicConfig, uint256 numberOfNodes) internal pure { - uint256 numberOfSourceChains = dynamicConfig.sourceChains.length; - for (uint256 i = 0; i < numberOfSourceChains; ++i) { - SourceChain memory currentSourceChain = dynamicConfig.sourceChains[i]; - // Ensure the source chain is unique. - for (uint256 j = i + 1; j < numberOfSourceChains; ++j) { - if (currentSourceChain.chainSelector == dynamicConfig.sourceChains[j].chainSelector) { - revert DuplicateSourceChain(); - } - } - - // all observer node indices are valid - uint256 bitmap = currentSourceChain.observerNodesBitmap; - // Check if there are any bits set for indexes outside of the expected range. - if (bitmap & (type(uint256).max >> (256 - numberOfNodes)) != bitmap) { - revert OutOfBoundsObserverNodeIndex(); - } - - uint256 observersCount = 0; - for (; bitmap != 0; ++observersCount) { - bitmap &= bitmap - 1; - } - - // minObservers are tenable - if (currentSourceChain.minObservers > observersCount) { - revert MinObserversTooHigh(); - } - } - } - - function _validateDynamicConfig(StaticConfig memory staticConfig, DynamicConfig memory dynamicConfig) internal pure { - _validateDynamicConfigParsed(dynamicConfig, staticConfig.nodes.length); - } - - function _validateCaller() internal view { - _validateOwnership(); - } - // ================================================================ // │ Getters │ // ================================================================ @@ -173,20 +84,22 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { return (s_configs[s_primaryConfigIndex].configDigest, s_configs[s_primaryConfigIndex ^ 1].configDigest); } - /// @notice Returns the primary config digest for for a given key. + /// @notice Returns the primary config digest function getPrimaryDigest() public view returns (bytes32) { return s_configs[s_primaryConfigIndex].configDigest; } - /// @notice Returns the secondary config digest for for a given key. + /// @notice Returns the secondary config digest function getSecondaryDigest() public view returns (bytes32) { return s_configs[s_primaryConfigIndex ^ 1].configDigest; } - /// @notice Returns the stored config for a given digest. Will always return an empty config if the digest is the zero - /// digest. This is done to prevent exposing old config state that is invalid. + /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use + /// in case one of the configs is too large to be returnable by one of the other getters. /// @param configDigest The digest of the config to fetch. - function _getStoredConfig(bytes32 configDigest) internal view returns (VersionedConfig memory storedConfig, bool ok) { + /// @return versionedConfig The config and its version. + /// @return ok True if the config was found, false otherwise. + function getConfig(bytes32 configDigest) external view returns (VersionedConfig memory versionedConfig, bool ok) { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { // We never want to return true for a zero digest, even if the caller is asking for it, as this can expose old // config state that is invalid. @@ -194,29 +107,25 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { return (s_configs[i], true); } } - return (storedConfig, false); + return (versionedConfig, false); } - /// @notice Returns the primary stored config for a given key. - /// @return primaryConfig The primary stored config. - /// @return ok True if the config was found, false otherwise. - function _getPrimaryStoredConfig() internal view returns (VersionedConfig memory primaryConfig, bool ok) { - if (s_configs[s_primaryConfigIndex].configDigest == ZERO_DIGEST) { - return (primaryConfig, false); + function getAllConfigs() + external + view + returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) + { + VersionedConfig memory storedPrimaryConfig = s_configs[s_primaryConfigIndex]; + if (storedPrimaryConfig.configDigest != ZERO_DIGEST) { + primaryConfig = storedPrimaryConfig; } - return (s_configs[s_primaryConfigIndex], true); - } - - /// @notice Returns the secondary stored config for a given key. - /// @return secondaryConfig The secondary stored config. - /// @return ok True if the config was found, false otherwise. - function _getSecondaryStoredConfig() internal view returns (VersionedConfig memory secondaryConfig, bool ok) { - if (s_configs[s_primaryConfigIndex ^ 1].configDigest == ZERO_DIGEST) { - return (secondaryConfig, false); + VersionedConfig memory storedSecondaryConfig = s_configs[s_primaryConfigIndex ^ 1]; + if (storedSecondaryConfig.configDigest != ZERO_DIGEST) { + secondaryConfig = storedSecondaryConfig; } - return (s_configs[s_primaryConfigIndex ^ 1], true); + return (primaryConfig, secondaryConfig); } // ================================================================ @@ -233,8 +142,7 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { StaticConfig calldata staticConfig, DynamicConfig calldata dynamicConfig, bytes32 digestToOverwrite - ) external returns (bytes32 newConfigDigest) { - _validateCaller(); + ) external onlyOwner returns (bytes32 newConfigDigest) { _validateStaticAndDynamicConfig(staticConfig, dynamicConfig); bytes32 existingDigest = getSecondaryDigest(); @@ -264,9 +172,7 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(bytes32 configDigest) external { - _validateCaller(); - + function revokeSecondary(bytes32 configDigest) external onlyOwner { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; if (s_configs[secondaryConfigIndex].configDigest != configDigest) { revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, configDigest); @@ -282,9 +188,7 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @notice Promotes the secondary config to the primary config and revokes the primary config. /// @param digestToPromote The digest of the config to promote. /// @param digestToRevoke The digest of the config to revoke. - function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external { - _validateCaller(); - + function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; if (s_configs[secondaryConfigIndex].configDigest != digestToPromote) { revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToPromote); @@ -308,13 +212,10 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @param newDynamicConfig The new dynamic config. /// @param currentDigest The digest of the config to update. /// @dev This does not update the config digest as only the static config is part of the digest. - function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 currentDigest) external { - _validateCaller(); - + function setDynamicConfig(DynamicConfig calldata newDynamicConfig, bytes32 currentDigest) external onlyOwner { for (uint256 i = 0; i < MAX_CONCURRENT_CONFIGS; ++i) { if (s_configs[i].configDigest == currentDigest && currentDigest != ZERO_DIGEST) { - _validateDynamicConfig(s_configs[i].staticConfig, newDynamicConfig); - + _validateDynamicConfig(newDynamicConfig, s_configs[i].staticConfig.nodes.length); // Since the static config doesn't change we don't have to update the digest or version. s_configs[i].dynamicConfig = newDynamicConfig; @@ -340,4 +241,62 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { ) ); } + + // ================================================================ + // │ Validation │ + // ================================================================ + + function _validateStaticAndDynamicConfig( + StaticConfig memory staticConfig, + DynamicConfig memory dynamicConfig + ) internal pure { + // Ensure that observerNodesBitmap can be bit-encoded into a uint256. + if (staticConfig.nodes.length > 256) { + revert OutOfBoundsNodesLength(); + } + + // Ensure no peerId or offchainPublicKey is duplicated. + for (uint256 i = 0; i < staticConfig.nodes.length; ++i) { + for (uint256 j = i + 1; j < staticConfig.nodes.length; ++j) { + if (staticConfig.nodes[i].peerId == staticConfig.nodes[j].peerId) { + revert DuplicatePeerId(); + } + if (staticConfig.nodes[i].offchainPublicKey == staticConfig.nodes[j].offchainPublicKey) { + revert DuplicateOffchainPublicKey(); + } + } + } + + _validateDynamicConfig(dynamicConfig, staticConfig.nodes.length); + } + + function _validateDynamicConfig(DynamicConfig memory dynamicConfig, uint256 numberOfNodes) internal pure { + uint256 numberOfSourceChains = dynamicConfig.sourceChains.length; + for (uint256 i = 0; i < numberOfSourceChains; ++i) { + SourceChain memory currentSourceChain = dynamicConfig.sourceChains[i]; + // Ensure the source chain is unique. + for (uint256 j = i + 1; j < numberOfSourceChains; ++j) { + if (currentSourceChain.chainSelector == dynamicConfig.sourceChains[j].chainSelector) { + revert DuplicateSourceChain(); + } + } + + // all observer node indices are valid + uint256 bitmap = currentSourceChain.observerNodesBitmap; + // Check if there are any bits set for indexes outside of the expected range. + if (bitmap & (type(uint256).max >> (256 - numberOfNodes)) != bitmap) { + revert OutOfBoundsObserverNodeIndex(); + } + + uint256 observersCount = 0; + for (; bitmap != 0; ++observersCount) { + bitmap &= bitmap - 1; + } + + // minObservers are tenable + if (currentSourceChain.minObservers > observersCount) { + revert MinObserversTooHigh(); + } + } + } } From 57f63e32bc91f4b91e6fd428e67a87bb6ba7493d Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 25 Sep 2024 15:41:17 +0200 Subject: [PATCH 32/36] add storing of ccip config --- .../src/v0.8/ccip/capability/CCIPHome.sol | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index 38325625f4..cea7f4c780 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -21,7 +21,7 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, event ChainConfigRemoved(uint64 chainSelector); event ChainConfigSet(uint64 chainSelector, ChainConfig chainConfig); - event ConfigSet(bytes32 indexed configDigest, VersionedConfig versionedConfig); + event ConfigSet(bytes32 indexed configDigest, uint32 version, OCR3Config config); event ConfigRevoked(bytes32 indexed configDigest); event DynamicConfigSet(bytes32 indexed configDigest, bytes dynamicConfig); event ConfigPromoted(bytes32 indexed configDigest); @@ -298,14 +298,25 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, uint32 newVersion = ++s_configCount; - VersionedConfig memory newConfig = VersionedConfig({configDigest: newDigest, version: newVersion, config: config}); - VersionedConfig storage existingConfig = s_configs[pluginKey][s_primaryConfigIndex ^ 1]; - // TODO existingConfig.config = config; existingConfig.version = newVersion; existingConfig.configDigest = newDigest; - emit ConfigSet(newConfig.configDigest, newConfig); + existingConfig.config.pluginType = config.pluginType; + existingConfig.config.chainSelector = config.chainSelector; + existingConfig.config.FRoleDON = config.FRoleDON; + existingConfig.config.offchainConfigVersion = config.offchainConfigVersion; + existingConfig.config.offrampAddress = config.offrampAddress; + existingConfig.config.offchainConfig = config.offchainConfig; + + while (existingConfig.config.nodes.length > 0) { + existingConfig.config.nodes.pop(); + } + for (uint256 i = 0; i < config.nodes.length; ++i) { + existingConfig.config.nodes.push(config.nodes[i]); + } + + emit ConfigSet(newDigest, newVersion, config); return newDigest; } From d309e11ad41064fae3187dfd820f9b8f1eb72fdb Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 26 Sep 2024 11:40:10 +0200 Subject: [PATCH 33/36] fix typos --- contracts/src/v0.8/ccip/offRamp/OffRamp.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/src/v0.8/ccip/offRamp/OffRamp.sol b/contracts/src/v0.8/ccip/offRamp/OffRamp.sol index 5fcddb486e..4a62bef9f7 100644 --- a/contracts/src/v0.8/ccip/offRamp/OffRamp.sol +++ b/contracts/src/v0.8/ccip/offRamp/OffRamp.sol @@ -948,8 +948,8 @@ contract OffRamp is ITypeAndVersion, MultiOCR3Base { emit SourceChainSelectorAdded(sourceChainSelector); } else if (currentConfig.minSeqNr != 1) { // OnRamp updates should only happens due to a misconfiguration - // If an OnRamp is misconfigured not reports should have been committed and no messages should have been executed - // This is enforced byt the onRamp address check in the commit function + // If an OnRamp is misconfigured no reports should have been committed and no messages should have been executed + // This is enforced by the onRamp address check in the commit function revert InvalidOnRampUpdate(sourceChainSelector); } From 3854ec9cbe3fc3b5cd40d7bc90db62de94940935 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 26 Sep 2024 11:50:09 +0200 Subject: [PATCH 34/36] rename blue/green/primary/secondary -> active/candidate --- contracts/gas-snapshots/ccip.gas-snapshot | 348 +++++++++--------- .../src/v0.8/ccip/capability/CCIPHome.sol | 129 +++---- .../src/v0.8/ccip/capability/RMNHome.sol | 80 ++-- .../ccip/test/capability/RMNHomeTest.t.sol | 100 ++--- 4 files changed, 329 insertions(+), 328 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index b2f97021c8..0b1af96d31 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -516,29 +516,29 @@ MultiOCR3Base_transmit:test_UnauthorizedSigner_Revert() (gas: 69917) MultiOCR3Base_transmit:test_UnconfiguredPlugin_Revert() (gas: 42537) MultiOCR3Base_transmit:test_ZeroSignatures_Revert() (gas: 37478) MultiOnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 304699) -MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1926302) -NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 40448) -NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 27403) -NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 41879) -NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 79874) -NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 316259) -NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 322432) -NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 417132) -NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 375380) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 327638) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 313211) -NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 183405) -NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 193460) -NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 268061) -NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 153619) -NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 125710) -NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 134448) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 47118) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 69128) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 46949) -NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 72431) -NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12892) -NonceManager_typeAndVersion:test_typeAndVersion() (gas: 10623) +MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1504663) +NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 37934) +NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 23706) +NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 38778) +NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 71901) +NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 262159) +NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 265836) +NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 329824) +NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 300784) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 249120) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 237027) +NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 153748) +NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 168959) +NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 221148) +NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 126861) +NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 107723) +NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 123207) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 43079) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 64408) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 42943) +NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 66696) +NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12070) +NonceManager_typeAndVersion:test_typeAndVersion() (gas: 9705) OCR2BaseNoChecks_setOCR2Config:test_FMustBePositive_Revert() (gas: 15443) OCR2BaseNoChecks_setOCR2Config:test_RepeatAddress_Revert() (gas: 48841) OCR2BaseNoChecks_setOCR2Config:test_SetConfigSuccess_gas() (gas: 97138) @@ -564,140 +564,140 @@ OCR2Base_transmit:test_Transmit2SignersSuccess_gas() (gas: 58490) OCR2Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 27448) OCR2Base_transmit:test_UnauthorizedSigner_Revert() (gas: 45597) OCR2Base_transmit:test_WrongNumberOfSignatures_Revert() (gas: 23593) -OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 8902529) -OffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 495132) -OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 107802) -OffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 13418) -OffRamp_applySourceChainConfigUpdates:test_InvalidOnRampUpdate_Revert() (gas: 231022) -OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Success() (gas: 110522) -OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 124535) -OffRamp_applySourceChainConfigUpdates:test_RouterAddress_Revert() (gas: 15757) -OffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 77820) -OffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 17894) -OffRamp_batchExecute:test_MultipleReportsDifferentChainsSkipCursedChain_Success() (gas: 224083) -OffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 425996) -OffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 368952) -OffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 208104) -OffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 239311) -OffRamp_batchExecute:test_SingleReport_Success() (gas: 188653) -OffRamp_batchExecute:test_Unhealthy_Success() (gas: 717329) -OffRamp_batchExecute:test_ZeroReports_Revert() (gas: 12088) -OffRamp_ccipReceive:test_Reverts() (gas: 18753) -OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 109720) -OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 77314) -OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 83071) -OffRamp_commit:test_InvalidInterval_Revert() (gas: 77116) -OffRamp_commit:test_InvalidRootRevert() (gas: 75631) -OffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 9548401) -OffRamp_commit:test_NoConfig_Revert() (gas: 9122532) -OffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 130728) -OffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 147353) -OffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 130749) -OffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 424203) -OffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 190103) -OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 161941) -OffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 176263) -OffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 72480) -OffRamp_commit:test_StaleReportWithRoot_Success() (gas: 280739) -OffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 144515) -OffRamp_commit:test_Unhealthy_Revert() (gas: 71728) -OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 250379) -OffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 61202) -OffRamp_constructor:test_Constructor_Success() (gas: 9121263) -OffRamp_constructor:test_SourceChainSelector_Revert() (gas: 148706) -OffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 112646) -OffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 110448) -OffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 174260) -OffRamp_constructor:test_ZeroRMNRemote_Revert() (gas: 110450) -OffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 110395) -OffRamp_execute:test_IncorrectArrayType_Revert() (gas: 19670) -OffRamp_execute:test_LargeBatch_Success() (gas: 4789035) -OffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 462387) -OffRamp_execute:test_MultipleReports_Success() (gas: 392392) -OffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 9972739) -OffRamp_execute:test_NoConfig_Revert() (gas: 9179658) -OffRamp_execute:test_NonArray_Revert() (gas: 34048) -OffRamp_execute:test_SingleReport_Success() (gas: 209314) -OffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 173200) -OffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 9980482) -OffRamp_execute:test_ZeroReports_Revert() (gas: 19285) -OffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 23977) -OffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 277738) -OffRamp_executeSingleMessage:test_NonContract_Success() (gas: 26476) -OffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 236582) -OffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 60990) -OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 60132) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 267137) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 98126) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 320798) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithVInterception_Success() (gas: 108714) -OffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 36766) -OffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 24545) -OffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 595814) -OffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 60468) -OffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 42455) -OffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 37212) -OffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 221890) -OffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 237935) -OffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 52346) -OffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 625018) -OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 297913) -OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 252901) -OffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 271928) -OffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 307332) -OffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 168258) -OffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 502204) -OffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 71999) -OffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 86531) -OffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 733681) -OffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 666498) -OffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 42450) -OffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 710720) -OffRamp_executeSingleReport:test_Unhealthy_Success() (gas: 710723) -OffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 574134) -OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 172776) -OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 202237) -OffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 5703276) -OffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 148004) -OffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 111520) -OffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 128026) -OffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 103971) -OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 208382) -OffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 261275) -OffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 37688) -OffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 267627) -OffRamp_manuallyExecute:test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() (gas: 38216) -OffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 73056) -OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 742494) -OffRamp_manuallyExecute:test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() (gas: 408960) -OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails_Success() (gas: 3574005) -OffRamp_manuallyExecute:test_manuallyExecute_SourceChainSelectorMismatch_Revert() (gas: 199720) -OffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 279997) -OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 280693) -OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 1033763) -OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 461923) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 47648) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 121919) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 99377) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 44074) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 109868) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 46405) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 101128) -OffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 187037) -OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 28901) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 74851) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 94066) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 206891) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_WithGasOverride_Success() (gas: 209712) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 217613) -OffRamp_setDynamicConfig:test_FeeQuoterZeroAddress_Revert() (gas: 12524) -OffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 15529) -OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor_Success() (gas: 50816) -OffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 28782) -OffRamp_trialExecute:test_RateLimitError_Success() (gas: 259624) -OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 269734) -OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 386633) -OffRamp_trialExecute:test_trialExecute_Success() (gas: 329701) +OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 5656596) +OffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 469391) +OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 99637) +OffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 12591) +OffRamp_applySourceChainConfigUpdates:test_InvalidOnRampUpdate_Revert() (gas: 205322) +OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Success() (gas: 101437) +OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 111307) +OffRamp_applySourceChainConfigUpdates:test_RouterAddress_Revert() (gas: 13441) +OffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 72702) +OffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 15497) +OffRamp_batchExecute:test_MultipleReportsDifferentChainsSkipCursedChain_Success() (gas: 177979) +OffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 335646) +OffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 278912) +OffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 169308) +OffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 189031) +OffRamp_batchExecute:test_SingleReport_Success() (gas: 157132) +OffRamp_batchExecute:test_Unhealthy_Success() (gas: 554208) +OffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10600) +OffRamp_ccipReceive:test_Reverts() (gas: 15385) +OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 92905) +OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 64099) +OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 68124) +OffRamp_commit:test_InvalidInterval_Revert() (gas: 64291) +OffRamp_commit:test_InvalidRootRevert() (gas: 63356) +OffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6260756) +OffRamp_commit:test_NoConfig_Revert() (gas: 5844428) +OffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 113042) +OffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 121403) +OffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 113063) +OffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 355198) +OffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 164351) +OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 139364) +OffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 146555) +OffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 59858) +OffRamp_commit:test_StaleReportWithRoot_Success() (gas: 232074) +OffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 125409) +OffRamp_commit:test_Unhealthy_Revert() (gas: 58633) +OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 206729) +OffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 51722) +OffRamp_constructor:test_Constructor_Success() (gas: 5845416) +OffRamp_constructor:test_SourceChainSelector_Revert() (gas: 135514) +OffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 102949) +OffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 100843) +OffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 161040) +OffRamp_constructor:test_ZeroRMNRemote_Revert() (gas: 100763) +OffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 100801) +OffRamp_execute:test_IncorrectArrayType_Revert() (gas: 17639) +OffRamp_execute:test_LargeBatch_Success() (gas: 3426635) +OffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 373095) +OffRamp_execute:test_MultipleReports_Success() (gas: 301009) +OffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6669661) +OffRamp_execute:test_NoConfig_Revert() (gas: 5894136) +OffRamp_execute:test_NonArray_Revert() (gas: 27562) +OffRamp_execute:test_SingleReport_Success() (gas: 176364) +OffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 148382) +OffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6672410) +OffRamp_execute:test_ZeroReports_Revert() (gas: 17361) +OffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 18511) +OffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 244057) +OffRamp_executeSingleMessage:test_NonContract_Success() (gas: 20759) +OffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 205094) +OffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 49316) +OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 48760) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 218081) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 85349) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 274194) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithVInterception_Success() (gas: 91809) +OffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 28260) +OffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 22062) +OffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 481748) +OffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 48372) +OffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 33959) +OffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 28436) +OffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 188084) +OffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 198518) +OffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 40741) +OffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 413233) +OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 249776) +OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 193590) +OffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 213624) +OffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 249506) +OffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 142151) +OffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 409289) +OffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 58293) +OffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 73868) +OffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 583401) +OffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 532115) +OffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 33717) +OffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 549738) +OffRamp_executeSingleReport:test_Unhealthy_Success() (gas: 549752) +OffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 460495) +OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 135910) +OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 165615) +OffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3868658) +OffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 120474) +OffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 89178) +OffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 81284) +OffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 74161) +OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 173244) +OffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 214189) +OffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 27152) +OffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 165098) +OffRamp_manuallyExecute:test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() (gas: 27689) +OffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 55260) +OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 498614) +OffRamp_manuallyExecute:test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() (gas: 316158) +OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails_Success() (gas: 2245360) +OffRamp_manuallyExecute:test_manuallyExecute_SourceChainSelectorMismatch_Revert() (gas: 165602) +OffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 227234) +OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 227774) +OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 781510) +OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 347431) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 37634) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 104404) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 85342) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 36752) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 94382) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 39741) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 86516) +OffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 162381) +OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 23903) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 62751) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 79790) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 174512) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_WithGasOverride_Success() (gas: 176424) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 187723) +OffRamp_setDynamicConfig:test_FeeQuoterZeroAddress_Revert() (gas: 11269) +OffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 13884) +OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor_Success() (gas: 46421) +OffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 24463) +OffRamp_trialExecute:test_RateLimitError_Success() (gas: 219355) +OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 227977) +OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 295350) +OffRamp_trialExecute:test_trialExecute_Success() (gas: 277894) OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 508639) OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() (gas: 21134) OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_Revert() (gas: 80371) @@ -752,23 +752,23 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 21848) PingPong_plumbing:test_Pausing_Success() (gas: 19077) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 197173) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 215989) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() (gas: 10913) -RMNHome_promoteSecondaryAndRevokePrimary:test_promoteSecondaryAndRevokePrimary_success() (gas: 209) -RMNHome_revokeSecondary:test_revokeSecondary_ConfigDigestMismatch_reverts() (gas: 19032) -RMNHome_revokeSecondary:test_revokeSecondary_OnlyOwner_reverts() (gas: 10988) -RMNHome_revokeSecondary:test_revokeSecondary_success() (gas: 28209) -RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30203) -RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18870) -RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14137) -RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 104105) -RMNHome_setSecondary:test_setSecondary_DuplicateOffchainPublicKey_reverts() (gas: 18936) -RMNHome_setSecondary:test_setSecondary_DuplicatePeerId_reverts() (gas: 18730) -RMNHome_setSecondary:test_setSecondary_DuplicateSourceChain_reverts() (gas: 20418) -RMNHome_setSecondary:test_setSecondary_MinObserversTooHigh_reverts() (gas: 20857) -RMNHome_setSecondary:test_setSecondary_OnlyOwner_reverts() (gas: 15276) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsNodesLength_reverts() (gas: 137337) -RMNHome_setSecondary:test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() (gas: 20562) -RMNHome_setSecondary:test_setSecondary_success() (gas: 588484) +RMNHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_OnlyOwner_reverts() (gas: 10979) +RMNHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_success() (gas: 164) +RMNHome_revokeCandidate:test_revokeCandidate_ConfigDigestMismatch_reverts() (gas: 19073) +RMNHome_revokeCandidate:test_revokeCandidate_OnlyOwner_reverts() (gas: 10984) +RMNHome_revokeCandidate:test_revokeCandidate_success() (gas: 28184) +RMNHome_setCandidate:test_setCandidate_DuplicateOffchainPublicKey_reverts() (gas: 18871) +RMNHome_setCandidate:test_setCandidate_DuplicatePeerId_reverts() (gas: 18687) +RMNHome_setCandidate:test_setCandidate_DuplicateSourceChain_reverts() (gas: 20408) +RMNHome_setCandidate:test_setCandidate_MinObserversTooHigh_reverts() (gas: 20805) +RMNHome_setCandidate:test_setCandidate_OnlyOwner_reverts() (gas: 15233) +RMNHome_setCandidate:test_setCandidate_OutOfBoundsNodesLength_reverts() (gas: 137295) +RMNHome_setCandidate:test_setCandidate_OutOfBoundsObserverNodeIndex_reverts() (gas: 20521) +RMNHome_setCandidate:test_setCandidate_success() (gas: 588408) +RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30159) +RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18848) +RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) +RMNHome_setDynamicConfig:test_setDynamicConfig_success() (gas: 103992) RMNRemote_constructor:test_constructor_success() (gas: 8853) RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 61072) RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 156801) diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index cea7f4c780..1c639f420a 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -14,8 +14,6 @@ import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts /// @notice CCIPHome stores the configuration for the CCIP capability. /// We have two classes of configuration: chain configuration and DON (in the CapabilitiesRegistry sense) configuration. /// Each chain will have a single configuration which includes information like the router address. -/// Each CR DON will have up to four configurations: for each of (commit, exec), one blue and one green configuration. -/// This is done in order to achieve "blue-green" deployments. contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, IERC165 { using EnumerableSet for EnumerableSet.UintSet; @@ -49,7 +47,10 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, error DigestNotFound(bytes32 configDigest); error InvalidStateTransition( - bytes32 currentPrimaryDigest, bytes32 currentSecondaryDigest, bytes32 blue, bytes32 green + bytes32 currentActiveDigest, + bytes32 currentCandidateDigest, + bytes32 proposedActiveDigest, + bytes32 proposedCandidateDigest ); /// @notice Represents an oracle node in OCR3 configs part of the role DON. @@ -124,8 +125,8 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, /// @notice The total number of configs ever set, used for generating the version of the configs. uint32 private s_configCount = 0; - /// @notice The index of the primary config. - uint32 private s_primaryConfigIndex = 0; + /// @notice The index of the active config. + uint32 private s_activeConfigIndex = 0; /// @notice Constructor for the CCIPHome contract takes in the address of the capabilities registry. This address /// is the only allowed caller to mutate the configuration through beforeCapabilityConfigSet. @@ -165,37 +166,38 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, revert OnlyCapabilitiesRegistryCanCall(); } - (OCR3Config memory blue, OCR3Config memory green) = abi.decode(update, (OCR3Config, OCR3Config)); + (OCR3Config memory newActiveConfig, OCR3Config memory newCandidateConfig) = + abi.decode(update, (OCR3Config, OCR3Config)); bytes32 pluginKey = bytes32(uint256(donId)); uint32 newConfigVersion = s_configCount + 1; - (bytes32 currentBlueDigest, bytes32 currentGreenDigest) = getConfigDigests(pluginKey); - bytes32 newBlueDigest = _calculateConfigDigest(pluginKey, abi.encode(blue), newConfigVersion); - bytes32 newGreenDigest = _calculateConfigDigest(pluginKey, abi.encode(green), newConfigVersion); + (bytes32 currentActiveDigest, bytes32 currentCandidateDigest) = getConfigDigests(pluginKey); + bytes32 newActiveDigest = _calculateConfigDigest(pluginKey, abi.encode(newActiveConfig), newConfigVersion); + bytes32 newCandidateDigest = _calculateConfigDigest(pluginKey, abi.encode(newCandidateConfig), newConfigVersion); // Check the possible steps of the state machine - // 1. promoteSecondaryAndRevokePrimary requires - // - blue digest to be the current secondary digest - // - green digest to be the zero digest - if (currentGreenDigest == newBlueDigest && newGreenDigest == ZERO_DIGEST) { - _promoteSecondaryAndRevokePrimary(pluginKey, newBlueDigest); + // 1. promoteCandidateAndRevokeActive requires + // - newActiveDigest to be the current candidate digest + // - newCandidateDigest to be the zero digest + if (currentCandidateDigest == newActiveDigest && newCandidateDigest == ZERO_DIGEST) { + _promoteCandidateAndRevokeActive(pluginKey, newActiveDigest); return; } - // setSecondary and revokeSecondary require no changes to the blue config - if (currentBlueDigest == newBlueDigest) { - // 2. If the green config is non-zero, we call setSecondary - if (newGreenDigest != ZERO_DIGEST) { - _setSecondary(pluginKey, blue, newBlueDigest); + // setCandidate and revokeCandidate require no changes to the active config + if (currentActiveDigest == newActiveDigest) { + // 2. If the candidate config is non-zero, we call setCandidate + if (newCandidateDigest != ZERO_DIGEST) { + _setCandidate(pluginKey, newActiveConfig, newActiveDigest); return; } else { - // 3. If the green config is zero, we call revokeSecondary - _revokeSecondary(pluginKey, newGreenDigest); + // 3. If the candidate config is zero, we call revokeCandidate + _revokeCandidate(pluginKey, newCandidateDigest); return; } } // There are no other valid state transitions so we revert if we have not returned by now. - revert InvalidStateTransition(currentBlueDigest, currentGreenDigest, newBlueDigest, newGreenDigest); + revert InvalidStateTransition(currentActiveDigest, currentCandidateDigest, newActiveDigest, newCandidateDigest); } /// @inheritdoc ICapabilityConfiguration @@ -209,30 +211,29 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, // │ Getters │ // ================================================================ - /// @notice Returns the current primary and secondary config digests. + /// @notice Returns the current active and candidate config digests. /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. /// @param pluginKey The key of the plugin to get the config digests for. - /// @return primaryConfigDigest The digest of the primary config. - /// @return secondaryConfigDigest The digest of the secondary config. + /// @return activeConfigDigest The digest of the active config. + /// @return candidateConfigDigest The digest of the candidate config. function getConfigDigests( bytes32 pluginKey - ) public view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { + ) public view returns (bytes32 activeConfigDigest, bytes32 candidateConfigDigest) { return ( - s_configs[pluginKey][s_primaryConfigIndex].configDigest, - s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest + s_configs[pluginKey][s_activeConfigIndex].configDigest, s_configs[pluginKey][s_activeConfigIndex ^ 1].configDigest ); } - /// @notice Returns the primary config digest for for a given key. + /// @notice Returns the active config digest for for a given key. /// @param pluginKey The key of the plugin to get the config digests for. - function getPrimaryDigest(bytes32 pluginKey) public view returns (bytes32) { - return s_configs[pluginKey][s_primaryConfigIndex].configDigest; + function getActiveDigest(bytes32 pluginKey) public view returns (bytes32) { + return s_configs[pluginKey][s_activeConfigIndex].configDigest; } - /// @notice Returns the secondary config digest for for a given key. + /// @notice Returns the candidate config digest for for a given key. /// @param pluginKey The key of the plugin to get the config digests for. - function getSecondaryDigest(bytes32 pluginKey) public view returns (bytes32) { - return s_configs[pluginKey][s_primaryConfigIndex ^ 1].configDigest; + function getCandidateDigest(bytes32 pluginKey) public view returns (bytes32) { + return s_configs[pluginKey][s_activeConfigIndex ^ 1].configDigest; } /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use @@ -256,49 +257,49 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, return (versionedConfig, false); } - /// @notice Returns the primary and secondary configuration for a given plugin key. + /// @notice Returns the active and candidate configuration for a given plugin key. /// @param pluginKey The unique key for the DON that the configuration applies to. - /// @return primaryConfig The primary configuration. - /// @return secondaryConfig The secondary configuration. + /// @return activeConfig The active configuration. + /// @return candidateConfig The candidate configuration. function getAllConfigs( bytes32 pluginKey - ) external view returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) { - VersionedConfig memory storedPrimaryConfig = s_configs[pluginKey][s_primaryConfigIndex]; - if (storedPrimaryConfig.configDigest != ZERO_DIGEST) { - primaryConfig = storedPrimaryConfig; + ) external view returns (VersionedConfig memory activeConfig, VersionedConfig memory candidateConfig) { + VersionedConfig memory storedActiveConfig = s_configs[pluginKey][s_activeConfigIndex]; + if (storedActiveConfig.configDigest != ZERO_DIGEST) { + activeConfig = storedActiveConfig; } - VersionedConfig memory storedSecondaryConfig = s_configs[pluginKey][s_primaryConfigIndex ^ 1]; - if (storedSecondaryConfig.configDigest != ZERO_DIGEST) { - secondaryConfig = storedSecondaryConfig; + VersionedConfig memory storedCandidateConfig = s_configs[pluginKey][s_activeConfigIndex ^ 1]; + if (storedCandidateConfig.configDigest != ZERO_DIGEST) { + candidateConfig = storedCandidateConfig; } - return (primaryConfig, secondaryConfig); + return (activeConfig, candidateConfig); } // ================================================================ // │ State transitions │ // ================================================================ - /// @notice Sets a new config as the secondary config. Does not influence the primary config. + /// @notice Sets a new config as the candidate config. Does not influence the active config. /// @param pluginKey The key of the plugin to set the config for. /// @return newConfigDigest The digest of the new config. - function _setSecondary( + function _setCandidate( bytes32 pluginKey, OCR3Config memory config, bytes32 newDigest ) internal returns (bytes32 newConfigDigest) { _validateConfig(config); - bytes32 existingDigest = getSecondaryDigest(pluginKey); + bytes32 existingDigest = getCandidateDigest(pluginKey); // are we going to overwrite a config? If so, emit an event. - if (getSecondaryDigest(pluginKey) != ZERO_DIGEST) { + if (getCandidateDigest(pluginKey) != ZERO_DIGEST) { emit ConfigRevoked(existingDigest); } uint32 newVersion = ++s_configCount; - VersionedConfig storage existingConfig = s_configs[pluginKey][s_primaryConfigIndex ^ 1]; + VersionedConfig storage existingConfig = s_configs[pluginKey][s_activeConfigIndex ^ 1]; existingConfig.version = newVersion; existingConfig.configDigest = newDigest; @@ -324,38 +325,38 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration, /// @notice Revokes a specific config by digest. /// @param pluginKey The key of the plugin to revoke the config for. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function _revokeSecondary(bytes32 pluginKey, bytes32 configDigest) internal { - uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[pluginKey][secondaryConfigIndex].configDigest != configDigest) { - revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, configDigest); + function _revokeCandidate(bytes32 pluginKey, bytes32 configDigest) internal { + uint256 candidateConfigIndex = s_activeConfigIndex ^ 1; + if (s_configs[pluginKey][candidateConfigIndex].configDigest != configDigest) { + revert ConfigDigestMismatch(s_configs[pluginKey][candidateConfigIndex].configDigest, configDigest); } emit ConfigRevoked(configDigest); // Delete only the digest, as that's what's used to determine if a config is active. This means the actual // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in // the future. - delete s_configs[pluginKey][secondaryConfigIndex].configDigest; + delete s_configs[pluginKey][candidateConfigIndex].configDigest; } - /// @notice Promotes the secondary config to the primary config and revokes the primary config. + /// @notice Promotes the candidate config to the active config and revokes the active config. /// @param pluginKey The key of the plugin to promote the config for. /// @param digestToPromote The digest of the config to promote. - function _promoteSecondaryAndRevokePrimary(bytes32 pluginKey, bytes32 digestToPromote) internal { - uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[pluginKey][secondaryConfigIndex].configDigest != digestToPromote) { - revert ConfigDigestMismatch(s_configs[pluginKey][secondaryConfigIndex].configDigest, digestToPromote); + function _promoteCandidateAndRevokeActive(bytes32 pluginKey, bytes32 digestToPromote) internal { + uint256 candidateConfigIndex = s_activeConfigIndex ^ 1; + if (s_configs[pluginKey][candidateConfigIndex].configDigest != digestToPromote) { + revert ConfigDigestMismatch(s_configs[pluginKey][candidateConfigIndex].configDigest, digestToPromote); } - uint256 primaryConfigIndex = s_primaryConfigIndex; + uint256 activeConfigIndex = s_activeConfigIndex; - delete s_configs[pluginKey][primaryConfigIndex].configDigest; + delete s_configs[pluginKey][activeConfigIndex].configDigest; - bytes32 digestToRevoke = s_configs[pluginKey][primaryConfigIndex].configDigest; + bytes32 digestToRevoke = s_configs[pluginKey][activeConfigIndex].configDigest; if (digestToRevoke != ZERO_DIGEST) { emit ConfigRevoked(digestToRevoke); } - s_primaryConfigIndex ^= 1; + s_activeConfigIndex ^= 1; emit ConfigPromoted(digestToPromote); } diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index c8d554cb4b..f7f697a772 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -69,29 +69,29 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @notice The total number of configs ever set, used for generating the version of the configs. uint32 private s_configCount = 0; - /// @notice The index of the primary config. - uint32 private s_primaryConfigIndex = 0; + /// @notice The index of the active config. + uint32 private s_activeConfigIndex = 0; // ================================================================ // │ Getters │ // ================================================================ - /// @notice Returns the current primary and secondary config digests. + /// @notice Returns the current active and candidate config digests. /// @dev Can be bytes32(0) if no config has been set yet or it has been revoked. - /// @return primaryConfigDigest The digest of the primary config. - /// @return secondaryConfigDigest The digest of the secondary config. - function getConfigDigests() external view returns (bytes32 primaryConfigDigest, bytes32 secondaryConfigDigest) { - return (s_configs[s_primaryConfigIndex].configDigest, s_configs[s_primaryConfigIndex ^ 1].configDigest); + /// @return activeConfigDigest The digest of the active config. + /// @return candidateConfigDigest The digest of the candidate config. + function getConfigDigests() external view returns (bytes32 activeConfigDigest, bytes32 candidateConfigDigest) { + return (s_configs[s_activeConfigIndex].configDigest, s_configs[s_activeConfigIndex ^ 1].configDigest); } - /// @notice Returns the primary config digest - function getPrimaryDigest() public view returns (bytes32) { - return s_configs[s_primaryConfigIndex].configDigest; + /// @notice Returns the active config digest + function getActiveDigest() public view returns (bytes32) { + return s_configs[s_activeConfigIndex].configDigest; } - /// @notice Returns the secondary config digest - function getSecondaryDigest() public view returns (bytes32) { - return s_configs[s_primaryConfigIndex ^ 1].configDigest; + /// @notice Returns the candidate config digest + function getCandidateDigest() public view returns (bytes32) { + return s_configs[s_activeConfigIndex ^ 1].configDigest; } /// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use @@ -113,39 +113,39 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { function getAllConfigs() external view - returns (VersionedConfig memory primaryConfig, VersionedConfig memory secondaryConfig) + returns (VersionedConfig memory activeConfig, VersionedConfig memory candidateConfig) { - VersionedConfig memory storedPrimaryConfig = s_configs[s_primaryConfigIndex]; - if (storedPrimaryConfig.configDigest != ZERO_DIGEST) { - primaryConfig = storedPrimaryConfig; + VersionedConfig memory storedActiveConfig = s_configs[s_activeConfigIndex]; + if (storedActiveConfig.configDigest != ZERO_DIGEST) { + activeConfig = storedActiveConfig; } - VersionedConfig memory storedSecondaryConfig = s_configs[s_primaryConfigIndex ^ 1]; - if (storedSecondaryConfig.configDigest != ZERO_DIGEST) { - secondaryConfig = storedSecondaryConfig; + VersionedConfig memory storedCandidateConfig = s_configs[s_activeConfigIndex ^ 1]; + if (storedCandidateConfig.configDigest != ZERO_DIGEST) { + candidateConfig = storedCandidateConfig; } - return (primaryConfig, secondaryConfig); + return (activeConfig, candidateConfig); } // ================================================================ // │ State transitions │ // ================================================================ - /// @notice Sets a new config as the secondary config. Does not influence the primary config. + /// @notice Sets a new config as the candidate config. Does not influence the active config. /// @param staticConfig The static part of the config. /// @param dynamicConfig The dynamic part of the config. /// @param digestToOverwrite The digest of the config to overwrite, or ZERO_DIGEST if no config is to be overwritten. /// This is done to prevent accidental overwrites. /// @return newConfigDigest The digest of the new config. - function setSecondary( + function setCandidate( StaticConfig calldata staticConfig, DynamicConfig calldata dynamicConfig, bytes32 digestToOverwrite ) external onlyOwner returns (bytes32 newConfigDigest) { _validateStaticAndDynamicConfig(staticConfig, dynamicConfig); - bytes32 existingDigest = getSecondaryDigest(); + bytes32 existingDigest = getCandidateDigest(); if (existingDigest != digestToOverwrite) { revert ConfigDigestMismatch(existingDigest, digestToOverwrite); @@ -159,7 +159,7 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { uint32 newVersion = ++s_configCount; newConfigDigest = _calculateConfigDigest(abi.encode(staticConfig), newVersion); - VersionedConfig storage existingConfig = s_configs[s_primaryConfigIndex ^ 1]; + VersionedConfig storage existingConfig = s_configs[s_activeConfigIndex ^ 1]; existingConfig.configDigest = newConfigDigest; existingConfig.version = newVersion; existingConfig.staticConfig = staticConfig; @@ -172,36 +172,36 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @notice Revokes a specific config by digest. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. - function revokeSecondary(bytes32 configDigest) external onlyOwner { - uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[secondaryConfigIndex].configDigest != configDigest) { - revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, configDigest); + function revokeCandidate(bytes32 configDigest) external onlyOwner { + uint256 candidateConfigIndex = s_activeConfigIndex ^ 1; + if (s_configs[candidateConfigIndex].configDigest != configDigest) { + revert ConfigDigestMismatch(s_configs[candidateConfigIndex].configDigest, configDigest); } emit ConfigRevoked(configDigest); // Delete only the digest, as that's what's used to determine if a config is active. This means the actual // config stays in storage which should significantly reduce the gas cost of overwriting that storage space in // the future. - delete s_configs[secondaryConfigIndex].configDigest; + delete s_configs[candidateConfigIndex].configDigest; } - /// @notice Promotes the secondary config to the primary config and revokes the primary config. + /// @notice Promotes the candidate config to the active config and revokes the active config. /// @param digestToPromote The digest of the config to promote. /// @param digestToRevoke The digest of the config to revoke. - function promoteSecondaryAndRevokePrimary(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { - uint256 secondaryConfigIndex = s_primaryConfigIndex ^ 1; - if (s_configs[secondaryConfigIndex].configDigest != digestToPromote) { - revert ConfigDigestMismatch(s_configs[secondaryConfigIndex].configDigest, digestToPromote); + function promoteCandidateAndRevokeActive(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { + uint256 candidateConfigIndex = s_activeConfigIndex ^ 1; + if (s_configs[candidateConfigIndex].configDigest != digestToPromote) { + revert ConfigDigestMismatch(s_configs[candidateConfigIndex].configDigest, digestToPromote); } - uint256 primaryConfigIndex = s_primaryConfigIndex; - if (s_configs[primaryConfigIndex].configDigest != digestToRevoke) { - revert ConfigDigestMismatch(s_configs[primaryConfigIndex].configDigest, digestToRevoke); + uint256 activeConfigIndex = s_activeConfigIndex; + if (s_configs[activeConfigIndex].configDigest != digestToRevoke) { + revert ConfigDigestMismatch(s_configs[activeConfigIndex].configDigest, digestToRevoke); } - delete s_configs[primaryConfigIndex].configDigest; + delete s_configs[activeConfigIndex].configDigest; - s_primaryConfigIndex ^= 1; + s_activeConfigIndex ^= 1; if (digestToRevoke != ZERO_DIGEST) { emit ConfigRevoked(digestToRevoke); } diff --git a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol index 16c299a573..276a006f66 100644 --- a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol @@ -53,8 +53,8 @@ contract RMNHomeTest is Test { } } -contract RMNHome_setSecondary is RMNHomeTest { - function test_setSecondary_success() public { +contract RMNHome_setCandidate is RMNHomeTest { + function test_setCandidate_success() public { Config memory config = _getBaseConfig(); RMNHome.VersionedConfig memory versionedConfig = RMNHome.VersionedConfig({ version: 1, @@ -70,7 +70,7 @@ contract RMNHome_setSecondary is RMNHomeTest { versionedConfig.configDigest, versionedConfig.version, versionedConfig.staticConfig, versionedConfig.dynamicConfig ); - s_rmnHome.setSecondary(versionedConfig.staticConfig, versionedConfig.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(versionedConfig.staticConfig, versionedConfig.dynamicConfig, ZERO_DIGEST); (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(versionedConfig.configDigest); assertTrue(ok); @@ -96,61 +96,61 @@ contract RMNHome_setSecondary is RMNHomeTest { assertEq(storedStaticConfig.offchainConfig, versionedConfig.staticConfig.offchainConfig); } - function test_setSecondary_OutOfBoundsNodesLength_reverts() public { + function test_setCandidate_OutOfBoundsNodesLength_reverts() public { Config memory config = _getBaseConfig(); config.staticConfig.nodes = new RMNHome.Node[](257); vm.expectRevert(RMNHome.OutOfBoundsNodesLength.selector); - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setSecondary_DuplicatePeerId_reverts() public { + function test_setCandidate_DuplicatePeerId_reverts() public { Config memory config = _getBaseConfig(); config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; vm.expectRevert(RMNHome.DuplicatePeerId.selector); - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setSecondary_DuplicateOffchainPublicKey_reverts() public { + function test_setCandidate_DuplicateOffchainPublicKey_reverts() public { Config memory config = _getBaseConfig(); config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; vm.expectRevert(RMNHome.DuplicateOffchainPublicKey.selector); - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setSecondary_DuplicateSourceChain_reverts() public { + function test_setCandidate_DuplicateSourceChain_reverts() public { Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; vm.expectRevert(RMNHome.DuplicateSourceChain.selector); - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setSecondary_OutOfBoundsObserverNodeIndex_reverts() public { + function test_setCandidate_OutOfBoundsObserverNodeIndex_reverts() public { Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; vm.expectRevert(RMNHome.OutOfBoundsObserverNodeIndex.selector); - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setSecondary_MinObserversTooHigh_reverts() public { + function test_setCandidate_MinObserversTooHigh_reverts() public { Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setSecondary_OnlyOwner_reverts() public { + function test_setCandidate_OnlyOwner_reverts() public { Config memory config = _getBaseConfig(); vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } } @@ -158,23 +158,23 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } function test_setDynamicConfig_success() public { - (bytes32 priorPrimaryDigest,) = s_rmnHome.getConfigDigests(); + (bytes32 priorActiveDigest,) = s_rmnHome.getConfigDigests(); Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers--; - (, bytes32 secondaryConfigDigest) = s_rmnHome.getConfigDigests(); + (, bytes32 candidateConfigDigest) = s_rmnHome.getConfigDigests(); vm.expectEmit(); - emit RMNHome.DynamicConfigSet(secondaryConfigDigest, config.dynamicConfig); + emit RMNHome.DynamicConfigSet(candidateConfigDigest, config.dynamicConfig); - s_rmnHome.setDynamicConfig(config.dynamicConfig, secondaryConfigDigest); + s_rmnHome.setDynamicConfig(config.dynamicConfig, candidateConfigDigest); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(secondaryConfigDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(candidateConfigDigest); assertTrue(ok); assertEq( storedVersionedConfig.dynamicConfig.sourceChains[0].minObservers, @@ -182,9 +182,9 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { ); // Asser the digests don't change when updating the dynamic config - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); - assertEq(primaryDigest, priorPrimaryDigest); - assertEq(secondaryDigest, secondaryConfigDigest); + (bytes32 activeDigest, bytes32 candidateDigest) = s_rmnHome.getConfigDigests(); + assertEq(activeDigest, priorActiveDigest); + assertEq(candidateDigest, candidateConfigDigest); } // Asserts the validation function is being called @@ -217,63 +217,63 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { } } -contract RMNHome_revokeSecondary is RMNHomeTest { +contract RMNHome_revokeCandidate is RMNHomeTest { // Sets two configs function setUp() public override { super.setUp(); Config memory config = _getBaseConfig(); - bytes32 digest = s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); - s_rmnHome.promoteSecondaryAndRevokePrimary(digest, ZERO_DIGEST); + bytes32 digest = s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.promoteCandidateAndRevokeActive(digest, ZERO_DIGEST); config.dynamicConfig.sourceChains[0].minObservers--; - s_rmnHome.setSecondary(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_revokeSecondary_success() public { - (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); + function test_revokeCandidate_success() public { + (bytes32 priorActiveDigest, bytes32 priorCandidateDigest) = s_rmnHome.getConfigDigests(); vm.expectEmit(); - emit RMNHome.ConfigRevoked(priorSecondaryDigest); + emit RMNHome.ConfigRevoked(priorCandidateDigest); - s_rmnHome.revokeSecondary(priorSecondaryDigest); + s_rmnHome.revokeCandidate(priorCandidateDigest); - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorSecondaryDigest); + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorCandidateDigest); assertFalse(ok); // Ensure no old data is returned, even though it's still in storage assertEq(storedVersionedConfig.version, 0); assertEq(storedVersionedConfig.staticConfig.nodes.length, 0); assertEq(storedVersionedConfig.dynamicConfig.sourceChains.length, 0); - // Asser the primary digest is unaffected but the secondary digest is set to zero - (bytes32 primaryDigest, bytes32 secondaryDigest) = s_rmnHome.getConfigDigests(); - assertEq(primaryDigest, priorPrimaryDigest); - assertEq(secondaryDigest, ZERO_DIGEST); - assertTrue(secondaryDigest != priorSecondaryDigest); + // Asser the active digest is unaffected but the candidate digest is set to zero + (bytes32 activeDigest, bytes32 candidateDigest) = s_rmnHome.getConfigDigests(); + assertEq(activeDigest, priorActiveDigest); + assertEq(candidateDigest, ZERO_DIGEST); + assertTrue(candidateDigest != priorCandidateDigest); } - function test_revokeSecondary_ConfigDigestMismatch_reverts() public { - (, bytes32 priorSecondaryDigest) = s_rmnHome.getConfigDigests(); + function test_revokeCandidate_ConfigDigestMismatch_reverts() public { + (, bytes32 priorCandidateDigest) = s_rmnHome.getConfigDigests(); bytes32 wrongDigest = keccak256("wrong_digest"); - vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); - s_rmnHome.revokeSecondary(wrongDigest); + vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorCandidateDigest, wrongDigest)); + s_rmnHome.revokeCandidate(wrongDigest); } - function test_revokeSecondary_OnlyOwner_reverts() public { + function test_revokeCandidate_OnlyOwner_reverts() public { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.revokeSecondary(keccak256("configDigest")); + s_rmnHome.revokeCandidate(keccak256("configDigest")); } } -contract RMNHome_promoteSecondaryAndRevokePrimary is RMNHomeTest { - function test_promoteSecondaryAndRevokePrimary_success() public {} +contract RMNHome_promoteCandidateAndRevokeActive is RMNHomeTest { + function test_promoteCandidateAndRevokeActive_success() public {} - function test_promoteSecondaryAndRevokePrimary_OnlyOwner_reverts() public { + function test_promoteCandidateAndRevokeActive_OnlyOwner_reverts() public { vm.startPrank(address(0)); vm.expectRevert("Only callable by owner"); - s_rmnHome.promoteSecondaryAndRevokePrimary(keccak256("toPromote"), keccak256("ToRevoke")); + s_rmnHome.promoteCandidateAndRevokeActive(keccak256("toPromote"), keccak256("ToRevoke")); } } From 9426e0ccad23b23b263916d3b204caf151742279 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 26 Sep 2024 11:58:17 +0200 Subject: [PATCH 35/36] improve comments --- .../src/v0.8/ccip/capability/RMNHome.sol | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/contracts/src/v0.8/ccip/capability/RMNHome.sol b/contracts/src/v0.8/ccip/capability/RMNHome.sol index f7f697a772..f949c46e78 100644 --- a/contracts/src/v0.8/ccip/capability/RMNHome.sol +++ b/contracts/src/v0.8/ccip/capability/RMNHome.sol @@ -46,6 +46,8 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { bytes offchainConfig; // Offchain configuration for RMN nodes. } + /// @notice The main struct stored in the contract, containing the static and dynamic parts of the config as well as + /// the version and the digest of the config. struct VersionedConfig { uint32 version; bytes32 configDigest; @@ -62,6 +64,8 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { uint256 private constant MAX_CONCURRENT_CONFIGS = 2; /// @notice Helper to identify the zero config digest with less casting. bytes32 private constant ZERO_DIGEST = bytes32(uint256(0)); + // @notice To ensure that observerNodesBitmap can be bit-encoded into a uint256. + uint256 private constant MAX_NODES = 256; /// @notice This array holds the configs. /// @dev Value i in this array is valid iff s_configs[i].configDigest != 0. @@ -69,7 +73,11 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @notice The total number of configs ever set, used for generating the version of the configs. uint32 private s_configCount = 0; - /// @notice The index of the active config. + /// @notice The index of the active config. Used to determine which config is active. Adding the configs to a list + /// with two items and using this index to determine which one is active is a gas efficient way to handle this. Having + /// a set place for the active config would mean we have to copy the candidate config to the active config when it is + /// promoted, which would be more expensive. This index allows us to flip the configs around using `XOR 1`, which + /// flips 0 to 1 and 1 to 0. uint32 private s_activeConfigIndex = 0; // ================================================================ @@ -170,7 +178,9 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { return newConfigDigest; } - /// @notice Revokes a specific config by digest. + /// @notice Revokes a specific config by digest. This is used when the candidate config turns out to be incorrect to + /// remove it without it ever having to be promoted. It's also possible to revoke the candidate config by setting a + /// newer candidate config using `setCandidate`. /// @param configDigest The digest of the config to revoke. This is done to prevent accidental revokes. function revokeCandidate(bytes32 configDigest) external onlyOwner { uint256 candidateConfigIndex = s_activeConfigIndex ^ 1; @@ -188,6 +198,9 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { /// @notice Promotes the candidate config to the active config and revokes the active config. /// @param digestToPromote The digest of the config to promote. /// @param digestToRevoke The digest of the config to revoke. + /// @dev No config is changed in storage, the only storage changes that happen are + /// - The activeConfigIndex is flipped. + /// - The digest of the old active config is deleted. function promoteCandidateAndRevokeActive(bytes32 digestToPromote, bytes32 digestToRevoke) external onlyOwner { uint256 candidateConfigIndex = s_activeConfigIndex ^ 1; if (s_configs[candidateConfigIndex].configDigest != digestToPromote) { @@ -251,7 +264,7 @@ contract RMNHome is OwnerIsCreator, ITypeAndVersion { DynamicConfig memory dynamicConfig ) internal pure { // Ensure that observerNodesBitmap can be bit-encoded into a uint256. - if (staticConfig.nodes.length > 256) { + if (staticConfig.nodes.length > MAX_NODES) { revert OutOfBoundsNodesLength(); } From 60d07b4baea2e77ce45ac57cefb383a122523c30 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 26 Sep 2024 12:17:34 +0200 Subject: [PATCH 36/36] full cov rmn --- contracts/gas-snapshots/ccip.gas-snapshot | 336 +++++++++--------- .../ccip/test/capability/HomeBaseTest.t.sol | 199 ----------- .../ccip/test/capability/RMNHomeTest.t.sol | 219 +++++++----- 3 files changed, 304 insertions(+), 450 deletions(-) delete mode 100644 contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 0b1af96d31..6833727344 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -516,29 +516,29 @@ MultiOCR3Base_transmit:test_UnauthorizedSigner_Revert() (gas: 69917) MultiOCR3Base_transmit:test_UnconfiguredPlugin_Revert() (gas: 42537) MultiOCR3Base_transmit:test_ZeroSignatures_Revert() (gas: 37478) MultiOnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 304699) -MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1504663) -NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 37934) -NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 23706) -NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 38778) -NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 71901) -NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 262159) -NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 265836) -NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 329824) -NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 300784) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 249120) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 237027) -NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 153748) -NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 168959) -NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 221148) -NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 126861) -NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 107723) -NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 123207) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 43079) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 64408) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 42943) -NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 66696) -NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12070) -NonceManager_typeAndVersion:test_typeAndVersion() (gas: 9705) +MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1926302) +NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 40448) +NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 27403) +NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 41879) +NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 79874) +NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 316259) +NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 322432) +NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 417132) +NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 375380) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 327638) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 313211) +NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 183405) +NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 193460) +NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 268061) +NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 153619) +NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 125710) +NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 134448) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 47118) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 69128) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 46949) +NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 72431) +NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12892) +NonceManager_typeAndVersion:test_typeAndVersion() (gas: 10623) OCR2BaseNoChecks_setOCR2Config:test_FMustBePositive_Revert() (gas: 15443) OCR2BaseNoChecks_setOCR2Config:test_RepeatAddress_Revert() (gas: 48841) OCR2BaseNoChecks_setOCR2Config:test_SetConfigSuccess_gas() (gas: 97138) @@ -564,140 +564,140 @@ OCR2Base_transmit:test_Transmit2SignersSuccess_gas() (gas: 58490) OCR2Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 27448) OCR2Base_transmit:test_UnauthorizedSigner_Revert() (gas: 45597) OCR2Base_transmit:test_WrongNumberOfSignatures_Revert() (gas: 23593) -OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 5656596) -OffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 469391) -OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 99637) -OffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 12591) -OffRamp_applySourceChainConfigUpdates:test_InvalidOnRampUpdate_Revert() (gas: 205322) -OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Success() (gas: 101437) -OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 111307) -OffRamp_applySourceChainConfigUpdates:test_RouterAddress_Revert() (gas: 13441) -OffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 72702) -OffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 15497) -OffRamp_batchExecute:test_MultipleReportsDifferentChainsSkipCursedChain_Success() (gas: 177979) -OffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 335646) -OffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 278912) -OffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 169308) -OffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 189031) -OffRamp_batchExecute:test_SingleReport_Success() (gas: 157132) -OffRamp_batchExecute:test_Unhealthy_Success() (gas: 554208) -OffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10600) -OffRamp_ccipReceive:test_Reverts() (gas: 15385) -OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 92905) -OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 64099) -OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 68124) -OffRamp_commit:test_InvalidInterval_Revert() (gas: 64291) -OffRamp_commit:test_InvalidRootRevert() (gas: 63356) -OffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6260756) -OffRamp_commit:test_NoConfig_Revert() (gas: 5844428) -OffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 113042) -OffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 121403) -OffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 113063) -OffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 355198) -OffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 164351) -OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 139364) -OffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 146555) -OffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 59858) -OffRamp_commit:test_StaleReportWithRoot_Success() (gas: 232074) -OffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 125409) -OffRamp_commit:test_Unhealthy_Revert() (gas: 58633) -OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 206729) -OffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 51722) -OffRamp_constructor:test_Constructor_Success() (gas: 5845416) -OffRamp_constructor:test_SourceChainSelector_Revert() (gas: 135514) -OffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 102949) -OffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 100843) -OffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 161040) -OffRamp_constructor:test_ZeroRMNRemote_Revert() (gas: 100763) -OffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 100801) -OffRamp_execute:test_IncorrectArrayType_Revert() (gas: 17639) -OffRamp_execute:test_LargeBatch_Success() (gas: 3426635) -OffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 373095) -OffRamp_execute:test_MultipleReports_Success() (gas: 301009) -OffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6669661) -OffRamp_execute:test_NoConfig_Revert() (gas: 5894136) -OffRamp_execute:test_NonArray_Revert() (gas: 27562) -OffRamp_execute:test_SingleReport_Success() (gas: 176364) -OffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 148382) -OffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6672410) -OffRamp_execute:test_ZeroReports_Revert() (gas: 17361) -OffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 18511) -OffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 244057) -OffRamp_executeSingleMessage:test_NonContract_Success() (gas: 20759) -OffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 205094) -OffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 49316) -OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 48760) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 218081) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 85349) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 274194) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithVInterception_Success() (gas: 91809) -OffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 28260) -OffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 22062) -OffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 481748) -OffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 48372) -OffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 33959) -OffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 28436) -OffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 188084) -OffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 198518) -OffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 40741) -OffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 413233) -OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 249776) -OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 193590) -OffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 213624) -OffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 249506) -OffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 142151) -OffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 409289) -OffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 58293) -OffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 73868) -OffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 583401) -OffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 532115) -OffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 33717) -OffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 549738) -OffRamp_executeSingleReport:test_Unhealthy_Success() (gas: 549752) -OffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 460495) -OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 135910) -OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 165615) -OffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3868658) -OffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 120474) -OffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 89178) -OffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 81284) -OffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 74161) -OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 173244) -OffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 214189) -OffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 27152) -OffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 165098) -OffRamp_manuallyExecute:test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() (gas: 27689) -OffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 55260) -OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 498614) -OffRamp_manuallyExecute:test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() (gas: 316158) -OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails_Success() (gas: 2245360) -OffRamp_manuallyExecute:test_manuallyExecute_SourceChainSelectorMismatch_Revert() (gas: 165602) -OffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 227234) -OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 227774) -OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 781510) -OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 347431) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 37634) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 104404) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 85342) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 36752) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 94382) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 39741) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 86516) -OffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 162381) -OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 23903) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 62751) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 79790) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 174512) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_WithGasOverride_Success() (gas: 176424) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 187723) -OffRamp_setDynamicConfig:test_FeeQuoterZeroAddress_Revert() (gas: 11269) -OffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 13884) -OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor_Success() (gas: 46421) -OffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 24463) -OffRamp_trialExecute:test_RateLimitError_Success() (gas: 219355) -OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 227977) -OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 295350) -OffRamp_trialExecute:test_trialExecute_Success() (gas: 277894) +OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 8902529) +OffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 495132) +OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 107802) +OffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 13418) +OffRamp_applySourceChainConfigUpdates:test_InvalidOnRampUpdate_Revert() (gas: 231022) +OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Success() (gas: 110522) +OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 124535) +OffRamp_applySourceChainConfigUpdates:test_RouterAddress_Revert() (gas: 15757) +OffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 77820) +OffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 17894) +OffRamp_batchExecute:test_MultipleReportsDifferentChainsSkipCursedChain_Success() (gas: 224083) +OffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 425996) +OffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 368952) +OffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 208104) +OffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 239311) +OffRamp_batchExecute:test_SingleReport_Success() (gas: 188653) +OffRamp_batchExecute:test_Unhealthy_Success() (gas: 717329) +OffRamp_batchExecute:test_ZeroReports_Revert() (gas: 12088) +OffRamp_ccipReceive:test_Reverts() (gas: 18753) +OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 109720) +OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 77314) +OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 83071) +OffRamp_commit:test_InvalidInterval_Revert() (gas: 77116) +OffRamp_commit:test_InvalidRootRevert() (gas: 75631) +OffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 9548401) +OffRamp_commit:test_NoConfig_Revert() (gas: 9122532) +OffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 130728) +OffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 147353) +OffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 130749) +OffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 424203) +OffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 190103) +OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 161941) +OffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 176263) +OffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 72480) +OffRamp_commit:test_StaleReportWithRoot_Success() (gas: 280739) +OffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 144515) +OffRamp_commit:test_Unhealthy_Revert() (gas: 71728) +OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 250379) +OffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 61202) +OffRamp_constructor:test_Constructor_Success() (gas: 9121263) +OffRamp_constructor:test_SourceChainSelector_Revert() (gas: 148706) +OffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 112646) +OffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 110448) +OffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 174260) +OffRamp_constructor:test_ZeroRMNRemote_Revert() (gas: 110450) +OffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 110395) +OffRamp_execute:test_IncorrectArrayType_Revert() (gas: 19670) +OffRamp_execute:test_LargeBatch_Success() (gas: 4789035) +OffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 462387) +OffRamp_execute:test_MultipleReports_Success() (gas: 392392) +OffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 9972739) +OffRamp_execute:test_NoConfig_Revert() (gas: 9179658) +OffRamp_execute:test_NonArray_Revert() (gas: 34048) +OffRamp_execute:test_SingleReport_Success() (gas: 209314) +OffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 173200) +OffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 9980482) +OffRamp_execute:test_ZeroReports_Revert() (gas: 19285) +OffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 23977) +OffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 277738) +OffRamp_executeSingleMessage:test_NonContract_Success() (gas: 26476) +OffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 236582) +OffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 60990) +OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 60132) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 267137) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 98126) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 320798) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithVInterception_Success() (gas: 108714) +OffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 36766) +OffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 24545) +OffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 595814) +OffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 60468) +OffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 42455) +OffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 37212) +OffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 221890) +OffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 237935) +OffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 52346) +OffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 625018) +OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 297913) +OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 252901) +OffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 271928) +OffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 307332) +OffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 168258) +OffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 502204) +OffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 71999) +OffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 86531) +OffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 733681) +OffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 666498) +OffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 42450) +OffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 710720) +OffRamp_executeSingleReport:test_Unhealthy_Success() (gas: 710723) +OffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 574134) +OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 172776) +OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 202237) +OffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 5703276) +OffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 148004) +OffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 111520) +OffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 128026) +OffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 103971) +OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 208382) +OffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 261275) +OffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 37688) +OffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 267627) +OffRamp_manuallyExecute:test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() (gas: 38216) +OffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 73056) +OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 742494) +OffRamp_manuallyExecute:test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() (gas: 408960) +OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails_Success() (gas: 3574005) +OffRamp_manuallyExecute:test_manuallyExecute_SourceChainSelectorMismatch_Revert() (gas: 199720) +OffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 279997) +OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 280693) +OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 1033763) +OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 461923) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 47648) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 121919) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 99377) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 44074) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 109868) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 46405) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 101128) +OffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 187037) +OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 28901) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 74851) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 94066) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 206891) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_WithGasOverride_Success() (gas: 209712) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 217613) +OffRamp_setDynamicConfig:test_FeeQuoterZeroAddress_Revert() (gas: 12524) +OffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 15529) +OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor_Success() (gas: 50816) +OffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 28782) +OffRamp_trialExecute:test_RateLimitError_Success() (gas: 259624) +OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 269734) +OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 386633) +OffRamp_trialExecute:test_trialExecute_Success() (gas: 329701) OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 508639) OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() (gas: 21134) OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_Revert() (gas: 80371) @@ -752,19 +752,21 @@ PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 21848) PingPong_plumbing:test_Pausing_Success() (gas: 19077) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 197173) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 215989) -RMNHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_OnlyOwner_reverts() (gas: 10979) -RMNHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_success() (gas: 164) +RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_DuplicateOffchainPublicKey_reverts() (gas: 18822) +RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_DuplicatePeerId_reverts() (gas: 18682) +RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_DuplicateSourceChain_reverts() (gas: 20371) +RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_MinObserversTooHigh_reverts() (gas: 20810) +RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_OutOfBoundsNodesLength_reverts() (gas: 137268) +RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_OutOfBoundsObserverNodeIndex_reverts() (gas: 20472) +RMNHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_ConfigDigestMismatch_reverts() (gas: 23802) +RMNHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_OnlyOwner_reverts() (gas: 10957) +RMNHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_success() (gas: 1059318) RMNHome_revokeCandidate:test_revokeCandidate_ConfigDigestMismatch_reverts() (gas: 19073) RMNHome_revokeCandidate:test_revokeCandidate_OnlyOwner_reverts() (gas: 10984) RMNHome_revokeCandidate:test_revokeCandidate_success() (gas: 28184) -RMNHome_setCandidate:test_setCandidate_DuplicateOffchainPublicKey_reverts() (gas: 18871) -RMNHome_setCandidate:test_setCandidate_DuplicatePeerId_reverts() (gas: 18687) -RMNHome_setCandidate:test_setCandidate_DuplicateSourceChain_reverts() (gas: 20408) -RMNHome_setCandidate:test_setCandidate_MinObserversTooHigh_reverts() (gas: 20805) -RMNHome_setCandidate:test_setCandidate_OnlyOwner_reverts() (gas: 15233) -RMNHome_setCandidate:test_setCandidate_OutOfBoundsNodesLength_reverts() (gas: 137295) -RMNHome_setCandidate:test_setCandidate_OutOfBoundsObserverNodeIndex_reverts() (gas: 20521) -RMNHome_setCandidate:test_setCandidate_success() (gas: 588408) +RMNHome_setCandidate:test_setCandidate_ConfigDigestMismatch_reverts() (gas: 594772) +RMNHome_setCandidate:test_setCandidate_OnlyOwner_reverts() (gas: 15177) +RMNHome_setCandidate:test_setCandidate_success() (gas: 588430) RMNHome_setDynamicConfig:test_setDynamicConfig_DigestNotFound_reverts() (gas: 30159) RMNHome_setDynamicConfig:test_setDynamicConfig_MinObserversTooHigh_reverts() (gas: 18848) RMNHome_setDynamicConfig:test_setDynamicConfig_OnlyOwner_reverts() (gas: 14115) diff --git a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol b/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol deleted file mode 100644 index f33e693062..0000000000 --- a/contracts/src/v0.8/ccip/test/capability/HomeBaseTest.t.sol +++ /dev/null @@ -1,199 +0,0 @@ -//// SPDX-License-Identifier: BUSL-1.1 -//pragma solidity 0.8.24; -// -//import {HomeBase} from "../../capability/HomeBase.sol"; -//import {Internal} from "../../libraries/Internal.sol"; -//import {HomeBaseHelper} from "../helpers/HomeBaseHelper.sol"; -//import {Test} from "forge-std/Test.sol"; -//import {Vm} from "forge-std/Vm.sol"; -// -//contract HomeBaseTest is Test { -// bytes32 internal constant DON_ID = bytes32(uint256(0x87654321eabc)); -// -// bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); -// -// HomeBaseHelper internal s_homeBase; -// -// function setUp() public virtual { -// s_homeBase = new HomeBaseHelper(); -// } -// -// uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 -// uint256 public constant PREFIX = 0x0c0c << (256 - 16); -// -// function _getConfigDigest(bytes memory staticConfig, uint32 version) internal view returns (bytes32) { -// return bytes32( -// (PREFIX & PREFIX_MASK) -// | ( -// uint256( -// keccak256( -// bytes.concat(abi.encode(bytes32("EVM"), block.chainid, address(s_homeBase), DON_ID, version), staticConfig) -// ) -// ) & ~PREFIX_MASK -// ) -// ); -// } -// -// function _getStaticConfig() internal pure returns (bytes memory) { -// return abi.encode("staticConfig"); -// } -// -// function _getDynamicConfig() internal pure returns (bytes memory) { -// return abi.encode("dynamicConfig"); -// } -//} -// -//contract RMNHome_setSecondary is HomeBaseTest { -// function test_setSecondary_success() public { -// HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ -// configDigest: ZERO_DIGEST, -// version: 1, -// staticConfig: _getStaticConfig(), -// dynamicConfig: _getDynamicConfig() -// }); -// -// encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); -// -// vm.expectEmit(); -// emit HomeBase.ConfigSet(encodedConfig.configDigest, encodedConfig); -// -// s_homeBase.setSecondary(DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST); -// -// (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getSecondaryStoredConfig(DON_ID); -// assertTrue(ok); -// assertEq(storedConfig.version, encodedConfig.version); -// assertEq(storedConfig.configDigest, encodedConfig.configDigest); -// assertEq(storedConfig.staticConfig, encodedConfig.staticConfig); -// assertEq(storedConfig.dynamicConfig, encodedConfig.dynamicConfig); -// } -// -// function test_setSecondary_InvalidCaller_reverts() public { -// vm.startPrank(address(0)); -// -// vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); -// s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); -// } -//} -// -//contract RMNHome_setDynamicConfig is HomeBaseTest { -// function setUp() public override { -// super.setUp(); -// s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); -// } -// -// function test_setDynamicConfig_success() public { -// (bytes32 priorPrimaryDigest, bytes32 secondaryConfigDigest) = s_homeBase.getConfigDigests(DON_ID); -// -// bytes memory newDynamicConfig = abi.encode("newDynamicConfig"); -// -// vm.expectEmit(); -// emit HomeBase.DynamicConfigSet(secondaryConfigDigest, newDynamicConfig); -// -// s_homeBase.setDynamicConfig(DON_ID, newDynamicConfig, secondaryConfigDigest); -// -// (HomeBase.StoredConfig memory storedConfig, bool ok) = s_homeBase.getStoredConfig(DON_ID, secondaryConfigDigest); -// assertTrue(ok); -// assertEq(storedConfig.dynamicConfig, newDynamicConfig); -// -// // Asser the digests don't change when updating the dynamic config -// (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID); -// assertEq(primaryDigest, priorPrimaryDigest); -// assertEq(secondaryDigest, secondaryConfigDigest); -// } -// -// function test_setDynamicConfig_InvalidCaller_reverts() public { -// vm.startPrank(address(0)); -// -// vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); -// s_homeBase.setDynamicConfig(DON_ID, _getDynamicConfig(), keccak256("configDigest")); -// } -//} -// -//contract RMNHome_revokeSecondary is HomeBaseTest { -// // Sets two configs -// function setUp() public override { -// super.setUp(); -// bytes32 digest = s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); -// s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, digest, ZERO_DIGEST); -// s_homeBase.setSecondary(DON_ID, _getStaticConfig(), _getDynamicConfig(), ZERO_DIGEST); -// } -// -// function test_revokeSecondary_success() public { -// (bytes32 priorPrimaryDigest, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID); -// -// vm.expectEmit(); -// emit HomeBase.ConfigRevoked(priorSecondaryDigest); -// -// s_homeBase.revokeSecondary(DON_ID, priorSecondaryDigest); -// -// (HomeBase.StoredConfig memory storedVersionedConfig, bool ok) = -// s_homeBase.getStoredConfig(DON_ID, priorSecondaryDigest); -// assertFalse(ok); -// // Ensure no old data is returned, even though it's still in storage -// assertEq(storedVersionedConfig.version, 0); -// assertEq(storedVersionedConfig.staticConfig.length, 0); -// assertEq(storedVersionedConfig.dynamicConfig.length, 0); -// -// // Asser the primary digest is unaffected but the secondary digest is set to zero -// (bytes32 primaryDigest, bytes32 secondaryDigest) = s_homeBase.getConfigDigests(DON_ID); -// assertEq(primaryDigest, priorPrimaryDigest); -// assertEq(secondaryDigest, ZERO_DIGEST); -// assertTrue(secondaryDigest != priorSecondaryDigest); -// } -// -// function test_revokeSecondary_ConfigDigestMismatch_reverts() public { -// (, bytes32 priorSecondaryDigest) = s_homeBase.getConfigDigests(DON_ID); -// -// bytes32 wrongDigest = keccak256("wrong_digest"); -// vm.expectRevert(abi.encodeWithSelector(HomeBase.ConfigDigestMismatch.selector, priorSecondaryDigest, wrongDigest)); -// s_homeBase.revokeSecondary(DON_ID, wrongDigest); -// } -// -// function test_revokeSecondary_InvalidCaller_reverts() public { -// vm.startPrank(address(0)); -// -// vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); -// s_homeBase.revokeSecondary(DON_ID, keccak256("configDigest")); -// } -//} -// -//contract RMNHome_promoteSecondaryAndRevokePrimary is HomeBaseTest { -// function test_promoteSecondaryAndRevokePrimary_success() public {} -// -// function test_promoteSecondaryAndRevokePrimary_InvalidCaller_reverts() public { -// vm.startPrank(address(0)); -// -// vm.expectRevert(HomeBaseHelper.InvalidCaller.selector); -// s_homeBase.promoteSecondaryAndRevokePrimary(DON_ID, keccak256("toPromote"), keccak256("ToRevoke")); -// } -//} -// -////contract RMNHome_beforeCapabilityConfigSet is HomeBaseTest { -//// function test_beforeCapabilityConfigSet_success() public { -//// vm.startPrank(CAPABILITIES_REGISTRY); -//// -//// HomeBase.StoredConfig memory encodedConfig = HomeBase.StoredConfig({ -//// configDigest: ZERO_DIGEST, -//// version: 1, -//// staticConfig: _getStaticConfig(), -//// dynamicConfig: _getDynamicConfig() -//// }); -//// encodedConfig.configDigest = _getConfigDigest(encodedConfig.staticConfig, encodedConfig.version); -//// -//// bytes memory callPayload = abi.encodeCall( -//// HomeBase.setSecondary, (DON_ID, encodedConfig.staticConfig, encodedConfig.dynamicConfig, ZERO_DIGEST) -//// ); -//// -//// vm.expectEmit(); -//// emit HomeBase.ConfigSet(encodedConfig); -//// -//// s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), callPayload, 0, 0); -//// } -//// -//// function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public { -//// vm.startPrank(address(0)); -//// -//// vm.expectRevert(HomeBase.OnlyCapabilitiesRegistryCanCall.selector); -//// s_homeBase.beforeCapabilityConfigSet(new bytes32[](0), new bytes(0), 0, 0); -//// } -////} diff --git a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol index 276a006f66..1160c75cf9 100644 --- a/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/RMNHomeTest.t.sol @@ -13,12 +13,7 @@ contract RMNHomeTest is Test { } bytes32 internal constant ZERO_DIGEST = bytes32(uint256(0)); - - RMNHome public s_rmnHome; - - function setUp() public virtual { - s_rmnHome = new RMNHome(); - } + RMNHome public s_rmnHome = new RMNHome(); function _getBaseConfig() internal pure returns (Config memory) { RMNHome.Node[] memory nodes = new RMNHome.Node[](3); @@ -96,7 +91,134 @@ contract RMNHome_setCandidate is RMNHomeTest { assertEq(storedStaticConfig.offchainConfig, versionedConfig.staticConfig.offchainConfig); } - function test_setCandidate_OutOfBoundsNodesLength_reverts() public { + function test_setCandidate_ConfigDigestMismatch_reverts() public { + Config memory config = _getBaseConfig(); + + bytes32 digest = s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + + vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, digest, ZERO_DIGEST)); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + + vm.expectEmit(); + emit RMNHome.ConfigRevoked(digest); + + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, digest); + } + + function test_setCandidate_OnlyOwner_reverts() public { + Config memory config = _getBaseConfig(); + + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + } +} + +contract RMNHome_revokeCandidate is RMNHomeTest { + // Sets two configs + function setUp() public { + Config memory config = _getBaseConfig(); + bytes32 digest = s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + s_rmnHome.promoteCandidateAndRevokeActive(digest, ZERO_DIGEST); + + config.dynamicConfig.sourceChains[0].minObservers--; + s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + } + + function test_revokeCandidate_success() public { + (bytes32 priorActiveDigest, bytes32 priorCandidateDigest) = s_rmnHome.getConfigDigests(); + + vm.expectEmit(); + emit RMNHome.ConfigRevoked(priorCandidateDigest); + + s_rmnHome.revokeCandidate(priorCandidateDigest); + + (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorCandidateDigest); + assertFalse(ok); + // Ensure no old data is returned, even though it's still in storage + assertEq(storedVersionedConfig.version, 0); + assertEq(storedVersionedConfig.staticConfig.nodes.length, 0); + assertEq(storedVersionedConfig.dynamicConfig.sourceChains.length, 0); + + // Asser the active digest is unaffected but the candidate digest is set to zero + (bytes32 activeDigest, bytes32 candidateDigest) = s_rmnHome.getConfigDigests(); + assertEq(activeDigest, priorActiveDigest); + assertEq(candidateDigest, ZERO_DIGEST); + assertTrue(candidateDigest != priorCandidateDigest); + } + + function test_revokeCandidate_ConfigDigestMismatch_reverts() public { + (, bytes32 priorCandidateDigest) = s_rmnHome.getConfigDigests(); + + bytes32 wrongDigest = keccak256("wrong_digest"); + vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorCandidateDigest, wrongDigest)); + s_rmnHome.revokeCandidate(wrongDigest); + } + + function test_revokeCandidate_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.revokeCandidate(keccak256("configDigest")); + } +} + +contract RMNHome_promoteCandidateAndRevokeActive is RMNHomeTest { + function test_promoteCandidateAndRevokeActive_success() public { + Config memory config = _getBaseConfig(); + bytes32 firstConfigToPromote = s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + + vm.expectEmit(); + emit RMNHome.ConfigPromoted(firstConfigToPromote); + + s_rmnHome.promoteCandidateAndRevokeActive(firstConfigToPromote, ZERO_DIGEST); + + // Assert the active digest is updated and the candidate digest is set to zero + (bytes32 activeDigest, bytes32 candidateDigest) = s_rmnHome.getConfigDigests(); + assertEq(activeDigest, firstConfigToPromote); + assertEq(candidateDigest, ZERO_DIGEST); + + bytes32 secondConfigToPromote = s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); + + vm.expectEmit(); + emit RMNHome.ConfigRevoked(firstConfigToPromote); + + vm.expectEmit(); + emit RMNHome.ConfigPromoted(secondConfigToPromote); + + s_rmnHome.promoteCandidateAndRevokeActive(secondConfigToPromote, firstConfigToPromote); + + (activeDigest, candidateDigest) = s_rmnHome.getConfigDigests(); + assertEq(activeDigest, secondConfigToPromote); + assertEq(candidateDigest, ZERO_DIGEST); + } + + function test_promoteCandidateAndRevokeActive_ConfigDigestMismatch_reverts() public { + (bytes32 priorActiveDigest, bytes32 priorCandidateDigest) = s_rmnHome.getConfigDigests(); + bytes32 wrongActiveDigest = keccak256("wrongActiveDigest"); + bytes32 wrongCandidateDigest = keccak256("wrongCandidateDigest"); + + vm.expectRevert( + abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorActiveDigest, wrongCandidateDigest) + ); + s_rmnHome.promoteCandidateAndRevokeActive(wrongCandidateDigest, wrongActiveDigest); + + vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorActiveDigest, wrongActiveDigest)); + + s_rmnHome.promoteCandidateAndRevokeActive(priorCandidateDigest, wrongActiveDigest); + } + + function test_promoteCandidateAndRevokeActive_OnlyOwner_reverts() public { + vm.startPrank(address(0)); + + vm.expectRevert("Only callable by owner"); + s_rmnHome.promoteCandidateAndRevokeActive(keccak256("toPromote"), keccak256("ToRevoke")); + } +} + +contract RMNHome__validateStaticAndDynamicConfig is RMNHomeTest { + function test_validateStaticAndDynamicConfig_OutOfBoundsNodesLength_reverts() public { Config memory config = _getBaseConfig(); config.staticConfig.nodes = new RMNHome.Node[](257); @@ -104,7 +226,7 @@ contract RMNHome_setCandidate is RMNHomeTest { s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setCandidate_DuplicatePeerId_reverts() public { + function test_validateStaticAndDynamicConfig_DuplicatePeerId_reverts() public { Config memory config = _getBaseConfig(); config.staticConfig.nodes[1].peerId = config.staticConfig.nodes[0].peerId; @@ -112,7 +234,7 @@ contract RMNHome_setCandidate is RMNHomeTest { s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setCandidate_DuplicateOffchainPublicKey_reverts() public { + function test_validateStaticAndDynamicConfig_DuplicateOffchainPublicKey_reverts() public { Config memory config = _getBaseConfig(); config.staticConfig.nodes[1].offchainPublicKey = config.staticConfig.nodes[0].offchainPublicKey; @@ -120,7 +242,7 @@ contract RMNHome_setCandidate is RMNHomeTest { s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setCandidate_DuplicateSourceChain_reverts() public { + function test_validateStaticAndDynamicConfig_DuplicateSourceChain_reverts() public { Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[1].chainSelector = config.dynamicConfig.sourceChains[0].chainSelector; @@ -128,7 +250,7 @@ contract RMNHome_setCandidate is RMNHomeTest { s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setCandidate_OutOfBoundsObserverNodeIndex_reverts() public { + function test_validateStaticAndDynamicConfig_OutOfBoundsObserverNodeIndex_reverts() public { Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].observerNodesBitmap = 1 << config.staticConfig.nodes.length; @@ -136,27 +258,17 @@ contract RMNHome_setCandidate is RMNHomeTest { s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - function test_setCandidate_MinObserversTooHigh_reverts() public { + function test_validateStaticAndDynamicConfig_MinObserversTooHigh_reverts() public { Config memory config = _getBaseConfig(); config.dynamicConfig.sourceChains[0].minObservers++; vm.expectRevert(RMNHome.MinObserversTooHigh.selector); s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } - - function test_setCandidate_OnlyOwner_reverts() public { - Config memory config = _getBaseConfig(); - - vm.startPrank(address(0)); - - vm.expectRevert("Only callable by owner"); - s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); - } } contract RMNHome_setDynamicConfig is RMNHomeTest { - function setUp() public override { - super.setUp(); + function setUp() public { Config memory config = _getBaseConfig(); s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); } @@ -216,64 +328,3 @@ contract RMNHome_setDynamicConfig is RMNHomeTest { s_rmnHome.setDynamicConfig(config.dynamicConfig, keccak256("configDigest")); } } - -contract RMNHome_revokeCandidate is RMNHomeTest { - // Sets two configs - function setUp() public override { - super.setUp(); - Config memory config = _getBaseConfig(); - bytes32 digest = s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); - s_rmnHome.promoteCandidateAndRevokeActive(digest, ZERO_DIGEST); - - config.dynamicConfig.sourceChains[0].minObservers--; - s_rmnHome.setCandidate(config.staticConfig, config.dynamicConfig, ZERO_DIGEST); - } - - function test_revokeCandidate_success() public { - (bytes32 priorActiveDigest, bytes32 priorCandidateDigest) = s_rmnHome.getConfigDigests(); - - vm.expectEmit(); - emit RMNHome.ConfigRevoked(priorCandidateDigest); - - s_rmnHome.revokeCandidate(priorCandidateDigest); - - (RMNHome.VersionedConfig memory storedVersionedConfig, bool ok) = s_rmnHome.getConfig(priorCandidateDigest); - assertFalse(ok); - // Ensure no old data is returned, even though it's still in storage - assertEq(storedVersionedConfig.version, 0); - assertEq(storedVersionedConfig.staticConfig.nodes.length, 0); - assertEq(storedVersionedConfig.dynamicConfig.sourceChains.length, 0); - - // Asser the active digest is unaffected but the candidate digest is set to zero - (bytes32 activeDigest, bytes32 candidateDigest) = s_rmnHome.getConfigDigests(); - assertEq(activeDigest, priorActiveDigest); - assertEq(candidateDigest, ZERO_DIGEST); - assertTrue(candidateDigest != priorCandidateDigest); - } - - function test_revokeCandidate_ConfigDigestMismatch_reverts() public { - (, bytes32 priorCandidateDigest) = s_rmnHome.getConfigDigests(); - - bytes32 wrongDigest = keccak256("wrong_digest"); - vm.expectRevert(abi.encodeWithSelector(RMNHome.ConfigDigestMismatch.selector, priorCandidateDigest, wrongDigest)); - s_rmnHome.revokeCandidate(wrongDigest); - } - - function test_revokeCandidate_OnlyOwner_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert("Only callable by owner"); - s_rmnHome.revokeCandidate(keccak256("configDigest")); - } -} - -contract RMNHome_promoteCandidateAndRevokeActive is RMNHomeTest { - function test_promoteCandidateAndRevokeActive_success() public {} - - function test_promoteCandidateAndRevokeActive_OnlyOwner_reverts() public { - vm.startPrank(address(0)); - - vm.expectRevert("Only callable by owner"); - s_rmnHome.promoteCandidateAndRevokeActive(keccak256("toPromote"), keccak256("ToRevoke")); - } -}