Skip to content

Commit

Permalink
contracts/ccip/capability: per (donId, pluginType) active config inde…
Browse files Browse the repository at this point in the history
…xes (#1488)
  • Loading branch information
makramkd authored Oct 4, 2024
1 parent ace434b commit e97914d
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 60 deletions.
30 changes: 15 additions & 15 deletions contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,28 @@ CCIPHome_applyChainConfigUpdates:test_applyChainConfigUpdates_nodeNotInRegistry_
CCIPHome_applyChainConfigUpdates:test_applyChainConfigUpdates_removeChainConfigs_Success() (gas: 270824)
CCIPHome_applyChainConfigUpdates:test_applyChainConfigUpdates_selectorNotFound_Reverts() (gas: 14952)
CCIPHome_applyChainConfigUpdates:test_getPaginatedCCIPHomes_Success() (gas: 370980)
CCIPHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_DONIdMismatch_reverts() (gas: 27108)
CCIPHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_DONIdMismatch_reverts() (gas: 27137)
CCIPHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_InnerCallReverts_reverts() (gas: 11783)
CCIPHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_InvalidSelector_reverts() (gas: 11038)
CCIPHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 26121)
CCIPHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 1412263)
CCIPHome_constructor:test_constructor_CapabilitiesRegistryAddressZero_reverts() (gas: 63866)
CCIPHome_constructor:test_constructor_success() (gas: 3498390)
CCIPHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() (gas: 26150)
CCIPHome_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_success() (gas: 1436726)
CCIPHome_constructor:test_constructor_CapabilitiesRegistryAddressZero_reverts() (gas: 63878)
CCIPHome_constructor:test_constructor_success() (gas: 3521034)
CCIPHome_constructor:test_getCapabilityConfiguration_success() (gas: 9173)
CCIPHome_constructor:test_supportsInterface_success() (gas: 9865)
CCIPHome_getAllConfigs:test_getAllConfigs_success() (gas: 2735455)
CCIPHome_getConfigDigests:test_getConfigDigests_success() (gas: 2513909)
CCIPHome_getAllConfigs:test_getAllConfigs_success() (gas: 2765282)
CCIPHome_getConfigDigests:test_getConfigDigests_success() (gas: 2539724)
CCIPHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_CanOnlySelfCall_reverts() (gas: 9110)
CCIPHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_ConfigDigestMismatch_reverts() (gas: 21877)
CCIPHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_NoOpStateTransitionNotAllowed_reverts() (gas: 8796)
CCIPHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_success() (gas: 2556754)
CCIPHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_ConfigDigestMismatch_reverts() (gas: 23052)
CCIPHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_NoOpStateTransitionNotAllowed_reverts() (gas: 8818)
CCIPHome_promoteCandidateAndRevokeActive:test_promoteCandidateAndRevokeActive_multiplePlugins_success() (gas: 5096112)
CCIPHome_revokeCandidate:test_revokeCandidate_CanOnlySelfCall_reverts() (gas: 9068)
CCIPHome_revokeCandidate:test_revokeCandidate_ConfigDigestMismatch_reverts() (gas: 18421)
CCIPHome_revokeCandidate:test_revokeCandidate_ConfigDigestMismatch_reverts() (gas: 19128)
CCIPHome_revokeCandidate:test_revokeCandidate_RevokingZeroDigestNotAllowed_reverts() (gas: 8773)
CCIPHome_revokeCandidate:test_revokeCandidate_success() (gas: 29506)
CCIPHome_setCandidate:test_setCandidate_CanOnlySelfCall_reverts() (gas: 19022)
CCIPHome_setCandidate:test_setCandidate_ConfigDigestMismatch_reverts() (gas: 1385010)
CCIPHome_setCandidate:test_setCandidate_success() (gas: 1355245)
CCIPHome_revokeCandidate:test_revokeCandidate_success() (gas: 30676)
CCIPHome_setCandidate:test_setCandidate_CanOnlySelfCall_reverts() (gas: 19051)
CCIPHome_setCandidate:test_setCandidate_ConfigDigestMismatch_reverts() (gas: 1388198)
CCIPHome_setCandidate:test_setCandidate_success() (gas: 1357740)
CommitStore_constructor:test_Constructor_Success() (gas: 2855567)
CommitStore_isUnpausedAndRMNHealthy:test_RMN_Success() (gas: 73954)
CommitStore_report:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 28739)
Expand Down
35 changes: 18 additions & 17 deletions contracts/src/v0.8/ccip/capability/CCIPHome.sol
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,10 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration,
s_configs;

/// @notice The total number of configs ever set, used for generating the version of the configs.
/// @dev Used to ensure unique digests across all configurations.
uint32 private s_currentVersion = 0;
/// @notice The index of the active config.
uint32 private s_activeConfigIndex = 0;
/// @notice The index of the active config on a per-don and per-plugin basis.
mapping(uint32 donId => mapping(Internal.OCRPluginType pluginType => uint32)) private s_activeConfigIndexes;

/// @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.
Expand Down Expand Up @@ -263,21 +264,21 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration,
Internal.OCRPluginType pluginType
) public view returns (bytes32 activeConfigDigest, bytes32 candidateConfigDigest) {
return (
s_configs[donId][pluginType][_getActiveIndex()].configDigest,
s_configs[donId][pluginType][_getCandidateIndex()].configDigest
s_configs[donId][pluginType][_getActiveIndex(donId, pluginType)].configDigest,
s_configs[donId][pluginType][_getCandidateIndex(donId, pluginType)].configDigest
);
}

/// @notice Returns the active config digest for for a given key.
/// @param donId The key of the plugin to get the config digests for.
function getActiveDigest(uint32 donId, Internal.OCRPluginType pluginType) public view returns (bytes32) {
return s_configs[donId][pluginType][_getActiveIndex()].configDigest;
return s_configs[donId][pluginType][_getActiveIndex(donId, pluginType)].configDigest;
}

/// @notice Returns the candidate config digest for for a given key.
/// @param donId The key of the plugin to get the config digests for.
function getCandidateDigest(uint32 donId, Internal.OCRPluginType pluginType) public view returns (bytes32) {
return s_configs[donId][pluginType][_getCandidateIndex()].configDigest;
return s_configs[donId][pluginType][_getCandidateIndex(donId, pluginType)].configDigest;
}

/// @notice The offchain code can use this to fetch an old config which might still be in use by some remotes. Use
Expand Down Expand Up @@ -310,12 +311,12 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration,
uint32 donId,
Internal.OCRPluginType pluginType
) external view returns (VersionedConfig memory activeConfig, VersionedConfig memory candidateConfig) {
VersionedConfig memory storedActiveConfig = s_configs[donId][pluginType][_getActiveIndex()];
VersionedConfig memory storedActiveConfig = s_configs[donId][pluginType][_getActiveIndex(donId, pluginType)];
if (storedActiveConfig.configDigest != ZERO_DIGEST) {
activeConfig = storedActiveConfig;
}

VersionedConfig memory storedCandidateConfig = s_configs[donId][pluginType][_getCandidateIndex()];
VersionedConfig memory storedCandidateConfig = s_configs[donId][pluginType][_getCandidateIndex(donId, pluginType)];
if (storedCandidateConfig.configDigest != ZERO_DIGEST) {
candidateConfig = storedCandidateConfig;
}
Expand Down Expand Up @@ -353,7 +354,7 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration,
uint32 newVersion = ++s_currentVersion;
newConfigDigest = _calculateConfigDigest(donId, pluginType, abi.encode(config), newVersion);

VersionedConfig storage existingConfig = s_configs[donId][pluginType][_getCandidateIndex()];
VersionedConfig storage existingConfig = s_configs[donId][pluginType][_getCandidateIndex(donId, pluginType)];
existingConfig.configDigest = newConfigDigest;
existingConfig.version = newVersion;
existingConfig.config = config;
Expand All @@ -373,7 +374,7 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration,
revert RevokingZeroDigestNotAllowed();
}

uint256 candidateConfigIndex = _getCandidateIndex();
uint256 candidateConfigIndex = _getCandidateIndex(donId, pluginType);
if (s_configs[donId][pluginType][candidateConfigIndex].configDigest != configDigest) {
revert ConfigDigestMismatch(s_configs[donId][pluginType][candidateConfigIndex].configDigest, configDigest);
}
Expand All @@ -400,19 +401,19 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration,
revert NoOpStateTransitionNotAllowed();
}

uint256 candidateConfigIndex = _getCandidateIndex();
uint256 candidateConfigIndex = _getCandidateIndex(donId, pluginType);
if (s_configs[donId][pluginType][candidateConfigIndex].configDigest != digestToPromote) {
revert ConfigDigestMismatch(s_configs[donId][pluginType][candidateConfigIndex].configDigest, digestToPromote);
}

VersionedConfig storage activeConfig = s_configs[donId][pluginType][_getActiveIndex()];
VersionedConfig storage activeConfig = s_configs[donId][pluginType][_getActiveIndex(donId, pluginType)];
if (activeConfig.configDigest != digestToRevoke) {
revert ConfigDigestMismatch(activeConfig.configDigest, digestToRevoke);
}

delete activeConfig.configDigest;

s_activeConfigIndex ^= 1;
s_activeConfigIndexes[donId][pluginType] ^= 1;
if (digestToRevoke != ZERO_DIGEST) {
emit ActiveConfigRevoked(digestToRevoke);
}
Expand Down Expand Up @@ -444,12 +445,12 @@ contract CCIPHome is OwnerIsCreator, ITypeAndVersion, ICapabilityConfiguration,
);
}

function _getActiveIndex() private view returns (uint32) {
return s_activeConfigIndex;
function _getActiveIndex(uint32 donId, Internal.OCRPluginType pluginType) private view returns (uint32) {
return s_activeConfigIndexes[donId][pluginType];
}

function _getCandidateIndex() private view returns (uint32) {
return s_activeConfigIndex ^ 1;
function _getCandidateIndex(uint32 donId, Internal.OCRPluginType pluginType) private view returns (uint32) {
return s_activeConfigIndexes[donId][pluginType] ^ 1;
}

// ================================================================
Expand Down
75 changes: 49 additions & 26 deletions contracts/src/v0.8/ccip/test/capability/CCIPHome.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ contract CCIPHomeTest is Test {
);
}

function _getBaseConfig() internal pure returns (CCIPHome.OCR3Config memory) {
function _getBaseConfig(Internal.OCRPluginType pluginType) internal pure returns (CCIPHome.OCR3Config memory) {
CCIPHome.OCR3Node[] memory nodes = new CCIPHome.OCR3Node[](4);
for (uint256 i = 0; i < nodes.length; i++) {
nodes[i] = CCIPHome.OCR3Node({
Expand All @@ -87,7 +87,7 @@ contract CCIPHomeTest is Test {
}

return CCIPHome.OCR3Config({
pluginType: Internal.OCRPluginType.Commit,
pluginType: pluginType,
chainSelector: DEFAULT_CHAIN_SELECTOR,
FRoleDON: 1,
offchainConfigVersion: 98765,
Expand Down Expand Up @@ -131,8 +131,10 @@ contract CCIPHome_beforeCapabilityConfigSet is CCIPHomeTest {

function test_beforeCapabilityConfigSet_success() public {
// first set a config
bytes memory callData =
abi.encodeCall(CCIPHome.setCandidate, (DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(), ZERO_DIGEST));
bytes memory callData = abi.encodeCall(
CCIPHome.setCandidate,
(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(Internal.OCRPluginType.Commit), ZERO_DIGEST)
);

vm.expectCall(address(s_ccipHome), callData);

Expand All @@ -149,8 +151,10 @@ contract CCIPHome_beforeCapabilityConfigSet is CCIPHomeTest {
s_ccipHome.beforeCapabilityConfigSet(new bytes32[](0), callData, 0, DEFAULT_DON_ID);

// Then set a new config
callData =
abi.encodeCall(CCIPHome.setCandidate, (DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(), ZERO_DIGEST));
callData = abi.encodeCall(
CCIPHome.setCandidate,
(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(Internal.OCRPluginType.Commit), ZERO_DIGEST)
);

vm.expectCall(address(s_ccipHome), callData);

Expand All @@ -174,8 +178,10 @@ contract CCIPHome_beforeCapabilityConfigSet is CCIPHomeTest {
}

function test_beforeCapabilityConfigSet_OnlyCapabilitiesRegistryCanCall_reverts() public {
bytes memory callData =
abi.encodeCall(CCIPHome.setCandidate, (DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(), ZERO_DIGEST));
bytes memory callData = abi.encodeCall(
CCIPHome.setCandidate,
(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(Internal.OCRPluginType.Commit), ZERO_DIGEST)
);

vm.stopPrank();

Expand All @@ -194,8 +200,10 @@ contract CCIPHome_beforeCapabilityConfigSet is CCIPHomeTest {
function test_beforeCapabilityConfigSet_DONIdMismatch_reverts() public {
uint32 wrongDonId = DEFAULT_DON_ID + 1;

bytes memory callData =
abi.encodeCall(CCIPHome.setCandidate, (DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(), ZERO_DIGEST));
bytes memory callData = abi.encodeCall(
CCIPHome.setCandidate,
(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(Internal.OCRPluginType.Commit), ZERO_DIGEST)
);

vm.expectRevert(abi.encodeWithSelector(CCIPHome.DONIdMismatch.selector, DEFAULT_DON_ID, wrongDonId));
s_ccipHome.beforeCapabilityConfigSet(new bytes32[](0), callData, 0, wrongDonId);
Expand All @@ -215,7 +223,7 @@ contract CCIPHome_getConfigDigests is CCIPHomeTest {
assertEq(activeDigest, ZERO_DIGEST);
assertEq(candidateDigest, ZERO_DIGEST);

CCIPHome.OCR3Config memory config = _getBaseConfig();
CCIPHome.OCR3Config memory config = _getBaseConfig(Internal.OCRPluginType.Commit);
bytes32 firstDigest = s_ccipHome.setCandidate(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, config, ZERO_DIGEST);

(activeDigest, candidateDigest) = s_ccipHome.getConfigDigests(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE);
Expand All @@ -241,7 +249,7 @@ contract CCIPHome_getConfigDigests is CCIPHomeTest {

contract CCIPHome_getAllConfigs is CCIPHomeTest {
function test_getAllConfigs_success() public {
CCIPHome.OCR3Config memory config = _getBaseConfig();
CCIPHome.OCR3Config memory config = _getBaseConfig(Internal.OCRPluginType.Commit);
bytes32 firstDigest = s_ccipHome.setCandidate(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, config, ZERO_DIGEST);

(CCIPHome.VersionedConfig memory activeConfig, CCIPHome.VersionedConfig memory candidateConfig) =
Expand Down Expand Up @@ -273,7 +281,7 @@ contract CCIPHome_getAllConfigs is CCIPHomeTest {

contract CCIPHome_setCandidate is CCIPHomeTest {
function test_setCandidate_success() public {
CCIPHome.OCR3Config memory config = _getBaseConfig();
CCIPHome.OCR3Config memory config = _getBaseConfig(Internal.OCRPluginType.Commit);
CCIPHome.VersionedConfig memory versionedConfig =
CCIPHome.VersionedConfig({version: 1, config: config, configDigest: ZERO_DIGEST});

Expand All @@ -294,7 +302,7 @@ contract CCIPHome_setCandidate is CCIPHomeTest {
}

function test_setCandidate_ConfigDigestMismatch_reverts() public {
CCIPHome.OCR3Config memory config = _getBaseConfig();
CCIPHome.OCR3Config memory config = _getBaseConfig(Internal.OCRPluginType.Commit);

bytes32 digest = s_ccipHome.setCandidate(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, config, ZERO_DIGEST);

Expand All @@ -311,15 +319,17 @@ contract CCIPHome_setCandidate is CCIPHomeTest {
vm.stopPrank();

vm.expectRevert(CCIPHome.CanOnlySelfCall.selector);
s_ccipHome.setCandidate(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(), ZERO_DIGEST);
s_ccipHome.setCandidate(
DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, _getBaseConfig(Internal.OCRPluginType.Commit), ZERO_DIGEST
);
}
}

contract CCIPHome_revokeCandidate is CCIPHomeTest {
// Sets two configs
function setUp() public virtual override {
super.setUp();
CCIPHome.OCR3Config memory config = _getBaseConfig();
CCIPHome.OCR3Config memory config = _getBaseConfig(Internal.OCRPluginType.Commit);
bytes32 digest = s_ccipHome.setCandidate(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, config, ZERO_DIGEST);
s_ccipHome.promoteCandidateAndRevokeActive(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, digest, ZERO_DIGEST);

Expand Down Expand Up @@ -373,36 +383,49 @@ contract CCIPHome_revokeCandidate is CCIPHomeTest {
}

contract CCIPHome_promoteCandidateAndRevokeActive is CCIPHomeTest {
function test_promoteCandidateAndRevokeActive_success() public {
CCIPHome.OCR3Config memory config = _getBaseConfig();
bytes32 firstConfigToPromote = s_ccipHome.setCandidate(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, config, ZERO_DIGEST);
function test_promoteCandidateAndRevokeActive_multiplePlugins_success() public {
promoteCandidateAndRevokeActive(Internal.OCRPluginType.Commit);
promoteCandidateAndRevokeActive(Internal.OCRPluginType.Execution);

// check that the two plugins have only active configs and no candidates.
(bytes32 activeDigest, bytes32 candidateDigest) =
s_ccipHome.getConfigDigests(DEFAULT_DON_ID, Internal.OCRPluginType.Commit);
assertTrue(activeDigest != ZERO_DIGEST);
assertEq(candidateDigest, ZERO_DIGEST);

(activeDigest, candidateDigest) = s_ccipHome.getConfigDigests(DEFAULT_DON_ID, Internal.OCRPluginType.Execution);
assertTrue(activeDigest != ZERO_DIGEST);
assertEq(candidateDigest, ZERO_DIGEST);
}

function promoteCandidateAndRevokeActive(Internal.OCRPluginType pluginType) public {
CCIPHome.OCR3Config memory config = _getBaseConfig(pluginType);
bytes32 firstConfigToPromote = s_ccipHome.setCandidate(DEFAULT_DON_ID, pluginType, config, ZERO_DIGEST);

vm.expectEmit();
emit CCIPHome.ConfigPromoted(firstConfigToPromote);

s_ccipHome.promoteCandidateAndRevokeActive(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, firstConfigToPromote, ZERO_DIGEST);
s_ccipHome.promoteCandidateAndRevokeActive(DEFAULT_DON_ID, pluginType, firstConfigToPromote, ZERO_DIGEST);

// Assert the active digest is updated and the candidate digest is set to zero
(bytes32 activeDigest, bytes32 candidateDigest) = s_ccipHome.getConfigDigests(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE);
(bytes32 activeDigest, bytes32 candidateDigest) = s_ccipHome.getConfigDigests(DEFAULT_DON_ID, pluginType);
assertEq(activeDigest, firstConfigToPromote);
assertEq(candidateDigest, ZERO_DIGEST);

// Set a new candidate to promote over a non-zero active config.
config.offchainConfig = abi.encode("new_offchainConfig_config");
bytes32 secondConfigToPromote = s_ccipHome.setCandidate(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, config, ZERO_DIGEST);
bytes32 secondConfigToPromote = s_ccipHome.setCandidate(DEFAULT_DON_ID, pluginType, config, ZERO_DIGEST);

vm.expectEmit();
emit CCIPHome.ActiveConfigRevoked(firstConfigToPromote);

vm.expectEmit();
emit CCIPHome.ConfigPromoted(secondConfigToPromote);

s_ccipHome.promoteCandidateAndRevokeActive(
DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE, secondConfigToPromote, firstConfigToPromote
);
s_ccipHome.promoteCandidateAndRevokeActive(DEFAULT_DON_ID, pluginType, secondConfigToPromote, firstConfigToPromote);

(CCIPHome.VersionedConfig memory activeConfig, CCIPHome.VersionedConfig memory candidateConfig) =
s_ccipHome.getAllConfigs(DEFAULT_DON_ID, DEFAULT_PLUGIN_TYPE);
s_ccipHome.getAllConfigs(DEFAULT_DON_ID, pluginType);
assertEq(activeConfig.configDigest, secondConfigToPromote);
assertEq(candidateConfig.configDigest, ZERO_DIGEST);
assertEq(keccak256(abi.encode(activeConfig.config)), keccak256(abi.encode(config)));
Expand Down
Loading

0 comments on commit e97914d

Please sign in to comment.