diff --git a/pkg/ccip/chainaccessor/config.go b/pkg/ccip/chainaccessor/config.go index 72feb6eed..8ed108a52 100644 --- a/pkg/ccip/chainaccessor/config.go +++ b/pkg/ccip/chainaccessor/config.go @@ -9,6 +9,7 @@ import ( "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/ton" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/types/ccip/consts" "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" @@ -137,12 +138,12 @@ func (a *TONAccessor) GetOffRampConfig(ctx context.Context, block *ton.BlockIDEx // GetOffRampSourceChainConfigs retrieves multiple source chain configurations from the off-ramp contract func (a *TONAccessor) GetOffRampSourceChainConfigs(ctx context.Context, block *ton.BlockIDExt, sourceChainSelectors []ccipocr3.ChainSelector) (map[ccipocr3.ChainSelector]ccipocr3.SourceChainConfig, error) { + lggr := logger.With(a.lggr, "sourceChainSelectors", sourceChainSelectors) addr, err := a.getBinding(consts.ContractNameOffRamp) if err != nil { return nil, err } - var sourceChainConfigs = make(map[ccipocr3.ChainSelector]ccipocr3.SourceChainConfig, len(sourceChainSelectors)) var sourceConfigsGot offrampview.SourceChainConfigMap if err = sourceConfigsGot.Fetch(ctx, a.client, block, addr); err != nil { return nil, fmt.Errorf("failed to fetch source chain configs: %w", err) @@ -150,10 +151,22 @@ func (a *TONAccessor) GetOffRampSourceChainConfigs(ctx context.Context, block *t // if the dictionary is empty, we get back nil if len(sourceConfigsGot) == 0 { + lggr.Debugw("no source chain configs found, nothing to do") return nil, nil } - if len(sourceChainConfigs) == 0 { + sourceChainConfigs := filterSourceChainConfigs(sourceConfigsGot, sourceChainSelectors) + lggr.Debugw("GetOffRampSourceChainConfigs returning", "sourceChainConfigs", sourceChainConfigs) + return sourceChainConfigs, nil +} + +// filterSourceChainConfigs filters the fetched source chain configs based on the requested selectors. +// If sourceChainSelectors is empty, all configs are returned. +// If sourceChainSelectors is provided, only matching configs are returned, non-existent selectors are skipped. +func filterSourceChainConfigs(sourceConfigsGot offrampview.SourceChainConfigMap, sourceChainSelectors []ccipocr3.ChainSelector) map[ccipocr3.ChainSelector]ccipocr3.SourceChainConfig { + sourceChainConfigs := make(map[ccipocr3.ChainSelector]ccipocr3.SourceChainConfig, len(sourceChainSelectors)) + + if len(sourceChainSelectors) == 0 { // if no selectors specified, return all configs for selector, config := range sourceConfigsGot { sourceChainConfigs[ccipocr3.ChainSelector(selector)] = sourceChainConfigToGeneric(config) @@ -162,13 +175,13 @@ func (a *TONAccessor) GetOffRampSourceChainConfigs(ctx context.Context, block *t for _, selector := range sourceChainSelectors { config, ok := sourceConfigsGot[uint64(selector)] if !ok { - return nil, fmt.Errorf("source chain selector '%d' not found in off-ramp source chain configs, got %v", selector, sourceConfigsGot) + continue } sourceChainConfigs[selector] = sourceChainConfigToGeneric(config) } } - return sourceChainConfigs, nil + return sourceChainConfigs } // GetOffRampSourceChainConfig retrieves a specific source chain configuration diff --git a/pkg/ccip/chainaccessor/config_test.go b/pkg/ccip/chainaccessor/config_test.go index e5cb1ffb6..9e3757b24 100644 --- a/pkg/ccip/chainaccessor/config_test.go +++ b/pkg/ccip/chainaccessor/config_test.go @@ -5,8 +5,13 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/xssnick/tonutils-go/address" "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + + "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/common" + "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/offramp" + offrampview "github.com/smartcontractkit/chainlink-ton/pkg/ccip/view/offramp" ) func bigIntFromHex(s string) *big.Int { @@ -162,3 +167,118 @@ func TestParseCurseInfo(t *testing.T) { assert.True(t, result.CursedSourceChains[ccipocr3.ChainSelector(111111)]) }) } + +func TestFilterSourceChainConfigs(t *testing.T) { + testAddr := address.MustParseAddr("EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2") + makeConfig := func(minSeqNr uint64, isEnabled bool) offramp.SourceChainConfig { + return offramp.SourceChainConfig{ + Router: testAddr, + IsEnabled: isEnabled, + MinSeqNr: minSeqNr, + IsRMNVerificationDisabled: false, + OnRamp: common.CrossChainAddress{1, 2, 3}, + } + } + + t.Run("empty sourceChainSelectors returns all configs", func(t *testing.T) { + sourceConfigsGot := offrampview.SourceChainConfigMap{ + 1001: makeConfig(100, true), + 1002: makeConfig(200, true), + 1003: makeConfig(300, false), + } + + result := filterSourceChainConfigs(sourceConfigsGot, []ccipocr3.ChainSelector{}) + + assert.Len(t, result, 3, "should return all 3 configs") + assert.Equal(t, uint64(100), result[ccipocr3.ChainSelector(1001)].MinSeqNr) + assert.Equal(t, uint64(200), result[ccipocr3.ChainSelector(1002)].MinSeqNr) + assert.Equal(t, uint64(300), result[ccipocr3.ChainSelector(1003)].MinSeqNr) + }) + + t.Run("nil sourceChainSelectors returns all configs", func(t *testing.T) { + sourceConfigsGot := offrampview.SourceChainConfigMap{ + 2001: makeConfig(500, true), + 2002: makeConfig(600, true), + } + + result := filterSourceChainConfigs(sourceConfigsGot, nil) + + assert.Len(t, result, 2, "should return all 2 configs") + assert.Equal(t, uint64(500), result[ccipocr3.ChainSelector(2001)].MinSeqNr) + assert.Equal(t, uint64(600), result[ccipocr3.ChainSelector(2002)].MinSeqNr) + }) + + t.Run("specific selectors return only matching configs", func(t *testing.T) { + sourceConfigsGot := offrampview.SourceChainConfigMap{ + 3001: makeConfig(100, true), + 3002: makeConfig(200, true), + 3003: makeConfig(300, true), + } + + selectors := []ccipocr3.ChainSelector{3001, 3003} + result := filterSourceChainConfigs(sourceConfigsGot, selectors) + + assert.Len(t, result, 2, "should return only 2 matching configs") + assert.Equal(t, uint64(100), result[ccipocr3.ChainSelector(3001)].MinSeqNr) + assert.Equal(t, uint64(300), result[ccipocr3.ChainSelector(3003)].MinSeqNr) + _, exists := result[ccipocr3.ChainSelector(3002)] + assert.False(t, exists, "selector 3002 should not be in result") + }) + + t.Run("non-existent selectors are skipped", func(t *testing.T) { + sourceConfigsGot := offrampview.SourceChainConfigMap{ + 4001: makeConfig(100, true), + 4002: makeConfig(200, true), + } + + // Request selectors that don't all exist + selectors := []ccipocr3.ChainSelector{4001, 9999, 8888} + result := filterSourceChainConfigs(sourceConfigsGot, selectors) + + assert.Len(t, result, 1, "should return only 1 existing config") + assert.Equal(t, uint64(100), result[ccipocr3.ChainSelector(4001)].MinSeqNr) + }) + + t.Run("all requested selectors non-existent returns empty map", func(t *testing.T) { + sourceConfigsGot := offrampview.SourceChainConfigMap{ + 5001: makeConfig(100, true), + } + + selectors := []ccipocr3.ChainSelector{9999, 8888, 7777} + result := filterSourceChainConfigs(sourceConfigsGot, selectors) + + assert.Empty(t, result, "should return empty map when no selectors match") + }) + + t.Run("empty sourceConfigsGot with selectors returns empty map", func(t *testing.T) { + sourceConfigsGot := offrampview.SourceChainConfigMap{} + + selectors := []ccipocr3.ChainSelector{1001, 1002} + result := filterSourceChainConfigs(sourceConfigsGot, selectors) + + assert.Empty(t, result, "should return empty map") + }) + + t.Run("config fields are correctly converted", func(t *testing.T) { + onRampAddr := common.CrossChainAddress{0xAA, 0xBB, 0xCC} + sourceConfigsGot := offrampview.SourceChainConfigMap{ + 6001: { + Router: testAddr, + IsEnabled: true, + MinSeqNr: 42, + IsRMNVerificationDisabled: true, + OnRamp: onRampAddr, + }, + } + + selectors := []ccipocr3.ChainSelector{6001} + result := filterSourceChainConfigs(sourceConfigsGot, selectors) + + assert.Len(t, result, 1) + config := result[ccipocr3.ChainSelector(6001)] + assert.True(t, config.IsEnabled) + assert.Equal(t, uint64(42), config.MinSeqNr) + assert.True(t, config.IsRMNVerificationDisabled) + assert.Equal(t, ccipocr3.UnknownAddress(onRampAddr), config.OnRamp) + }) +}