diff --git a/core/services/ocr2/plugins/ccip/abihelpers/abi_helpers.go b/core/services/ocr2/plugins/ccip/abihelpers/abi_helpers.go index 49bdf9130f..095e1c0808 100644 --- a/core/services/ocr2/plugins/ccip/abihelpers/abi_helpers.go +++ b/core/services/ocr2/plugins/ccip/abihelpers/abi_helpers.go @@ -29,9 +29,8 @@ const ( ExecutionStateFailure ) +// TODO: Deprecate in favour of version specific types var EventSignatures struct { - // OnRamp - //SendRequested common.Hash // CommitStore ReportAccepted common.Hash // OffRamp @@ -45,10 +44,6 @@ var EventSignatures struct { FeeTokenAdded common.Hash FeeTokenRemoved common.Hash - USDCMessageSent common.Hash - - // offset || sourceChainID || seqNum || ... - SendRequestedSequenceNumberWord int // offset || priceUpdatesOffset || minSeqNum || maxSeqNum || merkleRoot ReportAcceptedMaxSequenceNumberWord int // sig || seqNum || messageId || ... diff --git a/core/services/ocr2/plugins/ccip/commit_plugin_test.go b/core/services/ocr2/plugins/ccip/commit_plugin_test.go index a7a53dd32b..710f668042 100644 --- a/core/services/ocr2/plugins/ccip/commit_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_plugin_test.go @@ -4,24 +4,24 @@ import ( "context" "fmt" "strconv" - "sync" "testing" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" - mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" + "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/testhelpers" + pipelinemocks "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/mocks" "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestGetCommitPluginFilterNamesFromSpec(t *testing.T) { + lggr := logger.TestLogger(t) testCases := []struct { description string spec *job.OCR2OracleSpec @@ -63,6 +63,7 @@ func TestGetCommitPluginFilterNamesFromSpec(t *testing.T) { for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { chainSet := &evmmocks.LegacyChainContainer{} + prMock := &pipelinemocks.Runner{} if tc.spec != nil { if chainID, ok := tc.spec.RelayConfig["chainID"]; ok { @@ -72,7 +73,7 @@ func TestGetCommitPluginFilterNamesFromSpec(t *testing.T) { } } - err := UnregisterCommitPluginLpFilters(context.Background(), tc.spec, chainSet) + err := UnregisterCommitPluginLpFilters(context.Background(), lggr, job.Job{OCR2OracleSpec: tc.spec}, prMock, chainSet) if tc.expectingErr { assert.Error(t, err) } else { @@ -104,13 +105,14 @@ func TestGetCommitPluginFilterNames(t *testing.T) { dstLP.On("UnregisterFilter", "Token pool added - 0xDAFeA492D9c6733Ae3D56b7eD1AdB60692C98BC4", mock.Anything).Return(nil) dstLP.On("UnregisterFilter", "Token pool removed - 0xDAFeA492D9c6733Ae3D56b7eD1AdB60692C98BC4", mock.Anything).Return(nil) - err := unregisterCommitPluginFilters(context.Background(), srcLP, dstLP, mockCommitStore, offRampAddr) + err := unregisterCommitPluginFilters(context.Background(), dstLP, mockCommitStore, offRampAddr) assert.NoError(t, err) srcLP.AssertExpectations(t) dstLP.AssertExpectations(t) } +/* func Test_updateCommitPluginLogPollerFilters(t *testing.T) { srcLP := &mocklp.LogPoller{} dstLP := &mocklp.LogPoller{} @@ -165,3 +167,4 @@ func Test_updateCommitPluginLogPollerFilters(t *testing.T) { srcLP.AssertExpectations(t) dstLP.AssertExpectations(t) } +*/ diff --git a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go index 21a9a2c34f..85fdb85114 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -103,7 +103,7 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { commitStore, _ := testhelpers.NewFakeCommitStore(t, tc.commitStoreSeqNum) commitStore.SetPaused(tc.commitStoreIsPaused) - sourceReader := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockOnRampReader(t) if len(tc.sendReqs) > 0 { sourceReader.On("GetSendRequestsGteSeqNum", ctx, onRampAddress, tc.commitStoreSeqNum, false, sourceFinalityDepth). Return(tc.sendReqs, nil) @@ -133,7 +133,6 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { p.lggr = logger.TestLogger(t) p.inflightReports = newInflightCommitReportsContainer(time.Hour) p.config.commitStore = commitStore - p.config.onRampAddress = onRampAddress p.offchainConfig.SourceFinalityDepth = uint32(sourceFinalityDepth) p.config.onRampReader = sourceReader p.tokenDecimalsCache = tokenDecimalsCache @@ -242,7 +241,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) { destReader.On("GetGasPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, uint64(sourceChainSelector), mock.Anything, 0).Return(tc.gasPriceUpdates, nil) destReader.On("GetTokenPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, mock.Anything, 0).Return(tc.tokenPriceUpdates, nil) - sourceReader := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockOnRampReader(t) if len(tc.sendRequests) > 0 { sourceReader.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddress, tc.expSeqNumRange.Min, tc.expSeqNumRange.Max, 0).Return(tc.sendRequests, nil) } @@ -253,9 +252,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) { p.destPriceRegistry = destPriceRegistry p.config.destReader = destReader p.config.onRampReader = sourceReader - p.config.onRampAddress = onRampAddress p.config.sourceChainSelector = uint64(sourceChainSelector) - p.config.leafHasher = &leafHasher123{} aos := make([]types.AttributedObservation, 0, len(tc.observations)) for _, o := range tc.observations { @@ -1034,7 +1031,7 @@ func TestCommitReportingPlugin_calculateMinMaxSequenceNumbers(t *testing.T) { } } - sourceReader := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockOnRampReader(t) var sendReqs []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] for _, seqNum := range tc.msgSeqNums { sendReqs = append(sendReqs, ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ diff --git a/core/services/ocr2/plugins/ccip/execution_plugin_test.go b/core/services/ocr2/plugins/ccip/execution_plugin_test.go index dec6e2f5ad..07f99402b2 100644 --- a/core/services/ocr2/plugins/ccip/execution_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_plugin_test.go @@ -14,10 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "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/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata/usdc" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestGetExecutionPluginFilterNamesFromSpec(t *testing.T) { @@ -57,7 +54,7 @@ func TestGetExecutionPluginFilterNamesFromSpec(t *testing.T) { for _, tc := range testCases { chainSet := &mocks.LegacyChainContainer{} t.Run(tc.description, func(t *testing.T) { - err := UnregisterExecPluginLpFilters(context.Background(), logger.TestLogger(t), tc.spec, chainSet) + err := UnregisterExecPluginLpFilters(context.Background(), logger.TestLogger(t), job.Job{OCR2OracleSpec: tc.spec}, chainSet) if tc.expectingErr { assert.Error(t, err) } else { @@ -78,20 +75,20 @@ func TestGetExecutionPluginFilterNames(t *testing.T) { mockOnRamp, onRampAddr := testhelpers.NewFakeOnRamp(t) mockOnRamp.SetDynamicCfg(evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{PriceRegistry: srcPriceRegAddr}) - pluginConfig := config.ExecutionPluginJobSpecConfig{ - USDCConfig: config.USDCConfig{ - SourceTokenAddress: utils.RandomAddress(), - SourceMessageTransmitterAddress: utils.RandomAddress(), - AttestationAPI: "http://localhost:8080", - }, - } + //pluginConfig := config.ExecutionPluginJobSpecConfig{ + // USDCConfig: config.USDCConfig{ + // SourceTokenAddress: utils.RandomAddress(), + // SourceMessageTransmitterAddress: utils.RandomAddress(), + // AttestationAPI: "http://localhost:8080", + // }, + //} srcLP := mocklp.NewLogPoller(t) srcFilters := []string{ "Exec ccip sends - " + onRampAddr.String(), "Fee token added - 0xdAFea492D9c6733aE3d56B7ed1ADb60692c98bC9", "Fee token removed - 0xdAFea492D9c6733aE3d56B7ed1ADb60692c98bC9", - usdc.MESSAGE_SENT_FILTER_NAME + " - " + pluginConfig.USDCConfig.SourceMessageTransmitterAddress.Hex(), + //usdc.MESSAGE_SENT_FILTER_NAME + " - " + pluginConfig.USDCConfig.SourceMessageTransmitterAddress.Hex(), } for _, f := range srcFilters { srcLP.On("UnregisterFilter", f, mock.Anything).Return(nil) @@ -112,18 +109,12 @@ func TestGetExecutionPluginFilterNames(t *testing.T) { err := unregisterExecutionPluginLpFilters( context.Background(), - logger.TestLogger(t), srcLP, dstLP, mockOffRamp, - evm_2_evm_offramp.EVM2EVMOffRampStaticConfig{ - CommitStore: commitStoreAddr, - OnRamp: onRampAddr, - SourceChainSelector: 5009297550715157269, - }, + commitStoreAddr, mockOnRamp, nil, - pluginConfig, ) assert.NoError(t, err) diff --git a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go index 18e1db63fc..b5c32ef475 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -143,10 +143,10 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { onRamp, onRampAddr := testhelpers.NewFakeOnRamp(t) p.config.onRamp = onRamp - sourceReader := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockOnRampReader(t) sourceReader.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, mock.Anything, mock.Anything, 0). Return(tc.sendRequests, nil).Maybe() - p.config.sourceReader = sourceReader + p.config.onRampReader = sourceReader cachedDestTokens := cache.NewMockAutoSync[cache.CachedTokens](t) cachedDestTokens.On("Get", ctx).Return(cache.CachedTokens{ @@ -373,8 +373,6 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { }, nil) p.config.destReader = destReader - p.config.leafHasher = leafHasher123{} - onRamp, onRampAddr := testhelpers.NewFakeOnRamp(t) p.config.onRamp = onRamp @@ -397,10 +395,10 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { }}, } } - sourceReader := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockOnRampReader(t) sourceReader.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, observations[0].SeqNr, observations[len(observations)-1].SeqNr, 0).Return(sendReqs, nil) - p.config.sourceReader = sourceReader + p.config.onRampReader = sourceReader execReport, err := p.buildReport(ctx, p.lggr, observations) assert.NoError(t, err) @@ -1022,18 +1020,18 @@ func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { offRamp, offRampAddr := testhelpers.NewFakeOffRamp(t) p.config.offRamp = offRamp - sourceReader := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockOnRampReader(t) sourceReader.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, tc.expQueryMin, tc.expQueryMax, 0). Return(tc.onchainEvents, nil).Maybe() - p.config.sourceReader = sourceReader + p.config.onRampReader = sourceReader destReader := ccipdata.NewMockReader(t) destReader.On("LatestBlock", ctx).Return(tc.destLatestBlock, nil).Maybe() var executedEvents []ccipdata.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] for _, executedSeqNum := range tc.destExecutedSeqNums { executedEvents = append(executedEvents, ccipdata.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]{ - Data: evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged{SequenceNumber: executedSeqNum}, - BlockMeta: ccipdata.BlockMeta{BlockNumber: tc.destLatestBlock - 10}, + Data: evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged{SequenceNumber: executedSeqNum}, + Meta: ccipdata.Meta{BlockNumber: tc.destLatestBlock - 10}, }) } destReader.On("GetExecutionStateChangesBetweenSeqNums", ctx, offRampAddr, tc.expQueryMin, tc.expQueryMax, 0).Return(executedEvents, nil).Maybe() @@ -1097,7 +1095,7 @@ func TestExecutionReportingPluginFactory_UpdateLogPollerFilters(t *testing.T) { }, } - for _, f := range getExecutionPluginSourceLpChainFilters(onRamp.Address(), sourcePriceRegistry.Address(), tokenDataProviders) { + for _, f := range getExecutionPluginSourceLpChainFilters(sourcePriceRegistry.Address()) { sourceLP.On("RegisterFilter", f).Return(nil) } for _, f := range getExecutionPluginDestLpChainFilters(commitStore.Address(), offRamp.Address(), destPriceRegistryAddr) { diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller.go index 0f8a0479ee..0672f90083 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -121,25 +120,6 @@ func (c *LogPollerReader) GetExecutionStateChangesBetweenSeqNums(ctx context.Con ) } -func (c *LogPollerReader) GetLastUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, txHash common.Hash) ([]byte, error) { - logs, err := c.lp.IndexedLogsByTxHash( - abihelpers.EventSignatures.USDCMessageSent, - txHash, - pg.WithParentCtx(ctx), - ) - if err != nil { - return nil, err - } - - for i := range logs { - current := logs[len(logs)-i-1] - if current.LogIndex < logIndex { - return current.Data, nil - } - } - return nil, errors.Errorf("no USDC message found prior to log index %d in tx %s", logIndex, txHash.Hex()) -} - func (c *LogPollerReader) LatestBlock(ctx context.Context) (int64, error) { return c.lp.LatestBlock(pg.WithParentCtx(ctx)) } diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller_test.go index 56f5ff1db2..cfd5de87ec 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller_test.go @@ -1,21 +1,15 @@ package ccipdata import ( - "context" "fmt" - "math/big" "testing" "time" "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -73,107 +67,3 @@ func Test_parseLogs(t *testing.T) { assert.Greater(t, ev.BlockTimestamp, time.Now().Add(-time.Minute)) } } - -func TestLogPollerClient_GetSendRequestsGteSeqNum(t *testing.T) { - onRampAddr := utils.RandomAddress() - seqNum := uint64(100) - confs := 4 - - t.Run("using confs", func(t *testing.T) { - lp := mocks.NewLogPoller(t) - lp.On("LogsDataWordGreaterThan", - abihelpers.EventSignatures.SendRequested, - onRampAddr, - abihelpers.EventSignatures.SendRequestedSequenceNumberWord, - abihelpers.EvmWord(seqNum), - confs, - mock.Anything, - ).Return([]logpoller.Log{}, nil) - - c := &LogPollerReader{lp: lp} - events, err := c.GetSendRequestsGteSeqNum( - context.Background(), - onRampAddr, - seqNum, - false, - confs, - ) - assert.NoError(t, err) - assert.Empty(t, events) - lp.AssertExpectations(t) - }) - - t.Run("using latest confirmed block", func(t *testing.T) { - h := &types.Header{Number: big.NewInt(100000)} - - lp := mocks.NewLogPoller(t) - lp.On("LogsUntilBlockHashDataWordGreaterThan", - abihelpers.EventSignatures.SendRequested, - onRampAddr, - abihelpers.EventSignatures.SendRequestedSequenceNumberWord, - abihelpers.EvmWord(seqNum), - h.Hash(), - mock.Anything, - ).Return([]logpoller.Log{}, nil) - - cl := evmClientMocks.NewClient(t) - cl.On("HeaderByNumber", mock.Anything, mock.Anything).Return(h, nil) - - c := &LogPollerReader{lp: lp, client: cl} - events, err := c.GetSendRequestsGteSeqNum( - context.Background(), - onRampAddr, - seqNum, - true, - confs, - ) - assert.NoError(t, err) - assert.Empty(t, events) - lp.AssertExpectations(t) - cl.AssertExpectations(t) - }) -} - -func TestLogPollerClient_GetLastUSDCMessagePriorToLogIndexInTx(t *testing.T) { - txHash := utils.RandomAddress().Hash() - ccipLogIndex := int64(100) - - expectedData := []byte("-1") - - t.Run("multiple found", func(t *testing.T) { - lp := mocks.NewLogPoller(t) - lp.On("IndexedLogsByTxHash", - abihelpers.EventSignatures.USDCMessageSent, - txHash, - mock.Anything, - ).Return([]logpoller.Log{ - {LogIndex: ccipLogIndex - 2, Data: []byte("-2")}, - {LogIndex: ccipLogIndex - 1, Data: expectedData}, - {LogIndex: ccipLogIndex, Data: []byte("0")}, - {LogIndex: ccipLogIndex + 1, Data: []byte("1")}, - }, nil) - - c := &LogPollerReader{lp: lp} - usdcMessageData, err := c.GetLastUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, txHash) - assert.NoError(t, err) - assert.Equal(t, expectedData, usdcMessageData) - - lp.AssertExpectations(t) - }) - - t.Run("none found", func(t *testing.T) { - lp := mocks.NewLogPoller(t) - lp.On("IndexedLogsByTxHash", - abihelpers.EventSignatures.USDCMessageSent, - txHash, - mock.Anything, - ).Return([]logpoller.Log{}, nil) - - c := &LogPollerReader{lp: lp} - usdcMessageData, err := c.GetLastUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, txHash) - assert.Errorf(t, err, fmt.Sprintf("no USDC message found prior to log index %d in tx %s", ccipLogIndex, txHash.Hex())) - assert.Nil(t, usdcMessageData) - - lp.AssertExpectations(t) - }) -} diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader.go index 13eceaea24..81e164d7f6 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader.go @@ -38,6 +38,7 @@ type EVM2EVMMessage struct { Log types.Log // Raw event data } +//go:generate mockery --quiet --name OnRampReader --output . --filename onramp_reader_mock.go --inpackage --case=underscore type OnRampReader interface { // GetSendRequestsGteSeqNum returns all the message send requests with sequence number greater than or equal to the provided. // If checkFinalityTags is set to true then confs param is ignored, the latest finalized block is used in the query. diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader_mock.go new file mode 100644 index 0000000000..3d1564db58 --- /dev/null +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_reader_mock.go @@ -0,0 +1,140 @@ +// Code generated by mockery v2.28.1. DO NOT EDIT. + +package ccipdata + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + evm_2_evm_offramp "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" + mock "github.com/stretchr/testify/mock" +) + +// MockOnRampReader is an autogenerated mock type for the OnRampReader type +type MockOnRampReader struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *MockOnRampReader) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetSendRequestsBetweenSeqNums provides a mock function with given fields: ctx, seqNumMin, seqNumMax, confs +func (_m *MockOnRampReader) GetSendRequestsBetweenSeqNums(ctx context.Context, seqNumMin uint64, seqNumMax uint64, confs int) ([]Event[EVM2EVMMessage], error) { + ret := _m.Called(ctx, seqNumMin, seqNumMax, confs) + + var r0 []Event[EVM2EVMMessage] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, int) ([]Event[EVM2EVMMessage], error)); ok { + return rf(ctx, seqNumMin, seqNumMax, confs) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, int) []Event[EVM2EVMMessage]); ok { + r0 = rf(ctx, seqNumMin, seqNumMax, confs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]Event[EVM2EVMMessage]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, uint64, int) error); ok { + r1 = rf(ctx, seqNumMin, seqNumMax, confs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetSendRequestsGteSeqNum provides a mock function with given fields: ctx, seqNum, confs +func (_m *MockOnRampReader) GetSendRequestsGteSeqNum(ctx context.Context, seqNum uint64, confs int) ([]Event[EVM2EVMMessage], error) { + ret := _m.Called(ctx, seqNum, confs) + + var r0 []Event[EVM2EVMMessage] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, int) ([]Event[EVM2EVMMessage], error)); ok { + return rf(ctx, seqNum, confs) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, int) []Event[EVM2EVMMessage]); ok { + r0 = rf(ctx, seqNum, confs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]Event[EVM2EVMMessage]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, int) error); ok { + r1 = rf(ctx, seqNum, confs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Router provides a mock function with given fields: +func (_m *MockOnRampReader) Router() common.Address { + ret := _m.Called() + + var r0 common.Address + if rf, ok := ret.Get(0).(func() common.Address); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(common.Address) + } + } + + return r0 +} + +// ToOffRampMessage provides a mock function with given fields: message +func (_m *MockOnRampReader) ToOffRampMessage(message EVM2EVMMessage) (*evm_2_evm_offramp.InternalEVM2EVMMessage, error) { + ret := _m.Called(message) + + var r0 *evm_2_evm_offramp.InternalEVM2EVMMessage + var r1 error + if rf, ok := ret.Get(0).(func(EVM2EVMMessage) (*evm_2_evm_offramp.InternalEVM2EVMMessage, error)); ok { + return rf(message) + } + if rf, ok := ret.Get(0).(func(EVM2EVMMessage) *evm_2_evm_offramp.InternalEVM2EVMMessage); ok { + r0 = rf(message) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*evm_2_evm_offramp.InternalEVM2EVMMessage) + } + } + + if rf, ok := ret.Get(1).(func(EVM2EVMMessage) error); ok { + r1 = rf(message) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewMockOnRampReader interface { + mock.TestingT + Cleanup(func()) +} + +// NewMockOnRampReader creates a new instance of MockOnRampReader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockOnRampReader(t mockConstructorTestingTNewMockOnRampReader) *MockOnRampReader { + mock := &MockOnRampReader{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_0_0_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_0_0_test.go index 811c757569..d202b903af 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_0_0_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_0_0_test.go @@ -3,13 +3,15 @@ package ccipdata import ( "encoding/hex" "math/big" + "strings" "testing" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp_1_0_0" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/hashlib" ) @@ -17,12 +19,15 @@ import ( func TestHasher(t *testing.T) { sourceChainSelector, destChainSelector := uint64(1), uint64(4) onRampAddress := common.HexToAddress("0x5550000000000000000000000000000000000001") + onRampABI, err := abi.JSON(strings.NewReader(evm_2_evm_onramp_1_0_0.EVM2EVMOnRampABI)) + require.NoError(t, err) + ramp, err := evm_2_evm_onramp_1_0_0.NewEVM2EVMOnRamp(onRampAddress, nil) + require.NoError(t, err) hashingCtx := hashlib.NewKeccakCtx() + hasher := NewLeafHasherV1_0_0(sourceChainSelector, destChainSelector, onRampAddress, hashingCtx, ramp) - hasher := NewLeafHasherV1_0_0(sourceChainSelector, destChainSelector, onRampAddress, hashingCtx) - - message := evm_2_evm_onramp.InternalEVM2EVMMessage{ + message := evm_2_evm_onramp_1_0_0.InternalEVM2EVMMessage{ SourceChainSelector: sourceChainSelector, Sender: common.HexToAddress("0x1110000000000000000000000000000000000001"), Receiver: common.HexToAddress("0x2220000000000000000000000000000000000001"), @@ -33,20 +38,19 @@ func TestHasher(t *testing.T) { FeeToken: common.Address{}, FeeTokenAmount: big.NewInt(1), Data: []byte{}, - TokenAmounts: []evm_2_evm_onramp.ClientEVMTokenAmount{{Token: common.HexToAddress("0x4440000000000000000000000000000000000001"), Amount: big.NewInt(12345678900)}}, - SourceTokenData: [][]byte{}, + TokenAmounts: []evm_2_evm_onramp_1_0_0.ClientEVMTokenAmount{{Token: common.HexToAddress("0x4440000000000000000000000000000000000001"), Amount: big.NewInt(12345678900)}}, MessageId: [32]byte{}, } - pack, err := abihelpers.MessageArgs.Pack(message) + data, err := onRampABI.Events["CCIPSendRequested"].Inputs.Pack(message) require.NoError(t, err) - hash, err := hasher.HashLeaf(types.Log{Topics: []common.Hash{abihelpers.EventSignatures.SendRequested}, Data: pack}) + hash, err := hasher.HashLeaf(types.Log{Topics: []common.Hash{abihelpers.GetIDOrPanic("CCIPSendRequested", onRampABI)}, Data: data}) require.NoError(t, err) // NOTE: Must match spec require.Equal(t, "e0f22328cc83d50c2861629eaabcad5b39e8d30ba163228ff3574a0a229f5c9f", hex.EncodeToString(hash[:])) - message = evm_2_evm_onramp.InternalEVM2EVMMessage{ + message = evm_2_evm_onramp_1_0_0.InternalEVM2EVMMessage{ SourceChainSelector: sourceChainSelector, Sender: common.HexToAddress("0x1110000000000000000000000000000000000001"), Receiver: common.HexToAddress("0x2220000000000000000000000000000000000001"), @@ -57,17 +61,16 @@ func TestHasher(t *testing.T) { FeeToken: common.Address{}, FeeTokenAmount: big.NewInt(1e12), Data: []byte("foo bar baz"), - TokenAmounts: []evm_2_evm_onramp.ClientEVMTokenAmount{ + TokenAmounts: []evm_2_evm_onramp_1_0_0.ClientEVMTokenAmount{ {Token: common.HexToAddress("0x4440000000000000000000000000000000000001"), Amount: big.NewInt(12345678900)}, {Token: common.HexToAddress("0x6660000000000000000000000000000000000001"), Amount: big.NewInt(4204242)}, }, - SourceTokenData: [][]byte{{0x2, 0x1}}, - MessageId: [32]byte{}, + MessageId: [32]byte{}, } - pack, err = abihelpers.MessageArgs.Pack(message) + data, err = onRampABI.Events["CCIPSendRequested"].Inputs.Pack(message) require.NoError(t, err) - hash, err = hasher.HashLeaf(types.Log{Topics: []common.Hash{abihelpers.EventSignatures.SendRequested}, Data: pack}) + hash, err = hasher.HashLeaf(types.Log{Topics: []common.Hash{abihelpers.GetIDOrPanic("CCIPSendRequested", onRampABI)}, Data: data}) require.NoError(t, err) // NOTE: Must match spec diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_2_0.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_2_0.go index 2875bb116e..9290d42218 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_2_0.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_2_0.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -21,25 +20,44 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +var ( + // Backwards compat for integration tests + CCIPSendRequestEventSigV1_2_0 common.Hash +) + +const ( + CCIPSendRequestSeqNumIndexV1_2_0 = 4 +) + +func init() { + onRampABI, err := abi.JSON(strings.NewReader(evm_2_evm_onramp.EVM2EVMOnRampABI)) + if err != nil { + panic(err) + } + CCIPSendRequestEventSigV1_2_0 = abihelpers.GetIDOrPanic("CCIPSendRequested", onRampABI) +} + type LeafHasherV1_2_0 struct { metaDataHash [32]byte ctx hashlib.Ctx[[32]byte] + onRamp *evm_2_evm_onramp.EVM2EVMOnRamp } -func NewLeafHasherV1_2_0(sourceChainSelector uint64, destChainSelector uint64, onRampId common.Address, ctx hashlib.Ctx[[32]byte]) *LeafHasherV1_2_0 { +func NewLeafHasherV1_2_0(sourceChainSelector uint64, destChainSelector uint64, onRampId common.Address, ctx hashlib.Ctx[[32]byte], onRamp *evm_2_evm_onramp.EVM2EVMOnRamp) *LeafHasherV1_2_0 { return &LeafHasherV1_2_0{ metaDataHash: getMetaDataHash(ctx, ctx.Hash([]byte("EVM2EVMMessageHashV2")), sourceChainSelector, onRampId, destChainSelector), ctx: ctx, + onRamp: onRamp, } } func (t *LeafHasherV1_2_0) HashLeaf(log types.Log) ([32]byte, error) { - message, err := abihelpers.DecodeOffRampMessage(log.Data) + msg, err := t.onRamp.ParseCCIPSendRequested(log) if err != nil { return [32]byte{}, err } - encodedTokens, err := abihelpers.TokenAmountsArgs.PackValues([]interface{}{message.TokenAmounts}) + encodedTokens, err := abihelpers.TokenAmountsArgs.PackValues([]interface{}{msg.Message.TokenAmounts}) if err != nil { return [32]byte{}, err } @@ -49,12 +67,11 @@ func (t *LeafHasherV1_2_0) HashLeaf(log types.Log) ([32]byte, error) { return [32]byte{}, err } - encodedSourceTokenData, err := abi.Arguments{abi.Argument{Type: bytesArray}}.PackValues([]interface{}{message.SourceTokenData}) + encodedSourceTokenData, err := abi.Arguments{abi.Argument{Type: bytesArray}}.PackValues([]interface{}{msg.Message.SourceTokenData}) if err != nil { return [32]byte{}, err } - // TODO: Update according 1.2 packedValues, err := utils.ABIEncode( `[ {"name": "leafDomainSeparator","type":"bytes1"}, @@ -73,17 +90,17 @@ func (t *LeafHasherV1_2_0) HashLeaf(log types.Log) ([32]byte, error) { ]`, leafDomainSeparator, t.metaDataHash, - message.SequenceNumber, - message.Nonce, - message.Sender, - message.Receiver, - t.ctx.Hash(message.Data), + msg.Message.SequenceNumber, + msg.Message.Nonce, + msg.Message.Sender, + msg.Message.Receiver, + t.ctx.Hash(msg.Message.Data), t.ctx.Hash(encodedTokens), t.ctx.Hash(encodedSourceTokenData), - message.GasLimit, - message.Strict, - message.FeeToken, - message.FeeTokenAmount, + msg.Message.GasLimit, + msg.Message.Strict, + msg.Message.FeeToken, + msg.Message.FeeTokenAmount, ) if err != nil { return [32]byte{}, err @@ -104,30 +121,10 @@ type OnRampV1_2_0 struct { client client.Client finalityTags bool filterName string - usdcMessageSent common.Hash sendRequestedEventSig common.Hash sendRequestedSeqNumberWord int } -func (o *OnRampV1_2_0) GetLastUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, txHash common.Hash) ([]byte, error) { - logs, err := o.lp.IndexedLogsByTxHash( - o.usdcMessageSent, - txHash, - pg.WithParentCtx(ctx), - ) - if err != nil { - return nil, err - } - - for i := range logs { - current := logs[len(logs)-i-1] - if current.LogIndex < logIndex { - return current.Data, nil - } - } - return nil, errors.Errorf("no USDC message found prior to log index %d in tx %s", logIndex, txHash.Hex()) -} - func (o *OnRampV1_2_0) logToMessage(log types.Log) (*EVM2EVMMessage, error) { msg, err := o.onRamp.ParseCCIPSendRequested(log) if err != nil { @@ -258,13 +255,12 @@ func NewOnRampV1_2_0( Addresses: []common.Address{onRampAddress}, }) return &OnRampV1_2_0{ - finalityTags: finalityTags, - lggr: lggr, - client: source, - lp: sourceLP, - leafHasher: NewLeafHasherV1_2_0(sourceSelector, destSelector, onRampAddress, hashlib.NewKeccakCtx()), - onRamp: onRamp, - filterName: name, - usdcMessageSent: utils.Keccak256Fixed([]byte("MessageSent(bytes)")), + finalityTags: finalityTags, + lggr: lggr, + client: source, + lp: sourceLP, + leafHasher: NewLeafHasherV1_2_0(sourceSelector, destSelector, onRampAddress, hashlib.NewKeccakCtx(), onRamp), + onRamp: onRamp, + filterName: name, }, nil } diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_2_0_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_2_0_test.go new file mode 100644 index 0000000000..2512aae632 --- /dev/null +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/onramp_v1_2_0_test.go @@ -0,0 +1,77 @@ +package ccipdata + +import ( + "context" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +func TestLogPollerClient_GetSendRequestsGteSeqNum(t *testing.T) { + onRampAddr := utils.RandomAddress() + seqNum := uint64(100) + confs := 4 + lggr := logger.TestLogger(t) + t.Run("using confs", func(t *testing.T) { + lp := mocks.NewLogPoller(t) + onRampV2, err := NewOnRampV1_2_0(lggr, 1, 1, onRampAddr, lp, nil, false) + require.NoError(t, err) + lp.On("LogsDataWordGreaterThan", + onRampV2.sendRequestedEventSig, + onRampAddr, + onRampV2.sendRequestedSeqNumberWord, + abihelpers.EvmWord(seqNum), + confs, + mock.Anything, + ).Return([]logpoller.Log{}, nil) + + //c := &LogPollerReader{lp: lp} + events, err := onRampV2.GetSendRequestsGteSeqNum( + context.Background(), + seqNum, + confs, + ) + assert.NoError(t, err) + assert.Empty(t, events) + lp.AssertExpectations(t) + }) + + t.Run("using latest confirmed block", func(t *testing.T) { + h := &types.Header{Number: big.NewInt(100000)} + lp := mocks.NewLogPoller(t) + onRampV2, err := NewOnRampV1_2_0(lggr, 1, 1, onRampAddr, lp, nil, false) + require.NoError(t, err) + lp.On("LogsUntilBlockHashDataWordGreaterThan", + onRampV2.sendRequestedEventSig, + onRampAddr, + onRampV2.sendRequestedSeqNumberWord, + abihelpers.EvmWord(seqNum), + h.Hash(), + mock.Anything, + ).Return([]logpoller.Log{}, nil) + + cl := evmClientMocks.NewClient(t) + cl.On("HeaderByNumber", mock.Anything, mock.Anything).Return(h, nil) + + events, err := onRampV2.GetSendRequestsGteSeqNum( + context.Background(), + seqNum, + confs, + ) + assert.NoError(t, err) + assert.Empty(t, events) + lp.AssertExpectations(t) + cl.AssertExpectations(t) + }) +} diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go index 1e59df9d7e..f670d4da8c 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go @@ -25,7 +25,7 @@ type Meta struct { // Client can be used to fetch CCIP related parsed on-chain data. // -//go:generate mockery --quiet --name Reader --output . --filename mock.go --inpackage --case=underscore +//go:generate mockery --quiet --name Reader --output . --filename reader_mock.go --inpackage --case=underscore type Reader interface { // GetTokenPriceUpdatesCreatedAfter returns all the token price updates that happened after the provided timestamp. GetTokenPriceUpdatesCreatedAfter(ctx context.Context, priceRegistry common.Address, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerTokenUpdated], error) @@ -41,8 +41,6 @@ type Reader interface { // GetAcceptedCommitReportsGteTimestamp returns all the commit reports with timestamp greater than or equal to the provided. GetAcceptedCommitReportsGteTimestamp(ctx context.Context, commitStoreAddress common.Address, ts time.Time, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) - // GetLastUSDCMessagePriorToLogIndexInTx returns the last USDC message that was sent before the provided log index in the given transaction. - GetLastUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, txHash common.Hash) ([]byte, error) // LatestBlock returns the latest known/parsed block of the underlying implementation. LatestBlock(ctx context.Context) (int64, error) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader_mock.go similarity index 69% rename from core/services/ocr2/plugins/ccip/internal/ccipdata/mock.go rename to core/services/ocr2/plugins/ccip/internal/ccipdata/reader_mock.go index 5790158c37..daabf4d95f 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/mock.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader_mock.go @@ -10,8 +10,6 @@ import ( evm_2_evm_offramp "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" - evm_2_evm_onramp "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" - mock "github.com/stretchr/testify/mock" price_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" @@ -128,84 +126,6 @@ func (_m *MockReader) GetGasPriceUpdatesCreatedAfter(ctx context.Context, priceR return r0, r1 } -// GetLastUSDCMessagePriorToLogIndexInTx provides a mock function with given fields: ctx, logIndex, txHash -func (_m *MockReader) GetLastUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, txHash common.Hash) ([]byte, error) { - ret := _m.Called(ctx, logIndex, txHash) - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64, common.Hash) ([]byte, error)); ok { - return rf(ctx, logIndex, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, int64, common.Hash) []byte); ok { - r0 = rf(ctx, logIndex, txHash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int64, common.Hash) error); ok { - r1 = rf(ctx, logIndex, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSendRequestsBetweenSeqNums provides a mock function with given fields: ctx, onRamp, seqNumMin, seqNumMax, confs -func (_m *MockReader) GetSendRequestsBetweenSeqNums(ctx context.Context, onRamp common.Address, seqNumMin uint64, seqNumMax uint64, confs int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error) { - ret := _m.Called(ctx, onRamp, seqNumMin, seqNumMax, confs) - - var r0 []Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, uint64, int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error)); ok { - return rf(ctx, onRamp, seqNumMin, seqNumMax, confs) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, uint64, int) []Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]); ok { - r0 = rf(ctx, onRamp, seqNumMin, seqNumMax, confs) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64, uint64, int) error); ok { - r1 = rf(ctx, onRamp, seqNumMin, seqNumMax, confs) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetSendRequestsGteSeqNum provides a mock function with given fields: ctx, onRamp, seqNum, checkFinalityTags, confs -func (_m *MockReader) GetSendRequestsGteSeqNum(ctx context.Context, onRamp common.Address, seqNum uint64, checkFinalityTags bool, confs int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error) { - ret := _m.Called(ctx, onRamp, seqNum, checkFinalityTags, confs) - - var r0 []Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, bool, int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error)); ok { - return rf(ctx, onRamp, seqNum, checkFinalityTags, confs) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, bool, int) []Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]); ok { - r0 = rf(ctx, onRamp, seqNum, checkFinalityTags, confs) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64, bool, int) error); ok { - r1 = rf(ctx, onRamp, seqNum, checkFinalityTags, confs) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetTokenPriceUpdatesCreatedAfter provides a mock function with given fields: ctx, priceRegistry, ts, confs func (_m *MockReader) GetTokenPriceUpdatesCreatedAfter(ctx context.Context, priceRegistry common.Address, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerTokenUpdated], error) { ret := _m.Called(ctx, priceRegistry, ts, confs) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go index a530d6b2ad..671f67d8f2 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go @@ -15,6 +15,7 @@ const ( MESSAGE_SENT_FILTER_NAME = "USDC message sent" ) +//go:generate mockery --quiet --name USDCReader --output . --filename usdc_reader_mock.go --inpackage --case=underscore type USDCReader interface { // GetLastUSDCMessagePriorToLogIndexInTx returns the last USDC message that was sent before the provided log index in the given transaction. GetLastUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, txHash common.Hash) ([]byte, error) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_mock.go new file mode 100644 index 0000000000..cf3623e208 --- /dev/null +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_mock.go @@ -0,0 +1,71 @@ +// Code generated by mockery v2.28.1. DO NOT EDIT. + +package ccipdata + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" +) + +// MockUSDCReader is an autogenerated mock type for the USDCReader type +type MockUSDCReader struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *MockUSDCReader) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetLastUSDCMessagePriorToLogIndexInTx provides a mock function with given fields: ctx, logIndex, txHash +func (_m *MockUSDCReader) GetLastUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, txHash common.Hash) ([]byte, error) { + ret := _m.Called(ctx, logIndex, txHash) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, common.Hash) ([]byte, error)); ok { + return rf(ctx, logIndex, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, common.Hash) []byte); ok { + r0 = rf(ctx, logIndex, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int64, common.Hash) error); ok { + r1 = rf(ctx, logIndex, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewMockUSDCReader interface { + mock.TestingT + Cleanup(func()) +} + +// NewMockUSDCReader creates a new instance of MockUSDCReader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockUSDCReader(t mockConstructorTestingTNewMockUSDCReader) *MockUSDCReader { + mock := &MockUSDCReader{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_test.go new file mode 100644 index 0000000000..4b85ceb274 --- /dev/null +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader_test.go @@ -0,0 +1,61 @@ +package ccipdata + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +func TestLogPollerClient_GetLastUSDCMessagePriorToLogIndexInTx(t *testing.T) { + txHash := utils.RandomAddress().Hash() + ccipLogIndex := int64(100) + + expectedData := []byte("-1") + + t.Run("multiple found", func(t *testing.T) { + lp := mocks.NewLogPoller(t) + u, err := NewUSDCReader(utils.RandomAddress(), lp) + require.NoError(t, err) + lp.On("IndexedLogsByTxHash", + u.usdcMessageSent, + txHash, + mock.Anything, + ).Return([]logpoller.Log{ + {LogIndex: ccipLogIndex - 2, Data: []byte("-2")}, + {LogIndex: ccipLogIndex - 1, Data: expectedData}, + {LogIndex: ccipLogIndex, Data: []byte("0")}, + {LogIndex: ccipLogIndex + 1, Data: []byte("1")}, + }, nil) + + usdcMessageData, err := u.GetLastUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, txHash) + assert.NoError(t, err) + assert.Equal(t, expectedData, usdcMessageData) + + lp.AssertExpectations(t) + }) + + t.Run("none found", func(t *testing.T) { + lp := mocks.NewLogPoller(t) + u, err := NewUSDCReader(utils.RandomAddress(), lp) + require.NoError(t, err) + lp.On("IndexedLogsByTxHash", + u.usdcMessageSent, + txHash, + mock.Anything, + ).Return([]logpoller.Log{}, nil) + + usdcMessageData, err := u.GetLastUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, txHash) + assert.Errorf(t, err, fmt.Sprintf("no USDC message found prior to log index %d in tx %s", ccipLogIndex, txHash.Hex())) + assert.Nil(t, usdcMessageData) + + lp.AssertExpectations(t) + }) +} diff --git a/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go b/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go index 092650d0d2..efdec65182 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go @@ -38,6 +38,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/hashlib" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/merklemulti" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -1358,7 +1359,7 @@ func (args *ManualExecArgs) execute(report *commit_store.CommitStoreCommitReport seqNr := args.seqNr // Build a merkle tree for the report mctx := hashlib.NewKeccakCtx() - leafHasher := hashlib.NewLeafHasher(args.SourceChainID, args.DestChainID, common.HexToAddress(args.OnRamp), mctx) + leafHasher := ccipdata.NewLeafHasherV1_2_0(args.SourceChainID, args.DestChainID, common.HexToAddress(args.OnRamp), mctx, &evm_2_evm_onramp.EVM2EVMOnRamp{}) onRampContract, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(common.HexToAddress(args.OnRamp), args.SourceChain) if err != nil { return nil, err @@ -1475,13 +1476,3 @@ func GetBalance(t *testing.T, chain bind.ContractBackend, tokenAddr common.Addre require.NoError(t, err) return bal } - -func GenerateCCIPSendLog(t *testing.T, message evm_2_evm_onramp.InternalEVM2EVMMessage) types.Log { - pack, err := abihelpers.MessageArgs.Pack(message) - require.NoError(t, err) - - return types.Log{ - Topics: []common.Hash{abihelpers.EventSignatures.SendRequested}, - Data: pack, - } -} diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go index 1b92a30996..3cb6ce5766 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go @@ -28,6 +28,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/loop" ctfClient "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" v2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -47,6 +48,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" @@ -155,9 +157,9 @@ func (node *Node) EventuallyHasReqSeqNum(t *testing.T, ccipContracts *CCIPIntegr ccipContracts.Source.Chain.Commit() ccipContracts.Dest.Chain.Commit() lgs, err := c.LogPoller().LogsDataWordRange( - abihelpers.EventSignatures.SendRequested, + ccipdata.CCIPSendRequestEventSigV1_2_0, onRamp, - abihelpers.EventSignatures.SendRequestedSequenceNumberWord, + ccipdata.CCIPSendRequestSeqNumIndexV1_2_0, abihelpers.EvmWord(uint64(seqNum)), abihelpers.EvmWord(uint64(seqNum)), 1, diff --git a/core/services/ocr2/plugins/ccip/tokendata/reader_mock.go b/core/services/ocr2/plugins/ccip/tokendata/reader_mock.go index 9a87fafb34..54638a9d7a 100644 --- a/core/services/ocr2/plugins/ccip/tokendata/reader_mock.go +++ b/core/services/ocr2/plugins/ccip/tokendata/reader_mock.go @@ -5,9 +5,7 @@ package tokendata import ( context "context" - logpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" internal "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal" - mock "github.com/stretchr/testify/mock" ) @@ -16,17 +14,15 @@ type MockReader struct { mock.Mock } -// GetSourceLogPollerFilters provides a mock function with given fields: -func (_m *MockReader) GetSourceLogPollerFilters() []logpoller.Filter { +// Close provides a mock function with given fields: +func (_m *MockReader) Close() error { ret := _m.Called() - var r0 []logpoller.Filter - if rf, ok := ret.Get(0).(func() []logpoller.Filter); ok { + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]logpoller.Filter) - } + r0 = ret.Error(0) } return r0 diff --git a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_blackbox_test.go b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_blackbox_test.go index 6e61be5ed3..6a303fa783 100644 --- a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_blackbox_test.go +++ b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_blackbox_test.go @@ -10,12 +10,10 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata/usdc" @@ -56,23 +54,23 @@ func TestUSDCReader_ReadTokenData(t *testing.T) { txHash := utils.RandomBytes32() logIndex := int64(4) - eventsClient := ccipdata.MockReader{} - eventsClient.On("GetSendRequestsBetweenSeqNums", - mock.Anything, - mockOnRampAddress, - seqNum, - seqNum, - 0, - ).Return([]ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ - { - Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ - Raw: types.Log{ - TxHash: txHash, - Index: uint(logIndex), - }, - }, - }, - }, nil) + eventsClient := ccipdata.MockUSDCReader{} + //eventsClient.On("GetSendRequestsBetweenSeqNums", + // mock.Anything, + // mockOnRampAddress, + // seqNum, + // seqNum, + // 0, + //).Return([]ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + // { + // Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + // Raw: types.Log{ + // TxHash: txHash, + // Index: uint(logIndex), + // }, + // }, + // }, + //}, nil) eventsClient.On("GetLastUSDCMessagePriorToLogIndexInTx", mock.Anything, @@ -82,7 +80,7 @@ func TestUSDCReader_ReadTokenData(t *testing.T) { attestationURI, err := url.ParseRequestURI(ts.URL) require.NoError(t, err) - usdcService := usdc.NewUSDCTokenDataReader(&eventsClient, mockUSDCTokenAddress, mockMsgTransmitter, mockOnRampAddress, attestationURI) + usdcService := usdc.NewUSDCTokenDataReader(&eventsClient, attestationURI) attestation, err := usdcService.ReadTokenData(context.Background(), internal.EVM2EVMOnRampCCIPSendRequestedWithMeta{ InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{ SequenceNumber: seqNum, diff --git a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_test.go b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_test.go index d61ef530d5..c48e1e906f 100644 --- a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_test.go +++ b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_test.go @@ -12,7 +12,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -29,7 +28,9 @@ func TestUSDCReader_callAttestationApi(t *testing.T) { usdcMessageHash := "912f22a13e9ccb979b621500f6952b2afd6e75be7eadaed93fc2625fe11c52a2" attestationURI, err := url.ParseRequestURI("https://iris-api-sandbox.circle.com") require.NoError(t, err) - usdcService := NewUSDCTokenDataReader(nil, mockUSDCTokenAddress, mockMsgTransmitter, mockOnRampAddress, attestationURI) + usdcReader, err := ccipdata.NewUSDCReader(mockMsgTransmitter, nil) + require.NoError(t, err) + usdcService := NewUSDCTokenDataReader(usdcReader, attestationURI) attestation, err := usdcService.callAttestationApi(context.Background(), [32]byte(common.FromHex(usdcMessageHash))) require.NoError(t, err) @@ -49,7 +50,9 @@ func TestUSDCReader_callAttestationApiMock(t *testing.T) { attestationURI, err := url.ParseRequestURI(ts.URL) require.NoError(t, err) - usdcService := NewUSDCTokenDataReader(nil, mockUSDCTokenAddress, mockMsgTransmitter, mockOnRampAddress, attestationURI) + usdcReader, err := ccipdata.NewUSDCReader(mockMsgTransmitter, nil) + require.NoError(t, err) + usdcService := NewUSDCTokenDataReader(usdcReader, attestationURI) attestation, err := usdcService.callAttestationApi(context.Background(), utils.RandomBytes32()) require.NoError(t, err) @@ -65,7 +68,9 @@ func TestUSDCReader_callAttestationApiMockError(t *testing.T) { attestationURI, err := url.ParseRequestURI(ts.URL) require.NoError(t, err) - usdcService := NewUSDCTokenDataReader(nil, mockUSDCTokenAddress, mockMsgTransmitter, mockOnRampAddress, attestationURI) + usdcReader, err := ccipdata.NewUSDCReader(mockMsgTransmitter, nil) + require.NoError(t, err) + usdcService := NewUSDCTokenDataReader(usdcReader, attestationURI) _, err = usdcService.callAttestationApi(context.Background(), utils.RandomBytes32()) require.Error(t, err) } @@ -80,29 +85,16 @@ func getMockUSDCEndpoint(t *testing.T, response attestationResponse) *httptest.S })) } -// Asserts the hard coded event signature matches Keccak256("MessageSent(bytes)") -func TestGetUSDCReaderSourceLPFilters(t *testing.T) { - usdcService := NewUSDCTokenDataReader(nil, mockUSDCTokenAddress, mockMsgTransmitter, mockOnRampAddress, nil) - - filters := usdcService.GetSourceLogPollerFilters() - - require.Equal(t, 1, len(filters)) - filter := filters[0] - require.Equal(t, logpoller.FilterName(MESSAGE_SENT_FILTER_NAME, mockMsgTransmitter.Hex()), filter.Name) - hash, err := utils.Keccak256([]byte("MessageSent(bytes)")) - require.NoError(t, err) - require.Equal(t, hash, filter.EventSigs[0].Bytes()) - require.Equal(t, mockMsgTransmitter, filter.Addresses[0]) -} - func TestGetUSDCMessageBody(t *testing.T) { expectedBody := []byte("TestGetUSDCMessageBody") expectedBodyHash := utils.Keccak256Fixed(expectedBody) - sourceChainEventsMock := ccipdata.MockReader{} + sourceChainEventsMock := ccipdata.MockUSDCReader{} sourceChainEventsMock.On("GetLastUSDCMessagePriorToLogIndexInTx", mock.Anything, mock.Anything, mock.Anything).Return(expectedBody, nil) - usdcService := NewUSDCTokenDataReader(&sourceChainEventsMock, mockUSDCTokenAddress, mockMsgTransmitter, mockOnRampAddress, nil) + usdcReader, err := ccipdata.NewUSDCReader(mockMsgTransmitter, nil) + require.NoError(t, err) + usdcService := NewUSDCTokenDataReader(usdcReader, nil) // Make the first call and assert the underlying function is called body, err := usdcService.getUSDCMessageBody(context.Background(), internal.EVM2EVMOnRampCCIPSendRequestedWithMeta{})