From 054de17be388b52287a097e1e435ea7f80e69868 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 20 Jul 2024 14:39:43 +0200 Subject: [PATCH] [CCIP-2498] Handling Unregister*PluginLpFilters for Exec + Commit in a LOOP compatible way (#1188) ## Motivation When jobs are deleted, `UnregisterExecPluginLpFilters` is called. It currently has EVM specific dependencies. ## Solution Rely on the `Close` method in the src and dst provider to unregister the necessary lp filters. ## Follow-on Work More comprehensive job deletion unit testing for CCIP exec + commit jobs: CCIP-2847 --- core/services/ocr2/delegate.go | 42 +++++- .../plugins/ccip/ccipcommit/initializers.go | 66 +-------- .../ccip/ccipcommit/initializers_test.go | 81 ----------- .../plugins/ccip/ccipexec/initializers.go | 92 +----------- .../ccip/ccipexec/initializers_test.go | 61 -------- .../ocr2/plugins/ccip/exportinternal.go | 16 +++ core/services/relay/evm/commit_provider.go | 128 ++++++++++++----- core/services/relay/evm/evm.go | 3 + core/services/relay/evm/exec_provider.go | 135 +++++++++++++----- 9 files changed, 260 insertions(+), 364 deletions(-) delete mode 100644 core/services/ocr2/plugins/ccip/ccipcommit/initializers_test.go delete mode 100644 core/services/ocr2/plugins/ccip/ccipexec/initializers_test.go diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index f080356730..b6ff05c03d 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -313,6 +313,7 @@ func (d *Delegate) cleanupEVM(ctx context.Context, jb job.Job, relayID types.Rel // an inconsistent state. This assumes UnregisterFilter will return nil if the filter wasn't found // at all (no rows deleted). spec := jb.OCR2OracleSpec + transmitterID := spec.TransmitterID.String chain, err := d.legacyChains.Get(relayID.ChainID) if err != nil { d.lggr.Errorw("cleanupEVM: failed to get chain id", "chainId", relayID.ChainID, "err", err) @@ -340,15 +341,48 @@ func (d *Delegate) cleanupEVM(ctx context.Context, jb job.Job, relayID types.Rel } filters = append(filters, filters21...) case types.CCIPCommit: - err = ccipcommit.UnregisterCommitPluginLpFilters(context.Background(), d.lggr, jb, d.legacyChains) + // Write PluginConfig bytes to send source/dest relayer provider + info outside of top level rargs/pargs over the wire + var pluginJobSpecConfig ccipconfig.CommitPluginJobSpecConfig + err = json.Unmarshal(spec.PluginConfig.Bytes(), &pluginJobSpecConfig) + if err != nil { + return err + } + + dstProvider, err2 := d.ccipCommitGetDstProvider(ctx, jb, pluginJobSpecConfig, transmitterID) + if err2 != nil { + return err2 + } + + srcProvider, _, err2 := d.ccipCommitGetSrcProvider(ctx, jb, pluginJobSpecConfig, transmitterID, dstProvider) + if err2 != nil { + return err2 + } + err2 = ccipcommit.UnregisterCommitPluginLpFilters(srcProvider, dstProvider) if err != nil { - d.lggr.Errorw("failed to unregister ccip commit plugin filters", "err", err, "spec", spec) + d.lggr.Errorw("failed to unregister ccip commit plugin filters", "err", err2, "spec", spec) } return nil case types.CCIPExecution: - err = ccipexec.UnregisterExecPluginLpFilters(context.Background(), d.lggr, jb, d.legacyChains) + // PROVIDER BASED ARG CONSTRUCTION + // Write PluginConfig bytes to send source/dest relayer provider + info outside of top level rargs/pargs over the wire + var pluginJobSpecConfig ccipconfig.ExecPluginJobSpecConfig + err = json.Unmarshal(spec.PluginConfig.Bytes(), &pluginJobSpecConfig) if err != nil { - d.lggr.Errorw("failed to unregister ccip exec plugin filters", "err", err, "spec", spec) + return err + } + + dstProvider, err2 := d.ccipExecGetDstProvider(ctx, jb, pluginJobSpecConfig, transmitterID) + if err2 != nil { + return err2 + } + + srcProvider, _, err2 := d.ccipExecGetSrcProvider(ctx, jb, pluginJobSpecConfig, transmitterID, dstProvider) + if err2 != nil { + return err2 + } + err2 = ccipexec.UnregisterExecPluginLpFilters(srcProvider, dstProvider) + if err2 != nil { + d.lggr.Errorw("failed to unregister ccip exec plugin filters", "err", err2, "spec", spec) } return nil default: diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go index fde5d61d73..e964896ab9 100644 --- a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go +++ b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go @@ -13,7 +13,6 @@ import ( "github.com/Masterminds/semver/v3" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" "go.uber.org/multierr" @@ -22,8 +21,6 @@ import ( commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" - cciporm "github.com/smartcontractkit/chainlink/v2/core/services/ccip" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" @@ -31,7 +28,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip" @@ -225,22 +221,13 @@ func CommitReportToEthTxMeta(typ ccipconfig.ContractType, ver semver.Version) (f // https://github.com/smartcontractkit/ccip/blob/68e2197472fb017dd4e5630d21e7878d58bc2a44/core/services/feeds/service.go#L716 // TODO once that transaction is broken up, we should be able to simply rely on oracle.Close() to cleanup the filters. // Until then we have to deterministically reload the readers from the spec (and thus their filters) and close them. -func UnregisterCommitPluginLpFilters(_ context.Context, lggr logger.Logger, jb job.Job, chainSet legacyevm.LegacyChainContainer) error { - params, err := extractJobSpecParams(jb, chainSet) - if err != nil { - return err - } - versionFinder := factory.NewEvmVersionFinder() - // TODO CCIP-2498 Use provider to close +func UnregisterCommitPluginLpFilters(srcProvider commontypes.CCIPCommitProvider, dstProvider commontypes.CCIPCommitProvider) error { unregisterFuncs := []func() error{ func() error { - return factory.CloseCommitStoreReader(lggr, versionFinder, params.commitStoreAddress, params.destChain.Client(), params.destChain.LogPoller()) - }, - func() error { - return factory.CloseOnRampReader(lggr, versionFinder, params.commitStoreStaticCfg.SourceChainSelector, params.commitStoreStaticCfg.ChainSelector, cciptypes.Address(params.commitStoreStaticCfg.OnRamp.String()), params.sourceChain.LogPoller(), params.sourceChain.Client()) + return srcProvider.Close() }, func() error { - return factory.CloseOffRampReader(lggr, versionFinder, params.pluginConfig.OffRamp, params.destChain.Client(), params.destChain.LogPoller(), params.destChain.GasEstimator(), params.destChain.Config().EVM().GasEstimator().PriceMax().ToInt()) + return dstProvider.Close() }, } @@ -252,50 +239,3 @@ func UnregisterCommitPluginLpFilters(_ context.Context, lggr logger.Logger, jb j } return multiErr } - -type jobSpecParams struct { - pluginConfig ccipconfig.CommitPluginJobSpecConfig - commitStoreAddress cciptypes.Address - commitStoreStaticCfg commit_store.CommitStoreStaticConfig - sourceChain legacyevm.Chain - destChain legacyevm.Chain -} - -func extractJobSpecParams(jb job.Job, chainSet legacyevm.LegacyChainContainer) (*jobSpecParams, error) { - if jb.OCR2OracleSpec == nil { - return nil, errors.New("spec is nil") - } - spec := jb.OCR2OracleSpec - - var pluginConfig ccipconfig.CommitPluginJobSpecConfig - err := json.Unmarshal(spec.PluginConfig.Bytes(), &pluginConfig) - if err != nil { - return nil, err - } - // ensure addresses are formatted properly - (lowercase to eip55 for evm) - pluginConfig.OffRamp = ccipcalc.HexToAddress(string(pluginConfig.OffRamp)) - - destChain, _, err := ccipconfig.GetChainFromSpec(spec, chainSet) - if err != nil { - return nil, err - } - - commitStoreAddress := common.HexToAddress(spec.ContractID) - staticConfig, err := ccipdata.FetchCommitStoreStaticConfig(commitStoreAddress, destChain.Client()) - if err != nil { - return nil, fmt.Errorf("get commit store static config: %w", err) - } - - sourceChain, _, err := ccipconfig.GetChainByChainSelector(chainSet, staticConfig.SourceChainSelector) - if err != nil { - return nil, err - } - - return &jobSpecParams{ - pluginConfig: pluginConfig, - commitStoreAddress: ccipcalc.EvmAddrToGeneric(commitStoreAddress), - commitStoreStaticCfg: staticConfig, - sourceChain: sourceChain, - destChain: destChain, - }, nil -} diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/initializers_test.go b/core/services/ocr2/plugins/ccip/ccipcommit/initializers_test.go deleted file mode 100644 index c8b37a339e..0000000000 --- a/core/services/ocr2/plugins/ccip/ccipcommit/initializers_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package ccipcommit - -import ( - "fmt" - "strconv" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - legacyEvmORMMocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/job" -) - -func TestGetCommitPluginFilterNamesFromSpec(t *testing.T) { - lggr := logger.TestLogger(t) - testCases := []struct { - description string - spec *job.OCR2OracleSpec - expectingErr bool - }{ - { - description: "should not panic with nil spec", - spec: nil, - expectingErr: true, - }, - { - description: "invalid config", - spec: &job.OCR2OracleSpec{ - ContractID: utils.ZeroAddress.String(), - PluginConfig: map[string]interface{}{}, - }, - expectingErr: true, - }, - { - description: "invalid contract id", - spec: &job.OCR2OracleSpec{ - ContractID: "whatever...", - }, - expectingErr: true, - }, - { - description: "valid config", - spec: &job.OCR2OracleSpec{ - ContractID: utils.ZeroAddress.String(), - PluginConfig: map[string]interface{}{}, - RelayConfig: map[string]interface{}{ - "chainID": 1234.0, - }, - }, - expectingErr: true, - }, - } - - ctx := testutils.Context(t) - - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - chainSet := &legacyEvmORMMocks.LegacyChainContainer{} - - if tc.spec != nil { - if chainID, ok := tc.spec.RelayConfig["chainID"]; ok { - chainIdStr := strconv.FormatInt(int64(chainID.(float64)), 10) - chainSet.On("Get", chainIdStr). - Return(nil, fmt.Errorf("chain %d not found", chainID)) - } - } - - err := UnregisterCommitPluginLpFilters(ctx, lggr, job.Job{OCR2OracleSpec: tc.spec}, chainSet) - if tc.expectingErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - - chainSet.AssertExpectations(t) - }) - } -} diff --git a/core/services/ocr2/plugins/ccip/ccipexec/initializers.go b/core/services/ocr2/plugins/ccip/ccipexec/initializers.go index e30ebd32c7..dfd3d287af 100644 --- a/core/services/ocr2/plugins/ccip/ccipexec/initializers.go +++ b/core/services/ocr2/plugins/ccip/ccipexec/initializers.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "math/big" - "strconv" "time" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -13,7 +12,6 @@ import ( "github.com/Masterminds/semver/v3" "go.uber.org/multierr" - chainselectors "github.com/smartcontractkit/chain-selectors" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -24,7 +22,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip" @@ -199,33 +196,17 @@ func NewExecServices(ctx context.Context, lggr logger.Logger, jb job.Job, srcPro // UnregisterExecPluginLpFilters unregisters all the registered filters for both source and dest chains. // See comment in UnregisterCommitPluginLpFilters // It MUST mirror the filters registered in NewExecServices. -func UnregisterExecPluginLpFilters(ctx context.Context, lggr logger.Logger, jb job.Job, chainSet legacyevm.LegacyChainContainer) error { - params, err := extractJobSpecParams(lggr, jb, chainSet, false) - if err != nil { - return err - } - - offRampAddress, err := params.offRampReader.Address(ctx) - if err != nil { - return fmt.Errorf("get offramp reader address: %w", err) - } - - versionFinder := factory.NewEvmVersionFinder() +// This currently works because the filters registered by the created custom providers when the job is first added +// are stored in the db. Those same filters are unregistered (i.e. deleted from the db) by the newly created providers +// that are passed in from cleanupEVM, as while the providers have no knowledge of each other, they are created +// on the same source and dest relayer. +func UnregisterExecPluginLpFilters(srcProvider types.CCIPExecProvider, dstProvider types.CCIPExecProvider) error { unregisterFuncs := []func() error{ func() error { - return factory.CloseCommitStoreReader(lggr, versionFinder, params.offRampConfig.CommitStore, params.destChain.Client(), params.destChain.LogPoller()) + return srcProvider.Close() }, func() error { - return factory.CloseOnRampReader(lggr, versionFinder, params.offRampConfig.SourceChainSelector, params.offRampConfig.ChainSelector, params.offRampConfig.OnRamp, params.sourceChain.LogPoller(), params.sourceChain.Client()) - }, - func() error { - return factory.CloseOffRampReader(lggr, versionFinder, offRampAddress, params.destChain.Client(), params.destChain.LogPoller(), params.destChain.GasEstimator(), params.destChain.Config().EVM().GasEstimator().PriceMax().ToInt()) - }, - func() error { // usdc token data reader - if usdcDisabled := params.pluginConfig.USDCConfig.AttestationAPI == ""; usdcDisabled { - return nil - } - return ccipdata.CloseUSDCReader(lggr, jobIDToString(jb.ID), params.pluginConfig.USDCConfig.SourceMessageTransmitterAddress, params.sourceChain.LogPoller()) + return dstProvider.Close() }, } @@ -243,62 +224,3 @@ func UnregisterExecPluginLpFilters(ctx context.Context, lggr logger.Logger, jb j func ExecReportToEthTxMeta(ctx context.Context, typ ccipconfig.ContractType, ver semver.Version) (func(report []byte) (*txmgr.TxMeta, error), error) { return factory.ExecReportToEthTxMeta(ctx, typ, ver) } - -type jobSpecParams struct { - pluginConfig ccipconfig.ExecPluginJobSpecConfig - offRampConfig cciptypes.OffRampStaticConfig - offRampReader ccipdata.OffRampReader - sourceChain legacyevm.Chain - destChain legacyevm.Chain -} - -func extractJobSpecParams(lggr logger.Logger, jb job.Job, chainSet legacyevm.LegacyChainContainer, registerFilters bool) (*jobSpecParams, error) { - if jb.OCR2OracleSpec == nil { - return nil, fmt.Errorf("spec is nil") - } - spec := jb.OCR2OracleSpec - var pluginConfig ccipconfig.ExecPluginJobSpecConfig - err := json.Unmarshal(spec.PluginConfig.Bytes(), &pluginConfig) - if err != nil { - return nil, err - } - - destChain, _, err := ccipconfig.GetChainFromSpec(spec, chainSet) - if err != nil { - return nil, err - } - - versionFinder := factory.NewEvmVersionFinder() - offRampAddress := ccipcalc.HexToAddress(spec.ContractID) - offRampReader, err := factory.NewOffRampReader(lggr, versionFinder, offRampAddress, destChain.Client(), destChain.LogPoller(), destChain.GasEstimator(), destChain.Config().EVM().GasEstimator().PriceMax().ToInt(), registerFilters) - if err != nil { - return nil, fmt.Errorf("create offRampReader: %w", err) - } - - offRampConfig, err := offRampReader.GetStaticConfig(context.Background()) - if err != nil { - return nil, fmt.Errorf("get offRamp static config: %w", err) - } - - chainID, err := chainselectors.ChainIdFromSelector(offRampConfig.SourceChainSelector) - if err != nil { - return nil, err - } - - sourceChain, err := chainSet.Get(strconv.FormatUint(chainID, 10)) - if err != nil { - return nil, fmt.Errorf("open source chain: %w", err) - } - - return &jobSpecParams{ - pluginConfig: pluginConfig, - offRampConfig: offRampConfig, - offRampReader: offRampReader, - sourceChain: sourceChain, - destChain: destChain, - }, nil -} - -func jobIDToString(id int32) string { - return fmt.Sprintf("job_%d", id) -} diff --git a/core/services/ocr2/plugins/ccip/ccipexec/initializers_test.go b/core/services/ocr2/plugins/ccip/ccipexec/initializers_test.go deleted file mode 100644 index 2498a74189..0000000000 --- a/core/services/ocr2/plugins/ccip/ccipexec/initializers_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package ccipexec - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - legacyEvmORMMocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/job" -) - -func TestGetExecutionPluginFilterNamesFromSpec(t *testing.T) { - testCases := []struct { - description string - spec *job.OCR2OracleSpec - expectingErr bool - }{ - { - description: "should not panic with nil spec", - spec: nil, - expectingErr: true, - }, - { - description: "invalid config", - spec: &job.OCR2OracleSpec{ - PluginConfig: map[string]interface{}{}, - }, - expectingErr: true, - }, - { - description: "invalid off ramp address", - spec: &job.OCR2OracleSpec{ - PluginConfig: map[string]interface{}{"offRamp": "123"}, - }, - expectingErr: true, - }, - { - description: "invalid contract id", - spec: &job.OCR2OracleSpec{ - ContractID: "whatever...", - }, - expectingErr: true, - }, - } - - ctx := testutils.Context(t) - - for _, tc := range testCases { - chainSet := &legacyEvmORMMocks.LegacyChainContainer{} - t.Run(tc.description, func(t *testing.T) { - err := UnregisterExecPluginLpFilters(ctx, logger.TestLogger(t), job.Job{OCR2OracleSpec: tc.spec}, chainSet) - if tc.expectingErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} diff --git a/core/services/ocr2/plugins/ccip/exportinternal.go b/core/services/ocr2/plugins/ccip/exportinternal.go index 035aa6f68a..2a5767ac85 100644 --- a/core/services/ocr2/plugins/ccip/exportinternal.go +++ b/core/services/ocr2/plugins/ccip/exportinternal.go @@ -41,10 +41,18 @@ func NewCommitStoreReader(lggr logger.Logger, versionFinder VersionFinder, addre return factory.NewCommitStoreReader(lggr, versionFinder, address, ec, lp) } +func CloseCommitStoreReader(lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller) error { + return factory.CloseCommitStoreReader(lggr, versionFinder, address, ec, lp) +} + func NewOffRampReader(lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool) (ccipdata.OffRampReader, error) { return factory.NewOffRampReader(lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, registerFilters) } +func CloseOffRampReader(lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) error { + return factory.CloseOffRampReader(lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice) +} + func NewEvmVersionFinder() factory.EvmVersionFinder { return factory.NewEvmVersionFinder() } @@ -53,6 +61,10 @@ func NewOnRampReader(lggr logger.Logger, versionFinder VersionFinder, sourceSele return factory.NewOnRampReader(lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source) } +func CloseOnRampReader(lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress ccip.Address, sourceLP logpoller.LogPoller, source client.Client) error { + return factory.CloseOnRampReader(lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source) +} + type OffRampReader = ccipdata.OffRampReader type DynamicPriceGetterClient = pricegetter.DynamicPriceGetterClient @@ -77,6 +89,10 @@ func NewUSDCReader(lggr logger.Logger, jobID string, transmitter common.Address, return ccipdata.NewUSDCReader(lggr, jobID, transmitter, lp, registerFilters) } +func CloseUSDCReader(lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller) error { + return ccipdata.CloseUSDCReader(lggr, jobID, transmitter, lp) +} + type USDCReaderImpl = ccipdata.USDCReaderImpl var DefaultRpcBatchSizeLimit = rpclib.DefaultRpcBatchSizeLimit diff --git a/core/services/relay/evm/commit_provider.go b/core/services/relay/evm/commit_provider.go index f2a25842f0..35de8efbd8 100644 --- a/core/services/relay/evm/commit_provider.go +++ b/core/services/relay/evm/commit_provider.go @@ -5,6 +5,8 @@ import ( "fmt" "math/big" + "go.uber.org/multierr" + "github.com/ethereum/go-ethereum/accounts/abi/bind" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -28,6 +30,11 @@ type SrcCommitProvider struct { lp logpoller.LogPoller estimator gas.EvmFeeEstimator maxGasPrice *big.Int + + // these values will be lazily initialized + seenOnRampAddress *cciptypes.Address + seenSourceChainSelector *uint64 + seenDestChainSelector *uint64 } func NewSrcCommitProvider( @@ -58,6 +65,10 @@ type DstCommitProvider struct { configWatcher *configWatcher gasEstimator gas.EvmFeeEstimator maxGasPrice big.Int + + // these values will be lazily initialized + seenCommitStoreAddress *cciptypes.Address + seenOffRampAddress *cciptypes.Address } func NewDstCommitProvider( @@ -84,85 +95,128 @@ func NewDstCommitProvider( } } -func (P SrcCommitProvider) Name() string { +func (P *SrcCommitProvider) Name() string { return "CCIPCommitProvider.SrcRelayerProvider" } -func (P SrcCommitProvider) Close() error { - return nil +// Close is called when the job that created this provider is deleted. +// At this time, any of the methods on the provider may or may not have been called. +// If NewOnRampReader has not been called, their corresponding +// Close methods will be expected to error. +func (P *SrcCommitProvider) Close() error { + versionFinder := ccip.NewEvmVersionFinder() + + unregisterFuncs := make([]func() error, 0, 2) + unregisterFuncs = append(unregisterFuncs, func() error { + // avoid panic in the case NewOnRampReader wasn't called + if P.seenOnRampAddress == nil { + return nil + } + return ccip.CloseOnRampReader(P.lggr, versionFinder, *P.seenSourceChainSelector, *P.seenDestChainSelector, *P.seenOnRampAddress, P.lp, P.client) + }) + + var multiErr error + for _, fn := range unregisterFuncs { + if err := fn(); err != nil { + multiErr = multierr.Append(multiErr, err) + } + } + return multiErr } -func (P SrcCommitProvider) Ready() error { +func (P *SrcCommitProvider) Ready() error { return nil } -func (P SrcCommitProvider) HealthReport() map[string]error { +func (P *SrcCommitProvider) HealthReport() map[string]error { return make(map[string]error) } -func (P SrcCommitProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { +func (P *SrcCommitProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { // TODO CCIP-2494 // "OffchainConfigDigester called on SrcCommitProvider. Valid on DstCommitProvider." return UnimplementedOffchainConfigDigester{} } -func (P SrcCommitProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { +func (P *SrcCommitProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { // // TODO CCIP-2494 // "ContractConfigTracker called on SrcCommitProvider. Valid on DstCommitProvider.") return UnimplementedContractConfigTracker{} } -func (P SrcCommitProvider) ContractTransmitter() ocrtypes.ContractTransmitter { +func (P *SrcCommitProvider) ContractTransmitter() ocrtypes.ContractTransmitter { // // TODO CCIP-2494 // "ContractTransmitter called on SrcCommitProvider. Valid on DstCommitProvider." return UnimplementedContractTransmitter{} } -func (P SrcCommitProvider) ChainReader() commontypes.ContractReader { +func (P *SrcCommitProvider) ChainReader() commontypes.ContractReader { return nil } -func (P SrcCommitProvider) Codec() commontypes.Codec { +func (P *SrcCommitProvider) Codec() commontypes.Codec { return nil } -func (P DstCommitProvider) Name() string { +func (P *DstCommitProvider) Name() string { return "CCIPCommitProvider.DstRelayerProvider" } -func (P DstCommitProvider) Close() error { - return nil +func (P *DstCommitProvider) Close() error { + versionFinder := ccip.NewEvmVersionFinder() + + unregisterFuncs := make([]func() error, 0, 2) + unregisterFuncs = append(unregisterFuncs, func() error { + if P.seenCommitStoreAddress == nil { + return nil + } + return ccip.CloseCommitStoreReader(P.lggr, versionFinder, *P.seenCommitStoreAddress, P.client, P.lp) + }) + unregisterFuncs = append(unregisterFuncs, func() error { + if P.seenOffRampAddress == nil { + return nil + } + return ccip.CloseOffRampReader(P.lggr, versionFinder, *P.seenOffRampAddress, P.client, P.lp, nil, big.NewInt(0)) + }) + + var multiErr error + for _, fn := range unregisterFuncs { + if err := fn(); err != nil { + multiErr = multierr.Append(multiErr, err) + } + } + return multiErr } -func (P DstCommitProvider) Ready() error { +func (P *DstCommitProvider) Ready() error { return nil } -func (P DstCommitProvider) HealthReport() map[string]error { +func (P *DstCommitProvider) HealthReport() map[string]error { return make(map[string]error) } -func (P DstCommitProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { +func (P *DstCommitProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { return P.configWatcher.OffchainConfigDigester() } -func (P DstCommitProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { +func (P *DstCommitProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return P.configWatcher.ContractConfigTracker() } -func (P DstCommitProvider) ContractTransmitter() ocrtypes.ContractTransmitter { +func (P *DstCommitProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return P.contractTransmitter } -func (P DstCommitProvider) ChainReader() commontypes.ContractReader { +func (P *DstCommitProvider) ChainReader() commontypes.ContractReader { return nil } -func (P DstCommitProvider) Codec() commontypes.Codec { +func (P *DstCommitProvider) Codec() commontypes.Codec { return nil } -func (P SrcCommitProvider) Start(ctx context.Context) error { +func (P *SrcCommitProvider) Start(ctx context.Context) error { if P.startBlock != 0 { P.lggr.Infow("start replaying src chain", "fromBlock", P.startBlock) return P.lp.Replay(ctx, int64(P.startBlock)) @@ -170,7 +224,7 @@ func (P SrcCommitProvider) Start(ctx context.Context) error { return nil } -func (P DstCommitProvider) Start(ctx context.Context) error { +func (P *DstCommitProvider) Start(ctx context.Context) error { if P.startBlock != 0 { P.lggr.Infow("start replaying dst chain", "fromBlock", P.startBlock) return P.lp.Replay(ctx, int64(P.startBlock)) @@ -178,55 +232,61 @@ func (P DstCommitProvider) Start(ctx context.Context) error { return nil } -func (P SrcCommitProvider) NewPriceGetter(ctx context.Context) (priceGetter cciptypes.PriceGetter, err error) { +func (P *SrcCommitProvider) NewPriceGetter(ctx context.Context) (priceGetter cciptypes.PriceGetter, err error) { return nil, fmt.Errorf("can't construct a price getter from one relayer") } -func (P DstCommitProvider) NewPriceGetter(ctx context.Context) (priceGetter cciptypes.PriceGetter, err error) { +func (P *DstCommitProvider) NewPriceGetter(ctx context.Context) (priceGetter cciptypes.PriceGetter, err error) { return nil, fmt.Errorf("can't construct a price getter from one relayer") } -func (P SrcCommitProvider) NewCommitStoreReader(ctx context.Context, commitStoreAddress cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) { +func (P *SrcCommitProvider) NewCommitStoreReader(ctx context.Context, commitStoreAddress cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) { commitStoreReader = NewIncompleteSourceCommitStoreReader(P.estimator, P.maxGasPrice) return } -func (P DstCommitProvider) NewCommitStoreReader(ctx context.Context, commitStoreAddress cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) { +func (P *DstCommitProvider) NewCommitStoreReader(ctx context.Context, commitStoreAddress cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) { + P.seenCommitStoreAddress = &commitStoreAddress + versionFinder := ccip.NewEvmVersionFinder() commitStoreReader, err = NewIncompleteDestCommitStoreReader(P.lggr, versionFinder, commitStoreAddress, P.client, P.lp) return } -func (P SrcCommitProvider) NewOnRampReader(ctx context.Context, onRampAddress cciptypes.Address, sourceChainSelector uint64, destChainSelector uint64) (onRampReader cciptypes.OnRampReader, err error) { +func (P *SrcCommitProvider) NewOnRampReader(ctx context.Context, onRampAddress cciptypes.Address, sourceChainSelector uint64, destChainSelector uint64) (onRampReader cciptypes.OnRampReader, err error) { + P.seenOnRampAddress = &onRampAddress + P.seenSourceChainSelector = &sourceChainSelector + P.seenDestChainSelector = &destChainSelector + versionFinder := ccip.NewEvmVersionFinder() onRampReader, err = ccip.NewOnRampReader(P.lggr, versionFinder, sourceChainSelector, destChainSelector, onRampAddress, P.lp, P.client) return } -func (P DstCommitProvider) NewOnRampReader(ctx context.Context, onRampAddress cciptypes.Address, sourceChainSelector uint64, destChainSelector uint64) (onRampReader cciptypes.OnRampReader, err error) { +func (P *DstCommitProvider) NewOnRampReader(ctx context.Context, onRampAddress cciptypes.Address, sourceChainSelector uint64, destChainSelector uint64) (onRampReader cciptypes.OnRampReader, err error) { return nil, fmt.Errorf("invalid: NewOnRampReader called for DstCommitProvider.NewOnRampReader should be called on SrcCommitProvider") } -func (P SrcCommitProvider) NewOffRampReader(ctx context.Context, offRampAddr cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { +func (P *SrcCommitProvider) NewOffRampReader(ctx context.Context, offRampAddr cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { return nil, fmt.Errorf("invalid: NewOffRampReader called for SrcCommitProvider. NewOffRampReader should be called on DstCommitProvider") } -func (P DstCommitProvider) NewOffRampReader(ctx context.Context, offRampAddr cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { +func (P *DstCommitProvider) NewOffRampReader(ctx context.Context, offRampAddr cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { offRampReader, err = ccip.NewOffRampReader(P.lggr, P.versionFinder, offRampAddr, P.client, P.lp, P.gasEstimator, &P.maxGasPrice, true) return } -func (P SrcCommitProvider) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (priceRegistryReader cciptypes.PriceRegistryReader, err error) { +func (P *SrcCommitProvider) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (priceRegistryReader cciptypes.PriceRegistryReader, err error) { return nil, fmt.Errorf("invalid: NewPriceRegistryReader called for SrcCommitProvider. NewOffRampReader should be called on DstCommitProvider") } -func (P DstCommitProvider) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (priceRegistryReader cciptypes.PriceRegistryReader, err error) { +func (P *DstCommitProvider) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (priceRegistryReader cciptypes.PriceRegistryReader, err error) { destPriceRegistry := ccip.NewEvmPriceRegistry(P.lp, P.client, P.lggr, ccip.CommitPluginLabel) priceRegistryReader, err = destPriceRegistry.NewPriceRegistryReader(ctx, addr) return } -func (P SrcCommitProvider) SourceNativeToken(ctx context.Context, sourceRouterAddr cciptypes.Address) (cciptypes.Address, error) { +func (P *SrcCommitProvider) SourceNativeToken(ctx context.Context, sourceRouterAddr cciptypes.Address) (cciptypes.Address, error) { sourceRouterAddrHex, err := ccip.GenericAddrToEvm(sourceRouterAddr) if err != nil { return "", err @@ -243,6 +303,6 @@ func (P SrcCommitProvider) SourceNativeToken(ctx context.Context, sourceRouterAd return ccip.EvmAddrToGeneric(sourceNative), nil } -func (P DstCommitProvider) SourceNativeToken(ctx context.Context, sourceRouterAddr cciptypes.Address) (cciptypes.Address, error) { +func (P *DstCommitProvider) SourceNativeToken(ctx context.Context, sourceRouterAddr cciptypes.Address) (cciptypes.Address, error) { return "", fmt.Errorf("invalid: SourceNativeToken called for DstCommitProvider. SourceNativeToken should be called on SrcCommitProvider") } diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 3cc7633aa3..90bc7ec106 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -12,6 +12,8 @@ import ( "sync" "time" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipcommit" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipexec" @@ -522,6 +524,7 @@ func (r *Relayer) NewCCIPExecProvider(rargs commontypes.RelayArgs, pargs commont configWatcher, r.chain.GasEstimator(), *r.chain.Config().EVM().GasEstimator().PriceMax().ToInt(), + cciptypes.Address(rargs.ContractID), ) } diff --git a/core/services/relay/evm/exec_provider.go b/core/services/relay/evm/exec_provider.go index 740a61f23d..45b2a4c401 100644 --- a/core/services/relay/evm/exec_provider.go +++ b/core/services/relay/evm/exec_provider.go @@ -7,6 +7,8 @@ import ( "net/url" "time" + "go.uber.org/multierr" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -35,6 +37,11 @@ type SrcExecProvider struct { usdcAttestationAPITimeoutSeconds int usdcAttestationAPIIntervalMilliseconds int usdcSrcMsgTransmitterAddr common.Address + + // these values are nil and are updated for Close() + seenOnRampAddress *cciptypes.Address + seenSourceChainSelector *uint64 + seenDestChainSelector *uint64 } func NewSrcExecProvider( @@ -76,11 +83,11 @@ func NewSrcExecProvider( }, nil } -func (s SrcExecProvider) Name() string { +func (s *SrcExecProvider) Name() string { return "CCIP.SrcExecProvider" } -func (s SrcExecProvider) Start(ctx context.Context) error { +func (s *SrcExecProvider) Start(ctx context.Context) error { if s.startBlock != 0 { s.lggr.Infow("start replaying src chain", "fromBlock", s.startBlock) return s.lp.Replay(ctx, int64(s.startBlock)) @@ -88,66 +95,91 @@ func (s SrcExecProvider) Start(ctx context.Context) error { return nil } -func (s SrcExecProvider) Close() error { - return nil +// Close is called when the job that created this provider is closed. +func (s *SrcExecProvider) Close() error { + versionFinder := ccip.NewEvmVersionFinder() + + unregisterFuncs := make([]func() error, 0, 2) + unregisterFuncs = append(unregisterFuncs, func() error { + // avoid panic in the case NewOnRampReader wasn't called + if s.seenOnRampAddress == nil { + return nil + } + return ccip.CloseOnRampReader(s.lggr, versionFinder, *s.seenSourceChainSelector, *s.seenDestChainSelector, *s.seenOnRampAddress, s.lp, s.client) + }) + unregisterFuncs = append(unregisterFuncs, func() error { + if s.usdcAttestationAPI == "" { + return nil + } + return ccip.CloseUSDCReader(s.lggr, s.lggr.Name(), s.usdcSrcMsgTransmitterAddr, s.lp) + }) + var multiErr error + for _, fn := range unregisterFuncs { + if err := fn(); err != nil { + multiErr = multierr.Append(multiErr, err) + } + } + return multiErr } -func (s SrcExecProvider) Ready() error { +func (s *SrcExecProvider) Ready() error { return nil } -func (s SrcExecProvider) HealthReport() map[string]error { +func (s *SrcExecProvider) HealthReport() map[string]error { return make(map[string]error) } -func (s SrcExecProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { +func (s *SrcExecProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { // TODO CCIP-2494 // OffchainConfigDigester called on SrcExecProvider. It should only be called on DstExecProvider return UnimplementedOffchainConfigDigester{} } -func (s SrcExecProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { +func (s *SrcExecProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { // TODO CCIP-2494 // "ContractConfigTracker called on SrcExecProvider. It should only be called on DstExecProvider return UnimplementedContractConfigTracker{} } -func (s SrcExecProvider) ContractTransmitter() ocrtypes.ContractTransmitter { +func (s *SrcExecProvider) ContractTransmitter() ocrtypes.ContractTransmitter { // TODO CCIP-2494 // "ContractTransmitter called on SrcExecProvider. It should only be called on DstExecProvider return UnimplementedContractTransmitter{} } -func (s SrcExecProvider) ChainReader() commontypes.ContractReader { +func (s *SrcExecProvider) ChainReader() commontypes.ContractReader { return nil } -func (s SrcExecProvider) Codec() commontypes.Codec { +func (s *SrcExecProvider) Codec() commontypes.Codec { return nil } -func (s SrcExecProvider) NewCommitStoreReader(ctx context.Context, addr cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) { +func (s *SrcExecProvider) NewCommitStoreReader(ctx context.Context, addr cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) { commitStoreReader = NewIncompleteSourceCommitStoreReader(s.estimator, s.maxGasPrice) return } -func (s SrcExecProvider) NewOffRampReader(ctx context.Context, addr cciptypes.Address) (cciptypes.OffRampReader, error) { +func (s *SrcExecProvider) NewOffRampReader(ctx context.Context, addr cciptypes.Address) (cciptypes.OffRampReader, error) { return nil, fmt.Errorf("invalid: NewOffRampReader called on SrcExecProvider. Valid on DstExecProvider") } -func (s SrcExecProvider) NewOnRampReader(ctx context.Context, onRampAddress cciptypes.Address, sourceChainSelector uint64, destChainSelector uint64) (onRampReader cciptypes.OnRampReader, err error) { +func (s *SrcExecProvider) NewOnRampReader(ctx context.Context, onRampAddress cciptypes.Address, sourceChainSelector uint64, destChainSelector uint64) (onRampReader cciptypes.OnRampReader, err error) { + s.seenOnRampAddress = &onRampAddress + versionFinder := ccip.NewEvmVersionFinder() onRampReader, err = ccip.NewOnRampReader(s.lggr, versionFinder, sourceChainSelector, destChainSelector, onRampAddress, s.lp, s.client) return } -func (s SrcExecProvider) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (priceRegistryReader cciptypes.PriceRegistryReader, err error) { +func (s *SrcExecProvider) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (priceRegistryReader cciptypes.PriceRegistryReader, err error) { srcPriceRegistry := ccip.NewEvmPriceRegistry(s.lp, s.client, s.lggr, ccip.ExecPluginLabel) priceRegistryReader, err = srcPriceRegistry.NewPriceRegistryReader(ctx, addr) return } -func (s SrcExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress cciptypes.Address) (tokenDataReader cciptypes.TokenDataReader, err error) { +func (s *SrcExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress cciptypes.Address) (tokenDataReader cciptypes.TokenDataReader, err error) { attestationURI, err2 := url.ParseRequestURI(s.usdcAttestationAPI) if err2 != nil { return nil, fmt.Errorf("failed to parse USDC attestation API: %w", err2) @@ -167,11 +199,11 @@ func (s SrcExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress cc return } -func (s SrcExecProvider) NewTokenPoolBatchedReader(ctx context.Context, offRampAddr cciptypes.Address, sourceChainSelector uint64) (cciptypes.TokenPoolBatchedReader, error) { +func (s *SrcExecProvider) NewTokenPoolBatchedReader(ctx context.Context, offRampAddr cciptypes.Address, sourceChainSelector uint64) (cciptypes.TokenPoolBatchedReader, error) { return nil, fmt.Errorf("invalid: NewTokenPoolBatchedReader called on SrcExecProvider. It should only be called on DstExecProvdier") } -func (s SrcExecProvider) SourceNativeToken(ctx context.Context, sourceRouterAddr cciptypes.Address) (cciptypes.Address, error) { +func (s *SrcExecProvider) SourceNativeToken(ctx context.Context, sourceRouterAddr cciptypes.Address) (cciptypes.Address, error) { sourceRouterAddrHex, err := ccip.GenericAddrToEvm(sourceRouterAddr) if err != nil { return "", err @@ -198,6 +230,10 @@ type DstExecProvider struct { configWatcher *configWatcher gasEstimator gas.EvmFeeEstimator maxGasPrice big.Int + offRampAddress cciptypes.Address + + // these values are nil and are updated for Close() + seenCommitStoreAddr *cciptypes.Address } func NewDstExecProvider( @@ -210,6 +246,7 @@ func NewDstExecProvider( configWatcher *configWatcher, gasEstimator gas.EvmFeeEstimator, maxGasPrice big.Int, + offRampAddress cciptypes.Address, ) (commontypes.CCIPExecProvider, error) { return &DstExecProvider{ lggr: lggr, @@ -221,14 +258,15 @@ func NewDstExecProvider( configWatcher: configWatcher, gasEstimator: gasEstimator, maxGasPrice: maxGasPrice, + offRampAddress: offRampAddress, }, nil } -func (d DstExecProvider) Name() string { +func (d *DstExecProvider) Name() string { return "CCIP.DestRelayerExecProvider" } -func (d DstExecProvider) Start(ctx context.Context) error { +func (d *DstExecProvider) Start(ctx context.Context) error { if d.startBlock != 0 { d.lggr.Infow("start replaying dst chain", "fromBlock", d.startBlock) return d.lp.Replay(ctx, int64(d.startBlock)) @@ -236,64 +274,89 @@ func (d DstExecProvider) Start(ctx context.Context) error { return nil } -func (d DstExecProvider) Close() error { - return nil +// Close is called when the job that created this provider is deleted +// At this time, any of the methods on the provider may or may not have been called. +// If NewOnRampReader and NewCommitStoreReader have not been called, their corresponding +// Close methods will be expected to error. +func (d *DstExecProvider) Close() error { + versionFinder := ccip.NewEvmVersionFinder() + + unregisterFuncs := make([]func() error, 0, 2) + unregisterFuncs = append(unregisterFuncs, func() error { + if d.seenCommitStoreAddr == nil { + return nil + } + return ccip.CloseCommitStoreReader(d.lggr, versionFinder, *d.seenCommitStoreAddr, d.client, d.lp) + }) + unregisterFuncs = append(unregisterFuncs, func() error { + return ccip.CloseOffRampReader(d.lggr, versionFinder, d.offRampAddress, d.client, d.lp, nil, big.NewInt(0)) + }) + + var multiErr error + for _, fn := range unregisterFuncs { + if err := fn(); err != nil { + multiErr = multierr.Append(multiErr, err) + } + } + return multiErr } -func (d DstExecProvider) Ready() error { +func (d *DstExecProvider) Ready() error { return nil } -func (d DstExecProvider) HealthReport() map[string]error { +func (d *DstExecProvider) HealthReport() map[string]error { return make(map[string]error) } -func (d DstExecProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { +func (d *DstExecProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { return d.configWatcher.OffchainConfigDigester() } -func (d DstExecProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { +func (d *DstExecProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return d.configWatcher.ContractConfigTracker() } -func (d DstExecProvider) ContractTransmitter() ocrtypes.ContractTransmitter { +func (d *DstExecProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return d.contractTransmitter } -func (d DstExecProvider) ChainReader() commontypes.ContractReader { +func (d *DstExecProvider) ChainReader() commontypes.ContractReader { return nil } -func (d DstExecProvider) Codec() commontypes.Codec { +func (d *DstExecProvider) Codec() commontypes.Codec { return nil } -func (d DstExecProvider) NewCommitStoreReader(ctx context.Context, addr cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) { +func (d *DstExecProvider) NewCommitStoreReader(ctx context.Context, addr cciptypes.Address) (commitStoreReader cciptypes.CommitStoreReader, err error) { + d.seenCommitStoreAddr = &addr + versionFinder := ccip.NewEvmVersionFinder() commitStoreReader, err = NewIncompleteDestCommitStoreReader(d.lggr, versionFinder, addr, d.client, d.lp) return } -func (d DstExecProvider) NewOffRampReader(ctx context.Context, offRampAddress cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { +func (d *DstExecProvider) NewOffRampReader(ctx context.Context, offRampAddress cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { offRampReader, err = ccip.NewOffRampReader(d.lggr, d.versionFinder, offRampAddress, d.client, d.lp, d.gasEstimator, &d.maxGasPrice, true) return } -func (d DstExecProvider) NewOnRampReader(ctx context.Context, addr cciptypes.Address, sourceChainSelector uint64, destChainSelector uint64) (cciptypes.OnRampReader, error) { +func (d *DstExecProvider) NewOnRampReader(ctx context.Context, addr cciptypes.Address, sourceChainSelector uint64, destChainSelector uint64) (cciptypes.OnRampReader, error) { return nil, fmt.Errorf("invalid: NewOnRampReader called on DstExecProvider. It should only be called on SrcExecProvider") } -func (d DstExecProvider) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (priceRegistryReader cciptypes.PriceRegistryReader, err error) { +func (d *DstExecProvider) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (priceRegistryReader cciptypes.PriceRegistryReader, err error) { destPriceRegistry := ccip.NewEvmPriceRegistry(d.lp, d.client, d.lggr, ccip.ExecPluginLabel) priceRegistryReader, err = destPriceRegistry.NewPriceRegistryReader(ctx, addr) return } -func (d DstExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress cciptypes.Address) (cciptypes.TokenDataReader, error) { +func (d *DstExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress cciptypes.Address) (cciptypes.TokenDataReader, error) { return nil, fmt.Errorf("invalid: NewTokenDataReader called on DstExecProvider. It should only be called on SrcExecProvider") } -func (d DstExecProvider) NewTokenPoolBatchedReader(ctx context.Context, offRampAddress cciptypes.Address, sourceChainSelector uint64) (tokenPoolBatchedReader cciptypes.TokenPoolBatchedReader, err error) { +func (d *DstExecProvider) NewTokenPoolBatchedReader(ctx context.Context, offRampAddress cciptypes.Address, sourceChainSelector uint64) (tokenPoolBatchedReader cciptypes.TokenPoolBatchedReader, err error) { batchCaller := ccip.NewDynamicLimitedBatchCaller( d.lggr, d.client, @@ -309,6 +372,6 @@ func (d DstExecProvider) NewTokenPoolBatchedReader(ctx context.Context, offRampA return } -func (d DstExecProvider) SourceNativeToken(ctx context.Context, addr cciptypes.Address) (cciptypes.Address, error) { +func (d *DstExecProvider) SourceNativeToken(ctx context.Context, addr cciptypes.Address) (cciptypes.Address, error) { return "", fmt.Errorf("invalid: SourceNativeToken called on DstExecProvider. It should only be called on SrcExecProvider") }