Skip to content

Commit

Permalink
CCIP-2594 Add pagination based query of CCIPConfig (#1136)
Browse files Browse the repository at this point in the history
## Motivation
CCIP Config can go to larger size and any query from offchain components
via rpc call can cause timeout issues

## Solution
add pagination to `getAllCCIPConfig` function which takes 
- pageSize
- startIndex

---------

Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com>
Co-authored-by: Makram Kamaleddine <[email protected]>
  • Loading branch information
3 people authored Aug 19, 2024
1 parent 5cf71a9 commit 8f3486c
Show file tree
Hide file tree
Showing 15 changed files with 146 additions and 72 deletions.
31 changes: 16 additions & 15 deletions contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ BurnWithFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 23951)
CCIPClientExample_sanity:test_ImmutableExamples_Success() (gas: 2132726)
CCIPConfigSetup:test_getCapabilityConfiguration_Success() (gas: 9517)
CCIPConfig_ConfigStateMachine:test__computeConfigDigest_Success() (gas: 70831)
CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_InitToRunning_Success() (gas: 363708)
CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_RunningToStaging_Success() (gas: 488870)
CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_StagingToRunning_Success() (gas: 453483)
CCIPConfig_ConfigStateMachine:test__groupByPluginType_TooManyOCR3Configs_Reverts() (gas: 37027)
CCIPConfig_ConfigStateMachine:test__groupByPluginType_threeCommitConfigs_Reverts() (gas: 61043)
CCIPConfig_ConfigStateMachine:test__groupByPluginType_threeExecutionConfigs_Reverts() (gas: 60963)
CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_InitToRunning_Success() (gas: 363664)
CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_RunningToStaging_Success() (gas: 488826)
CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_StagingToRunning_Success() (gas: 453439)
CCIPConfig_ConfigStateMachine:test__groupByPluginType_TooManyOCR3Configs_Reverts() (gas: 37049)
CCIPConfig_ConfigStateMachine:test__groupByPluginType_threeCommitConfigs_Reverts() (gas: 61065)
CCIPConfig_ConfigStateMachine:test__groupByPluginType_threeExecutionConfigs_Reverts() (gas: 60985)
CCIPConfig_ConfigStateMachine:test__stateFromConfigLength_Success() (gas: 11635)
CCIPConfig_ConfigStateMachine:test__validateConfigStateTransition_Success() (gas: 8831)
CCIPConfig_ConfigStateMachine:test__validateConfigTransition_InitToRunning_Success() (gas: 312055)
Expand All @@ -61,18 +61,19 @@ CCIPConfig_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_OnlyCapabili
CCIPConfig_beforeCapabilityConfigSet:test_beforeCapabilityConfigSet_ZeroLengthConfig_Success() (gas: 16070)
CCIPConfig_beforeCapabilityConfigSet:test_getCapabilityConfiguration_Success() (gas: 9605)
CCIPConfig_chainConfig:test__applyChainConfigUpdates_FChainNotPositive_Reverts() (gas: 184703)
CCIPConfig_chainConfig:test_applyChainConfigUpdates_addChainConfigs_Success() (gas: 344332)
CCIPConfig_chainConfig:test_applyChainConfigUpdates_addChainConfigs_Success() (gas: 344881)
CCIPConfig_chainConfig:test_applyChainConfigUpdates_nodeNotInRegistry_Reverts() (gas: 20258)
CCIPConfig_chainConfig:test_applyChainConfigUpdates_removeChainConfigs_Success() (gas: 267558)
CCIPConfig_chainConfig:test_applyChainConfigUpdates_selectorNotFound_Reverts() (gas: 14829)
CCIPConfig_chainConfig:test_getCapabilityConfiguration_Success() (gas: 9648)
CCIPConfig_constructor:test_constructor_Success() (gas: 3567986)
CCIPConfig_constructor:test_constructor_ZeroAddressNotAllowed_Revert() (gas: 61720)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_InitToRunning_Success() (gas: 1057346)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_InvalidConfigLength_Reverts() (gas: 27539)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_InvalidConfigStateTransition_Reverts() (gas: 23105)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_RunningToStaging_Success() (gas: 2009250)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_StagingToRunning_Success() (gas: 2616080)
CCIPConfig_chainConfig:test_getCapabilityConfiguration_Success() (gas: 9626)
CCIPConfig_chainConfig:test_getPaginatedCCIPConfigs_Success() (gas: 370235)
CCIPConfig_constructor:test_constructor_Success() (gas: 3612901)
CCIPConfig_constructor:test_constructor_ZeroAddressNotAllowed_Revert() (gas: 61777)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_InitToRunning_Success() (gas: 1057368)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_InvalidConfigLength_Reverts() (gas: 27561)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_InvalidConfigStateTransition_Reverts() (gas: 23127)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_RunningToStaging_Success() (gas: 2009285)
CCIPConfig_updatePluginConfig:test__updatePluginConfig_StagingToRunning_Success() (gas: 2616133)
CCIPConfig_updatePluginConfig:test_getCapabilityConfiguration_Success() (gas: 9605)
CCIPConfig_validateConfig:test__validateConfig_BootstrapP2PIdsHasDuplicates_Reverts() (gas: 294893)
CCIPConfig_validateConfig:test__validateConfig_BootstrapP2PIdsNotASubsetOfP2PIds_Reverts() (gas: 298325)
Expand Down
32 changes: 24 additions & 8 deletions contracts/src/v0.8/ccip/capability/CCIPConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,36 @@ contract CCIPConfig is ITypeAndVersion, ICapabilityConfiguration, OwnerIsCreator
}

/// @notice Returns all the chain configurations.
/// @return The chain configurations.
// TODO: will this eventually hit the RPC max response size limit?
function getAllChainConfigs() external view returns (CCIPConfigTypes.ChainConfigInfo[] memory) {
/// @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();
CCIPConfigTypes.ChainConfigInfo[] memory chainConfigs =
new CCIPConfigTypes.ChainConfigInfo[](s_remoteChainSelectors.length());
for (uint256 i = 0; i < chainSelectors.length; ++i) {
for (uint256 i = startIndex; i < endIndex; ++i) {
uint64 chainSelector = uint64(chainSelectors[i]);
chainConfigs[i] = CCIPConfigTypes.ChainConfigInfo({
paginatedChainConfigs[i - startIndex] = CCIPConfigTypes.ChainConfigInfo({
chainSelector: chainSelector,
chainConfig: s_chainConfigurations[chainSelector]
});
}
return chainConfigs;

return paginatedChainConfigs;
}

/// @notice Returns the OCR configuration for the given don ID and plugin type.
Expand Down
56 changes: 52 additions & 4 deletions contracts/src/v0.8/ccip/test/capability/CCIPConfig.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ contract CCIPConfig_constructor is Test {

contract CCIPConfig_chainConfig is CCIPConfigSetup {
// Successes.

function test_applyChainConfigUpdates_addChainConfigs_Success() public {
bytes32[] memory chainReaders = new bytes32[](1);
chainReaders[0] = keccak256(abi.encode(1));
Expand All @@ -141,7 +140,6 @@ contract CCIPConfig_chainConfig is CCIPConfigSetup {
chainSelector: 2,
chainConfig: CCIPConfigTypes.ChainConfig({readers: chainReaders, fChain: 1, config: bytes("config2")})
});

vm.mockCall(
CAPABILITIES_REGISTRY,
abi.encodeWithSelector(ICapabilitiesRegistry.getNode.selector, chainReaders[0]),
Expand All @@ -157,19 +155,69 @@ contract CCIPConfig_chainConfig is CCIPConfigSetup {
})
)
);

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();
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");
}

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));
Expand Down
44 changes: 27 additions & 17 deletions core/capabilities/ccip/ccip_integration_tests/home_chain_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ccip_integration_tests

import (
"math/big"
"testing"
"time"

Expand Down Expand Up @@ -35,6 +36,7 @@ func TestHomeChainReader(t *testing.T) {
}
p2pIDs := integrationhelpers.P2pIDsFromInts(arr)
uni.AddCapability(p2pIDs)

//==============================Apply configs to Capability Contract=================================
encodedChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{
GasPriceDeviationPPB: cciptypes.NewBigIntFromInt64(1000),
Expand All @@ -43,28 +45,26 @@ func TestHomeChainReader(t *testing.T) {
OptimisticConfirmations: 1,
})
require.NoError(t, err)
chainAConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainA, p2pIDs, integrationhelpers.FChainA, encodedChainConfig)
chainBConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainB, p2pIDs[1:], integrationhelpers.FChainB, encodedChainConfig)
chainCConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainC, p2pIDs[2:], integrationhelpers.FChainC, encodedChainConfig)
inputConfig := []capcfg.CCIPConfigTypesChainConfigInfo{
chainAConf,
chainBConf,
chainCConf,
integrationhelpers.SetupConfigInfo(integrationhelpers.ChainA, p2pIDs, integrationhelpers.FChainA, encodedChainConfig),
integrationhelpers.SetupConfigInfo(integrationhelpers.ChainB, p2pIDs[1:], integrationhelpers.FChainB, encodedChainConfig),
integrationhelpers.SetupConfigInfo(integrationhelpers.ChainC, p2pIDs[2:], integrationhelpers.FChainC, encodedChainConfig),
}
_, err = uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, nil, inputConfig)
require.NoError(t, err)
uni.Backend.Commit()
//================================Setup HomeChainReader===============================

pollDuration := time.Second
homeChain := uni.HomeChainReader
chainConfigInfos, err := uni.CcipCfg.GetAllChainConfigs(nil, big.NewInt(0), big.NewInt(100))
require.NoError(t, err)
require.Len(t, chainConfigInfos, len(inputConfig))

// Wait for the home chain reader to read the expected amount of chain configs.
gomega.NewWithT(t).Eventually(func() bool {
configs, _ := homeChain.GetAllChainConfigs()
return configs != nil
}, testutils.WaitTimeout(t), pollDuration*5).Should(gomega.BeTrue())
configs, _ := uni.HomeChainReader.GetAllChainConfigs()
return len(configs) == len(inputConfig)
}, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue())

t.Logf("homchain reader is ready")

//================================Test HomeChain Reader===============================
expectedChainConfigs := map[cciptypes.ChainSelector]ccipreader.ChainConfig{}
for _, c := range inputConfig {
Expand All @@ -74,16 +74,26 @@ func TestHomeChainReader(t *testing.T) {
Config: mustDecodeChainConfig(t, c.ChainConfig.Config),
}
}
configs, err := homeChain.GetAllChainConfigs()

configs, err := uni.HomeChainReader.GetAllChainConfigs()
require.NoError(t, err)

require.Equal(t, expectedChainConfigs, configs)
//=================================Remove ChainC from OnChainConfig=========================================

// Remove chain C from the chain configs and expect the home chain reader to
// update its state accordingly.
_, err = uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, []uint64{integrationhelpers.ChainC}, nil)
require.NoError(t, err)
uni.Backend.Commit()
time.Sleep(pollDuration * 5) // Wait for the chain reader to update
configs, err = homeChain.GetAllChainConfigs()

// Wait for the home chain reader to read the expected amount of chain configs.
gomega.NewWithT(t).Eventually(func() bool {
chainConfigs, _ := uni.HomeChainReader.GetAllChainConfigs()
return len(chainConfigs) == len(inputConfig)-1
}, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue())
configs, err = uni.HomeChainReader.GetAllChainConfigs()
require.NoError(t, err)

delete(expectedChainConfigs, cciptypes.ChainSelector(integrationhelpers.ChainC))
require.Equal(t, expectedChainConfigs, configs)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func TestIntegration_OCR3Nodes(t *testing.T) {
AddChainConfig(t, homeChainUni, getSelector(uni.chainID), p2pIDs, fChain)
}

cfgs, err := homeChainUni.ccipConfig.GetAllChainConfigs(callCtx)
cfgs, err := homeChainUni.ccipConfig.GetAllChainConfigs(callCtx, big.NewInt(0), big.NewInt(100))
require.NoError(t, err)
require.Len(t, cfgs, numChains)

Expand Down
Loading

0 comments on commit 8f3486c

Please sign in to comment.