From 97a0e6a07e21ec31069cb4d16bd52bbd72ad1e04 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Tue, 5 Sep 2023 16:48:22 +0300 Subject: [PATCH 01/25] init and re-write commit observation --- .../ocr2/plugins/ccip/ccipevents/client.go | 2 + .../ocr2/plugins/ccip/ccipevents/mock.go | 192 ++++++++++++++++++ .../ccip/commit_reporting_plugin_test.go | 134 ++++++++---- 3 files changed, 286 insertions(+), 42 deletions(-) create mode 100644 core/services/ocr2/plugins/ccip/ccipevents/mock.go diff --git a/core/services/ocr2/plugins/ccip/ccipevents/client.go b/core/services/ocr2/plugins/ccip/ccipevents/client.go index d3419b744a..348e456f48 100644 --- a/core/services/ocr2/plugins/ccip/ccipevents/client.go +++ b/core/services/ocr2/plugins/ccip/ccipevents/client.go @@ -22,6 +22,8 @@ type BlockMeta struct { } // Client can be used to fetch CCIP related parsed on-chain events. +// +//go:generate mockery --quiet --name Client --output . --filename mock.go --inpackage --case=underscore type Client 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/ccipevents/mock.go b/core/services/ocr2/plugins/ccip/ccipevents/mock.go new file mode 100644 index 0000000000..47bacd2821 --- /dev/null +++ b/core/services/ocr2/plugins/ccip/ccipevents/mock.go @@ -0,0 +1,192 @@ +// Code generated by mockery v2.28.1. DO NOT EDIT. + +package ccipevents + +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" + 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" + + time "time" +) + +// MockClient is an autogenerated mock type for the Client type +type MockClient struct { + mock.Mock +} + +// GetExecutionStateChangesBetweenSeqNums provides a mock function with given fields: ctx, offRamp, seqNumMin, seqNumMax, confs +func (_m *MockClient) GetExecutionStateChangesBetweenSeqNums(ctx context.Context, offRamp common.Address, seqNumMin uint64, seqNumMax uint64, confs int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error) { + ret := _m.Called(ctx, offRamp, seqNumMin, seqNumMax, confs) + + var r0 []Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, uint64, int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error)); ok { + return rf(ctx, offRamp, seqNumMin, seqNumMax, confs) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, uint64, int) []Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]); ok { + r0 = rf(ctx, offRamp, seqNumMin, seqNumMax, confs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64, uint64, int) error); ok { + r1 = rf(ctx, offRamp, seqNumMin, seqNumMax, confs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetGasPriceUpdatesCreatedAfter provides a mock function with given fields: ctx, priceRegistry, chainSelector, ts, confs +func (_m *MockClient) GetGasPriceUpdatesCreatedAfter(ctx context.Context, priceRegistry common.Address, chainSelector uint64, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerUnitGasUpdated], error) { + ret := _m.Called(ctx, priceRegistry, chainSelector, ts, confs) + + var r0 []Event[price_registry.PriceRegistryUsdPerUnitGasUpdated] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, time.Time, int) ([]Event[price_registry.PriceRegistryUsdPerUnitGasUpdated], error)); ok { + return rf(ctx, priceRegistry, chainSelector, ts, confs) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, time.Time, int) []Event[price_registry.PriceRegistryUsdPerUnitGasUpdated]); ok { + r0 = rf(ctx, priceRegistry, chainSelector, ts, confs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]Event[price_registry.PriceRegistryUsdPerUnitGasUpdated]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64, time.Time, int) error); ok { + r1 = rf(ctx, priceRegistry, chainSelector, ts, confs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetSendRequestsBetweenSeqNums provides a mock function with given fields: ctx, onRamp, seqNumMin, seqNumMax, confs +func (_m *MockClient) 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 *MockClient) 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 *MockClient) GetTokenPriceUpdatesCreatedAfter(ctx context.Context, priceRegistry common.Address, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerTokenUpdated], error) { + ret := _m.Called(ctx, priceRegistry, ts, confs) + + var r0 []Event[price_registry.PriceRegistryUsdPerTokenUpdated] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, time.Time, int) ([]Event[price_registry.PriceRegistryUsdPerTokenUpdated], error)); ok { + return rf(ctx, priceRegistry, ts, confs) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, time.Time, int) []Event[price_registry.PriceRegistryUsdPerTokenUpdated]); ok { + r0 = rf(ctx, priceRegistry, ts, confs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]Event[price_registry.PriceRegistryUsdPerTokenUpdated]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, time.Time, int) error); ok { + r1 = rf(ctx, priceRegistry, ts, confs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// LatestBlock provides a mock function with given fields: ctx +func (_m *MockClient) LatestBlock(ctx context.Context) (int64, error) { + ret := _m.Called(ctx) + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (int64, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) int64); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewMockClient interface { + mock.TestingT + Cleanup(func()) +} + +// NewMockClient creates a new instance of MockClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockClient(t mockConstructorTestingTNewMockClient) *MockClient { + mock := &MockClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} 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 be1037d0b2..1689958473 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -2,7 +2,6 @@ package ccip import ( "context" - "encoding/json" "fmt" "math/big" "math/rand" @@ -27,6 +26,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" @@ -191,65 +191,115 @@ func TestCommitReportEncoding(t *testing.T) { } func TestCommitObservation(t *testing.T) { - th := setupCommitTestHarness(t) - th.plugin.F = 1 - - mb := th.GenerateAndSendMessageBatch(t, 1, 0, 0) + sourceNativeTokenAddr := common.HexToAddress("1000") + someTokenAddr := common.HexToAddress("2000") - tests := []struct { - name string - commitStoreDown bool - expected *CommitObservation - expectedError bool + testCases := []struct { + name string + epochAndRound types.ReportTimestamp + commitStoreIsPaused bool + commitStoreSeqNum uint64 + tokenPrices map[common.Address]*big.Int + sendReqs []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + tokenDecimals map[common.Address]uint8 + fee *big.Int + + expErr bool + expObs CommitObservation }{ { - "base", - false, - &CommitObservation{ - Interval: mb.Interval, - SourceGasPriceUSD: new(big.Int).Mul(defaultGasPrice, big.NewInt(100)), + name: "base report", + commitStoreSeqNum: 54, + tokenPrices: map[common.Address]*big.Int{ + someTokenAddr: big.NewInt(2), + sourceNativeTokenAddr: big.NewInt(2), + }, + sendReqs: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + {Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 54}}}, + {Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 55}}}, + }, + fee: big.NewInt(100), + tokenDecimals: map[common.Address]uint8{ + someTokenAddr: 8, + }, + expObs: CommitObservation{ TokenPricesUSD: map[common.Address]*big.Int{ - th.Dest.LinkToken.Address(): new(big.Int).Mul(big.NewInt(200), big.NewInt(1e18)), + someTokenAddr: big.NewInt(20000000000), + }, + SourceGasPriceUSD: big.NewInt(0), + Interval: commit_store.CommitStoreInterval{ + Min: 54, + Max: 55, }, }, - false, }, { - "commitStore down", - true, - nil, - true, + name: "commit store is down", + commitStoreIsPaused: true, + expErr: true, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.commitStoreDown && !isCommitStoreDownNow(testutils.Context(t), th.Lggr, th.Dest.CommitStore) { - _, err := th.Dest.CommitStore.Pause(th.Dest.User) - require.NoError(t, err) - th.CommitAndPollLogs(t) - } else if !tt.commitStoreDown && isCommitStoreDownNow(testutils.Context(t), th.Lggr, th.Dest.CommitStore) { - _, err := th.Dest.CommitStore.Unpause(th.Dest.User) - require.NoError(t, err) - th.CommitAndPollLogs(t) + ctx := testutils.Context(t) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + onRampAddress := utils.RandomAddress() + sourceFinalityDepth := 10 + + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("IsUnpausedAndARMHealthy", mock.Anything).Return(!tc.commitStoreIsPaused, nil) + if tc.commitStoreSeqNum > 0 { + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(tc.commitStoreSeqNum, nil) } - gotObs, err := th.plugin.Observation(testutils.Context(t), types.ReportTimestamp{}, types.Query{}) + sourceEvents := ccipevents.NewMockClient(t) + if len(tc.sendReqs) > 0 { + sourceEvents.On("GetSendRequestsGteSeqNum", ctx, onRampAddress, tc.commitStoreSeqNum, false, sourceFinalityDepth). + Return(tc.sendReqs, nil) + } - if tt.expectedError { - require.Error(t, err) - } else { - require.NoError(t, err) + tokenDecimalsCache := cache.NewMockAutoSync[map[common.Address]uint8](t) + if len(tc.tokenDecimals) > 0 { + tokenDecimalsCache.On("Get", ctx).Return(tc.tokenDecimals, nil) } - var decodedObservation *CommitObservation - if gotObs != nil { - decodedObservation = new(CommitObservation) - err = json.Unmarshal(gotObs, decodedObservation) - require.NoError(t, err) + priceGet := newMockPriceGetter() + if len(tc.tokenPrices) > 0 { + addrs := []common.Address{sourceNativeTokenAddr} + for addr := range tc.tokenDecimals { + addrs = append(addrs, addr) + } + priceGet.On("TokenPricesUSD", addrs).Return(tc.tokenPrices, nil) + } + + sourceFeeEst := mocks.NewEvmFeeEstimator(t) + if tc.fee != nil { + sourceFeeEst.On("GetFee", ctx, []byte(nil), uint32(0), assets.NewWei(big.NewInt(0))). + Return(gas.EvmFee{Legacy: assets.NewWei(tc.fee)}, uint32(0), nil) + } + + p := &CommitReportingPlugin{} + 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.sourceEvents = sourceEvents + p.tokenDecimalsCache = tokenDecimalsCache + p.config.priceGetter = priceGet + p.config.sourceFeeEstimator = sourceFeeEst + p.config.sourceNative = sourceNativeTokenAddr + + obs, err := p.Observation(ctx, tc.epochAndRound, types.Query{}) + if tc.expErr { + assert.Error(t, err) + return } - assert.Equal(t, tt.expected, decodedObservation) + assert.NoError(t, err) + + expObsBytes, _ := tc.expObs.Marshal() + assert.Equal(t, expObsBytes, []byte(obs)) }) } } From 8cc66b3a5eb6d154e90fa692b87a1bcfd0ac204a Mon Sep 17 00:00:00 2001 From: dimkouv Date: Tue, 5 Sep 2023 22:24:42 +0300 Subject: [PATCH 02/25] fix commit report phase --- .../ccip/commit_reporting_plugin_test.go | 134 ++++++++++++------ 1 file changed, 91 insertions(+), 43 deletions(-) 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 1689958473..3e8e01db53 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -3,6 +3,7 @@ package ccip import ( "context" "fmt" + gethtypes "github.com/ethereum/go-ethereum/core/types" "math/big" "math/rand" "reflect" @@ -190,7 +191,7 @@ func TestCommitReportEncoding(t *testing.T) { assert.Equal(t, newTokenPrice, linkTokenPrice.Value) } -func TestCommitObservation(t *testing.T) { +func TestCommitReportingPlugin_Observation(t *testing.T) { sourceNativeTokenAddr := common.HexToAddress("1000") someTokenAddr := common.HexToAddress("2000") @@ -304,28 +305,39 @@ func TestCommitObservation(t *testing.T) { } } -func TestCommitReport(t *testing.T) { - th := setupCommitTestHarness(t) - th.plugin.F = 1 - - mb := th.GenerateAndSendMessageBatch(t, 1, 0, 0) - - tests := []struct { - name string - observations []CommitObservation - shouldReport bool - commitReport *commit_store.CommitStoreCommitReport - expectedError bool +func TestCommitReportingPlugin_Report(t *testing.T) { + testCases := []struct { + name string + observations []CommitObservation + f int + gasPriceUpdates []ccipevents.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated] + tokenPriceUpdates []ccipevents.Event[price_registry.PriceRegistryUsdPerTokenUpdated] + sendRequests []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + + expCommitReport *commit_store.CommitStoreCommitReport + expSeqNumRange commit_store.CommitStoreInterval + expReport bool + expErr bool }{ { - "base", - []CommitObservation{ + name: "base", + observations: []CommitObservation{ {Interval: commit_store.CommitStoreInterval{Min: 1, Max: 1}}, {Interval: commit_store.CommitStoreInterval{Min: 1, Max: 1}}, }, - true, - &commit_store.CommitStoreCommitReport{ - MerkleRoot: mb.Root, + f: 1, + sendRequests: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + { + Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + Message: evm_2_evm_onramp.InternalEVM2EVMMessage{ + SequenceNumber: 1, + }, + }, + }, + }, + expSeqNumRange: commit_store.CommitStoreInterval{Min: 1, Max: 1}, + expCommitReport: &commit_store.CommitStoreCommitReport{ + MerkleRoot: [32]byte{}, Interval: commit_store.CommitStoreInterval{Min: 1, Max: 1}, PriceUpdates: commit_store.InternalPriceUpdates{ TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, @@ -333,59 +345,89 @@ func TestCommitReport(t *testing.T) { UsdPerUnitGas: new(big.Int), }, }, - false, + expReport: true, + expErr: false, }, { - "not enough observations", - []CommitObservation{ + name: "not enough observations", + observations: []CommitObservation{ {Interval: commit_store.CommitStoreInterval{Min: 1, Max: 1}}, }, - false, - nil, - true, + f: 1, + sendRequests: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{{}}, + expSeqNumRange: commit_store.CommitStoreInterval{Min: 1, Max: 1}, + expErr: true, }, { - "empty", - []CommitObservation{ + name: "empty", + observations: []CommitObservation{ {Interval: commit_store.CommitStoreInterval{Min: 0, Max: 0}}, {Interval: commit_store.CommitStoreInterval{Min: 0, Max: 0}}, }, - false, - nil, - false, + f: 1, + expErr: false, }, { - "no leaves", - []CommitObservation{ + name: "no leaves", + observations: []CommitObservation{ {Interval: commit_store.CommitStoreInterval{Min: 2, Max: 2}}, {Interval: commit_store.CommitStoreInterval{Min: 2, Max: 2}}, }, - false, - nil, - true, + f: 1, + sendRequests: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{{}}, + expSeqNumRange: commit_store.CommitStoreInterval{Min: 2, Max: 2}, + expErr: true, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - aos := make([]types.AttributedObservation, 0, len(tt.observations)) - for _, o := range tt.observations { + ctx := testutils.Context(t) + destPriceRegistryAddress := utils.RandomAddress() + onRampAddress := + utils.RandomAddress() + sourceChainSelector := rand.Int() + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + destPriceRegistry := mock_contracts.NewPriceRegistryInterface(t) + destPriceRegistry.On("Address").Return(destPriceRegistryAddress) + + destEvents := ccipevents.NewMockClient(t) + destEvents.On("GetGasPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, uint64(sourceChainSelector), mock.Anything, 0).Return(tc.gasPriceUpdates, nil) + destEvents.On("GetTokenPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, mock.Anything, 0).Return(tc.tokenPriceUpdates, nil) + + sourceEvents := ccipevents.NewMockClient(t) + if len(tc.sendRequests) > 0 { + sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddress, tc.expSeqNumRange.Min, tc.expSeqNumRange.Max, 0).Return(tc.sendRequests, nil) + } + + p := &CommitReportingPlugin{} + p.lggr = logger.TestLogger(t) + p.inflightReports = newInflightCommitReportsContainer(time.Minute) + p.destPriceRegistry = destPriceRegistry + p.config.destEvents = destEvents + p.config.sourceEvents = sourceEvents + p.config.onRampAddress = onRampAddress + p.config.sourceChainSelector = uint64(sourceChainSelector) + p.config.leafHasher = &nopLeafHasher{} + + aos := make([]types.AttributedObservation, 0, len(tc.observations)) + for _, o := range tc.observations { obs, err := o.Marshal() require.NoError(t, err) aos = append(aos, types.AttributedObservation{Observation: obs}) } - gotShouldReport, gotReport, err := th.plugin.Report(testutils.Context(t), types.ReportTimestamp{}, types.Query{}, aos) + gotShouldReport, gotReport, err := p.Report(ctx, types.ReportTimestamp{}, types.Query{}, aos) - if tt.expectedError { + if tc.expErr { require.Error(t, err) } else { require.NoError(t, err) } - assert.Equal(t, tt.shouldReport, gotShouldReport) + assert.Equal(t, tc.expReport, gotShouldReport) var expectedReport types.Report - if tt.commitReport != nil { - expectedReport, err = abihelpers.EncodeCommitReport(*tt.commitReport) + if tc.expCommitReport != nil { + expectedReport, err = abihelpers.EncodeCommitReport(*tc.expCommitReport) require.NoError(t, err) } assert.Equal(t, expectedReport, gotReport) @@ -1291,3 +1333,9 @@ func Test_isStaleReport(t *testing.T) { MerkleRoot: merkleRoot1}, false, types.ReportTimestamp{})) }) } + +type nopLeafHasher struct{} + +func (n nopLeafHasher) HashLeaf(log gethtypes.Log) ([32]byte, error) { + return [32]byte{}, nil +} From 45919b13888f8ff21e01bbcda119a743fcfbf9fa Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 13:04:07 +0300 Subject: [PATCH 03/25] fix commit report encoding test --- .../ccip/abihelpers/abi_helpers_test.go | 28 +++++++++ .../ccip/commit_reporting_plugin_test.go | 57 +------------------ 2 files changed, 30 insertions(+), 55 deletions(-) diff --git a/core/services/ocr2/plugins/ccip/abihelpers/abi_helpers_test.go b/core/services/ocr2/plugins/ccip/abihelpers/abi_helpers_test.go index 1dddc00c35..4cc5ccfac0 100644 --- a/core/services/ocr2/plugins/ccip/abihelpers/abi_helpers_test.go +++ b/core/services/ocr2/plugins/ccip/abihelpers/abi_helpers_test.go @@ -4,8 +4,12 @@ import ( "fmt" "math" "math/big" + "math/rand" "testing" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" + "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/test-go/testify/require" @@ -58,6 +62,30 @@ func TestProofFlagToBits(t *testing.T) { } } +func TestCommitReportEncoding(t *testing.T) { + report := commit_store.CommitStoreCommitReport{ + PriceUpdates: commit_store.InternalPriceUpdates{ + TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{ + { + SourceToken: utils.RandomAddress(), + UsdPerToken: big.NewInt(9e18), + }, + }, + DestChainSelector: rand.Uint64(), + UsdPerUnitGas: big.NewInt(2000e9), + }, + MerkleRoot: [32]byte{123}, + Interval: commit_store.CommitStoreInterval{Min: 1, Max: 10}, + } + + encodedReport, err := EncodeCommitReport(report) + require.NoError(t, err) + + decodedReport, err := DecodeCommitReport(encodedReport) + require.NoError(t, err) + require.Equal(t, report, decodedReport) +} + func TestExecutionReportEncoding(t *testing.T) { // Note could consider some fancier testing here (fuzz/property) // but I think that would essentially be testing geth's abi library 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 3e8e01db53..1b3f7281f5 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -3,7 +3,6 @@ package ccip import ( "context" "fmt" - gethtypes "github.com/ethereum/go-ethereum/core/types" "math/big" "math/rand" "reflect" @@ -11,6 +10,8 @@ import ( "testing" "time" + gethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/leanovate/gopter" @@ -137,60 +138,6 @@ func TestCommitReportSize(t *testing.T) { p.TestingRun(t) } -func TestCommitReportEncoding(t *testing.T) { - th := plugintesthelpers.SetupCCIPTestHarness(t) - newTokenPrice := big.NewInt(9e18) // $9 - newGasPrice := big.NewInt(2000e9) // $2000 per eth * 1gwei - - // Send a report. - mctx := hasher.NewKeccakCtx() - tree, err := merklemulti.NewTree(mctx, [][32]byte{mctx.Hash([]byte{0xaa})}) - require.NoError(t, err) - report := commit_store.CommitStoreCommitReport{ - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{ - { - SourceToken: th.Dest.LinkToken.Address(), - UsdPerToken: newTokenPrice, - }, - }, - DestChainSelector: th.Source.ChainSelector, - UsdPerUnitGas: newGasPrice, - }, - MerkleRoot: tree.Root(), - Interval: commit_store.CommitStoreInterval{Min: 1, Max: 10}, - } - out, err := abihelpers.EncodeCommitReport(report) - require.NoError(t, err) - decodedReport, err := abihelpers.DecodeCommitReport(out) - require.NoError(t, err) - require.Equal(t, report, decodedReport) - - latestEpocAndRound, err := th.Dest.CommitStoreHelper.GetLatestPriceEpochAndRound(nil) - require.NoError(t, err) - - tx, err := th.Dest.CommitStoreHelper.Report(th.Dest.User, out, big.NewInt(int64(latestEpocAndRound+1))) - require.NoError(t, err) - th.CommitAndPollLogs(t) - res, err := th.Dest.Chain.TransactionReceipt(testutils.Context(t), tx.Hash()) - require.NoError(t, err) - assert.Equal(t, uint64(1), res.Status) - - // Ensure root exists. - ts, err := th.Dest.CommitStore.GetMerkleRoot(nil, tree.Root()) - require.NoError(t, err) - require.NotEqual(t, ts.String(), "0") - - // Ensure price update went through - destChainGasPrice, err := th.Dest.PriceRegistry.GetDestinationChainGasPrice(nil, th.Source.ChainSelector) - require.NoError(t, err) - assert.Equal(t, newGasPrice, destChainGasPrice.Value) - - linkTokenPrice, err := th.Dest.PriceRegistry.GetTokenPrice(nil, th.Dest.LinkToken.Address()) - require.NoError(t, err) - assert.Equal(t, newTokenPrice, linkTokenPrice.Value) -} - func TestCommitReportingPlugin_Observation(t *testing.T) { sourceNativeTokenAddr := common.HexToAddress("1000") someTokenAddr := common.HexToAddress("2000") From 2dc608bfc13d24bb2ee600b4137dcbc2449f1203 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 13:17:47 +0300 Subject: [PATCH 04/25] remove non-plugin test from plugin test suite --- .../ccip/commit_reporting_plugin_test.go | 40 ------------------- 1 file changed, 40 deletions(-) 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 1b3f7281f5..451d48f8d3 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -31,7 +31,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" @@ -865,45 +864,6 @@ func TestGeneratePriceUpdates(t *testing.T) { } } -func TestUpdateTokenToDecimalMapping(t *testing.T) { - th := plugintesthelpers.SetupCCIPTestHarness(t) - - destToken, _, _, err := link_token_interface.DeployLinkToken(th.Dest.User, th.Dest.Chain) - require.NoError(t, err) - - feeToken, _, _, err := link_token_interface.DeployLinkToken(th.Dest.User, th.Dest.Chain) - require.NoError(t, err) - th.CommitAndPollLogs(t) - - tokens := []common.Address{} - tokens = append(tokens, destToken) - tokens = append(tokens, feeToken) - - mockOffRamp := &mock_contracts.EVM2EVMOffRampInterface{} - mockOffRamp.On("GetDestinationTokens", mock.Anything).Return([]common.Address{destToken}, nil) - mockOffRamp.On("Address").Return(common.Address{}) - - mockPriceRegistry := &mock_contracts.PriceRegistryInterface{} - mockPriceRegistry.On("GetFeeTokens", mock.Anything).Return([]common.Address{feeToken}, nil) - mockPriceRegistry.On("Address").Return(common.Address{}) - - backendClient := client.NewSimulatedBackendClient(t, th.Dest.Chain, new(big.Int).SetUint64(th.Dest.ChainID)) - plugin := CommitReportingPlugin{ - config: CommitPluginConfig{ - offRamp: mockOffRamp, - destClient: backendClient, - }, - destPriceRegistry: mockPriceRegistry, - tokenDecimalsCache: cache.NewTokenToDecimals(th.Lggr, th.DestLP, mockOffRamp, mockPriceRegistry, backendClient, 0), - } - - tokenMapping, err := plugin.tokenDecimalsCache.Get(testutils.Context(t)) - require.NoError(t, err) - assert.Equal(t, len(tokens), len(tokenMapping)) - assert.Equal(t, uint8(18), tokenMapping[destToken]) - assert.Equal(t, uint8(18), tokenMapping[feeToken]) -} - func TestCalculateUsdPer1e18TokenAmount(t *testing.T) { t.Parallel() From cfaa333d2f3501614b4b714a91ec521b28299d9c Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 14:04:06 +0300 Subject: [PATCH 05/25] cleanup commit shouldTransmit test --- .../ccip/commit_reporting_plugin_test.go | 113 +++++++----------- 1 file changed, 42 insertions(+), 71 deletions(-) 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 451d48f8d3..56a7c49ddb 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -906,82 +906,53 @@ func TestCalculateUsdPer1e18TokenAmount(t *testing.T) { } } -func TestShouldTransmitAcceptedReport(t *testing.T) { - th := setupCommitTestHarness(t) - tokenPrice := big.NewInt(9e18) // $9 - gasPrice := big.NewInt(1500e9) // $1500 per eth * 1gwei - - nextMinSeqNr := uint64(10) - _, err := th.Dest.CommitStore.SetMinSeqNr(th.Dest.User, nextMinSeqNr) - require.NoError(t, err) - _, err = th.Dest.PriceRegistry.UpdatePrices(th.Dest.User, price_registry.InternalPriceUpdates{ - TokenPriceUpdates: []price_registry.InternalTokenPriceUpdate{ - {SourceToken: th.Dest.LinkToken.Address(), UsdPerToken: tokenPrice}, +func TestCommitReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { + report := commit_store.CommitStoreCommitReport{ + PriceUpdates: commit_store.InternalPriceUpdates{ + TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{ + {SourceToken: utils.RandomAddress(), UsdPerToken: big.NewInt(9e18)}, + }, + DestChainSelector: rand.Uint64(), + UsdPerUnitGas: big.NewInt(2000e9), }, - DestChainSelector: th.Source.ChainSelector, - UsdPerUnitGas: gasPrice, - }) - require.NoError(t, err) - th.CommitAndPollLogs(t) - round := uint8(1) - - tests := []struct { - name string - seq uint64 - gasPrice *big.Int - tokenPrice *big.Int - expected bool - }{ - {"base", nextMinSeqNr, nil, nil, true}, - {"future", nextMinSeqNr + 10, nil, nil, true}, - {"empty", 0, nil, nil, false}, - {"gasPrice update", 0, big.NewInt(10), nil, true}, - {"gasPrice stale", 0, gasPrice, nil, false}, - {"tokenPrice update", 0, nil, big.NewInt(20), true}, - {"tokenPrice stale", 0, nil, tokenPrice, false}, - {"token price and gas price stale", 0, gasPrice, tokenPrice, false}, + MerkleRoot: [32]byte{123}, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var destChainSelector uint64 - gasPrice := new(big.Int) - if tt.gasPrice != nil { - destChainSelector = th.Source.ChainSelector - gasPrice = tt.gasPrice - } - - var tokenPrices []commit_store.InternalTokenPriceUpdate - if tt.tokenPrice != nil { - tokenPrices = []commit_store.InternalTokenPriceUpdate{ - {SourceToken: th.Dest.LinkToken.Address(), UsdPerToken: tt.tokenPrice}, - } - } else { - tokenPrices = []commit_store.InternalTokenPriceUpdate{} - } - - var root [32]byte - if tt.seq > 0 { - root = testutils.Random32Byte() - } + ctx := testutils.Context(t) + p := &CommitReportingPlugin{} + commitStore := mock_contracts.NewCommitStoreInterface(t) + p.config.commitStore = commitStore + p.inflightReports = newInflightCommitReportsContainer(time.Minute) + p.lggr = logger.TestLogger(t) + + onChainSeqNum := uint64(100) + + t.Run("should transmit when report is not stale", func(t *testing.T) { + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + // not-stale since report interval is not behind on chain seq num + report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum, Max: onChainSeqNum + 10} + encodedReport, err := abihelpers.EncodeCommitReport(report) + assert.NoError(t, err) + shouldTransmit, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, encodedReport) + assert.NoError(t, err) + assert.True(t, shouldTransmit) + }) - report, err := abihelpers.EncodeCommitReport(commit_store.CommitStoreCommitReport{ - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: tokenPrices, - DestChainSelector: destChainSelector, - UsdPerUnitGas: gasPrice, - }, - MerkleRoot: root, - Interval: commit_store.CommitStoreInterval{Min: tt.seq, Max: tt.seq}, - }) - require.NoError(t, err) + t.Run("should not transmit when report is stale", func(t *testing.T) { + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + // stale since report interval is behind on chain seq num + report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum - 2, Max: onChainSeqNum + 10} + encodedReport, err := abihelpers.EncodeCommitReport(report) + assert.NoError(t, err) + shouldTransmit, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, encodedReport) + assert.NoError(t, err) + assert.False(t, shouldTransmit) + }) - got, err := th.plugin.ShouldTransmitAcceptedReport(testutils.Context(t), types.ReportTimestamp{Epoch: 1, Round: round}, report) - round++ - require.NoError(t, err) - assert.Equal(t, tt.expected, got) - }) - } + t.Run("error when report cannot be decoded", func(t *testing.T) { + _, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, []byte("whatever")) + assert.Error(t, err) + }) } func TestShouldAcceptFinalizedReport(t *testing.T) { From 918a93203b78b80f1e46056d79d4e35a82a3a7b5 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 15:28:58 +0300 Subject: [PATCH 06/25] test commit shouldAccept phase, remove commit test harness --- .../ccip/commit_reporting_plugin_test.go | 417 +++++++----------- .../ccip/execution_reporting_plugin_test.go | 2 + 2 files changed, 166 insertions(+), 253 deletions(-) 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 56a7c49ddb..01de449958 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -24,7 +24,6 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink/v2/core/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" @@ -40,103 +39,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/hasher" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/merklemulti" - plugintesthelpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers/plugins" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) -var defaultGasPrice = big.NewInt(3e9) - -type commitTestHarness = struct { - plugintesthelpers.CCIPPluginTestHarness - plugin *CommitReportingPlugin - mockedGetFee *mock.Call -} - -func setupCommitTestHarness(t *testing.T) commitTestHarness { - th := plugintesthelpers.SetupCCIPTestHarness(t) - - sourceFeeEstimator := mocks.NewEvmFeeEstimator(t) - - mockedGetFee := sourceFeeEstimator.On( - "GetFee", - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - ).Maybe().Return(gas.EvmFee{Legacy: assets.NewWei(defaultGasPrice)}, uint32(200e3), nil) - - lggr := logger.TestLogger(t) - priceGetter := newMockPriceGetter() - - backendClient := client.NewSimulatedBackendClient(t, th.Dest.Chain, new(big.Int).SetUint64(th.Dest.ChainID)) - plugin := CommitReportingPlugin{ - config: CommitPluginConfig{ - lggr: th.Lggr, - sourceLP: th.SourceLP, - destLP: th.DestLP, - sourceEvents: ccipevents.NewLogPollerClient(th.SourceLP, lggr, backendClient), - destEvents: ccipevents.NewLogPollerClient(th.DestLP, lggr, backendClient), - offRamp: th.Dest.OffRamp, - onRampAddress: th.Source.OnRamp.Address(), - commitStore: th.Dest.CommitStore, - priceGetter: priceGetter, - sourceNative: utils.RandomAddress(), - sourceFeeEstimator: sourceFeeEstimator, - sourceChainSelector: th.Source.ChainSelector, - destClient: backendClient, - sourceClient: backendClient, - leafHasher: hasher.NewLeafHasher(th.Source.ChainSelector, th.Dest.ChainSelector, th.Source.OnRamp.Address(), hasher.NewKeccakCtx()), - }, - inflightReports: newInflightCommitReportsContainer(time.Hour), - onchainConfig: th.CommitOnchainConfig, - offchainConfig: ccipconfig.CommitOffchainConfig{ - SourceFinalityDepth: 0, - DestFinalityDepth: 0, - FeeUpdateDeviationPPB: 5e7, - FeeUpdateHeartBeat: models.MustMakeDuration(12 * time.Hour), - MaxGasPrice: 200e9, - }, - lggr: th.Lggr, - destPriceRegistry: th.Dest.PriceRegistry, - tokenDecimalsCache: cache.NewTokenToDecimals(th.Lggr, th.DestLP, th.Dest.OffRamp, th.Dest.PriceRegistry, backendClient, 0), - } - - priceGetter.On("TokenPricesUSD", mock.Anything, mock.Anything).Return(map[common.Address]*big.Int{ - plugin.config.sourceNative: big.NewInt(0).Mul(big.NewInt(100), big.NewInt(1e18)), - th.Source.LinkToken.Address(): big.NewInt(0).Mul(big.NewInt(200), big.NewInt(1e18)), - th.Dest.LinkToken.Address(): big.NewInt(0).Mul(big.NewInt(200), big.NewInt(1e18)), - }, nil) - - return commitTestHarness{ - CCIPPluginTestHarness: th, - plugin: &plugin, - mockedGetFee: mockedGetFee, - } -} - -func TestCommitReportSize(t *testing.T) { - testParams := gopter.DefaultTestParameters() - testParams.MinSuccessfulTests = 100 - p := gopter.NewProperties(testParams) - p.Property("bounded commit report size", prop.ForAll(func(root []byte, min, max uint64) bool { - var root32 [32]byte - copy(root32[:], root) - rep, err := abihelpers.EncodeCommitReport(commit_store.CommitStoreCommitReport{ - MerkleRoot: root32, - Interval: commit_store.CommitStoreInterval{Min: min, Max: max}, - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, - DestChainSelector: 1337, - UsdPerUnitGas: big.NewInt(2000e9), // $2000 per eth * 1gwei = 2000e9 - }, - }) - require.NoError(t, err) - return len(rep) <= MaxCommitReportLength - }, gen.SliceOfN(32, gen.UInt8()), gen.UInt64(), gen.UInt64())) - p.TestingRun(t) -} - func TestCommitReportingPlugin_Observation(t *testing.T) { sourceNativeTokenAddr := common.HexToAddress("1000") someTokenAddr := common.HexToAddress("2000") @@ -381,6 +287,170 @@ func TestCommitReportingPlugin_Report(t *testing.T) { } } +func TestCommitReportingPlugin_ShouldAcceptFinalizedReport(t *testing.T) { + ctx := testutils.Context(t) + + newPlugin := func() *CommitReportingPlugin { + p := &CommitReportingPlugin{} + p.lggr = logger.TestLogger(t) + p.inflightReports = newInflightCommitReportsContainer(time.Minute) + return p + } + + t.Run("report cannot be decoded leads to error", func(t *testing.T) { + p := newPlugin() + encodedReport := []byte("whatever") + _, err := p.ShouldAcceptFinalizedReport(ctx, types.ReportTimestamp{}, encodedReport) + assert.Error(t, err) + }) + + t.Run("empty report should not be accepted", func(t *testing.T) { + p := newPlugin() + report := commit_store.CommitStoreCommitReport{ + // UsdPerUnitGas is mandatory otherwise report cannot be encoded/decoded + PriceUpdates: commit_store.InternalPriceUpdates{UsdPerUnitGas: big.NewInt(int64(rand.Int()))}, + } + encodedReport, err := abihelpers.EncodeCommitReport(report) + assert.NoError(t, err) + shouldAccept, err := p.ShouldAcceptFinalizedReport(ctx, types.ReportTimestamp{}, encodedReport) + assert.NoError(t, err) + assert.False(t, shouldAccept) + }) + + t.Run("stale report should not be accepted", func(t *testing.T) { + onChainSeqNum := uint64(100) + + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + + p := newPlugin() + p.config.commitStore = commitStore + + report := commit_store.CommitStoreCommitReport{ + PriceUpdates: commit_store.InternalPriceUpdates{UsdPerUnitGas: big.NewInt(int64(rand.Int()))}, + MerkleRoot: [32]byte{123}, // this report is considered non-empty since it has a merkle root + } + + // stale since report interval is behind on chain seq num + report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum - 2, Max: onChainSeqNum + 10} + encodedReport, err := abihelpers.EncodeCommitReport(report) + assert.NoError(t, err) + + shouldAccept, err := p.ShouldAcceptFinalizedReport(ctx, types.ReportTimestamp{}, encodedReport) + assert.NoError(t, err) + assert.False(t, shouldAccept) + }) + + t.Run("non-stale report should be accepted and added inflight", func(t *testing.T) { + onChainSeqNum := uint64(100) + + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + + p := newPlugin() + p.config.commitStore = commitStore + + report := commit_store.CommitStoreCommitReport{ + PriceUpdates: commit_store.InternalPriceUpdates{ + TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{ + { + SourceToken: utils.RandomAddress(), + UsdPerToken: big.NewInt(int64(rand.Int())), + }, + }, + DestChainSelector: rand.Uint64(), + UsdPerUnitGas: big.NewInt(int64(rand.Int())), + }, + MerkleRoot: [32]byte{123}, + } + + // non-stale since report interval is not behind on-chain seq num + report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum, Max: onChainSeqNum + 10} + encodedReport, err := abihelpers.EncodeCommitReport(report) + assert.NoError(t, err) + + shouldAccept, err := p.ShouldAcceptFinalizedReport(ctx, types.ReportTimestamp{}, encodedReport) + assert.NoError(t, err) + assert.True(t, shouldAccept) + + // make sure that the report was added inflight + tokenPriceUpdates := p.inflightReports.latestInflightTokenPriceUpdates() + priceUpdate := tokenPriceUpdates[report.PriceUpdates.TokenPriceUpdates[0].SourceToken] + assert.Equal(t, report.PriceUpdates.TokenPriceUpdates[0].UsdPerToken.Uint64(), priceUpdate.value.Uint64()) + }) +} + +func TestCommitReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { + report := commit_store.CommitStoreCommitReport{ + PriceUpdates: commit_store.InternalPriceUpdates{ + TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{ + {SourceToken: utils.RandomAddress(), UsdPerToken: big.NewInt(9e18)}, + }, + DestChainSelector: rand.Uint64(), + UsdPerUnitGas: big.NewInt(2000e9), + }, + MerkleRoot: [32]byte{123}, + } + + ctx := testutils.Context(t) + p := &CommitReportingPlugin{} + commitStore := mock_contracts.NewCommitStoreInterface(t) + p.config.commitStore = commitStore + p.inflightReports = newInflightCommitReportsContainer(time.Minute) + p.lggr = logger.TestLogger(t) + + t.Run("should transmit when report is not stale", func(t *testing.T) { + onChainSeqNum := uint64(100) + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + // not-stale since report interval is not behind on chain seq num + report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum, Max: onChainSeqNum + 10} + encodedReport, err := abihelpers.EncodeCommitReport(report) + assert.NoError(t, err) + shouldTransmit, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, encodedReport) + assert.NoError(t, err) + assert.True(t, shouldTransmit) + }) + + t.Run("should not transmit when report is stale", func(t *testing.T) { + onChainSeqNum := uint64(100) + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + // stale since report interval is behind on chain seq num + report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum - 2, Max: onChainSeqNum + 10} + encodedReport, err := abihelpers.EncodeCommitReport(report) + assert.NoError(t, err) + shouldTransmit, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, encodedReport) + assert.NoError(t, err) + assert.False(t, shouldTransmit) + }) + + t.Run("error when report cannot be decoded", func(t *testing.T) { + _, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, []byte("whatever")) + assert.Error(t, err) + }) +} + +func TestCommitReportSize(t *testing.T) { + testParams := gopter.DefaultTestParameters() + testParams.MinSuccessfulTests = 100 + p := gopter.NewProperties(testParams) + p.Property("bounded commit report size", prop.ForAll(func(root []byte, min, max uint64) bool { + var root32 [32]byte + copy(root32[:], root) + rep, err := abihelpers.EncodeCommitReport(commit_store.CommitStoreCommitReport{ + MerkleRoot: root32, + Interval: commit_store.CommitStoreInterval{Min: min, Max: max}, + PriceUpdates: commit_store.InternalPriceUpdates{ + TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, + DestChainSelector: 1337, + UsdPerUnitGas: big.NewInt(2000e9), // $2000 per eth * 1gwei = 2000e9 + }, + }) + require.NoError(t, err) + return len(rep) <= MaxCommitReportLength + }, gen.SliceOfN(32, gen.UInt8()), gen.UInt64(), gen.UInt64())) + p.TestingRun(t) +} + func TestCalculatePriceUpdates(t *testing.T) { t.Parallel() @@ -906,165 +976,6 @@ func TestCalculateUsdPer1e18TokenAmount(t *testing.T) { } } -func TestCommitReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { - report := commit_store.CommitStoreCommitReport{ - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{ - {SourceToken: utils.RandomAddress(), UsdPerToken: big.NewInt(9e18)}, - }, - DestChainSelector: rand.Uint64(), - UsdPerUnitGas: big.NewInt(2000e9), - }, - MerkleRoot: [32]byte{123}, - } - - ctx := testutils.Context(t) - p := &CommitReportingPlugin{} - commitStore := mock_contracts.NewCommitStoreInterface(t) - p.config.commitStore = commitStore - p.inflightReports = newInflightCommitReportsContainer(time.Minute) - p.lggr = logger.TestLogger(t) - - onChainSeqNum := uint64(100) - - t.Run("should transmit when report is not stale", func(t *testing.T) { - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() - // not-stale since report interval is not behind on chain seq num - report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum, Max: onChainSeqNum + 10} - encodedReport, err := abihelpers.EncodeCommitReport(report) - assert.NoError(t, err) - shouldTransmit, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, encodedReport) - assert.NoError(t, err) - assert.True(t, shouldTransmit) - }) - - t.Run("should not transmit when report is stale", func(t *testing.T) { - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() - // stale since report interval is behind on chain seq num - report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum - 2, Max: onChainSeqNum + 10} - encodedReport, err := abihelpers.EncodeCommitReport(report) - assert.NoError(t, err) - shouldTransmit, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, encodedReport) - assert.NoError(t, err) - assert.False(t, shouldTransmit) - }) - - t.Run("error when report cannot be decoded", func(t *testing.T) { - _, err := p.ShouldTransmitAcceptedReport(ctx, types.ReportTimestamp{}, []byte("whatever")) - assert.Error(t, err) - }) -} - -func TestShouldAcceptFinalizedReport(t *testing.T) { - nextMinSeqNr := uint64(10) - - tests := []struct { - name string - seq uint64 - latestPriceEpochAndRound int64 - epoch uint32 - round uint8 - destChainSelector int - skipRoot bool - expected bool - err bool - }{ - { - name: "future", - seq: nextMinSeqNr * 2, - epoch: 1, - round: 1, - }, - { - name: "empty", - epoch: 1, - round: 2, - }, - { - name: "stale", - seq: nextMinSeqNr - 1, - epoch: 1, - round: 3, - }, - { - name: "base", - seq: nextMinSeqNr, - epoch: 1, - round: 4, - expected: true, - }, - { - name: "price update - epoch and round is ok", - seq: nextMinSeqNr, - latestPriceEpochAndRound: int64(mergeEpochAndRound(2, 10)), - epoch: 2, - round: 11, - destChainSelector: rand.Int(), - skipRoot: true, - expected: true, - }, - { - name: "price update - epoch and round is behind", - seq: nextMinSeqNr, - latestPriceEpochAndRound: int64(mergeEpochAndRound(2, 10)), - epoch: 2, - round: 9, - destChainSelector: rand.Int(), - skipRoot: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - th := setupCommitTestHarness(t) - _, err := th.Dest.CommitStore.SetMinSeqNr(th.Dest.User, nextMinSeqNr) - require.NoError(t, err) - - _, err = th.Dest.CommitStoreHelper.SetLatestPriceEpochAndRound(th.Dest.User, big.NewInt(tt.latestPriceEpochAndRound)) - require.NoError(t, err) - - th.CommitAndPollLogs(t) - - var root [32]byte - if tt.seq > 0 { - root = testutils.Random32Byte() - } - - r := commit_store.CommitStoreCommitReport{ - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, - DestChainSelector: uint64(tt.destChainSelector), - UsdPerUnitGas: new(big.Int), - }, - Interval: commit_store.CommitStoreInterval{Min: tt.seq, Max: tt.seq}, - } - if !tt.skipRoot { - r.MerkleRoot = root - } - report, err := abihelpers.EncodeCommitReport(r) - require.NoError(t, err) - - got, err := th.plugin.ShouldAcceptFinalizedReport( - testutils.Context(t), - types.ReportTimestamp{Epoch: tt.epoch, Round: tt.round}, report) - if tt.err { - require.Error(t, err) - } else { - require.NoError(t, err) - } - assert.Equal(t, tt.expected, got) - - if got { // already added to inflight, should not be accepted again - got, err = th.plugin.ShouldAcceptFinalizedReport( - testutils.Context(t), - types.ReportTimestamp{Epoch: tt.epoch, Round: tt.round}, report) - require.NoError(t, err) - assert.False(t, got) - } - }) - } -} - func TestCommitReportToEthTxMeta(t *testing.T) { mctx := hasher.NewKeccakCtx() tree, err := merklemulti.NewTree(mctx, [][32]byte{mctx.Hash([]byte{0xaa})}) 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 2c2d8c7a3d..c456ee6bd6 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -53,6 +53,8 @@ type execTestHarness = struct { plugin *ExecutionReportingPlugin } +var defaultGasPrice = big.NewInt(3e9) + func setupExecTestHarness(t *testing.T) execTestHarness { th := plugintesthelpers.SetupCCIPTestHarness(t) From 2c675f335cca348a27153d7097f0aa5fddc7762a Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 16:55:59 +0300 Subject: [PATCH 07/25] test/refactor exec buildReport --- .../ocr2/plugins/ccip/ccipevents/client.go | 4 + .../ocr2/plugins/ccip/ccipevents/logpoller.go | 43 ++++++ .../ocr2/plugins/ccip/ccipevents/mock.go | 32 +++- .../ccip/commit_reporting_plugin_test.go | 2 +- .../plugins/ccip/execution_batch_building.go | 27 +--- .../ccip/execution_reporting_plugin.go | 4 +- .../ccip/execution_reporting_plugin_test.go | 146 +++++++++++++----- 7 files changed, 196 insertions(+), 62 deletions(-) diff --git a/core/services/ocr2/plugins/ccip/ccipevents/client.go b/core/services/ocr2/plugins/ccip/ccipevents/client.go index 348e456f48..a76f6a080a 100644 --- a/core/services/ocr2/plugins/ccip/ccipevents/client.go +++ b/core/services/ocr2/plugins/ccip/ccipevents/client.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "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/gethwrappers/ccip/generated/price_registry" @@ -41,6 +42,9 @@ type Client interface { // GetExecutionStateChangesBetweenSeqNums returns all the execution state change events for the provided message sequence numbers (inclusive). GetExecutionStateChangesBetweenSeqNums(ctx context.Context, offRamp common.Address, seqNumMin, seqNumMax uint64, confs int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error) + // GetAcceptedCommitReportsGteSeqNum returns all the accepted commit reports that have sequence number greater than or equal to the provided. + GetAcceptedCommitReportsGteSeqNum(ctx context.Context, commitStoreAddress common.Address, seqNum uint64, confs int) ([]Event[commit_store.CommitStoreReportAccepted], 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/ccipevents/logpoller.go b/core/services/ocr2/plugins/ccip/ccipevents/logpoller.go index 2fe7411d01..5d9ad3234a 100644 --- a/core/services/ocr2/plugins/ccip/ccipevents/logpoller.go +++ b/core/services/ocr2/plugins/ccip/ccipevents/logpoller.go @@ -14,6 +14,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "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/gethwrappers/ccip/generated/price_registry" @@ -220,6 +221,33 @@ func (c *LogPollerClient) LatestBlock(ctx context.Context) (int64, error) { return c.lp.LatestBlock(pg.WithParentCtx(ctx)) } +func (c *LogPollerClient) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, commitStoreAddress common.Address, seqNum uint64, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { + commitStore, err := c.loadCommitStore(commitStoreAddress) + if err != nil { + return nil, err + } + + logs, err := c.lp.LogsDataWordGreaterThan( + abihelpers.EventSignatures.ReportAccepted, + commitStoreAddress, + abihelpers.EventSignatures.ReportAcceptedMaxSequenceNumberWord, + logpoller.EvmWord(seqNum), + confs, + pg.WithParentCtx(ctx), + ) + if err != nil { + return nil, err + } + + return parseLogs[commit_store.CommitStoreReportAccepted]( + logs, + c.lggr, + func(log types.Log) (*commit_store.CommitStoreReportAccepted, error) { + return commitStore.ParseReportAccepted(log) + }, + ) +} + func parseLogs[T any](logs []logpoller.Log, lggr logger.Logger, parseFunc func(log types.Log) (*T, error)) ([]Event[T], error) { reqs := make([]Event[T], 0, len(logs)) for _, log := range logs { @@ -286,6 +314,21 @@ func (c *LogPollerClient) loadOffRamp(addr common.Address) (*evm_2_evm_offramp.E return offRamp, nil } +func (c *LogPollerClient) loadCommitStore(addr common.Address) (*commit_store.CommitStoreFilterer, error) { + commitStore, exists := loadCachedDependency[*commit_store.CommitStoreFilterer](&c.dependencyCache, addr) + if exists { + return commitStore, nil + } + + commitStore, err := commit_store.NewCommitStoreFilterer(addr, c.client) + if err != nil { + return nil, err + } + + c.dependencyCache.Store(addr, commitStore) + return commitStore, nil +} + func loadCachedDependency[T any](cache *sync.Map, addr common.Address) (T, bool) { var empty T diff --git a/core/services/ocr2/plugins/ccip/ccipevents/mock.go b/core/services/ocr2/plugins/ccip/ccipevents/mock.go index 47bacd2821..a6f8e5f76b 100644 --- a/core/services/ocr2/plugins/ccip/ccipevents/mock.go +++ b/core/services/ocr2/plugins/ccip/ccipevents/mock.go @@ -3,11 +3,13 @@ package ccipevents import ( - context "context" - common "github.com/ethereum/go-ethereum/common" + commit_store "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" + + context "context" 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" @@ -22,6 +24,32 @@ type MockClient struct { mock.Mock } +// GetAcceptedCommitReportsGteSeqNum provides a mock function with given fields: ctx, commitStoreAddress, seqNum, confs +func (_m *MockClient) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, commitStoreAddress common.Address, seqNum uint64, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { + ret := _m.Called(ctx, commitStoreAddress, seqNum, confs) + + var r0 []Event[commit_store.CommitStoreReportAccepted] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, int) ([]Event[commit_store.CommitStoreReportAccepted], error)); ok { + return rf(ctx, commitStoreAddress, seqNum, confs) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, int) []Event[commit_store.CommitStoreReportAccepted]); ok { + r0 = rf(ctx, commitStoreAddress, seqNum, confs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]Event[commit_store.CommitStoreReportAccepted]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64, int) error); ok { + r1 = rf(ctx, commitStoreAddress, seqNum, confs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetExecutionStateChangesBetweenSeqNums provides a mock function with given fields: ctx, offRamp, seqNumMin, seqNumMax, confs func (_m *MockClient) GetExecutionStateChangesBetweenSeqNums(ctx context.Context, offRamp common.Address, seqNumMin uint64, seqNumMax uint64, confs int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error) { ret := _m.Called(ctx, offRamp, seqNumMin, seqNumMax, confs) 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 01de449958..c98283b6a7 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -1126,5 +1126,5 @@ func Test_isStaleReport(t *testing.T) { type nopLeafHasher struct{} func (n nopLeafHasher) HashLeaf(log gethtypes.Log) ([32]byte, error) { - return [32]byte{}, nil + return [32]byte{123}, nil } diff --git a/core/services/ocr2/plugins/ccip/execution_batch_building.go b/core/services/ocr2/plugins/ccip/execution_batch_building.go index ede8b919a1..a021bdbd62 100644 --- a/core/services/ocr2/plugins/ccip/execution_batch_building.go +++ b/core/services/ocr2/plugins/ccip/execution_batch_building.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "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" @@ -16,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipevents" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/hasher" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/merklemulti" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) func getProofData( @@ -99,27 +97,18 @@ func validateSeqNumbers(serviceCtx context.Context, commitStore commit_store.Com } // Gets the commit report from the saved logs for a given sequence number. -func getCommitReportForSeqNum(ctx context.Context, dstLogPoller logpoller.LogPoller, commitStore commit_store.CommitStoreInterface, seqNr uint64) (commit_store.CommitStoreCommitReport, error) { - // fetch commitReports which report.Interval.Max >= seqNr - logs, err := dstLogPoller.LogsDataWordGreaterThan( - abihelpers.EventSignatures.ReportAccepted, - commitStore.Address(), - abihelpers.EventSignatures.ReportAcceptedMaxSequenceNumberWord, - logpoller.EvmWord(seqNr), - 0, - pg.WithParentCtx(ctx), - ) +func getCommitReportForSeqNum(ctx context.Context, destEvents ccipevents.Client, commitStore commit_store.CommitStoreInterface, seqNum uint64) (commit_store.CommitStoreCommitReport, error) { + acceptedReports, err := destEvents.GetAcceptedCommitReportsGteSeqNum(ctx, commitStore.Address(), seqNum, 0) if err != nil { return commit_store.CommitStoreCommitReport{}, err } - for _, log := range logs { - reportAccepted, err := commitStore.ParseReportAccepted(log.ToGethLog()) - if err != nil { - return commit_store.CommitStoreCommitReport{}, err - } - if reportAccepted.Report.Interval.Min <= seqNr && seqNr <= reportAccepted.Report.Interval.Max { - return reportAccepted.Report, nil + + for _, acceptedReport := range acceptedReports { + reportInterval := acceptedReport.Data.Report.Interval + if reportInterval.Min <= seqNum && seqNum <= reportInterval.Max { + return acceptedReport.Data.Report, nil } } + return commit_store.CommitStoreCommitReport{}, errors.Errorf("seq number not committed") } diff --git a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go index 21224cdc87..f6535487ff 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go @@ -818,7 +818,7 @@ func (r *ExecutionReportingPlugin) buildReport(ctx context.Context, lggr logger. if err := validateSeqNumbers(ctx, r.config.commitStore, observedMessages); err != nil { return nil, err } - commitReport, err := getCommitReportForSeqNum(ctx, r.config.destLP, r.config.commitStore, observedMessages[0].SeqNr) + commitReport, err := getCommitReportForSeqNum(ctx, r.config.destEvents, r.config.commitStore, observedMessages[0].SeqNr) if err != nil { return nil, err } @@ -839,7 +839,7 @@ func (r *ExecutionReportingPlugin) buildReport(ctx context.Context, lggr logger. capped := sort.Search(len(observedMessages), func(i int) bool { report, _, err2 := buildExecutionReportForMessages(messages, leaves, tree, commitReport.Interval, observedMessages[:i+1]) if err2 != nil { - r.lggr.Errorw("build execution report", "err", err) + r.lggr.Errorw("build execution report", "err", err2) return false } 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 c456ee6bd6..55690b37ba 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "github.com/cometbft/cometbft/libs/rand" "github.com/ethereum/go-ethereum/common" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" @@ -111,51 +112,120 @@ func setupExecTestHarness(t *testing.T) execTestHarness { } } -func TestMaxExecutionReportSize(t *testing.T) { - // Ensure that given max payload size and max num tokens, - // Our report size is under the tx size limit. - th := setupExecTestHarness(t) - th.plugin.F = 1 - mb := th.GenerateAndSendMessageBatch(t, 50, MaxPayloadLength, MaxTokensPerMessage) +// TODO: refactor this - it's too complicated +func TestExecutionReportingPlugin_buildReport(t *testing.T) { + const numMessages = 100 + const tokensPerMessage = 20 + const bytesPerMessage = 1000 - // commit root - encoded, err := abihelpers.EncodeCommitReport(commit_store.CommitStoreCommitReport{ - Interval: mb.Interval, - MerkleRoot: mb.Root, - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, - DestChainSelector: 0, - UsdPerUnitGas: big.NewInt(0), - }, - }) - require.NoError(t, err) - latestEpocAndRound, err := th.Dest.CommitStoreHelper.GetLatestPriceEpochAndRound(nil) - require.NoError(t, err) - _, err = th.Dest.CommitStoreHelper.Report(th.Dest.User, encoded, big.NewInt(int64(latestEpocAndRound+1))) - require.NoError(t, err) - // double commit to ensure enough confirmations - th.CommitAndPollLogs(t) - th.CommitAndPollLogs(t) + ctx := testutils.Context(t) + + messages := make([]evm_2_evm_offramp.InternalEVM2EVMMessage, numMessages) + offChainTokenData := make([][][]byte, numMessages) - fullReport, err := abihelpers.EncodeExecutionReport(evm_2_evm_offramp.InternalExecutionReport{ - Messages: mb.Messages, - OffchainTokenData: mb.TokenData, - Proofs: mb.Proof.Hashes, - ProofFlagBits: mb.ProofBits, + for i := range messages { + tokenAmounts := make([]evm_2_evm_offramp.ClientEVMTokenAmount, tokensPerMessage) + for j := range tokenAmounts { + tokenAmounts[j] = evm_2_evm_offramp.ClientEVMTokenAmount{ + Token: utils.RandomAddress(), Amount: big.NewInt(math.MaxInt64)} + } + messages[i] = evm_2_evm_offramp.InternalEVM2EVMMessage{ + SourceChainSelector: math.MaxUint64, + SequenceNumber: uint64(i + 1), + FeeTokenAmount: big.NewInt(math.MaxInt64), + Sender: utils.RandomAddress(), + Nonce: math.MaxUint64, + GasLimit: big.NewInt(math.MaxInt64), + Strict: false, + Receiver: utils.RandomAddress(), + Data: bytes.Repeat([]byte{0}, bytesPerMessage), + TokenAmounts: tokenAmounts, + FeeToken: utils.RandomAddress(), + MessageId: [32]byte{12}, + } + + data := []byte(`{"foo": "bar"}`) + offChainTokenData[i] = [][]byte{data, data, data} + } + + encodedReport, err := abihelpers.EncodeExecutionReport(evm_2_evm_offramp.InternalExecutionReport{ + Messages: messages, + OffchainTokenData: offChainTokenData, + Proofs: make([][32]byte, numMessages), + ProofFlagBits: big.NewInt(rand.Int64()), }) - require.NoError(t, err) + assert.NoError(t, err) // ensure "naive" full report would be bigger than limit - require.Greater(t, len(fullReport), MaxExecutionReportLength, "full execution report length") + assert.Greater(t, len(encodedReport), MaxExecutionReportLength, "full execution report length") - observations := make([]ObservedMessage, len(mb.Messages)) - for i, msg := range mb.Messages { - observations[i] = NewObservedMessage(msg.SequenceNumber, mb.TokenData[i]) + observations := make([]ObservedMessage, len(messages)) + for i, msg := range messages { + observations[i] = NewObservedMessage(msg.SequenceNumber, offChainTokenData[i]) } - // buildReport should cap the built report to fit in MaxExecutionReportLength - execReport, err := th.plugin.buildReport(testutils.Context(t), th.Lggr, observations) - require.NoError(t, err) - require.LessOrEqual(t, len(execReport), MaxExecutionReportLength, "built execution report length") + // ensure that buildReport should cap the built report to fit in MaxExecutionReportLength + p := &ExecutionReportingPlugin{} + p.lggr = logger.TestLogger(t) + + commitStoreAddress := utils.RandomAddress() + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("Address").Return(commitStoreAddress) + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything). + Return(messages[len(messages)-1].SequenceNumber+1, nil) + commitStore.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(math.MaxInt64), nil) + p.config.commitStore = commitStore + + destEvents := ccipevents.NewMockClient(t) + + destEvents.On("GetAcceptedCommitReportsGteSeqNum", ctx, commitStoreAddress, observations[0].SeqNr, 0). + Return([]ccipevents.Event[commit_store.CommitStoreReportAccepted]{ + { + Data: commit_store.CommitStoreReportAccepted{ + Report: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{ + Min: observations[0].SeqNr, + Max: observations[len(observations)-1].SeqNr, + }, + }, + }, + }, + }, nil) + p.config.destEvents = destEvents + + p.config.leafHasher = nopLeafHasher{} + + onRampAddr := utils.RandomAddress() + onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) + onRamp.On("Address").Return(onRampAddr) + p.config.onRamp = onRamp + + sendReqs := make([]ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], len(observations)) + for i := range observations { + sendReqs[i] = ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{Message: evm_2_evm_onramp.InternalEVM2EVMMessage{ + SourceChainSelector: math.MaxUint64, + SequenceNumber: uint64(i + 1), + FeeTokenAmount: big.NewInt(math.MaxInt64), + Sender: utils.RandomAddress(), + Nonce: math.MaxUint64, + GasLimit: big.NewInt(math.MaxInt64), + Strict: false, + Receiver: utils.RandomAddress(), + Data: bytes.Repeat([]byte{0}, bytesPerMessage), + TokenAmounts: nil, + FeeToken: utils.RandomAddress(), + MessageId: [32]byte{12}, + }}, + } + } + sourceEvents := ccipevents.NewMockClient(t) + sourceEvents.On("GetSendRequestsBetweenSeqNums", + ctx, onRampAddr, observations[0].SeqNr, observations[len(observations)-1].SeqNr, 0).Return(sendReqs, nil) + p.config.sourceEvents = sourceEvents + + execReport, err := p.buildReport(ctx, p.lggr, observations) + assert.NoError(t, err) + assert.LessOrEqual(t, len(execReport), MaxExecutionReportLength, "built execution report length") } func TestExecutionReportToEthTxMetadata(t *testing.T) { From 7c5379935eaefe8af244b2bed8d6f7b31eaeb75b Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 18:59:09 +0300 Subject: [PATCH 08/25] test exec report to eth tx meta --- .../ocr2/plugins/ccip/execution_plugin.go | 3 +- .../ccip/execution_reporting_plugin_test.go | 133 ++++++++---------- 2 files changed, 62 insertions(+), 74 deletions(-) diff --git a/core/services/ocr2/plugins/ccip/execution_plugin.go b/core/services/ocr2/plugins/ccip/execution_plugin.go index 45185988a7..2320adba89 100644 --- a/core/services/ocr2/plugins/ccip/execution_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_plugin.go @@ -298,10 +298,9 @@ func unregisterExecutionPluginLpFilters( } // ExecutionReportToEthTxMeta generates a txmgr.EthTxMeta from the given report. -// all the message ids will be added to the tx metadata. +// Only MessageIDs will be populated in the TxMeta. func ExecutionReportToEthTxMeta(report []byte) (*txmgr.TxMeta, error) { execReport, err := abihelpers.DecodeExecutionReport(report) - if err != nil { return nil, 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 55690b37ba..a5de29da7f 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -114,53 +114,21 @@ func setupExecTestHarness(t *testing.T) execTestHarness { // TODO: refactor this - it's too complicated func TestExecutionReportingPlugin_buildReport(t *testing.T) { + ctx := testutils.Context(t) + const numMessages = 100 const tokensPerMessage = 20 const bytesPerMessage = 1000 - ctx := testutils.Context(t) - - messages := make([]evm_2_evm_offramp.InternalEVM2EVMMessage, numMessages) - offChainTokenData := make([][][]byte, numMessages) - - for i := range messages { - tokenAmounts := make([]evm_2_evm_offramp.ClientEVMTokenAmount, tokensPerMessage) - for j := range tokenAmounts { - tokenAmounts[j] = evm_2_evm_offramp.ClientEVMTokenAmount{ - Token: utils.RandomAddress(), Amount: big.NewInt(math.MaxInt64)} - } - messages[i] = evm_2_evm_offramp.InternalEVM2EVMMessage{ - SourceChainSelector: math.MaxUint64, - SequenceNumber: uint64(i + 1), - FeeTokenAmount: big.NewInt(math.MaxInt64), - Sender: utils.RandomAddress(), - Nonce: math.MaxUint64, - GasLimit: big.NewInt(math.MaxInt64), - Strict: false, - Receiver: utils.RandomAddress(), - Data: bytes.Repeat([]byte{0}, bytesPerMessage), - TokenAmounts: tokenAmounts, - FeeToken: utils.RandomAddress(), - MessageId: [32]byte{12}, - } - - data := []byte(`{"foo": "bar"}`) - offChainTokenData[i] = [][]byte{data, data, data} - } - - encodedReport, err := abihelpers.EncodeExecutionReport(evm_2_evm_offramp.InternalExecutionReport{ - Messages: messages, - OffchainTokenData: offChainTokenData, - Proofs: make([][32]byte, numMessages), - ProofFlagBits: big.NewInt(rand.Int64()), - }) + executionReport := generateExecutionReport(t, numMessages, tokensPerMessage, bytesPerMessage) + encodedReport, err := abihelpers.EncodeExecutionReport(executionReport) assert.NoError(t, err) // ensure "naive" full report would be bigger than limit assert.Greater(t, len(encodedReport), MaxExecutionReportLength, "full execution report length") - observations := make([]ObservedMessage, len(messages)) - for i, msg := range messages { - observations[i] = NewObservedMessage(msg.SequenceNumber, offChainTokenData[i]) + observations := make([]ObservedMessage, len(executionReport.Messages)) + for i, msg := range executionReport.Messages { + observations[i] = NewObservedMessage(msg.SequenceNumber, executionReport.OffchainTokenData[i]) } // ensure that buildReport should cap the built report to fit in MaxExecutionReportLength @@ -171,7 +139,7 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { commitStore := mock_contracts.NewCommitStoreInterface(t) commitStore.On("Address").Return(commitStoreAddress) commitStore.On("GetExpectedNextSequenceNumber", mock.Anything). - Return(messages[len(messages)-1].SequenceNumber+1, nil) + Return(executionReport.Messages[len(executionReport.Messages)-1].SequenceNumber+1, nil) commitStore.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(math.MaxInt64), nil) p.config.commitStore = commitStore @@ -228,38 +196,19 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { assert.LessOrEqual(t, len(execReport), MaxExecutionReportLength, "built execution report length") } -func TestExecutionReportToEthTxMetadata(t *testing.T) { - c := plugintesthelpers.SetupCCIPTestHarness(t) - tests := []struct { - name string - msgBatch plugintesthelpers.MessageBatch - err error - }{ - { - "happy flow", - c.GenerateAndSendMessageBatch(t, 5, MaxPayloadLength, MaxTokensPerMessage), - nil, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - encExecReport, err := abihelpers.EncodeExecutionReport(evm_2_evm_offramp.InternalExecutionReport{ - Messages: tc.msgBatch.Messages, - OffchainTokenData: tc.msgBatch.TokenData, - Proofs: tc.msgBatch.Proof.Hashes, - ProofFlagBits: tc.msgBatch.ProofBits, - }) - require.NoError(t, err) - txMeta, err := ExecutionReportToEthTxMeta(encExecReport) - if tc.err != nil { - require.Equal(t, tc.err.Error(), err.Error()) - return - } - require.NoError(t, err) - require.NotNil(t, txMeta) - require.Len(t, txMeta.MessageIDs, len(tc.msgBatch.Messages)) - }) - } +func TestExecutionReportToEthTxMeta(t *testing.T) { + t.Run("happy flow", func(t *testing.T) { + executionReport := generateExecutionReport(t, 10, 3, 1000) + encExecReport, err := abihelpers.EncodeExecutionReport(executionReport) + txMeta, err := ExecutionReportToEthTxMeta(encExecReport) + assert.NoError(t, err) + assert.Len(t, txMeta.MessageIDs, len(executionReport.Messages)) + }) + + t.Run("invalid report", func(t *testing.T) { + _, err := ExecutionReportToEthTxMeta([]byte("whatever")) + assert.Error(t, err) + }) } func TestUpdateSourceToDestTokenMapping(t *testing.T) { @@ -1369,3 +1318,43 @@ func Test_inflightAggregates(t *testing.T) { }) } } + +func generateExecutionReport(t *testing.T, numMsgs, tokensPerMsg, bytesPerMsg int) evm_2_evm_offramp.InternalExecutionReport { + messages := make([]evm_2_evm_offramp.InternalEVM2EVMMessage, numMsgs) + + offChainTokenData := make([][][]byte, numMsgs) + for i := range messages { + tokenAmounts := make([]evm_2_evm_offramp.ClientEVMTokenAmount, tokensPerMsg) + for j := range tokenAmounts { + tokenAmounts[j] = evm_2_evm_offramp.ClientEVMTokenAmount{ + Token: utils.RandomAddress(), + Amount: big.NewInt(math.MaxInt64), + } + } + + messages[i] = evm_2_evm_offramp.InternalEVM2EVMMessage{ + SourceChainSelector: rand.Uint64(), + SequenceNumber: uint64(i + 1), + FeeTokenAmount: big.NewInt(rand.Int64()), + Sender: utils.RandomAddress(), + Nonce: rand.Uint64(), + GasLimit: big.NewInt(rand.Int64()), + Strict: false, + Receiver: utils.RandomAddress(), + Data: bytes.Repeat([]byte{1}, bytesPerMsg), + TokenAmounts: tokenAmounts, + FeeToken: utils.RandomAddress(), + MessageId: utils.RandomBytes32(), + } + + data := []byte(`{"foo": "bar"}`) + offChainTokenData[i] = [][]byte{data, data, data} + } + + return evm_2_evm_offramp.InternalExecutionReport{ + Messages: messages, + OffchainTokenData: offChainTokenData, + Proofs: make([][32]byte, numMsgs), + ProofFlagBits: big.NewInt(rand.Int64()), + } +} From 68410237cdfb6e37e26f1f9c5005f50499355267 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 19:18:02 +0300 Subject: [PATCH 09/25] fix commit report tests --- .../ccip/commit_reporting_plugin_test.go | 48 +++++++++---------- .../ccip/execution_reporting_plugin_test.go | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) 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 c98283b6a7..898c29c21a 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -166,9 +166,9 @@ func TestCommitReportingPlugin_Report(t *testing.T) { tokenPriceUpdates []ccipevents.Event[price_registry.PriceRegistryUsdPerTokenUpdated] sendRequests []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] - expCommitReport *commit_store.CommitStoreCommitReport + expCommitReport commit_store.CommitStoreCommitReport expSeqNumRange commit_store.CommitStoreInterval - expReport bool + shouldGetReport bool expErr bool }{ { @@ -188,17 +188,17 @@ func TestCommitReportingPlugin_Report(t *testing.T) { }, }, expSeqNumRange: commit_store.CommitStoreInterval{Min: 1, Max: 1}, - expCommitReport: &commit_store.CommitStoreCommitReport{ - MerkleRoot: [32]byte{}, + expCommitReport: commit_store.CommitStoreCommitReport{ + MerkleRoot: [32]byte{123}, Interval: commit_store.CommitStoreInterval{Min: 1, Max: 1}, PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, + TokenPriceUpdates: nil, DestChainSelector: 0, - UsdPerUnitGas: new(big.Int), + UsdPerUnitGas: big.NewInt(0), }, }, - expReport: true, - expErr: false, + shouldGetReport: true, + expErr: false, }, { name: "not enough observations", @@ -234,8 +234,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) { ctx := testutils.Context(t) destPriceRegistryAddress := utils.RandomAddress() - onRampAddress := - utils.RandomAddress() + onRampAddress := utils.RandomAddress() sourceChainSelector := rand.Int() for _, tc := range testCases { @@ -260,29 +259,29 @@ func TestCommitReportingPlugin_Report(t *testing.T) { p.config.sourceEvents = sourceEvents p.config.onRampAddress = onRampAddress p.config.sourceChainSelector = uint64(sourceChainSelector) - p.config.leafHasher = &nopLeafHasher{} + p.config.leafHasher = &leafHasher123{} aos := make([]types.AttributedObservation, 0, len(tc.observations)) for _, o := range tc.observations { obs, err := o.Marshal() - require.NoError(t, err) + assert.NoError(t, err) aos = append(aos, types.AttributedObservation{Observation: obs}) } - gotShouldReport, gotReport, err := p.Report(ctx, types.ReportTimestamp{}, types.Query{}, aos) + gotSomeReport, gotReport, err := p.Report(ctx, types.ReportTimestamp{}, types.Query{}, aos) if tc.expErr { - require.Error(t, err) - } else { - require.NoError(t, err) + assert.Error(t, err) + return } - assert.Equal(t, tc.expReport, gotShouldReport) - var expectedReport types.Report - if tc.expCommitReport != nil { - expectedReport, err = abihelpers.EncodeCommitReport(*tc.expCommitReport) - require.NoError(t, err) + assert.NoError(t, err) + assert.Equal(t, tc.shouldGetReport, gotSomeReport) + + if tc.shouldGetReport { + encodedExpectedReport, err := abihelpers.EncodeCommitReport(tc.expCommitReport) + assert.NoError(t, err) + assert.Equal(t, types.Report(encodedExpectedReport), gotReport) } - assert.Equal(t, expectedReport, gotReport) }) } } @@ -1123,8 +1122,9 @@ func Test_isStaleReport(t *testing.T) { }) } -type nopLeafHasher struct{} +// leafHasher123 always returns '123' followed by zeroes in HashLeaf method. +type leafHasher123 struct{} -func (n nopLeafHasher) HashLeaf(log gethtypes.Log) ([32]byte, error) { +func (h leafHasher123) HashLeaf(_ gethtypes.Log) ([32]byte, error) { return [32]byte{123}, nil } 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 a5de29da7f..ee1933b212 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -160,7 +160,7 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { }, nil) p.config.destEvents = destEvents - p.config.leafHasher = nopLeafHasher{} + p.config.leafHasher = leafHasher123{} onRampAddr := utils.RandomAddress() onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) From 8c88c804d690fce9dd41a84d13f0e7f06374fb46 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 21:37:02 +0300 Subject: [PATCH 10/25] write remaining tests for exec plugin + delete test harness --- .../ocr2/plugins/ccip/ccipevents/client.go | 3 + .../ocr2/plugins/ccip/ccipevents/logpoller.go | 26 ++ .../ocr2/plugins/ccip/ccipevents/mock.go | 26 ++ .../ccip/execution_reporting_plugin.go | 23 +- .../ccip/execution_reporting_plugin_test.go | 421 ++++++++---------- 5 files changed, 241 insertions(+), 258 deletions(-) diff --git a/core/services/ocr2/plugins/ccip/ccipevents/client.go b/core/services/ocr2/plugins/ccip/ccipevents/client.go index a76f6a080a..723a8346c3 100644 --- a/core/services/ocr2/plugins/ccip/ccipevents/client.go +++ b/core/services/ocr2/plugins/ccip/ccipevents/client.go @@ -45,6 +45,9 @@ type Client interface { // GetAcceptedCommitReportsGteSeqNum returns all the accepted commit reports that have sequence number greater than or equal to the provided. GetAcceptedCommitReportsGteSeqNum(ctx context.Context, commitStoreAddress common.Address, seqNum uint64, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) + // 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) + // 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/ccipevents/logpoller.go b/core/services/ocr2/plugins/ccip/ccipevents/logpoller.go index 5d9ad3234a..ab6d14e6a0 100644 --- a/core/services/ocr2/plugins/ccip/ccipevents/logpoller.go +++ b/core/services/ocr2/plugins/ccip/ccipevents/logpoller.go @@ -248,6 +248,32 @@ func (c *LogPollerClient) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, ) } +func (c *LogPollerClient) GetAcceptedCommitReportsGteTimestamp(ctx context.Context, commitStoreAddress common.Address, ts time.Time, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { + commitStore, err := c.loadCommitStore(commitStoreAddress) + if err != nil { + return nil, err + } + + logs, err := c.lp.LogsCreatedAfter( + abihelpers.EventSignatures.ReportAccepted, + commitStoreAddress, + ts, + confs, + pg.WithParentCtx(ctx), + ) + if err != nil { + return nil, err + } + + return parseLogs[commit_store.CommitStoreReportAccepted]( + logs, + c.lggr, + func(log types.Log) (*commit_store.CommitStoreReportAccepted, error) { + return commitStore.ParseReportAccepted(log) + }, + ) +} + func parseLogs[T any](logs []logpoller.Log, lggr logger.Logger, parseFunc func(log types.Log) (*T, error)) ([]Event[T], error) { reqs := make([]Event[T], 0, len(logs)) for _, log := range logs { diff --git a/core/services/ocr2/plugins/ccip/ccipevents/mock.go b/core/services/ocr2/plugins/ccip/ccipevents/mock.go index a6f8e5f76b..73d8bd9173 100644 --- a/core/services/ocr2/plugins/ccip/ccipevents/mock.go +++ b/core/services/ocr2/plugins/ccip/ccipevents/mock.go @@ -50,6 +50,32 @@ func (_m *MockClient) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, com return r0, r1 } +// GetAcceptedCommitReportsGteTimestamp provides a mock function with given fields: ctx, commitStoreAddress, ts, confs +func (_m *MockClient) GetAcceptedCommitReportsGteTimestamp(ctx context.Context, commitStoreAddress common.Address, ts time.Time, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { + ret := _m.Called(ctx, commitStoreAddress, ts, confs) + + var r0 []Event[commit_store.CommitStoreReportAccepted] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, time.Time, int) ([]Event[commit_store.CommitStoreReportAccepted], error)); ok { + return rf(ctx, commitStoreAddress, ts, confs) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, time.Time, int) []Event[commit_store.CommitStoreReportAccepted]); ok { + r0 = rf(ctx, commitStoreAddress, ts, confs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]Event[commit_store.CommitStoreReportAccepted]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, time.Time, int) error); ok { + r1 = rf(ctx, commitStoreAddress, ts, confs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetExecutionStateChangesBetweenSeqNums provides a mock function with given fields: ctx, offRamp, seqNumMin, seqNumMax, confs func (_m *MockClient) GetExecutionStateChangesBetweenSeqNums(ctx context.Context, offRamp common.Address, seqNumMin uint64, seqNumMax uint64, confs int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error) { ret := _m.Called(ctx, offRamp, seqNumMin, seqNumMax, confs) diff --git a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go index f6535487ff..eab1b417a0 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go @@ -12,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - gethtypes "github.com/ethereum/go-ethereum/core/types" "golang.org/x/sync/errgroup" "github.com/pkg/errors" @@ -36,7 +35,6 @@ import ( ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/hasher" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/observability" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) const ( @@ -235,7 +233,7 @@ type evm2EVMOnRampCCIPSendRequestedWithMeta struct { func (r *ExecutionReportingPlugin) getExecutableObservations(ctx context.Context, lggr logger.Logger, timestamp types.ReportTimestamp, inflight []InflightInternalExecutionReport) ([]ObservedMessage, error) { unexpiredReports, err := getUnexpiredCommitReports( ctx, - r.config.destLP, + r.config.destEvents, r.config.commitStore, r.onchainConfig.PermissionLessExecutionThresholdDuration(), ) @@ -1121,30 +1119,23 @@ func getTokensPrices(ctx context.Context, feeTokens []common.Address, priceRegis func getUnexpiredCommitReports( ctx context.Context, - dstLogPoller logpoller.LogPoller, + destEvents ccipevents.Client, commitStore commit_store.CommitStoreInterface, permissionExecutionThreshold time.Duration, ) ([]commit_store.CommitStoreCommitReport, error) { - logs, err := dstLogPoller.LogsCreatedAfter( - abihelpers.EventSignatures.ReportAccepted, + acceptedReports, err := destEvents.GetAcceptedCommitReportsGteTimestamp( + ctx, commitStore.Address(), time.Now().Add(-permissionExecutionThreshold), 0, - pg.WithParentCtx(ctx), ) if err != nil { return nil, err } + var reports []commit_store.CommitStoreCommitReport - for _, log := range logs { - reportAccepted, err := commitStore.ParseReportAccepted(gethtypes.Log{ - Topics: log.GetTopics(), - Data: log.Data, - }) - if err != nil { - return nil, err - } - reports = append(reports, reportAccepted.Report) + for _, acceptedReport := range acceptedReports { + reports = append(reports, acceptedReport.Data.Report) } return reports, nil } 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 ee1933b212..994ad8f3f9 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -15,12 +15,13 @@ import ( "github.com/cometbft/cometbft/libs/rand" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/libocr/offchainreporting2/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" lpMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" @@ -32,87 +33,14 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipevents" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" - plugintesthelpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers/plugins" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/core/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/hasher" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) -var ( - MaxTokensPerMessage = 5 - MaxPayloadLength = 100_000 -) - -type execTestHarness = struct { - plugintesthelpers.CCIPPluginTestHarness - plugin *ExecutionReportingPlugin -} - -var defaultGasPrice = big.NewInt(3e9) - -func setupExecTestHarness(t *testing.T) execTestHarness { - th := plugintesthelpers.SetupCCIPTestHarness(t) - - lggr := logger.TestLogger(t) - destFeeEstimator := mocks.NewEvmFeeEstimator(t) - - destFeeEstimator.On( - "GetFee", - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - ).Maybe().Return(gas.EvmFee{Legacy: assets.NewWei(defaultGasPrice)}, uint32(200e3), nil) - - offchainConfig := ccipconfig.ExecOffchainConfig{ - SourceFinalityDepth: 0, - DestOptimisticConfirmations: 0, - MaxGasPrice: 200e9, - BatchGasLimit: 5e6, - RootSnoozeTime: models.MustMakeDuration(10 * time.Minute), - InflightCacheExpiry: models.MustMakeDuration(3 * time.Minute), - RelativeBoostPerWaitHour: 0.07, - } - plugin := ExecutionReportingPlugin{ - config: ExecutionPluginConfig{ - lggr: th.Lggr, - sourceLP: th.SourceLP, - destLP: th.DestLP, - sourceEvents: ccipevents.NewLogPollerClient(th.SourceLP, lggr, th.SourceClient), - destEvents: ccipevents.NewLogPollerClient(th.DestLP, lggr, th.DestClient), - sourcePriceRegistry: th.Source.PriceRegistry, - onRamp: th.Source.OnRamp, - commitStore: th.Dest.CommitStore, - offRamp: th.Dest.OffRamp, - destClient: th.DestClient, - sourceClient: th.SourceClient, - sourceWrappedNativeToken: th.Source.WrappedNative.Address(), - leafHasher: hasher.NewLeafHasher(th.Source.ChainSelector, th.Dest.ChainSelector, th.Source.OnRamp.Address(), hasher.NewKeccakCtx()), - destGasEstimator: destFeeEstimator, - }, - onchainConfig: th.ExecOnchainConfig, - offchainConfig: offchainConfig, - lggr: th.Lggr.Named("ExecutionReportingPlugin"), - snoozedRoots: cache.NewSnoozedRoots(th.ExecOnchainConfig.PermissionLessExecutionThresholdDuration(), offchainConfig.RootSnoozeTime.Duration()), - inflightReports: newInflightExecReportsContainer(offchainConfig.InflightCacheExpiry.Duration()), - destPriceRegistry: th.Dest.PriceRegistry, - destWrappedNative: th.Dest.WrappedNative.Address(), - cachedSourceFeeTokens: cache.NewCachedFeeTokens(th.SourceLP, th.Source.PriceRegistry, int64(offchainConfig.SourceFinalityDepth)), - cachedDestTokens: cache.NewCachedSupportedTokens(th.DestLP, th.Dest.OffRamp, th.Dest.PriceRegistry, int64(offchainConfig.DestOptimisticConfirmations)), - } - return execTestHarness{ - CCIPPluginTestHarness: th, - plugin: &plugin, - } -} - -// TODO: refactor this - it's too complicated func TestExecutionReportingPlugin_buildReport(t *testing.T) { ctx := testutils.Context(t) @@ -243,205 +171,214 @@ func TestUpdateSourceToDestTokenMapping(t *testing.T) { require.Equal(t, destToken, value.SupportedTokens[sourceToken]) } -func TestExecObservation(t *testing.T) { - th := setupExecTestHarness(t) - th.plugin.F = 1 - mb := th.GenerateAndSendMessageBatch(t, 2, 10, 1) - - // commit root - encoded, err := abihelpers.EncodeCommitReport(commit_store.CommitStoreCommitReport{ - Interval: mb.Interval, - MerkleRoot: mb.Root, - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, - DestChainSelector: 0, - UsdPerUnitGas: big.NewInt(0), - }, - }) - require.NoError(t, err) - latestEpocAndRound, err := th.Dest.CommitStoreHelper.GetLatestPriceEpochAndRound(nil) - require.NoError(t, err) - _, err = th.Dest.CommitStoreHelper.Report(th.Dest.User, encoded, big.NewInt(int64(latestEpocAndRound+1))) - require.NoError(t, err) - // double commit to ensure enough confirmations - th.CommitAndPollLogs(t) - th.CommitAndPollLogs(t) - - expectedObservations := NewExecutionObservation([]ObservedMessage{ - {SeqNr: 1, MsgData: MsgData{TokenData: [][]byte{{}}}}, - {SeqNr: 2, MsgData: MsgData{TokenData: [][]byte{{}}}}, - }) - tests := []struct { - name string - commitStoreDown bool - expected *ExecutionObservation - expectedError bool +func TestExecutionReportingPlugin_Observation(t *testing.T) { + testCases := []struct { + name string + commitStoreDown bool + inflightReports []InflightInternalExecutionReport + unexpiredReports []ccipevents.Event[commit_store.CommitStoreReportAccepted] + sendRequests []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + executedSeqNums []uint64 + blessedRoots [][32]byte + senderNonce uint64 + rateLimiterState evm_2_evm_offramp.RateLimiterTokenBucket + expErr bool }{ { - "base", - false, - &expectedObservations, - false, + name: "commit store is down", + commitStoreDown: true, + expErr: true, }, { - "commitStore down", - true, - nil, - true, + name: "happy flow", + commitStoreDown: false, + inflightReports: []InflightInternalExecutionReport{}, + unexpiredReports: []ccipevents.Event[commit_store.CommitStoreReportAccepted]{ + { + Data: commit_store.CommitStoreReportAccepted{ + Report: commit_store.CommitStoreCommitReport{ + PriceUpdates: commit_store.InternalPriceUpdates{}, + Interval: commit_store.CommitStoreInterval{Min: 10, Max: 12}, + MerkleRoot: [32]byte{123}, + }, + }, + }, + }, + blessedRoots: [][32]byte{ + {123}, + }, + rateLimiterState: evm_2_evm_offramp.RateLimiterTokenBucket{ + IsEnabled: false, + }, + senderNonce: 9, + sendRequests: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + { + Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 10}, + }, + }, + { + Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 11}, + }, + }, + { + Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 12}, + }, + }, + }, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.commitStoreDown && !isCommitStoreDownNow(testutils.Context(t), th.Lggr, th.Dest.CommitStore) { - _, err := th.Dest.CommitStore.Pause(th.Dest.User) - require.NoError(t, err) - th.CommitAndPollLogs(t) - } else if !tt.commitStoreDown && isCommitStoreDownNow(testutils.Context(t), th.Lggr, th.Dest.CommitStore) { - _, err := th.Dest.CommitStore.Unpause(th.Dest.User) - require.NoError(t, err) - th.CommitAndPollLogs(t) + ctx := testutils.Context(t) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &ExecutionReportingPlugin{} + p.inflightReports = newInflightExecReportsContainer(time.Minute) + p.inflightReports.reports = tc.inflightReports + p.lggr = logger.TestLogger(t) + + commitStoreAddr := utils.RandomAddress() + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("IsUnpausedAndARMHealthy", mock.Anything).Return(!tc.commitStoreDown, nil) + commitStore.On("Address").Return(commitStoreAddr).Maybe() + for _, root := range tc.blessedRoots { + commitStore.On("IsBlessed", mock.Anything, root).Return(true, nil) } - - gotObs, err := th.plugin.Observation(testutils.Context(t), ocrtypes.ReportTimestamp{}, ocrtypes.Query{}) - - if tt.expectedError { - require.Error(t, err) - } else { - require.NoError(t, err) + p.config.commitStore = commitStore + + offRampAddr := utils.RandomAddress() + offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) + offRamp.On("Address").Return(offRampAddr).Maybe() + offRamp.On("CurrentRateLimiterState", mock.Anything).Return(tc.rateLimiterState, nil).Maybe() + offRamp.On("GetSenderNonce", mock.Anything, mock.Anything).Return(tc.senderNonce, nil).Maybe() + p.config.offRamp = offRamp + + destEvents := ccipevents.NewMockClient(t) + destEvents.On("GetAcceptedCommitReportsGteTimestamp", ctx, commitStoreAddr, mock.Anything, 0). + Return(tc.unexpiredReports, nil).Maybe() + destEvents.On("LatestBlock", ctx).Return(int64(1234), nil).Maybe() + var executionEvents []ccipevents.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] + for _, seqNum := range tc.executedSeqNums { + executionEvents = append(executionEvents, ccipevents.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]{ + Data: evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged{SequenceNumber: seqNum}, + }) } + destEvents.On("GetExecutionStateChangesBetweenSeqNums", ctx, offRampAddr, mock.Anything, mock.Anything, 0). + Return(executionEvents, nil).Maybe() + p.config.destEvents = destEvents + + onRampAddr := utils.RandomAddress() + onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) + onRamp.On("Address").Return(onRampAddr).Maybe() + p.config.onRamp = onRamp + + sourceEvents := ccipevents.NewMockClient(t) + sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, mock.Anything, mock.Anything, 0). + Return(tc.sendRequests, nil).Maybe() + p.config.sourceEvents = sourceEvents + + cachedDestTokens := cache.NewMockAutoSync[cache.CachedTokens](t) + cachedDestTokens.On("Get", ctx).Return(cache.CachedTokens{ + SupportedTokens: map[common.Address]common.Address{}, + FeeTokens: []common.Address{}, + }, nil).Maybe() + p.cachedDestTokens = cachedDestTokens + + priceRegistryAddr := utils.RandomAddress() + priceRegistry := mock_contracts.NewPriceRegistryInterface(t) + priceRegistry.On("Address").Return(priceRegistryAddr).Maybe() + priceRegistry.On("GetTokenPrices", mock.Anything, mock.Anything). + Return([]price_registry.InternalTimestampedUint192Value{ + { + Value: big.NewInt(123), + Timestamp: uint64(time.Now().Unix()), + }, + }, nil).Maybe() + p.destPriceRegistry = priceRegistry + p.config.sourcePriceRegistry = priceRegistry + + sourceFeeTokens := cache.NewMockAutoSync[[]common.Address](t) + sourceFeeTokens.On("Get", ctx).Return([]common.Address{}, nil).Maybe() + p.cachedSourceFeeTokens = sourceFeeTokens - var decodedObservation *ExecutionObservation - if gotObs != nil { - decodedObservation = new(ExecutionObservation) - err = json.Unmarshal(gotObs, decodedObservation) - require.NoError(t, err) + p.snoozedRoots = cache.NewSnoozedRoots(time.Minute, time.Minute) + _, err := p.Observation(ctx, types.ReportTimestamp{}, types.Query{}) + if tc.expErr { + assert.Error(t, err) + return } - assert.Equal(t, tt.expected, decodedObservation) + assert.NoError(t, err) }) } } -func TestExecReport(t *testing.T) { - th := setupExecTestHarness(t) - th.plugin.F = 1 - mb := th.GenerateAndSendMessageBatch(t, 2, 10, 1) - - // commit root - encoded, err := abihelpers.EncodeCommitReport(commit_store.CommitStoreCommitReport{ - Interval: mb.Interval, - MerkleRoot: mb.Root, - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, - DestChainSelector: 0, - UsdPerUnitGas: big.NewInt(0), - }, - }) - require.NoError(t, err) - execReport := mb.ToExecutionReport() - - latestEpocAndRound, err := th.Dest.CommitStoreHelper.GetLatestPriceEpochAndRound(nil) - require.NoError(t, err) - _, err = th.Dest.CommitStoreHelper.Report(th.Dest.User, encoded, big.NewInt(int64(latestEpocAndRound+1))) - require.NoError(t, err) - // double commit to ensure enough confirmations - th.CommitAndPollLogs(t) - th.CommitAndPollLogs(t) +func TestExecutionReportingPlugin_Report(t *testing.T) { + testCases := []struct { + name string + f int + committedSeqNum uint64 + observations []ExecutionObservation - tests := []struct { - name string - commitStoreDown bool - observations [][]ObservedMessage - expectedShouldReport bool - expectedReport *evm_2_evm_offramp.InternalExecutionReport - expectedError bool + expectingSomeReport bool + expectedReport evm_2_evm_offramp.InternalExecutionReport + expectingSomeErr bool }{ { - "base", - false, - [][]ObservedMessage{ - {NewObservedMessage(1, [][]byte{{}}), NewObservedMessage(2, [][]byte{{}})}, - {NewObservedMessage(1, [][]byte{{}}), NewObservedMessage(2, [][]byte{{}})}, - }, - true, - &execReport, - false, - }, - { - "partial observation", - false, - [][]ObservedMessage{ - {NewObservedMessage(1, [][]byte{{}}), NewObservedMessage(2, [][]byte{{}})}, - {NewObservedMessage(1, [][]byte{{}})}, + name: "not enough observations to form consensus", + f: 5, + committedSeqNum: 5, + observations: []ExecutionObservation{ + {Messages: map[uint64]MsgData{3: {}, 4: {}}}, + {Messages: map[uint64]MsgData{3: {}, 4: {}}}, }, - true, - func() *evm_2_evm_offramp.InternalExecutionReport { - mb2 := mb - mb2.Messages = mb.Messages[:1] - mb2.Messages = mb.Messages[:1] - mb2.TokenData = mb.TokenData[:1] - mb2.Interval = commit_store.CommitStoreInterval{Min: 1, Max: 1} - mb2.Proof, err = mb2.Tree.Prove([]int{0}) - assert.NoError(t, err) - mb2.ProofBits = abihelpers.ProofFlagsToBits(mb2.Proof.SourceFlags) - report := mb2.ToExecutionReport() - return &report - }(), - false, + expectingSomeErr: false, + expectingSomeReport: false, }, { - "empty", - false, - [][]ObservedMessage{ - {NewObservedMessage(1, [][]byte{{}}), NewObservedMessage(2, [][]byte{{}})}, - {}, - }, - false, - nil, - false, - }, - { - "unknown seqNr", - false, - [][]ObservedMessage{ - {NewObservedMessage(1, [][]byte{{}}), NewObservedMessage(2, [][]byte{{}}), NewObservedMessage(3, [][]byte{{}})}, - {NewObservedMessage(1, [][]byte{{}}), NewObservedMessage(2, [][]byte{{}}), NewObservedMessage(3, [][]byte{{}})}, - }, - false, - nil, - true, + name: "zero observations", + f: 0, + committedSeqNum: 5, + observations: []ExecutionObservation{}, + expectingSomeErr: false, + expectingSomeReport: false, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var obs []ocrtypes.AttributedObservation - for _, o := range tt.observations { - encoded, err := NewExecutionObservation(o).Marshal() - require.NoError(t, err) - obs = append(obs, ocrtypes.AttributedObservation{Observation: encoded}) - } - gotShouldReport, gotReport, err := th.plugin.Report(testutils.Context(t), ocrtypes.ReportTimestamp{}, ocrtypes.Query{}, obs) - - if tt.expectedError { - require.Error(t, err) - } else { - require.NoError(t, err) + ctx := testutils.Context(t) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := ExecutionReportingPlugin{} + p.lggr = logger.TestLogger(t) + p.F = tc.f + + commitStoreAddr := utils.RandomAddress() + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(tc.committedSeqNum+1, nil).Maybe() + commitStore.On("Address").Return(commitStoreAddr, nil).Maybe() + p.config.commitStore = commitStore + + observations := make([]types.AttributedObservation, len(tc.observations)) + for i := range observations { + b, err := json.Marshal(tc.observations[i]) + assert.NoError(t, err) + observations[i] = types.AttributedObservation{Observation: b, Observer: commontypes.OracleID(i + 1)} } - require.Equal(t, tt.expectedShouldReport, gotShouldReport) - var encodedReport ocrtypes.Report - if tt.expectedReport != nil { - encodedReport, err = abihelpers.EncodeExecutionReport(*tt.expectedReport) - require.NoError(t, err) + _, _, err := p.Report(ctx, types.ReportTimestamp{}, types.Query{}, observations) + if tc.expectingSomeErr { + assert.Error(t, err) + return } - assert.Equal(t, encodedReport, gotReport) + assert.NoError(t, err) }) } + } -func TestExecShouldAcceptFinalizedReport(t *testing.T) { +func TestExecutionReportingPlugin_ShouldAcceptFinalizedReport(t *testing.T) { msg := evm_2_evm_offramp.InternalEVM2EVMMessage{ SequenceNumber: 12, FeeTokenAmount: big.NewInt(1e9), @@ -486,7 +423,7 @@ func TestExecShouldAcceptFinalizedReport(t *testing.T) { assert.Equal(t, false, should) } -func TestExecShouldTransmitAcceptedReport(t *testing.T) { +func TestExecutionReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { msg := evm_2_evm_offramp.InternalEVM2EVMMessage{ SequenceNumber: 12, FeeTokenAmount: big.NewInt(1e9), From 8f6e74c143e9128056d3f1dbd6b8e8f1f32e2f14 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Wed, 6 Sep 2023 21:48:24 +0300 Subject: [PATCH 11/25] fix linter error --- .../ocr2/plugins/ccip/execution_reporting_plugin_test.go | 1 + 1 file changed, 1 insertion(+) 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 994ad8f3f9..66cbee595f 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -128,6 +128,7 @@ func TestExecutionReportToEthTxMeta(t *testing.T) { t.Run("happy flow", func(t *testing.T) { executionReport := generateExecutionReport(t, 10, 3, 1000) encExecReport, err := abihelpers.EncodeExecutionReport(executionReport) + assert.NoError(t, err) txMeta, err := ExecutionReportToEthTxMeta(encExecReport) assert.NoError(t, err) assert.Len(t, txMeta.MessageIDs, len(executionReport.Messages)) From 42555e79590aad2dfe525c293008d111d622c842 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 7 Sep 2023 13:01:55 +0300 Subject: [PATCH 12/25] TestCommitReportingPlugin_calculateMinMaxSequenceNumbers --- .../ccip/commit_reporting_plugin_test.go | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) 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 898c29c21a..84f2fa928a 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -1122,6 +1122,109 @@ func Test_isStaleReport(t *testing.T) { }) } +func TestCommitReportingPlugin_calculateMinMaxSequenceNumbers(t *testing.T) { + testCases := []struct { + name string + commitStoreSeqNum uint64 + inflightSeqNum uint64 + msgSeqNums []uint64 + + expQueryMin uint64 // starting seq num that is used in the query to get messages + expMin uint64 + expMax uint64 + expErr bool + }{ + { + name: "happy flow inflight", + commitStoreSeqNum: 9, + inflightSeqNum: 10, + msgSeqNums: []uint64{11, 12, 13, 14}, + expQueryMin: 11, // inflight+1 + expMin: 11, + expMax: 14, + expErr: false, + }, + { + name: "happy flow no inflight", + commitStoreSeqNum: 9, + msgSeqNums: []uint64{11, 12, 13, 14}, + expQueryMin: 9, // from commit store + expMin: 11, + expMax: 14, + expErr: false, + }, + { + name: "gap in msg seq nums", + commitStoreSeqNum: 10, + inflightSeqNum: 9, + expQueryMin: 10, + msgSeqNums: []uint64{11, 12, 14}, + expErr: true, + }, + { + name: "no new messages", + commitStoreSeqNum: 9, + msgSeqNums: []uint64{}, + expQueryMin: 9, + expMin: 0, + expMax: 0, + expErr: false, + }, + { + name: "unordered seq nums", + commitStoreSeqNum: 9, + msgSeqNums: []uint64{11, 13, 14, 10}, + expQueryMin: 9, + expErr: true, + }, + } + + ctx := testutils.Context(t) + lggr := logger.TestLogger(t) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &CommitReportingPlugin{} + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(tc.commitStoreSeqNum, nil) + p.config.commitStore = commitStore + + p.inflightReports = newInflightCommitReportsContainer(time.Minute) + if tc.inflightSeqNum > 0 { + p.inflightReports.inFlight[[32]byte{}] = InflightCommitReport{ + report: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{ + Min: tc.inflightSeqNum, + Max: tc.inflightSeqNum, + }, + }, + } + } + + sourceEvents := ccipevents.NewMockClient(t) + var sendReqs []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + for _, seqNum := range tc.msgSeqNums { + sendReqs = append(sendReqs, ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: seqNum}, + }, + }) + } + sourceEvents.On("GetSendRequestsGteSeqNum", ctx, mock.Anything, tc.expQueryMin, false, 0).Return(sendReqs, nil) + p.config.sourceEvents = sourceEvents + + minSeqNum, maxSeqNum, err := p.calculateMinMaxSequenceNumbers(ctx, lggr) + if tc.expErr { + assert.Error(t, err) + return + } + + assert.Equal(t, tc.expMin, minSeqNum) + assert.Equal(t, tc.expMax, maxSeqNum) + }) + } +} + // leafHasher123 always returns '123' followed by zeroes in HashLeaf method. type leafHasher123 struct{} From 5336c638cf6cb195e8f811fdc3fd2534d33afa81 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 7 Sep 2023 13:51:27 +0300 Subject: [PATCH 13/25] test get latest price updates --- .../ccip/commit_reporting_plugin_test.go | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) 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 84f2fa928a..2c74e4c69d 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -1225,6 +1225,215 @@ func TestCommitReportingPlugin_calculateMinMaxSequenceNumbers(t *testing.T) { } } +func TestCommitReportingPlugin_getLatestGasPriceUpdate(t *testing.T) { + now := time.Now() + + testCases := []struct { + name string + checkInflight bool + inflightGasPriceUpdate *update + destGasPriceUpdates []update + expUpdate update + expErr bool + }{ + { + name: "only inflight gas price", + checkInflight: true, + inflightGasPriceUpdate: &update{timestamp: now, value: big.NewInt(1000)}, + expUpdate: update{timestamp: now, value: big.NewInt(1000)}, + expErr: false, + }, + { + name: "inflight price is nil", + checkInflight: true, + inflightGasPriceUpdate: &update{timestamp: now, value: nil}, + destGasPriceUpdates: []update{ + {timestamp: now.Add(time.Minute), value: big.NewInt(2000)}, + {timestamp: now.Add(2 * time.Minute), value: big.NewInt(3000)}, + }, + expUpdate: update{timestamp: now.Add(2 * time.Minute), value: big.NewInt(3000)}, + expErr: false, + }, + { + name: "inflight updates are skipped", + checkInflight: false, + inflightGasPriceUpdate: &update{timestamp: now, value: big.NewInt(1000)}, + destGasPriceUpdates: []update{ + {timestamp: now.Add(time.Minute), value: big.NewInt(2000)}, + {timestamp: now.Add(2 * time.Minute), value: big.NewInt(3000)}, + }, + expUpdate: update{timestamp: now.Add(2 * time.Minute), value: big.NewInt(3000)}, + expErr: false, + }, + } + + ctx := testutils.Context(t) + lggr := logger.TestLogger(t) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &CommitReportingPlugin{} + p.inflightReports = newInflightCommitReportsContainer(time.Minute) + p.lggr = lggr + destPriceRegistry := mock_contracts.NewPriceRegistryInterface(t) + destPriceRegistry.On("Address").Return(utils.RandomAddress()).Maybe() + p.destPriceRegistry = destPriceRegistry + + if tc.inflightGasPriceUpdate != nil { + p.inflightReports.inFlightPriceUpdates = append( + p.inflightReports.inFlightPriceUpdates, + InflightPriceUpdate{ + createdAt: tc.inflightGasPriceUpdate.timestamp, + priceUpdates: commit_store.InternalPriceUpdates{ + DestChainSelector: 1234, + UsdPerUnitGas: tc.inflightGasPriceUpdate.value, + }, + }, + ) + } + + if len(tc.destGasPriceUpdates) > 0 { + var events []ccipevents.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated] + for _, u := range tc.destGasPriceUpdates { + events = append(events, ccipevents.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated]{ + Data: price_registry.PriceRegistryUsdPerUnitGasUpdated{ + Value: u.value, + Timestamp: big.NewInt(u.timestamp.Unix()), + }, + }) + } + destEvents := ccipevents.NewMockClient(t) + destEvents.On("GetGasPriceUpdatesCreatedAfter", ctx, mock.Anything, uint64(0), mock.Anything, 0).Return(events, nil) + p.config.destEvents = destEvents + } + + priceUpdate, err := p.getLatestGasPriceUpdate(ctx, time.Now(), tc.checkInflight) + if tc.expErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.Equal(t, tc.expUpdate.timestamp.Truncate(time.Second), priceUpdate.timestamp.Truncate(time.Second)) + assert.Equal(t, tc.expUpdate.value.Uint64(), priceUpdate.value.Uint64()) + }) + } +} + +func TestCommitReportingPlugin_getLatestTokenPriceUpdates(t *testing.T) { + now := time.Now() + tk1 := utils.RandomAddress() + tk2 := utils.RandomAddress() + + testCases := []struct { + name string + priceRegistryUpdates []price_registry.PriceRegistryUsdPerTokenUpdated + checkInflight bool + inflightUpdates map[common.Address]update + expUpdates map[common.Address]update + expErr bool + }{ + { + name: "ignore inflight updates", + priceRegistryUpdates: []price_registry.PriceRegistryUsdPerTokenUpdated{ + { + Token: tk1, + Value: big.NewInt(1000), + Timestamp: big.NewInt(now.Add(1 * time.Minute).Unix()), + }, + { + Token: tk2, + Value: big.NewInt(2000), + Timestamp: big.NewInt(now.Add(2 * time.Minute).Unix()), + }, + }, + checkInflight: false, + expUpdates: map[common.Address]update{ + tk1: {timestamp: now.Add(1 * time.Minute), value: big.NewInt(1000)}, + tk2: {timestamp: now.Add(2 * time.Minute), value: big.NewInt(2000)}, + }, + expErr: false, + }, + { + name: "consider inflight updates", + priceRegistryUpdates: []price_registry.PriceRegistryUsdPerTokenUpdated{ + { + Token: tk1, + Value: big.NewInt(1000), + Timestamp: big.NewInt(now.Add(1 * time.Minute).Unix()), + }, + { + Token: tk2, + Value: big.NewInt(2000), + Timestamp: big.NewInt(now.Add(2 * time.Minute).Unix()), + }, + }, + checkInflight: true, + inflightUpdates: map[common.Address]update{ + tk1: {timestamp: now, value: big.NewInt(500)}, // inflight but older + tk2: {timestamp: now.Add(4 * time.Minute), value: big.NewInt(4000)}, + }, + expUpdates: map[common.Address]update{ + tk1: {timestamp: now.Add(1 * time.Minute), value: big.NewInt(1000)}, + tk2: {timestamp: now.Add(4 * time.Minute), value: big.NewInt(4000)}, + }, + expErr: false, + }, + } + + ctx := testutils.Context(t) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &CommitReportingPlugin{} + + priceRegAddr := utils.RandomAddress() + priceReg := mock_contracts.NewPriceRegistryInterface(t) + priceReg.On("Address").Return(priceRegAddr) + p.destPriceRegistry = priceReg + + destEvents := ccipevents.NewMockClient(t) + var events []ccipevents.Event[price_registry.PriceRegistryUsdPerTokenUpdated] + for _, up := range tc.priceRegistryUpdates { + events = append(events, ccipevents.Event[price_registry.PriceRegistryUsdPerTokenUpdated]{ + Data: price_registry.PriceRegistryUsdPerTokenUpdated{ + Token: up.Token, + Value: up.Value, + Timestamp: up.Timestamp, + }, + }) + } + destEvents.On("GetTokenPriceUpdatesCreatedAfter", ctx, priceRegAddr, mock.Anything, 0).Return(events, nil) + p.config.destEvents = destEvents + + p.inflightReports = newInflightCommitReportsContainer(time.Minute) + if len(tc.inflightUpdates) > 0 { + for tk, upd := range tc.inflightUpdates { + p.inflightReports.inFlightPriceUpdates = append(p.inflightReports.inFlightPriceUpdates, InflightPriceUpdate{ + createdAt: upd.timestamp, + priceUpdates: commit_store.InternalPriceUpdates{ + TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{ + {SourceToken: tk, UsdPerToken: upd.value}, + }, + }, + }) + } + } + + updates, err := p.getLatestTokenPriceUpdates(ctx, now, tc.checkInflight) + if tc.expErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, len(tc.expUpdates), len(updates)) + for k, v := range updates { + assert.Equal(t, tc.expUpdates[k].timestamp.Truncate(time.Second), v.timestamp.Truncate(time.Second)) + assert.Equal(t, tc.expUpdates[k].value.Uint64(), v.value.Uint64()) + } + }) + } + +} + // leafHasher123 always returns '123' followed by zeroes in HashLeaf method. type leafHasher123 struct{} From 06401ebf812ea2c38d1ea36a173ea38fa5cbeced Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 7 Sep 2023 14:01:27 +0300 Subject: [PATCH 14/25] rename commit tests --- .../ccip/commit_reporting_plugin_test.go | 314 +++++++++--------- 1 file changed, 157 insertions(+), 157 deletions(-) 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 2c74e4c69d..9fae829bda 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -428,29 +428,7 @@ func TestCommitReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { }) } -func TestCommitReportSize(t *testing.T) { - testParams := gopter.DefaultTestParameters() - testParams.MinSuccessfulTests = 100 - p := gopter.NewProperties(testParams) - p.Property("bounded commit report size", prop.ForAll(func(root []byte, min, max uint64) bool { - var root32 [32]byte - copy(root32[:], root) - rep, err := abihelpers.EncodeCommitReport(commit_store.CommitStoreCommitReport{ - MerkleRoot: root32, - Interval: commit_store.CommitStoreInterval{Min: min, Max: max}, - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, - DestChainSelector: 1337, - UsdPerUnitGas: big.NewInt(2000e9), // $2000 per eth * 1gwei = 2000e9 - }, - }) - require.NoError(t, err) - return len(rep) <= MaxCommitReportLength - }, gen.SliceOfN(32, gen.UInt8()), gen.UInt64(), gen.UInt64())) - p.TestingRun(t) -} - -func TestCalculatePriceUpdates(t *testing.T) { +func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) { t.Parallel() const defaultSourceChainSelector = 10 // we reuse this value across all test cases @@ -677,52 +655,7 @@ func TestCalculatePriceUpdates(t *testing.T) { } } -func TestCalculateIntervalConsensus(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - intervals []commit_store.CommitStoreInterval - rangeLimit uint64 - f int - wantMin uint64 - wantMax uint64 - wantErr bool - }{ - {"no obs", []commit_store.CommitStoreInterval{{Min: 0, Max: 0}}, 0, 0, 0, 0, false}, - {"basic", []commit_store.CommitStoreInterval{ - {Min: 9, Max: 14}, - {Min: 10, Max: 12}, - {Min: 10, Max: 14}, - }, 0, 1, 10, 14, false}, - {"not enough intervals", []commit_store.CommitStoreInterval{}, 0, 1, 0, 0, true}, - {"min > max", []commit_store.CommitStoreInterval{ - {Min: 9, Max: 4}, - {Min: 10, Max: 4}, - {Min: 10, Max: 6}, - }, 0, 1, 0, 0, true}, - { - "range limit", []commit_store.CommitStoreInterval{ - {Min: 10, Max: 100}, - {Min: 1, Max: 1000}, - }, 256, 1, 10, 265, false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := calculateIntervalConsensus(tt.intervals, tt.f, tt.rangeLimit) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - assert.Equal(t, tt.wantMin, got.Min) - assert.Equal(t, tt.wantMax, got.Max) - }) - } -} - -func TestGeneratePriceUpdates(t *testing.T) { +func TestCommitReportingPlugin_generatePriceUpdates(t *testing.T) { t.Parallel() val1e18 := func(val int64) *big.Int { return new(big.Int).Mul(big.NewInt(1e18), big.NewInt(val)) } @@ -933,93 +866,7 @@ func TestGeneratePriceUpdates(t *testing.T) { } } -func TestCalculateUsdPer1e18TokenAmount(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - price *big.Int - decimal uint8 - wantResult *big.Int - }{ - { - name: "18-decimal token, $6.5 per token", - price: big.NewInt(65e17), - decimal: 18, - wantResult: big.NewInt(65e17), - }, - { - name: "6-decimal token, $1 per token", - price: big.NewInt(1e18), - decimal: 6, - wantResult: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e12)), // 1e30 - }, - { - name: "0-decimal token, $1 per token", - price: big.NewInt(1e18), - decimal: 0, - wantResult: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e18)), // 1e36 - }, - { - name: "36-decimal token, $1 per token", - price: big.NewInt(1e18), - decimal: 36, - wantResult: big.NewInt(1), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := calculateUsdPer1e18TokenAmount(tt.price, tt.decimal) - assert.Equal(t, tt.wantResult, got) - }) - } -} - -func TestCommitReportToEthTxMeta(t *testing.T) { - mctx := hasher.NewKeccakCtx() - tree, err := merklemulti.NewTree(mctx, [][32]byte{mctx.Hash([]byte{0xaa})}) - require.NoError(t, err) - - tests := []struct { - name string - min, max uint64 - expectedRange []uint64 - }{ - { - "happy flow", - 1, 10, - []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - }, - { - "same sequence", - 1, 1, - []uint64{1}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - report := commit_store.CommitStoreCommitReport{ - PriceUpdates: commit_store.InternalPriceUpdates{ - TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, - DestChainSelector: uint64(1337), - UsdPerUnitGas: big.NewInt(2000e9), // $2000 per eth * 1gwei = 2000e9 - }, - MerkleRoot: tree.Root(), - Interval: commit_store.CommitStoreInterval{Min: tc.min, Max: tc.max}, - } - out, err := abihelpers.EncodeCommitReport(report) - require.NoError(t, err) - - txMeta, err := CommitReportToEthTxMeta(out) - require.NoError(t, err) - require.NotNil(t, txMeta) - require.EqualValues(t, tc.expectedRange, txMeta.SeqNumbers) - }) - } -} - -func TestNextMin(t *testing.T) { +func TestCommitReportingPlugin_nextMinSeqNum(t *testing.T) { lggr := logger.TestLogger(t) commitStore := mock_contracts.CommitStoreInterface{} cp := CommitReportingPlugin{config: CommitPluginConfig{commitStore: &commitStore}, inflightReports: newInflightCommitReportsContainer(time.Hour)} @@ -1075,7 +922,7 @@ func TestNextMin(t *testing.T) { } } -func Test_isStaleReport(t *testing.T) { +func TestCommitReportingPlugin_isStaleReport(t *testing.T) { ctx := context.Background() lggr := logger.TestLogger(t) merkleRoot1 := utils.Keccak256Fixed([]byte("some merkle root 1")) @@ -1434,6 +1281,159 @@ func TestCommitReportingPlugin_getLatestTokenPriceUpdates(t *testing.T) { } +func Test_commitReportSize(t *testing.T) { + testParams := gopter.DefaultTestParameters() + testParams.MinSuccessfulTests = 100 + p := gopter.NewProperties(testParams) + p.Property("bounded commit report size", prop.ForAll(func(root []byte, min, max uint64) bool { + var root32 [32]byte + copy(root32[:], root) + rep, err := abihelpers.EncodeCommitReport(commit_store.CommitStoreCommitReport{ + MerkleRoot: root32, + Interval: commit_store.CommitStoreInterval{Min: min, Max: max}, + PriceUpdates: commit_store.InternalPriceUpdates{ + TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, + DestChainSelector: 1337, + UsdPerUnitGas: big.NewInt(2000e9), // $2000 per eth * 1gwei = 2000e9 + }, + }) + require.NoError(t, err) + return len(rep) <= MaxCommitReportLength + }, gen.SliceOfN(32, gen.UInt8()), gen.UInt64(), gen.UInt64())) + p.TestingRun(t) +} + +func Test_calculateIntervalConsensus(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + intervals []commit_store.CommitStoreInterval + rangeLimit uint64 + f int + wantMin uint64 + wantMax uint64 + wantErr bool + }{ + {"no obs", []commit_store.CommitStoreInterval{{Min: 0, Max: 0}}, 0, 0, 0, 0, false}, + {"basic", []commit_store.CommitStoreInterval{ + {Min: 9, Max: 14}, + {Min: 10, Max: 12}, + {Min: 10, Max: 14}, + }, 0, 1, 10, 14, false}, + {"not enough intervals", []commit_store.CommitStoreInterval{}, 0, 1, 0, 0, true}, + {"min > max", []commit_store.CommitStoreInterval{ + {Min: 9, Max: 4}, + {Min: 10, Max: 4}, + {Min: 10, Max: 6}, + }, 0, 1, 0, 0, true}, + { + "range limit", []commit_store.CommitStoreInterval{ + {Min: 10, Max: 100}, + {Min: 1, Max: 1000}, + }, 256, 1, 10, 265, false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := calculateIntervalConsensus(tt.intervals, tt.f, tt.rangeLimit) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + assert.Equal(t, tt.wantMin, got.Min) + assert.Equal(t, tt.wantMax, got.Max) + }) + } +} + +func Test_calculateUsdPer1e18TokenAmount(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + price *big.Int + decimal uint8 + wantResult *big.Int + }{ + { + name: "18-decimal token, $6.5 per token", + price: big.NewInt(65e17), + decimal: 18, + wantResult: big.NewInt(65e17), + }, + { + name: "6-decimal token, $1 per token", + price: big.NewInt(1e18), + decimal: 6, + wantResult: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e12)), // 1e30 + }, + { + name: "0-decimal token, $1 per token", + price: big.NewInt(1e18), + decimal: 0, + wantResult: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e18)), // 1e36 + }, + { + name: "36-decimal token, $1 per token", + price: big.NewInt(1e18), + decimal: 36, + wantResult: big.NewInt(1), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := calculateUsdPer1e18TokenAmount(tt.price, tt.decimal) + assert.Equal(t, tt.wantResult, got) + }) + } +} + +func TestCommitReportToEthTxMeta(t *testing.T) { + mctx := hasher.NewKeccakCtx() + tree, err := merklemulti.NewTree(mctx, [][32]byte{mctx.Hash([]byte{0xaa})}) + require.NoError(t, err) + + tests := []struct { + name string + min, max uint64 + expectedRange []uint64 + }{ + { + "happy flow", + 1, 10, + []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }, + { + "same sequence", + 1, 1, + []uint64{1}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + report := commit_store.CommitStoreCommitReport{ + PriceUpdates: commit_store.InternalPriceUpdates{ + TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{}, + DestChainSelector: uint64(1337), + UsdPerUnitGas: big.NewInt(2000e9), // $2000 per eth * 1gwei = 2000e9 + }, + MerkleRoot: tree.Root(), + Interval: commit_store.CommitStoreInterval{Min: tc.min, Max: tc.max}, + } + out, err := abihelpers.EncodeCommitReport(report) + require.NoError(t, err) + + txMeta, err := CommitReportToEthTxMeta(out) + require.NoError(t, err) + require.NotNil(t, txMeta) + require.EqualValues(t, tc.expectedRange, txMeta.SeqNumbers) + }) + } +} + // leafHasher123 always returns '123' followed by zeroes in HashLeaf method. type leafHasher123 struct{} From 3cbcf565e17cc19935943f68a6592632f73b459f Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 7 Sep 2023 14:47:01 +0300 Subject: [PATCH 15/25] rename and reorder exec plugin tests --- .../ccip/execution_reporting_plugin_test.go | 603 +++++++++--------- 1 file changed, 302 insertions(+), 301 deletions(-) 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 66cbee595f..42861931f6 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -41,137 +41,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/store/models" ) -func TestExecutionReportingPlugin_buildReport(t *testing.T) { - ctx := testutils.Context(t) - - const numMessages = 100 - const tokensPerMessage = 20 - const bytesPerMessage = 1000 - - executionReport := generateExecutionReport(t, numMessages, tokensPerMessage, bytesPerMessage) - encodedReport, err := abihelpers.EncodeExecutionReport(executionReport) - assert.NoError(t, err) - // ensure "naive" full report would be bigger than limit - assert.Greater(t, len(encodedReport), MaxExecutionReportLength, "full execution report length") - - observations := make([]ObservedMessage, len(executionReport.Messages)) - for i, msg := range executionReport.Messages { - observations[i] = NewObservedMessage(msg.SequenceNumber, executionReport.OffchainTokenData[i]) - } - - // ensure that buildReport should cap the built report to fit in MaxExecutionReportLength - p := &ExecutionReportingPlugin{} - p.lggr = logger.TestLogger(t) - - commitStoreAddress := utils.RandomAddress() - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("Address").Return(commitStoreAddress) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything). - Return(executionReport.Messages[len(executionReport.Messages)-1].SequenceNumber+1, nil) - commitStore.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(math.MaxInt64), nil) - p.config.commitStore = commitStore - - destEvents := ccipevents.NewMockClient(t) - - destEvents.On("GetAcceptedCommitReportsGteSeqNum", ctx, commitStoreAddress, observations[0].SeqNr, 0). - Return([]ccipevents.Event[commit_store.CommitStoreReportAccepted]{ - { - Data: commit_store.CommitStoreReportAccepted{ - Report: commit_store.CommitStoreCommitReport{ - Interval: commit_store.CommitStoreInterval{ - Min: observations[0].SeqNr, - Max: observations[len(observations)-1].SeqNr, - }, - }, - }, - }, - }, nil) - p.config.destEvents = destEvents - - p.config.leafHasher = leafHasher123{} - - onRampAddr := utils.RandomAddress() - onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) - onRamp.On("Address").Return(onRampAddr) - p.config.onRamp = onRamp - - sendReqs := make([]ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], len(observations)) - for i := range observations { - sendReqs[i] = ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ - Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{Message: evm_2_evm_onramp.InternalEVM2EVMMessage{ - SourceChainSelector: math.MaxUint64, - SequenceNumber: uint64(i + 1), - FeeTokenAmount: big.NewInt(math.MaxInt64), - Sender: utils.RandomAddress(), - Nonce: math.MaxUint64, - GasLimit: big.NewInt(math.MaxInt64), - Strict: false, - Receiver: utils.RandomAddress(), - Data: bytes.Repeat([]byte{0}, bytesPerMessage), - TokenAmounts: nil, - FeeToken: utils.RandomAddress(), - MessageId: [32]byte{12}, - }}, - } - } - sourceEvents := ccipevents.NewMockClient(t) - sourceEvents.On("GetSendRequestsBetweenSeqNums", - ctx, onRampAddr, observations[0].SeqNr, observations[len(observations)-1].SeqNr, 0).Return(sendReqs, nil) - p.config.sourceEvents = sourceEvents - - execReport, err := p.buildReport(ctx, p.lggr, observations) - assert.NoError(t, err) - assert.LessOrEqual(t, len(execReport), MaxExecutionReportLength, "built execution report length") -} - -func TestExecutionReportToEthTxMeta(t *testing.T) { - t.Run("happy flow", func(t *testing.T) { - executionReport := generateExecutionReport(t, 10, 3, 1000) - encExecReport, err := abihelpers.EncodeExecutionReport(executionReport) - assert.NoError(t, err) - txMeta, err := ExecutionReportToEthTxMeta(encExecReport) - assert.NoError(t, err) - assert.Len(t, txMeta.MessageIDs, len(executionReport.Messages)) - }) - - t.Run("invalid report", func(t *testing.T) { - _, err := ExecutionReportToEthTxMeta([]byte("whatever")) - assert.Error(t, err) - }) -} - -func TestUpdateSourceToDestTokenMapping(t *testing.T) { - expectedNewBlockNumber := int64(10000) - logs := []logpoller.Log{{BlockNumber: expectedNewBlockNumber}} - mockDestLP := &lpMocks.LogPoller{} - - mockDestLP.On("LatestLogEventSigsAddrsWithConfs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(logs, nil) - mockDestLP.On("LatestBlock", mock.Anything).Return(expectedNewBlockNumber, nil) - - sourceToken, destToken := common.HexToAddress("111111"), common.HexToAddress("222222") - - mockOffRamp := &mock_contracts.EVM2EVMOffRampInterface{} - mockOffRamp.On("Address").Return(common.HexToAddress("0x01")) - mockOffRamp.On("GetSupportedTokens", mock.Anything).Return([]common.Address{sourceToken}, nil) - mockOffRamp.On("GetDestinationToken", mock.Anything, sourceToken).Return(destToken, nil) - - mockPriceRegistry := &mock_contracts.PriceRegistryInterface{} - mockPriceRegistry.On("Address").Return(common.HexToAddress("0x02")) - mockPriceRegistry.On("GetFeeTokens", mock.Anything).Return([]common.Address{}, nil) - - plugin := ExecutionReportingPlugin{ - config: ExecutionPluginConfig{ - destLP: mockDestLP, - offRamp: mockOffRamp, - }, - cachedDestTokens: cache.NewCachedSupportedTokens(mockDestLP, mockOffRamp, mockPriceRegistry, 0), - } - - value, err := plugin.cachedDestTokens.Get(context.Background()) - require.NoError(t, err) - require.Equal(t, destToken, value.SupportedTokens[sourceToken]) -} - func TestExecutionReportingPlugin_Observation(t *testing.T) { testCases := []struct { name string @@ -471,7 +340,90 @@ func TestExecutionReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { assert.Equal(t, false, should) } -func TestBuildBatch(t *testing.T) { +func TestExecutionReportingPlugin_buildReport(t *testing.T) { + ctx := testutils.Context(t) + + const numMessages = 100 + const tokensPerMessage = 20 + const bytesPerMessage = 1000 + + executionReport := generateExecutionReport(t, numMessages, tokensPerMessage, bytesPerMessage) + encodedReport, err := abihelpers.EncodeExecutionReport(executionReport) + assert.NoError(t, err) + // ensure "naive" full report would be bigger than limit + assert.Greater(t, len(encodedReport), MaxExecutionReportLength, "full execution report length") + + observations := make([]ObservedMessage, len(executionReport.Messages)) + for i, msg := range executionReport.Messages { + observations[i] = NewObservedMessage(msg.SequenceNumber, executionReport.OffchainTokenData[i]) + } + + // ensure that buildReport should cap the built report to fit in MaxExecutionReportLength + p := &ExecutionReportingPlugin{} + p.lggr = logger.TestLogger(t) + + commitStoreAddress := utils.RandomAddress() + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("Address").Return(commitStoreAddress) + commitStore.On("GetExpectedNextSequenceNumber", mock.Anything). + Return(executionReport.Messages[len(executionReport.Messages)-1].SequenceNumber+1, nil) + commitStore.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(math.MaxInt64), nil) + p.config.commitStore = commitStore + + destEvents := ccipevents.NewMockClient(t) + + destEvents.On("GetAcceptedCommitReportsGteSeqNum", ctx, commitStoreAddress, observations[0].SeqNr, 0). + Return([]ccipevents.Event[commit_store.CommitStoreReportAccepted]{ + { + Data: commit_store.CommitStoreReportAccepted{ + Report: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{ + Min: observations[0].SeqNr, + Max: observations[len(observations)-1].SeqNr, + }, + }, + }, + }, + }, nil) + p.config.destEvents = destEvents + + p.config.leafHasher = leafHasher123{} + + onRampAddr := utils.RandomAddress() + onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) + onRamp.On("Address").Return(onRampAddr) + p.config.onRamp = onRamp + + sendReqs := make([]ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], len(observations)) + for i := range observations { + sendReqs[i] = ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{Message: evm_2_evm_onramp.InternalEVM2EVMMessage{ + SourceChainSelector: math.MaxUint64, + SequenceNumber: uint64(i + 1), + FeeTokenAmount: big.NewInt(math.MaxInt64), + Sender: utils.RandomAddress(), + Nonce: math.MaxUint64, + GasLimit: big.NewInt(math.MaxInt64), + Strict: false, + Receiver: utils.RandomAddress(), + Data: bytes.Repeat([]byte{0}, bytesPerMessage), + TokenAmounts: nil, + FeeToken: utils.RandomAddress(), + MessageId: [32]byte{12}, + }}, + } + } + sourceEvents := ccipevents.NewMockClient(t) + sourceEvents.On("GetSendRequestsBetweenSeqNums", + ctx, onRampAddr, observations[0].SeqNr, observations[len(observations)-1].SeqNr, 0).Return(sendReqs, nil) + p.config.sourceEvents = sourceEvents + + execReport, err := p.buildReport(ctx, p.lggr, observations) + assert.NoError(t, err) + assert.LessOrEqual(t, len(execReport), MaxExecutionReportLength, "built execution report length") +} + +func TestExecutionReportingPlugin_buildBatch(t *testing.T) { c, _ := testhelpers.SetupChain(t) mockOffRamp := mock_contracts.EVM2EVMOffRampInterface{} // We do this just to have the parsing available. @@ -724,28 +676,228 @@ func TestBuildBatch(t *testing.T) { } } -func Test_calculateObservedMessagesConsensus(t *testing.T) { - type args struct { - observations []ExecutionObservation - f int - } - tests := []struct { - name string - args args - want []ObservedMessage +func TestExecutionReportingPlugin_isRateLimitEnoughForTokenPool(t *testing.T) { + testCases := []struct { + name string + destTokenPoolRateLimits map[common.Address]*big.Int + tokenAmounts []evm_2_evm_offramp.ClientEVMTokenAmount + inflightTokenAmounts map[common.Address]*big.Int + srcToDestToken map[common.Address]common.Address + exp bool }{ { - name: "no observations", - args: args{ - observations: nil, - f: 0, + name: "base", + destTokenPoolRateLimits: map[common.Address]*big.Int{ + common.HexToAddress("10"): big.NewInt(100), + common.HexToAddress("20"): big.NewInt(50), }, - want: []ObservedMessage{}, + tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ + {Token: common.HexToAddress("1"), Amount: big.NewInt(50)}, + {Token: common.HexToAddress("2"), Amount: big.NewInt(20)}, + }, + srcToDestToken: map[common.Address]common.Address{ + common.HexToAddress("1"): common.HexToAddress("10"), + common.HexToAddress("2"): common.HexToAddress("20"), + }, + inflightTokenAmounts: map[common.Address]*big.Int{ + common.HexToAddress("1"): big.NewInt(20), + common.HexToAddress("2"): big.NewInt(30), + }, + exp: true, }, { - name: "common path", - args: args{ - observations: []ExecutionObservation{ + name: "rate limit hit", + destTokenPoolRateLimits: map[common.Address]*big.Int{ + common.HexToAddress("10"): big.NewInt(100), + common.HexToAddress("20"): big.NewInt(50), + }, + srcToDestToken: map[common.Address]common.Address{ + common.HexToAddress("1"): common.HexToAddress("10"), + common.HexToAddress("2"): common.HexToAddress("20"), + }, + tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ + {Token: common.HexToAddress("1"), Amount: big.NewInt(50)}, + {Token: common.HexToAddress("2"), Amount: big.NewInt(51)}, + }, + exp: true, + }, + { + name: "rate limit hit, inflight included", + destTokenPoolRateLimits: map[common.Address]*big.Int{ + common.HexToAddress("10"): big.NewInt(100), + common.HexToAddress("20"): big.NewInt(50), + }, + srcToDestToken: map[common.Address]common.Address{ + common.HexToAddress("1"): common.HexToAddress("10"), + common.HexToAddress("2"): common.HexToAddress("20"), + }, + tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ + {Token: common.HexToAddress("1"), Amount: big.NewInt(50)}, + {Token: common.HexToAddress("2"), Amount: big.NewInt(20)}, + }, + inflightTokenAmounts: map[common.Address]*big.Int{ + common.HexToAddress("1"): big.NewInt(51), + common.HexToAddress("2"): big.NewInt(30), + }, + exp: true, + }, + { + destTokenPoolRateLimits: map[common.Address]*big.Int{}, + tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ + {Token: common.HexToAddress("1"), Amount: big.NewInt(50)}, + {Token: common.HexToAddress("2"), Amount: big.NewInt(20)}, + }, + srcToDestToken: map[common.Address]common.Address{ + common.HexToAddress("1"): common.HexToAddress("10"), + common.HexToAddress("2"): common.HexToAddress("20"), + }, + inflightTokenAmounts: map[common.Address]*big.Int{ + common.HexToAddress("1"): big.NewInt(20), + common.HexToAddress("2"): big.NewInt(30), + }, + name: "rate limit not applied to token", + exp: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &ExecutionReportingPlugin{lggr: logger.TestLogger(t)} + p.isRateLimitEnoughForTokenPool(tc.destTokenPoolRateLimits, tc.tokenAmounts, tc.inflightTokenAmounts, tc.srcToDestToken) + }) + } +} + +func TestExecutionReportingPluginFactory_UpdateLogPollerFilters(t *testing.T) { + const numFilters = 10 + filters := make([]logpoller.Filter, numFilters) + for i := range filters { + filters[i] = logpoller.Filter{ + Name: fmt.Sprintf("filter-%d", i), + EventSigs: []common.Hash{common.HexToHash(fmt.Sprintf("%d", i))}, + Addresses: []common.Address{common.HexToAddress(fmt.Sprintf("%d", i))}, + Retention: time.Duration(i) * time.Second, + } + } + + destLP := lpMocks.NewLogPoller(t) + sourceLP := lpMocks.NewLogPoller(t) + + onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) + onRamp.On("Address").Return(utils.RandomAddress(), nil) + + sourcePriceRegistry := mock_contracts.NewPriceRegistryInterface(t) + sourcePriceRegistry.On("Address").Return(utils.RandomAddress(), nil) + + commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore.On("Address").Return(utils.RandomAddress(), nil) + + offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) + offRamp.On("Address").Return(utils.RandomAddress(), nil) + + destPriceRegistryAddr := utils.RandomAddress() + + rf := &ExecutionReportingPluginFactory{ + filtersMu: &sync.Mutex{}, + sourceChainFilters: filters[:5], + destChainFilters: filters[5:10], + config: ExecutionPluginConfig{ + destLP: destLP, + sourceLP: sourceLP, + onRamp: onRamp, + commitStore: commitStore, + offRamp: offRamp, + sourcePriceRegistry: sourcePriceRegistry, + }, + } + + for _, f := range getExecutionPluginSourceLpChainFilters(onRamp.Address(), sourcePriceRegistry.Address()) { + sourceLP.On("RegisterFilter", f).Return(nil) + } + for _, f := range getExecutionPluginDestLpChainFilters(commitStore.Address(), offRamp.Address(), destPriceRegistryAddr) { + destLP.On("RegisterFilter", f).Return(nil) + } + for _, f := range rf.sourceChainFilters[1:] { // zero address is skipped + sourceLP.On("UnregisterFilter", f.Name, mock.Anything).Return(nil) + } + for _, f := range rf.destChainFilters { + destLP.On("UnregisterFilter", f.Name, mock.Anything).Return(nil) + } + + err := rf.UpdateLogPollerFilters(destPriceRegistryAddr) + assert.NoError(t, err) +} + +func TestExecutionReportToEthTxMeta(t *testing.T) { + t.Run("happy flow", func(t *testing.T) { + executionReport := generateExecutionReport(t, 10, 3, 1000) + encExecReport, err := abihelpers.EncodeExecutionReport(executionReport) + assert.NoError(t, err) + txMeta, err := ExecutionReportToEthTxMeta(encExecReport) + assert.NoError(t, err) + assert.Len(t, txMeta.MessageIDs, len(executionReport.Messages)) + }) + + t.Run("invalid report", func(t *testing.T) { + _, err := ExecutionReportToEthTxMeta([]byte("whatever")) + assert.Error(t, err) + }) +} + +func TestUpdateSourceToDestTokenMapping(t *testing.T) { + expectedNewBlockNumber := int64(10000) + logs := []logpoller.Log{{BlockNumber: expectedNewBlockNumber}} + mockDestLP := &lpMocks.LogPoller{} + + mockDestLP.On("LatestLogEventSigsAddrsWithConfs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(logs, nil) + mockDestLP.On("LatestBlock", mock.Anything).Return(expectedNewBlockNumber, nil) + + sourceToken, destToken := common.HexToAddress("111111"), common.HexToAddress("222222") + + mockOffRamp := &mock_contracts.EVM2EVMOffRampInterface{} + mockOffRamp.On("Address").Return(common.HexToAddress("0x01")) + mockOffRamp.On("GetSupportedTokens", mock.Anything).Return([]common.Address{sourceToken}, nil) + mockOffRamp.On("GetDestinationToken", mock.Anything, sourceToken).Return(destToken, nil) + + mockPriceRegistry := &mock_contracts.PriceRegistryInterface{} + mockPriceRegistry.On("Address").Return(common.HexToAddress("0x02")) + mockPriceRegistry.On("GetFeeTokens", mock.Anything).Return([]common.Address{}, nil) + + plugin := ExecutionReportingPlugin{ + config: ExecutionPluginConfig{ + destLP: mockDestLP, + offRamp: mockOffRamp, + }, + cachedDestTokens: cache.NewCachedSupportedTokens(mockDestLP, mockOffRamp, mockPriceRegistry, 0), + } + + value, err := plugin.cachedDestTokens.Get(context.Background()) + require.NoError(t, err) + require.Equal(t, destToken, value.SupportedTokens[sourceToken]) +} + +func Test_calculateObservedMessagesConsensus(t *testing.T) { + type args struct { + observations []ExecutionObservation + f int + } + tests := []struct { + name string + args args + want []ObservedMessage + }{ + { + name: "no observations", + args: args{ + observations: nil, + f: 0, + }, + want: []ObservedMessage{}, + }, + { + name: "common path", + args: args{ + observations: []ExecutionObservation{ { Messages: map[uint64]MsgData{ 1: {TokenData: [][]byte{{0x1}, {0x1}, {0x1}}}, @@ -982,158 +1134,6 @@ func Test_calculateMessageMaxGas(t *testing.T) { } } -func TestExecutionReportingPlugin_isRateLimitEnoughForTokenPool(t *testing.T) { - testCases := []struct { - name string - destTokenPoolRateLimits map[common.Address]*big.Int - tokenAmounts []evm_2_evm_offramp.ClientEVMTokenAmount - inflightTokenAmounts map[common.Address]*big.Int - srcToDestToken map[common.Address]common.Address - exp bool - }{ - { - name: "base", - destTokenPoolRateLimits: map[common.Address]*big.Int{ - common.HexToAddress("10"): big.NewInt(100), - common.HexToAddress("20"): big.NewInt(50), - }, - tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ - {Token: common.HexToAddress("1"), Amount: big.NewInt(50)}, - {Token: common.HexToAddress("2"), Amount: big.NewInt(20)}, - }, - srcToDestToken: map[common.Address]common.Address{ - common.HexToAddress("1"): common.HexToAddress("10"), - common.HexToAddress("2"): common.HexToAddress("20"), - }, - inflightTokenAmounts: map[common.Address]*big.Int{ - common.HexToAddress("1"): big.NewInt(20), - common.HexToAddress("2"): big.NewInt(30), - }, - exp: true, - }, - { - name: "rate limit hit", - destTokenPoolRateLimits: map[common.Address]*big.Int{ - common.HexToAddress("10"): big.NewInt(100), - common.HexToAddress("20"): big.NewInt(50), - }, - srcToDestToken: map[common.Address]common.Address{ - common.HexToAddress("1"): common.HexToAddress("10"), - common.HexToAddress("2"): common.HexToAddress("20"), - }, - tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ - {Token: common.HexToAddress("1"), Amount: big.NewInt(50)}, - {Token: common.HexToAddress("2"), Amount: big.NewInt(51)}, - }, - exp: true, - }, - { - name: "rate limit hit, inflight included", - destTokenPoolRateLimits: map[common.Address]*big.Int{ - common.HexToAddress("10"): big.NewInt(100), - common.HexToAddress("20"): big.NewInt(50), - }, - srcToDestToken: map[common.Address]common.Address{ - common.HexToAddress("1"): common.HexToAddress("10"), - common.HexToAddress("2"): common.HexToAddress("20"), - }, - tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ - {Token: common.HexToAddress("1"), Amount: big.NewInt(50)}, - {Token: common.HexToAddress("2"), Amount: big.NewInt(20)}, - }, - inflightTokenAmounts: map[common.Address]*big.Int{ - common.HexToAddress("1"): big.NewInt(51), - common.HexToAddress("2"): big.NewInt(30), - }, - exp: true, - }, - { - destTokenPoolRateLimits: map[common.Address]*big.Int{}, - tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ - {Token: common.HexToAddress("1"), Amount: big.NewInt(50)}, - {Token: common.HexToAddress("2"), Amount: big.NewInt(20)}, - }, - srcToDestToken: map[common.Address]common.Address{ - common.HexToAddress("1"): common.HexToAddress("10"), - common.HexToAddress("2"): common.HexToAddress("20"), - }, - inflightTokenAmounts: map[common.Address]*big.Int{ - common.HexToAddress("1"): big.NewInt(20), - common.HexToAddress("2"): big.NewInt(30), - }, - name: "rate limit not applied to token", - exp: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - p := &ExecutionReportingPlugin{lggr: logger.TestLogger(t)} - p.isRateLimitEnoughForTokenPool(tc.destTokenPoolRateLimits, tc.tokenAmounts, tc.inflightTokenAmounts, tc.srcToDestToken) - }) - } -} - -func TestExecutionReportingPluginFactory_UpdateLogPollerFilters(t *testing.T) { - const numFilters = 10 - filters := make([]logpoller.Filter, numFilters) - for i := range filters { - filters[i] = logpoller.Filter{ - Name: fmt.Sprintf("filter-%d", i), - EventSigs: []common.Hash{common.HexToHash(fmt.Sprintf("%d", i))}, - Addresses: []common.Address{common.HexToAddress(fmt.Sprintf("%d", i))}, - Retention: time.Duration(i) * time.Second, - } - } - - destLP := lpMocks.NewLogPoller(t) - sourceLP := lpMocks.NewLogPoller(t) - - onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) - onRamp.On("Address").Return(utils.RandomAddress(), nil) - - sourcePriceRegistry := mock_contracts.NewPriceRegistryInterface(t) - sourcePriceRegistry.On("Address").Return(utils.RandomAddress(), nil) - - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("Address").Return(utils.RandomAddress(), nil) - - offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) - offRamp.On("Address").Return(utils.RandomAddress(), nil) - - destPriceRegistryAddr := utils.RandomAddress() - - rf := &ExecutionReportingPluginFactory{ - filtersMu: &sync.Mutex{}, - sourceChainFilters: filters[:5], - destChainFilters: filters[5:10], - config: ExecutionPluginConfig{ - destLP: destLP, - sourceLP: sourceLP, - onRamp: onRamp, - commitStore: commitStore, - offRamp: offRamp, - sourcePriceRegistry: sourcePriceRegistry, - }, - } - - for _, f := range getExecutionPluginSourceLpChainFilters(onRamp.Address(), sourcePriceRegistry.Address()) { - sourceLP.On("RegisterFilter", f).Return(nil) - } - for _, f := range getExecutionPluginDestLpChainFilters(commitStore.Address(), offRamp.Address(), destPriceRegistryAddr) { - destLP.On("RegisterFilter", f).Return(nil) - } - for _, f := range rf.sourceChainFilters[1:] { // zero address is skipped - sourceLP.On("UnregisterFilter", f.Name, mock.Anything).Return(nil) - } - for _, f := range rf.destChainFilters { - destLP.On("UnregisterFilter", f.Name, mock.Anything).Return(nil) - } - - err := rf.UpdateLogPollerFilters(destPriceRegistryAddr) - assert.NoError(t, err) -} - func Test_inflightAggregates(t *testing.T) { const n = 10 addrs := make([]common.Address, n) @@ -1257,6 +1257,7 @@ func Test_inflightAggregates(t *testing.T) { } } +// generateExecutionReport generates an execution report that can be used in tests func generateExecutionReport(t *testing.T, numMsgs, tokensPerMsg, bytesPerMsg int) evm_2_evm_offramp.InternalExecutionReport { messages := make([]evm_2_evm_offramp.InternalEVM2EVMMessage, numMsgs) From e3990d960332033eee1466603c0d6c05c7a4ae40 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 7 Sep 2023 16:29:45 +0300 Subject: [PATCH 16/25] test remaining exec plugin methods --- .../ccip/execution_reporting_plugin.go | 30 +- .../ccip/execution_reporting_plugin_test.go | 479 ++++++++++++++++++ 2 files changed, 497 insertions(+), 12 deletions(-) diff --git a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go index eab1b417a0..6b072c573d 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go @@ -67,17 +67,18 @@ type ExecutionPluginConfig struct { } type ExecutionReportingPlugin struct { - config ExecutionPluginConfig - F int - lggr logger.Logger - inflightReports *inflightExecReportsContainer - snoozedRoots cache.SnoozedRoots - destPriceRegistry price_registry.PriceRegistryInterface - destWrappedNative common.Address - onchainConfig ccipconfig.ExecOnchainConfig - offchainConfig ccipconfig.ExecOffchainConfig - cachedSourceFeeTokens cache.AutoSync[[]common.Address] - cachedDestTokens cache.AutoSync[cache.CachedTokens] + config ExecutionPluginConfig + F int + lggr logger.Logger + inflightReports *inflightExecReportsContainer + snoozedRoots cache.SnoozedRoots + destPriceRegistry price_registry.PriceRegistryInterface + destWrappedNative common.Address + onchainConfig ccipconfig.ExecOnchainConfig + offchainConfig ccipconfig.ExecOffchainConfig + cachedSourceFeeTokens cache.AutoSync[[]common.Address] + cachedDestTokens cache.AutoSync[cache.CachedTokens] + customTokenPoolFactory func(ctx context.Context, bind bind.ContractBackend, poolAddress common.Address) (custom_token_pool.CustomTokenPoolInterface, error) } type ExecutionReportingPluginFactory struct { @@ -140,6 +141,9 @@ func (rf *ExecutionReportingPluginFactory) NewReportingPlugin(config types.Repor offchainConfig: offchainConfig, cachedDestTokens: cachedDestTokens, cachedSourceFeeTokens: cachedSourceFeeTokens, + customTokenPoolFactory: func(ctx context.Context, contractBackend bind.ContractBackend, poolAddress common.Address) (custom_token_pool.CustomTokenPoolInterface, error) { + return custom_token_pool.NewCustomTokenPool(poolAddress, contractBackend) + }, }, types.ReportingPluginInfo{ Name: "CCIPExecution", // Setting this to false saves on calldata since OffRamp doesn't require agreement between NOPs @@ -364,6 +368,8 @@ func (r *ExecutionReportingPlugin) getExecutableObservations(ctx context.Context return []ObservedMessage{}, nil } +// destPoolRateLimits returns a map that consists of the rate limits of each destination tokens of the provided reports. +// If a token is missing from the returned map it either means that token was not found or token pool is disabled for this token. func (r *ExecutionReportingPlugin) destPoolRateLimits(ctx context.Context, commitReports []commitReportWithSendRequests, sourceToDestToken map[common.Address]common.Address) (map[common.Address]*big.Int, error) { dstTokens := make(map[common.Address]struct{}) // todo: replace with a set or uniqueSlice data structure for _, msg := range commitReports { @@ -386,7 +392,7 @@ func (r *ExecutionReportingPlugin) destPoolRateLimits(ctx context.Context, commi return nil, fmt.Errorf("get pool by dest token (%s): %w", dstToken, err) } - tokenPool, err := custom_token_pool.NewCustomTokenPool(poolAddress, r.config.destClient) + tokenPool, err := r.customTokenPoolFactory(ctx, r.config.destClient, poolAddress) if err != nil { return nil, fmt.Errorf("new custom dest token pool %s: %w", poolAddress, 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 42861931f6..8c79bc9829 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -14,7 +14,9 @@ import ( "time" "github.com/cometbft/cometbft/libs/rand" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -22,8 +24,12 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" lpMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/custom_token_pool" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" @@ -768,6 +774,315 @@ func TestExecutionReportingPlugin_isRateLimitEnoughForTokenPool(t *testing.T) { } } +func TestExecutionReportingPlugin_destPoolRateLimits(t *testing.T) { + tk1 := utils.RandomAddress() + tk1dest := utils.RandomAddress() + tk1pool := utils.RandomAddress() + + tk2 := utils.RandomAddress() + tk2dest := utils.RandomAddress() + tk2pool := utils.RandomAddress() + + testCases := []struct { + name string + tokenAmounts []evm_2_evm_offramp.ClientEVMTokenAmount + sourceToDestToken map[common.Address]common.Address + destPools map[common.Address]common.Address + poolRateLimits map[common.Address]custom_token_pool.RateLimiterTokenBucket + + expRateLimits map[common.Address]*big.Int + expErr bool + }{ + { + name: "happy flow", + tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ + {Token: tk1}, + {Token: tk2}, + {Token: tk1}, + {Token: tk1}, + }, + sourceToDestToken: map[common.Address]common.Address{ + tk1: tk1dest, + tk2: tk2dest, + }, + destPools: map[common.Address]common.Address{ + tk1dest: tk1pool, + tk2dest: tk2pool, + }, + poolRateLimits: map[common.Address]custom_token_pool.RateLimiterTokenBucket{ + tk1pool: {Tokens: big.NewInt(1000), IsEnabled: true}, + tk2pool: {Tokens: big.NewInt(2000), IsEnabled: true}, + }, + expRateLimits: map[common.Address]*big.Int{ + tk1dest: big.NewInt(1000), + tk2dest: big.NewInt(2000), + }, + expErr: false, + }, + { + name: "token missing from source to dest mapping", + tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ + {Token: tk1}, + {Token: tk2}, // <-- missing form sourceToDestToken + }, + sourceToDestToken: map[common.Address]common.Address{ + tk1: tk1dest, + }, + destPools: map[common.Address]common.Address{ + tk1dest: tk1pool, + }, + poolRateLimits: map[common.Address]custom_token_pool.RateLimiterTokenBucket{ + tk1pool: {Tokens: big.NewInt(1000), IsEnabled: true}, + }, + expRateLimits: map[common.Address]*big.Int{ + tk1dest: big.NewInt(1000), + }, + expErr: false, + }, + { + name: "pool is disabled", + tokenAmounts: []evm_2_evm_offramp.ClientEVMTokenAmount{ + {Token: tk1}, + {Token: tk2}, + }, + sourceToDestToken: map[common.Address]common.Address{ + tk1: tk1dest, + tk2: tk2dest, + }, + destPools: map[common.Address]common.Address{ + tk1dest: tk1pool, + tk2dest: tk2pool, + }, + poolRateLimits: map[common.Address]custom_token_pool.RateLimiterTokenBucket{ + tk1pool: {Tokens: big.NewInt(1000), IsEnabled: true}, + tk2pool: {Tokens: big.NewInt(2000), IsEnabled: false}, // <--- pool disabled + }, + expRateLimits: map[common.Address]*big.Int{ + tk1dest: big.NewInt(1000), + }, + expErr: false, + }, + } + + ctx := testutils.Context(t) + lggr := logger.TestLogger(t) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &ExecutionReportingPlugin{} + p.lggr = lggr + + offRampAddr := utils.RandomAddress() + offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) + for destTk, pool := range tc.destPools { + offRamp.On("GetPoolByDestToken", mock.Anything, destTk).Return(pool, nil) + } + offRamp.On("Address").Return(offRampAddr) + p.config.offRamp = offRamp + + p.customTokenPoolFactory = func(ctx context.Context, _ bind.ContractBackend, poolAddress common.Address) (custom_token_pool.CustomTokenPoolInterface, error) { + mp := &mockPool{} + mp.On("CurrentOffRampRateLimiterState", mock.Anything, offRampAddr).Return(tc.poolRateLimits[poolAddress], nil) + return mp, nil + } + + rateLimits, err := p.destPoolRateLimits(ctx, []commitReportWithSendRequests{ + { + sendRequestsWithMeta: []evm2EVMOnRampCCIPSendRequestedWithMeta{ + { + InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{ + TokenAmounts: tc.tokenAmounts, + }, + }, + }, + }, + }, tc.sourceToDestToken) + if tc.expErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tc.expRateLimits, rateLimits) + }) + } +} + +func TestExecutionReportingPlugin_estimateDestinationGasPrice(t *testing.T) { + testCases := []struct { + name string + evmFee gas.EvmFee + evmFeeErr error + + expRes *big.Int + expErr bool + }{ + { + name: "dynamic fee cap has precedence over legacy", + evmFee: gas.EvmFee{ + Legacy: assets.NewWei(big.NewInt(1000)), + DynamicFeeCap: assets.NewWei(big.NewInt(2000)), + }, + expRes: big.NewInt(2000), + }, + { + name: "legacy is used if dynamic fee cap is not provided", + evmFee: gas.EvmFee{ + Legacy: assets.NewWei(big.NewInt(1000)), + }, + expRes: big.NewInt(1000), + }, + { + name: "stop on error", + evmFeeErr: errors.New("some error"), + expErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &ExecutionReportingPlugin{} + mockEstimator := mocks.NewEvmFeeEstimator(t) + mockEstimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.evmFee, uint32(0), tc.evmFeeErr) + p.config.destGasEstimator = mockEstimator + + res, err := p.estimateDestinationGasPrice(testutils.Context(t)) + if tc.expErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tc.expRes, res) + }) + } +} + +func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { + testCases := []struct { + name string + reports []commit_store.CommitStoreCommitReport + expQueryMin uint64 // expected min/max used in the query to get ccipevents + expQueryMax uint64 + onchainEvents []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + destLatestBlock int64 + destExecutedSeqNums []uint64 + + expReports []commitReportWithSendRequests + expErr bool + }{ + { + name: "no reports", + reports: nil, + expReports: nil, + expErr: false, + }, + { + name: "two reports happy flow", + reports: []commit_store.CommitStoreCommitReport{ + { + Interval: commit_store.CommitStoreInterval{Min: 1, Max: 2}, + MerkleRoot: [32]byte{100}, + }, + { + Interval: commit_store.CommitStoreInterval{Min: 3, Max: 3}, + MerkleRoot: [32]byte{200}, + }, + }, + expQueryMin: 1, + expQueryMax: 3, + onchainEvents: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + {Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 1}, + }}, + {Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 2}, + }}, + {Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ + Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 3}, + }}, + }, + destLatestBlock: 10_000, + destExecutedSeqNums: []uint64{1}, + expReports: []commitReportWithSendRequests{ + { + commitReport: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{Min: 1, Max: 2}, + MerkleRoot: [32]byte{100}, + }, + sendRequestsWithMeta: []evm2EVMOnRampCCIPSendRequestedWithMeta{ + { + InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{SequenceNumber: 1}, + executed: true, + finalized: true, + }, + { + InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{SequenceNumber: 2}, + executed: false, + finalized: false, + }, + }, + }, + { + commitReport: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{Min: 3, Max: 3}, + MerkleRoot: [32]byte{200}, + }, + sendRequestsWithMeta: []evm2EVMOnRampCCIPSendRequestedWithMeta{ + { + InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{SequenceNumber: 3}, + executed: false, + finalized: false, + }, + }, + }, + }, + expErr: false, + }, + } + + ctx := testutils.Context(t) + lggr := logger.TestLogger(t) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &ExecutionReportingPlugin{} + p.lggr = lggr + + onRampAddr := utils.RandomAddress() + onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) + onRamp.On("Address").Return(onRampAddr).Maybe() + p.config.onRamp = onRamp + + offRampAddr := utils.RandomAddress() + offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) + offRamp.On("Address").Return(offRampAddr).Maybe() + p.config.offRamp = offRamp + + sourceEvents := ccipevents.NewMockClient(t) + sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, tc.expQueryMin, tc.expQueryMax, 0). + Return(tc.onchainEvents, nil).Maybe() + p.config.sourceEvents = sourceEvents + + destEvents := ccipevents.NewMockClient(t) + destEvents.On("LatestBlock", ctx).Return(tc.destLatestBlock, nil).Maybe() + var executedEvents []ccipevents.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] + for _, executedSeqNum := range tc.destExecutedSeqNums { + executedEvents = append(executedEvents, ccipevents.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]{ + Data: evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged{SequenceNumber: executedSeqNum}, + BlockMeta: ccipevents.BlockMeta{BlockNumber: tc.destLatestBlock - 10}, + }) + } + destEvents.On("GetExecutionStateChangesBetweenSeqNums", ctx, offRampAddr, tc.expQueryMin, tc.expQueryMax, 0).Return(executedEvents, nil).Maybe() + p.config.destEvents = destEvents + + populatedReports, err := p.getReportsWithSendRequests(ctx, tc.reports) + if tc.expErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tc.expReports, populatedReports) + }) + } +} + func TestExecutionReportingPluginFactory_UpdateLogPollerFilters(t *testing.T) { const numFilters = 10 filters := make([]logpoller.Filter, numFilters) @@ -1257,6 +1572,160 @@ func Test_inflightAggregates(t *testing.T) { } } +func Test_commitReportWithSendRequests_validate(t *testing.T) { + testCases := []struct { + name string + reportInterval commit_store.CommitStoreInterval + numReqs int + expValid bool + }{ + { + name: "valid report", + reportInterval: commit_store.CommitStoreInterval{Min: 10, Max: 20}, + numReqs: 11, + expValid: true, + }, + { + name: "report with one request", + reportInterval: commit_store.CommitStoreInterval{Min: 1234, Max: 1234}, + numReqs: 1, + expValid: true, + }, + { + name: "request is missing", + reportInterval: commit_store.CommitStoreInterval{Min: 1234, Max: 1234}, + numReqs: 0, + expValid: false, + }, + { + name: "requests are missing", + reportInterval: commit_store.CommitStoreInterval{Min: 1, Max: 10}, + numReqs: 5, + expValid: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rep := commitReportWithSendRequests{ + commitReport: commit_store.CommitStoreCommitReport{ + Interval: tc.reportInterval, + }, + sendRequestsWithMeta: make([]evm2EVMOnRampCCIPSendRequestedWithMeta, tc.numReqs), + } + err := rep.validate() + isValid := err == nil + assert.Equal(t, tc.expValid, isValid) + }) + } +} + +func Test_commitReportWithSendRequests_allRequestsAreExecutedAndFinalized(t *testing.T) { + testCases := []struct { + name string + reqs []evm2EVMOnRampCCIPSendRequestedWithMeta + expRes bool + }{ + { + name: "all requests executed and finalized", + reqs: []evm2EVMOnRampCCIPSendRequestedWithMeta{ + {executed: true, finalized: true}, + {executed: true, finalized: true}, + {executed: true, finalized: true}, + }, + expRes: true, + }, + { + name: "true when there are zero requests", + reqs: []evm2EVMOnRampCCIPSendRequestedWithMeta{}, + expRes: true, + }, + { + name: "some request not executed", + reqs: []evm2EVMOnRampCCIPSendRequestedWithMeta{ + {executed: true, finalized: true}, + {executed: true, finalized: true}, + {executed: false, finalized: true}, + }, + expRes: false, + }, + { + name: "some request not finalized", + reqs: []evm2EVMOnRampCCIPSendRequestedWithMeta{ + {executed: true, finalized: true}, + {executed: true, finalized: true}, + {executed: true, finalized: false}, + }, + expRes: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rep := commitReportWithSendRequests{sendRequestsWithMeta: tc.reqs} + res := rep.allRequestsAreExecutedAndFinalized() + assert.Equal(t, tc.expRes, res) + }) + } +} + +func Test_commitReportWithSendRequests_sendReqFits(t *testing.T) { + testCases := []struct { + name string + req evm2EVMOnRampCCIPSendRequestedWithMeta + report commit_store.CommitStoreCommitReport + expRes bool + }{ + { + name: "all requests executed and finalized", + req: evm2EVMOnRampCCIPSendRequestedWithMeta{ + InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{SequenceNumber: 1}, + }, + report: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{Min: 1, Max: 10}, + }, + expRes: true, + }, + { + name: "all requests executed and finalized", + req: evm2EVMOnRampCCIPSendRequestedWithMeta{ + InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{SequenceNumber: 10}, + }, + report: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{Min: 1, Max: 10}, + }, + expRes: true, + }, + { + name: "all requests executed and finalized", + req: evm2EVMOnRampCCIPSendRequestedWithMeta{ + InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{SequenceNumber: 11}, + }, + report: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{Min: 1, Max: 10}, + }, + expRes: false, + }, + { + name: "all requests executed and finalized", + req: evm2EVMOnRampCCIPSendRequestedWithMeta{ + InternalEVM2EVMMessage: evm_2_evm_offramp.InternalEVM2EVMMessage{SequenceNumber: 10}, + }, + report: commit_store.CommitStoreCommitReport{ + Interval: commit_store.CommitStoreInterval{Min: 10, Max: 10}, + }, + expRes: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + r := &commitReportWithSendRequests{commitReport: tc.report} + assert.Equal(t, tc.expRes, r.sendReqFits(tc.req)) + }) + } +} + // generateExecutionReport generates an execution report that can be used in tests func generateExecutionReport(t *testing.T, numMsgs, tokensPerMsg, bytesPerMsg int) evm_2_evm_offramp.InternalExecutionReport { messages := make([]evm_2_evm_offramp.InternalEVM2EVMMessage, numMsgs) @@ -1297,3 +1766,13 @@ func generateExecutionReport(t *testing.T, numMsgs, tokensPerMsg, bytesPerMsg in ProofFlagBits: big.NewInt(rand.Int64()), } } + +type mockPool struct { + custom_token_pool.CustomTokenPoolInterface + mock.Mock +} + +func (mp *mockPool) CurrentOffRampRateLimiterState(opts *bind.CallOpts, offRamp common.Address) (custom_token_pool.RateLimiterTokenBucket, error) { + args := mp.Called(opts, offRamp) + return args.Get(0).(custom_token_pool.RateLimiterTokenBucket), args.Error(1) +} From 61a11ce7bb71cfc44225473dfcfa3b56903a3ee0 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Fri, 8 Sep 2023 12:08:24 +0300 Subject: [PATCH 17/25] update wait for healthy postgres script --- .../setup-postgres/wait-for-healthy-postgres.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup-postgres/wait-for-healthy-postgres.sh b/.github/actions/setup-postgres/wait-for-healthy-postgres.sh index 3f0efd66f3..438cfbaff3 100755 --- a/.github/actions/setup-postgres/wait-for-healthy-postgres.sh +++ b/.github/actions/setup-postgres/wait-for-healthy-postgres.sh @@ -2,10 +2,24 @@ RETRIES=10 until [ $RETRIES -eq 0 ]; do - if docker compose ps postgres --status running --format json | jq >/dev/null -e 'if (.[0].Health == "healthy") then true else false end'; then + DOCKER_OUTPUT=$(docker compose ps postgres --status running --format json) + JSON_TYPE=$(echo "$DOCKER_OUTPUT" | jq -r 'type') + + if [ "$JSON_TYPE" == "array" ]; then + HEALTH_STATUS=$(echo "$DOCKER_OUTPUT" | jq -r '.[0].Health') + elif [ "$JSON_TYPE" == "object" ]; then + HEALTH_STATUS=$(echo "$DOCKER_OUTPUT" | jq -r '.Health') + else + HEALTH_STATUS="Unknown JSON type: $JSON_TYPE" + fi + + echo "postgres health status: $HEALTH_STATUS" + if [ "$HEALTH_STATUS" == "healthy" ]; then exit 0 fi + echo "Waiting for postgres server, $((RETRIES--)) remaining attempts..." sleep 2 done + exit 1 From 10e20299cd6046c1c4cebad417ed275eb93d046d Mon Sep 17 00:00:00 2001 From: dimkouv Date: Fri, 8 Sep 2023 14:01:55 +0300 Subject: [PATCH 18/25] fix failing test --- .../plugins/ccip/execution_reporting_plugin_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 8c79bc9829..2b9de36bdc 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -1078,7 +1078,15 @@ func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { return } assert.NoError(t, err) - assert.Equal(t, tc.expReports, populatedReports) + assert.Equal(t, len(tc.expReports), len(populatedReports)) + for i, expReport := range tc.expReports { + assert.Equal(t, len(expReport.sendRequestsWithMeta), len(populatedReports[i].sendRequestsWithMeta)) + for j, expReq := range expReport.sendRequestsWithMeta { + assert.Equal(t, expReq.executed, populatedReports[i].sendRequestsWithMeta[j].executed) + assert.Equal(t, expReq.finalized, populatedReports[i].sendRequestsWithMeta[j].finalized) + assert.Equal(t, expReq.SequenceNumber, populatedReports[i].sendRequestsWithMeta[j].SequenceNumber) + } + } }) } } From 003873b93dc4abee1d142a9d164352b5f8d547fb Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 14 Sep 2023 17:04:29 +0300 Subject: [PATCH 19/25] resolve linter errors --- .../ccip/config/offchain_config_test.go | 3 +- .../ccip/execution_reporting_plugin.go | 6 +- .../ccip/execution_reporting_plugin_test.go | 2 +- .../ocr2/plugins/ccip/integration_test.go | 22 +- .../ccip/testhelpers/integration/chainlink.go | 4 +- .../ccip/testhelpers/integration/jobspec.go | 1 + .../testhelpers/plugins/plugin_harness.go | 275 ------------------ 7 files changed, 19 insertions(+), 294 deletions(-) delete mode 100644 core/services/ocr2/plugins/ccip/testhelpers/plugins/plugin_harness.go diff --git a/core/services/ocr2/plugins/ccip/config/offchain_config_test.go b/core/services/ocr2/plugins/ccip/config/offchain_config_test.go index 2a42d65e96..5edf21c176 100644 --- a/core/services/ocr2/plugins/ccip/config/offchain_config_test.go +++ b/core/services/ocr2/plugins/ccip/config/offchain_config_test.go @@ -109,7 +109,8 @@ func TestExecOffchainConfig_Encoding(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - encode, err := EncodeOffchainConfig(&tc.want) + exp := tc.want + encode, err := EncodeOffchainConfig(&exp) require.NoError(t, err) got, err := DecodeOffchainConfig[ExecOffchainConfig](encode) diff --git a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go index a37680ec78..a2d16f0aa7 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go @@ -79,7 +79,7 @@ type ExecutionReportingPlugin struct { offchainConfig ccipconfig.ExecOffchainConfig cachedSourceFeeTokens cache.AutoSync[[]common.Address] cachedDestTokens cache.AutoSync[cache.CachedTokens] - customTokenPoolFactory func(ctx context.Context, bind bind.ContractBackend, poolAddress common.Address) (custom_token_pool.CustomTokenPoolInterface, error) + customTokenPoolFactory func(ctx context.Context, poolAddress common.Address, bind bind.ContractBackend) (custom_token_pool.CustomTokenPoolInterface, error) } type ExecutionReportingPluginFactory struct { @@ -142,7 +142,7 @@ func (rf *ExecutionReportingPluginFactory) NewReportingPlugin(config types.Repor offchainConfig: offchainConfig, cachedDestTokens: cachedDestTokens, cachedSourceFeeTokens: cachedSourceFeeTokens, - customTokenPoolFactory: func(ctx context.Context, contractBackend bind.ContractBackend, poolAddress common.Address) (custom_token_pool.CustomTokenPoolInterface, error) { + customTokenPoolFactory: func(ctx context.Context, poolAddress common.Address, contractBackend bind.ContractBackend) (custom_token_pool.CustomTokenPoolInterface, error) { return custom_token_pool.NewCustomTokenPool(poolAddress, contractBackend) }, }, types.ReportingPluginInfo{ @@ -393,7 +393,7 @@ func (r *ExecutionReportingPlugin) destPoolRateLimits(ctx context.Context, commi return nil, fmt.Errorf("get pool by dest token (%s): %w", dstToken, err) } - tokenPool, err := r.customTokenPoolFactory(ctx, r.config.destClient, poolAddress) + tokenPool, err := r.customTokenPoolFactory(ctx, poolAddress, r.config.destClient) if err != nil { return nil, fmt.Errorf("new custom dest token pool %s: %w", poolAddress, 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 9c014678d4..a22236803b 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -879,7 +879,7 @@ func TestExecutionReportingPlugin_destPoolRateLimits(t *testing.T) { offRamp.On("Address").Return(offRampAddr) p.config.offRamp = offRamp - p.customTokenPoolFactory = func(ctx context.Context, _ bind.ContractBackend, poolAddress common.Address) (custom_token_pool.CustomTokenPoolInterface, error) { + p.customTokenPoolFactory = func(ctx context.Context, poolAddress common.Address, _ bind.ContractBackend) (custom_token_pool.CustomTokenPoolInterface, error) { mp := &mockPool{} mp.On("CurrentOffRampRateLimiterState", mock.Anything, offRampAddr).Return(tc.poolRateLimits[poolAddress], nil) return mp, nil diff --git a/core/services/ocr2/plugins/ccip/integration_test.go b/core/services/ocr2/plugins/ccip/integration_test.go index 958fa06480..912b6444b4 100644 --- a/core/services/ocr2/plugins/ccip/integration_test.go +++ b/core/services/ocr2/plugins/ccip/integration_test.go @@ -33,8 +33,8 @@ func TestIntegration_CCIP(t *testing.T) { _, err := w.Write([]byte(`{"UsdPerETH": "1700000000000000000000"}`)) require.NoError(t, err) })) - wrapped, err := ccipTH.Source.Router.GetWrappedNative(nil) - require.NoError(t, err) + wrapped, err1 := ccipTH.Source.Router.GetWrappedNative(nil) + require.NoError(t, err1) tokenPricesUSDPipeline := fmt.Sprintf(` // Price 1 link [type=http method=GET url="%s"]; @@ -310,7 +310,7 @@ merge [type=merge left="{}" right="{\\\"%s\\\":$(link_parse), \\\"%s\\\":$(eth_p linkToTransferToOnRamp := big.NewInt(1e18) // transfer some link to onramp to pay the nops - _, err = ccipTH.Source.LinkToken.Transfer(ccipTH.Source.User, ccipTH.Source.OnRamp.Address(), linkToTransferToOnRamp) + _, err := ccipTH.Source.LinkToken.Transfer(ccipTH.Source.User, ccipTH.Source.OnRamp.Address(), linkToTransferToOnRamp) require.NoError(t, err) ccipTH.Source.Chain.Commit() @@ -477,15 +477,13 @@ merge [type=merge left="{}" right="{\\\"%s\\\":$(link_parse), \\\"%s\\\":$(eth_p seqNumber := currentSeqNum + 1 defer msgWg.Done() for { - select { - case <-ticker.C: - t.Logf("sending request for seqnum %d", seqNumber) - ccipContracts.SendMessage(t, gasLimit, tokenAmount, ccipTH.Dest.Receivers[0].Receiver.Address()) - ccipContracts.Source.Chain.Commit() - seqNumber++ - if seqNumber == endSeq { - return - } + <-ticker.C // wait for ticker + t.Logf("sending request for seqnum %d", seqNumber) + ccipContracts.SendMessage(t, gasLimit, tokenAmount, ccipTH.Dest.Receivers[0].Receiver.Address()) + ccipContracts.Source.Chain.Commit() + seqNumber++ + if seqNumber == endSeq { + return } } }(ccipTH.CCIPContracts, currentSeqNum) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go index 9dc4ddd6f8..1b92a30996 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go @@ -18,9 +18,7 @@ import ( "github.com/google/uuid" "github.com/onsi/gomega" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - ctfClient "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2/confighelper" types4 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -28,6 +26,8 @@ import ( "go.uber.org/zap" "k8s.io/utils/pointer" + "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" diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go index d3372d7ed2..13e75f8084 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/jobspec.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/lib/pq" + "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/integration-tests/client" diff --git a/core/services/ocr2/plugins/ccip/testhelpers/plugins/plugin_harness.go b/core/services/ocr2/plugins/ccip/testhelpers/plugins/plugin_harness.go deleted file mode 100644 index 61de46d80f..0000000000 --- a/core/services/ocr2/plugins/ccip/testhelpers/plugins/plugin_harness.go +++ /dev/null @@ -1,275 +0,0 @@ -package plugintesthelpers - -import ( - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/stretchr/testify/require" - - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "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/hashlib" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/merklemulti" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -type OCR2TestContract interface { - SetOCR2Config(opts *bind.TransactOpts, signers []common.Address, transmitters []common.Address, f uint8, onchainConfig []byte, offchainConfigVersion uint64, offchainConfig []byte) (*types.Transaction, error) -} - -func generateAndSetTestOCR2Config(contract OCR2TestContract, owner *bind.TransactOpts, onChainConfig []byte) (*types.Transaction, error) { - var signers []common.Address - var transmitters []common.Address - - for i := 0; i < 4; i++ { - signers = append(signers, utils.RandomAddress()) - transmitters = append(transmitters, utils.RandomAddress()) - } - - return contract.SetOCR2Config(owner, signers, transmitters, 1, onChainConfig, 2, nil) -} - -type CCIPPluginTestHarness struct { - testhelpers.CCIPContracts - Lggr logger.Logger - - SourceLP logpoller.LogPollerTest - DestLP logpoller.LogPollerTest - DestClient evmclient.Client - SourceClient evmclient.Client - - CommitOnchainConfig ccipconfig.CommitOnchainConfig - ExecOnchainConfig ccipconfig.ExecOnchainConfig -} - -func (th *CCIPPluginTestHarness) CommitAndPollLogs(t *testing.T) { - th.Source.Chain.Commit() - th.SourceLP.PollAndSaveLogs(testutils.Context(t), th.Source.Chain.Blockchain().CurrentBlock().Number.Int64()) - - th.Dest.Chain.Commit() - th.DestLP.PollAndSaveLogs(testutils.Context(t), th.Dest.Chain.Blockchain().CurrentBlock().Number.Int64()) -} - -func SetupCCIPTestHarness(t *testing.T) CCIPPluginTestHarness { - c := testhelpers.SetupCCIPContracts(t, testhelpers.SourceChainID, testhelpers.SourceChainSelector, testhelpers.DestChainID, testhelpers.DestChainSelector) - - lggr := logger.TestLogger(t) - - // db, clients and logpollers - db := pgtest.NewSqlxDB(t) - - sourceORM := logpoller.NewORM(new(big.Int).SetUint64(c.Source.ChainID), db, lggr, pgtest.NewQConfig(true)) - var sourceLP logpoller.LogPollerTest = logpoller.NewLogPoller( - sourceORM, - evmclient.NewSimulatedBackendClient(t, c.Source.Chain, new(big.Int).SetUint64(c.Source.ChainID)), - lggr.Named("sourceLP"), - 1*time.Hour, 1, 10, 10, 1000, - ) - - destORM := logpoller.NewORM(new(big.Int).SetUint64(c.Dest.ChainID), db, lggr, pgtest.NewQConfig(true)) - var destLP logpoller.LogPollerTest = logpoller.NewLogPoller( - destORM, - evmclient.NewSimulatedBackendClient(t, c.Dest.Chain, new(big.Int).SetUint64(c.Dest.ChainID)), - lggr.Named("destLP"), - 1*time.Hour, 1, 10, 10, 1000, - ) - - // onChain configs - encodedCommitOnchainConfig := c.CreateDefaultCommitOnchainConfig(t) - commitOnchainConfig, err := abihelpers.DecodeAbiStruct[ccipconfig.CommitOnchainConfig](encodedCommitOnchainConfig) - require.NoError(t, err) - - _, err = generateAndSetTestOCR2Config(c.Dest.CommitStore, c.Dest.User, encodedCommitOnchainConfig) - require.NoError(t, err) - - encodedExecOnchainConfig := c.CreateDefaultExecOnchainConfig(t) - execOnchainConfig, err := abihelpers.DecodeAbiStruct[ccipconfig.ExecOnchainConfig](encodedExecOnchainConfig) - require.NoError(t, err) - - _, err = generateAndSetTestOCR2Config(c.Dest.OffRamp, c.Dest.User, encodedExecOnchainConfig) - require.NoError(t, err) - c.Dest.Chain.Commit() - - // approve router - _, err = c.Source.LinkToken.Approve(c.Source.User, c.Source.Router.Address(), testhelpers.Link(500)) - require.NoError(t, err) - c.Source.Chain.Commit() - - _, err = c.Dest.PriceRegistry.UpdatePrices(c.Dest.User, price_registry.InternalPriceUpdates{ - TokenPriceUpdates: []price_registry.InternalTokenPriceUpdate{ - {SourceToken: c.Dest.LinkToken.Address(), UsdPerToken: big.NewInt(5)}, - {SourceToken: c.Dest.WrappedNative.Address(), UsdPerToken: big.NewInt(5)}, - }, - DestChainSelector: c.Dest.ChainSelector, - UsdPerUnitGas: big.NewInt(1), - }) - require.NoError(t, err) - - // register filters in logPoller - require.NoError(t, sourceLP.RegisterFilter(logpoller.Filter{ - Name: logpoller.FilterName("Commit ccip sends", c.Source.OnRamp.Address().String()), - EventSigs: []common.Hash{abihelpers.EventSignatures.SendRequested}, Addresses: []common.Address{c.Source.OnRamp.Address()}, - })) - require.NoError(t, destLP.RegisterFilter(logpoller.Filter{ - Name: logpoller.FilterName("Commit price updates", c.Dest.PriceRegistry.Address()), - EventSigs: []common.Hash{abihelpers.EventSignatures.UsdPerUnitGasUpdated, abihelpers.EventSignatures.UsdPerTokenUpdated}, Addresses: []common.Address{c.Dest.PriceRegistry.Address()}, - })) - require.NoError(t, destLP.RegisterFilter(logpoller.Filter{ - Name: logpoller.FilterName("Exec report accepts", c.Dest.CommitStore.Address().String()), - EventSigs: []common.Hash{abihelpers.EventSignatures.ReportAccepted}, Addresses: []common.Address{c.Dest.CommitStore.Address()}, - })) - require.NoError(t, destLP.RegisterFilter(logpoller.Filter{ - Name: logpoller.FilterName("Exec execution state changes", c.Dest.OffRamp.Address().String()), - EventSigs: []common.Hash{abihelpers.EventSignatures.ExecutionStateChanged}, Addresses: []common.Address{c.Dest.OffRamp.Address()}, - })) - require.NoError(t, destLP.RegisterFilter(logpoller.Filter{ - Name: logpoller.FilterName("Token pool added", c.Dest.OffRamp.Address().String()), - EventSigs: []common.Hash{abihelpers.EventSignatures.PoolAdded}, Addresses: []common.Address{c.Dest.OffRamp.Address()}, - })) - require.NoError(t, destLP.RegisterFilter(logpoller.Filter{ - Name: logpoller.FilterName("Token pool removed", c.Dest.OffRamp.Address().String()), - EventSigs: []common.Hash{abihelpers.EventSignatures.PoolRemoved}, Addresses: []common.Address{c.Dest.OffRamp.Address()}, - })) - require.NoError(t, destLP.RegisterFilter(logpoller.Filter{ - Name: logpoller.FilterName("Fee token added", c.Dest.PriceRegistry.Address().String()), - EventSigs: []common.Hash{abihelpers.EventSignatures.FeeTokenAdded}, Addresses: []common.Address{c.Dest.PriceRegistry.Address()}, - })) - require.NoError(t, destLP.RegisterFilter(logpoller.Filter{ - Name: logpoller.FilterName("Fee token removed", c.Dest.PriceRegistry.Address().String()), - EventSigs: []common.Hash{abihelpers.EventSignatures.FeeTokenRemoved}, Addresses: []common.Address{c.Dest.PriceRegistry.Address()}, - })) - - // start and backfill logpollers - require.NoError(t, sourceLP.Start(testutils.Context(t))) - require.NoError(t, destLP.Start(testutils.Context(t))) - require.NoError(t, sourceLP.Replay(testutils.Context(t), 1)) - require.NoError(t, destLP.Replay(testutils.Context(t), 1)) - - th := CCIPPluginTestHarness{ - CCIPContracts: c, - Lggr: lggr, - - SourceLP: sourceLP, - DestLP: destLP, - DestClient: evmclient.NewSimulatedBackendClient(t, c.Dest.Chain, new(big.Int).SetUint64(c.Dest.ChainID)), - SourceClient: evmclient.NewSimulatedBackendClient(t, c.Source.Chain, new(big.Int).SetUint64(c.Source.ChainID)), - CommitOnchainConfig: commitOnchainConfig, - ExecOnchainConfig: execOnchainConfig, - } - - th.CommitAndPollLogs(t) - return th -} - -type MessageBatch struct { - TokenData [][][]byte - Messages []evm_2_evm_offramp.InternalEVM2EVMMessage - Interval commit_store.CommitStoreInterval - Root [32]byte - Proof merklemulti.Proof[[32]byte] - ProofBits *big.Int - Tree *merklemulti.Tree[[32]byte] -} - -func (mb MessageBatch) ToExecutionReport() evm_2_evm_offramp.InternalExecutionReport { - return evm_2_evm_offramp.InternalExecutionReport{ - Messages: mb.Messages, - OffchainTokenData: mb.TokenData, - Proofs: mb.Proof.Hashes, - ProofFlagBits: mb.ProofBits, - } -} - -func (th *CCIPPluginTestHarness) GenerateAndSendMessageBatch(t *testing.T, nMessages int, payloadSize int, nTokensPerMessage int) MessageBatch { - mctx := hashlib.NewKeccakCtx() - leafHasher := hashlib.NewLeafHasher(th.Source.ChainSelector, th.Dest.ChainSelector, th.Source.OnRamp.Address(), mctx) - - maxPayload := make([]byte, payloadSize) - for i := 0; i < payloadSize; i++ { - maxPayload[i] = 0xa - } - - var offchainTokenData [][]byte - var tokenAmounts []router.ClientEVMTokenAmount - for i := 0; i < nTokensPerMessage; i++ { - tokenAmounts = append(tokenAmounts, router.ClientEVMTokenAmount{ - Token: th.Source.LinkToken.Address(), - Amount: big.NewInt(int64(1 + i)), - }) - offchainTokenData = append(offchainTokenData, []byte{}) - } - - th.Source.Chain.Commit() - startBlock := th.Source.Chain.Blockchain().CurrentBlock().Number - var lastFlush int - for i := 0; i < nMessages; i++ { - routerMsg := router.ClientEVM2AnyMessage{ - Receiver: testhelpers.MustEncodeAddress(t, th.Dest.Receivers[0].Receiver.Address()), - FeeToken: th.Source.LinkToken.Address(), - TokenAmounts: tokenAmounts, - Data: maxPayload, - ExtraArgs: []byte{}, - } - _, err := th.Source.Router.CcipSend(th.Source.User, th.Dest.ChainSelector, routerMsg) - require.NoError(t, err) - lastFlush++ - if lastFlush*payloadSize > 700_000 { - th.CommitAndPollLogs(t) - lastFlush = 0 - } - } - th.CommitAndPollLogs(t) - - leafHashes := make([][32]byte, nMessages) - tokenData := make([][][]byte, nMessages) - indices := make([]int, nMessages) - messages := make([]evm_2_evm_offramp.InternalEVM2EVMMessage, nMessages) - seqNums := make([]uint64, nMessages) - - sendEvents, err := th.Source.OnRamp.FilterCCIPSendRequested(&bind.FilterOpts{Start: startBlock.Uint64(), Context: testutils.Context(t)}) - require.NoError(t, err) - var i int - for ; sendEvents.Next(); i++ { - indices[i] = i - tokenData[i] = offchainTokenData - messages[i] = abihelpers.OnRampMessageToOffRampMessage(sendEvents.Event.Message) - leafHash, err2 := leafHasher.HashLeaf(sendEvents.Event.Raw) - require.NoError(t, err2) - leafHashes[i] = leafHash - seqNums[i] = sendEvents.Event.Message.SequenceNumber - } - require.Equal(t, nMessages, i) - - tree, err := merklemulti.NewTree(mctx, leafHashes) - require.NoError(t, err) - proof, err := tree.Prove(indices) - require.NoError(t, err) - root := tree.Root() - rootLocal, err := merklemulti.VerifyComputeRoot(mctx, leafHashes, proof) - require.NoError(t, err) - require.Equal(t, root, rootLocal) - - return MessageBatch{ - Messages: messages, - Interval: commit_store.CommitStoreInterval{Min: seqNums[0], Max: seqNums[len(seqNums)-1]}, - TokenData: tokenData, - Root: root, - Proof: proof, - ProofBits: abihelpers.ProofFlagsToBits(proof.SourceFlags), - Tree: tree, - } -} From 4f0f91f4500eede8b2855a3411e54b827857f913 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 14 Sep 2023 17:05:05 +0300 Subject: [PATCH 20/25] gitignore vendor dir --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cd6a6ea09c..9c3a332b0f 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,5 @@ contracts/yarn.lock /core/scripts/ccip/json/credentials /core/scripts/ccip/revert-reason/bin/ccip-revert-reason +# dependencies generated after running `go mod vendor` +vendor/ From c0279a14423e16b661e91fb13e2731c0f8ad64e6 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 14 Sep 2023 17:12:20 +0300 Subject: [PATCH 21/25] improve test --- .../plugins/ccip/commit_reporting_plugin_test.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) 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 631ea2123e..2ca1c727df 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -166,9 +166,8 @@ func TestCommitReportingPlugin_Report(t *testing.T) { tokenPriceUpdates []ccipevents.Event[price_registry.PriceRegistryUsdPerTokenUpdated] sendRequests []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] - expCommitReport commit_store.CommitStoreCommitReport + expCommitReport *commit_store.CommitStoreCommitReport expSeqNumRange commit_store.CommitStoreInterval - shouldGetReport bool expErr bool }{ { @@ -188,7 +187,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) { }, }, expSeqNumRange: commit_store.CommitStoreInterval{Min: 1, Max: 1}, - expCommitReport: commit_store.CommitStoreCommitReport{ + expCommitReport: &commit_store.CommitStoreCommitReport{ MerkleRoot: [32]byte{123}, Interval: commit_store.CommitStoreInterval{Min: 1, Max: 1}, PriceUpdates: commit_store.InternalPriceUpdates{ @@ -197,8 +196,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) { UsdPerUnitGas: big.NewInt(0), }, }, - shouldGetReport: true, - expErr: false, + expErr: false, }, { name: "not enough observations", @@ -273,12 +271,11 @@ func TestCommitReportingPlugin_Report(t *testing.T) { assert.Error(t, err) return } - assert.NoError(t, err) - assert.Equal(t, tc.shouldGetReport, gotSomeReport) - if tc.shouldGetReport { - encodedExpectedReport, err := abihelpers.EncodeCommitReport(tc.expCommitReport) + if tc.expCommitReport != nil { + assert.True(t, gotSomeReport) + encodedExpectedReport, err := abihelpers.EncodeCommitReport(*tc.expCommitReport) assert.NoError(t, err) assert.Equal(t, types.Report(encodedExpectedReport), gotReport) } From e22e2986ad729db4a74d745c5d0992e0d6dcbb98 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Thu, 14 Sep 2023 17:26:20 +0300 Subject: [PATCH 22/25] assert no err --- .../services/ocr2/plugins/ccip/commit_reporting_plugin_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 2ca1c727df..0d7720e10c 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -151,7 +151,8 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { } assert.NoError(t, err) - expObsBytes, _ := tc.expObs.Marshal() + expObsBytes, err := tc.expObs.Marshal() + assert.NoError(t, err) assert.Equal(t, expObsBytes, []byte(obs)) }) } From 71f34370710ea0a0e00c7ec716486070f88e2a69 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Fri, 15 Sep 2023 16:03:34 +0300 Subject: [PATCH 23/25] rename ccipevents.Client to ccipdata.Reader --- .../ocr2/plugins/ccip/commit_plugin.go | 6 +-- .../plugins/ccip/commit_reporting_plugin.go | 14 ++--- .../ccip/commit_reporting_plugin_test.go | 54 +++++++++---------- .../plugins/ccip/execution_batch_building.go | 8 +-- .../ocr2/plugins/ccip/execution_plugin.go | 6 +-- .../ccip/execution_reporting_plugin.go | 10 ++-- .../ccip/execution_reporting_plugin_test.go | 42 +++++++-------- .../{ccipevents => ccipdata}/logpoller.go | 36 ++++++------- .../logpoller_test.go | 8 +-- .../internal/{ccipevents => ccipdata}/mock.go | 30 +++++------ .../client.go => ccipdata/reader.go} | 8 +-- .../ocr2/plugins/ccip/plugins_common.go | 4 +- 12 files changed, 113 insertions(+), 113 deletions(-) rename core/services/ocr2/plugins/ccip/internal/{ccipevents => ccipdata}/logpoller.go (90%) rename core/services/ocr2/plugins/ccip/internal/{ccipevents => ccipdata}/logpoller_test.go (96%) rename core/services/ocr2/plugins/ccip/internal/{ccipevents => ccipdata}/mock.go (91%) rename core/services/ocr2/plugins/ccip/internal/{ccipevents/client.go => ccipdata/reader.go} (94%) diff --git a/core/services/ocr2/plugins/ccip/commit_plugin.go b/core/services/ocr2/plugins/ccip/commit_plugin.go index f86b06b15d..b978176ecb 100644 --- a/core/services/ocr2/plugins/ccip/commit_plugin.go +++ b/core/services/ocr2/plugins/ccip/commit_plugin.go @@ -14,7 +14,7 @@ import ( libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipevents" + "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/oraclelib" @@ -105,8 +105,8 @@ func NewCommitServices(lggr logger.Logger, jb job.Job, chainSet evm.LegacyChainC lggr: commitLggr, sourceLP: sourceChain.LogPoller(), destLP: destChain.LogPoller(), - sourceEvents: ccipevents.NewLogPollerClient(sourceChain.LogPoller(), commitLggr, sourceChain.Client()), - destEvents: ccipevents.NewLogPollerClient(destChain.LogPoller(), commitLggr, destChain.Client()), + sourceReader: ccipdata.NewLogPollerReader(sourceChain.LogPoller(), commitLggr, sourceChain.Client()), + destReader: ccipdata.NewLogPollerReader(destChain.LogPoller(), commitLggr, destChain.Client()), offRamp: offRamp, onRampAddress: onRamp.Address(), priceGetter: priceGetterObject, diff --git a/core/services/ocr2/plugins/ccip/commit_reporting_plugin.go b/core/services/ocr2/plugins/ccip/commit_reporting_plugin.go index 8d22fb8461..65f0e3d758 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin.go @@ -25,9 +25,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "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/internal/cache" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipevents" "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/services/pg" @@ -55,8 +55,8 @@ type update struct { type CommitPluginConfig struct { lggr logger.Logger sourceLP, destLP logpoller.LogPoller - sourceEvents ccipevents.Client - destEvents ccipevents.Client + sourceReader ccipdata.Reader + destReader ccipdata.Reader offRamp evm_2_evm_offramp.EVM2EVMOffRampInterface onRampAddress common.Address commitStore commit_store.CommitStoreInterface @@ -237,7 +237,7 @@ func (r *CommitReportingPlugin) calculateMinMaxSequenceNumbers(ctx context.Conte return 0, 0, err } - msgRequests, err := r.config.sourceEvents.GetSendRequestsGteSeqNum(ctx, r.config.onRampAddress, nextInflightMin, r.config.checkFinalityTags, int(r.offchainConfig.SourceFinalityDepth)) + msgRequests, err := r.config.sourceReader.GetSendRequestsGteSeqNum(ctx, r.config.onRampAddress, nextInflightMin, r.config.checkFinalityTags, int(r.offchainConfig.SourceFinalityDepth)) if err != nil { return 0, 0, err } @@ -366,7 +366,7 @@ func calculateUsdPer1e18TokenAmount(price *big.Int, decimals uint8) *big.Int { // Gets the latest token price updates based on logs within the heartbeat // The updates returned by this function are guaranteed to not contain nil values. func (r *CommitReportingPlugin) getLatestTokenPriceUpdates(ctx context.Context, now time.Time, checkInflight bool) (map[common.Address]update, error) { - tokenPriceUpdates, err := r.config.destEvents.GetTokenPriceUpdatesCreatedAfter( + tokenPriceUpdates, err := r.config.destReader.GetTokenPriceUpdatesCreatedAfter( ctx, r.destPriceRegistry.Address(), now.Add(-r.offchainConfig.FeeUpdateHeartBeat.Duration()), @@ -422,7 +422,7 @@ func (r *CommitReportingPlugin) getLatestGasPriceUpdate(ctx context.Context, now } // If there are no price updates inflight, check latest prices onchain - gasPriceUpdates, err := r.config.destEvents.GetGasPriceUpdatesCreatedAfter( + gasPriceUpdates, err := r.config.destReader.GetGasPriceUpdatesCreatedAfter( ctx, r.destPriceRegistry.Address(), r.config.sourceChainSelector, @@ -656,7 +656,7 @@ func (r *CommitReportingPlugin) buildReport(ctx context.Context, lggr logger.Log // Logs are guaranteed to be in order of seq num, since these are finalized logs only // and the contract's seq num is auto-incrementing. - sendRequests, err := r.config.sourceEvents.GetSendRequestsBetweenSeqNums( + sendRequests, err := r.config.sourceReader.GetSendRequestsBetweenSeqNums( ctx, r.config.onRampAddress, interval.Min, 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 0d7720e10c..63a3457f98 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -35,7 +35,7 @@ import ( "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/cache" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipevents" + "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" @@ -53,7 +53,7 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { commitStoreIsPaused bool commitStoreSeqNum uint64 tokenPrices map[common.Address]*big.Int - sendReqs []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + sendReqs []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] tokenDecimals map[common.Address]uint8 fee *big.Int @@ -67,7 +67,7 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { someTokenAddr: big.NewInt(2), sourceNativeTokenAddr: big.NewInt(2), }, - sendReqs: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + sendReqs: []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ {Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 54}}}, {Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 55}}}, }, @@ -105,7 +105,7 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(tc.commitStoreSeqNum, nil) } - sourceEvents := ccipevents.NewMockClient(t) + sourceEvents := ccipdata.NewMockReader(t) if len(tc.sendReqs) > 0 { sourceEvents.On("GetSendRequestsGteSeqNum", ctx, onRampAddress, tc.commitStoreSeqNum, false, sourceFinalityDepth). Return(tc.sendReqs, nil) @@ -137,7 +137,7 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { p.config.commitStore = commitStore p.config.onRampAddress = onRampAddress p.offchainConfig.SourceFinalityDepth = uint32(sourceFinalityDepth) - p.config.sourceEvents = sourceEvents + p.config.sourceReader = sourceEvents p.tokenDecimalsCache = tokenDecimalsCache p.config.priceGetter = priceGet p.config.sourceFeeEstimator = sourceFeeEst @@ -163,9 +163,9 @@ func TestCommitReportingPlugin_Report(t *testing.T) { name string observations []CommitObservation f int - gasPriceUpdates []ccipevents.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated] - tokenPriceUpdates []ccipevents.Event[price_registry.PriceRegistryUsdPerTokenUpdated] - sendRequests []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + gasPriceUpdates []ccipdata.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated] + tokenPriceUpdates []ccipdata.Event[price_registry.PriceRegistryUsdPerTokenUpdated] + sendRequests []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] expCommitReport *commit_store.CommitStoreCommitReport expSeqNumRange commit_store.CommitStoreInterval @@ -178,7 +178,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) { {Interval: commit_store.CommitStoreInterval{Min: 1, Max: 1}}, }, f: 1, - sendRequests: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + sendRequests: []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ { Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ Message: evm_2_evm_onramp.InternalEVM2EVMMessage{ @@ -205,7 +205,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) { {Interval: commit_store.CommitStoreInterval{Min: 1, Max: 1}}, }, f: 1, - sendRequests: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{{}}, + sendRequests: []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{{}}, expSeqNumRange: commit_store.CommitStoreInterval{Min: 1, Max: 1}, expErr: true, }, @@ -225,7 +225,7 @@ func TestCommitReportingPlugin_Report(t *testing.T) { {Interval: commit_store.CommitStoreInterval{Min: 2, Max: 2}}, }, f: 1, - sendRequests: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{{}}, + sendRequests: []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{{}}, expSeqNumRange: commit_store.CommitStoreInterval{Min: 2, Max: 2}, expErr: true, }, @@ -241,11 +241,11 @@ func TestCommitReportingPlugin_Report(t *testing.T) { destPriceRegistry := mock_contracts.NewPriceRegistryInterface(t) destPriceRegistry.On("Address").Return(destPriceRegistryAddress) - destEvents := ccipevents.NewMockClient(t) + destEvents := ccipdata.NewMockReader(t) destEvents.On("GetGasPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, uint64(sourceChainSelector), mock.Anything, 0).Return(tc.gasPriceUpdates, nil) destEvents.On("GetTokenPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, mock.Anything, 0).Return(tc.tokenPriceUpdates, nil) - sourceEvents := ccipevents.NewMockClient(t) + sourceEvents := ccipdata.NewMockReader(t) if len(tc.sendRequests) > 0 { sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddress, tc.expSeqNumRange.Min, tc.expSeqNumRange.Max, 0).Return(tc.sendRequests, nil) } @@ -254,8 +254,8 @@ func TestCommitReportingPlugin_Report(t *testing.T) { p.lggr = logger.TestLogger(t) p.inflightReports = newInflightCommitReportsContainer(time.Minute) p.destPriceRegistry = destPriceRegistry - p.config.destEvents = destEvents - p.config.sourceEvents = sourceEvents + p.config.destReader = destEvents + p.config.sourceReader = sourceEvents p.config.onRampAddress = onRampAddress p.config.sourceChainSelector = uint64(sourceChainSelector) p.config.leafHasher = &leafHasher123{} @@ -1046,17 +1046,17 @@ func TestCommitReportingPlugin_calculateMinMaxSequenceNumbers(t *testing.T) { } } - sourceEvents := ccipevents.NewMockClient(t) - var sendReqs []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + sourceEvents := ccipdata.NewMockReader(t) + var sendReqs []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] for _, seqNum := range tc.msgSeqNums { - sendReqs = append(sendReqs, ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + sendReqs = append(sendReqs, ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: seqNum}, }, }) } sourceEvents.On("GetSendRequestsGteSeqNum", ctx, mock.Anything, tc.expQueryMin, false, 0).Return(sendReqs, nil) - p.config.sourceEvents = sourceEvents + p.config.sourceReader = sourceEvents minSeqNum, maxSeqNum, err := p.calculateMinMaxSequenceNumbers(ctx, lggr) if tc.expErr { @@ -1137,18 +1137,18 @@ func TestCommitReportingPlugin_getLatestGasPriceUpdate(t *testing.T) { } if len(tc.destGasPriceUpdates) > 0 { - var events []ccipevents.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated] + var events []ccipdata.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated] for _, u := range tc.destGasPriceUpdates { - events = append(events, ccipevents.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated]{ + events = append(events, ccipdata.Event[price_registry.PriceRegistryUsdPerUnitGasUpdated]{ Data: price_registry.PriceRegistryUsdPerUnitGasUpdated{ Value: u.value, Timestamp: big.NewInt(u.timestamp.Unix()), }, }) } - destEvents := ccipevents.NewMockClient(t) + destEvents := ccipdata.NewMockReader(t) destEvents.On("GetGasPriceUpdatesCreatedAfter", ctx, mock.Anything, uint64(0), mock.Anything, 0).Return(events, nil) - p.config.destEvents = destEvents + p.config.destReader = destEvents } priceUpdate, err := p.getLatestGasPriceUpdate(ctx, time.Now(), tc.checkInflight) @@ -1235,10 +1235,10 @@ func TestCommitReportingPlugin_getLatestTokenPriceUpdates(t *testing.T) { priceReg.On("Address").Return(priceRegAddr) p.destPriceRegistry = priceReg - destEvents := ccipevents.NewMockClient(t) - var events []ccipevents.Event[price_registry.PriceRegistryUsdPerTokenUpdated] + destEvents := ccipdata.NewMockReader(t) + var events []ccipdata.Event[price_registry.PriceRegistryUsdPerTokenUpdated] for _, up := range tc.priceRegistryUpdates { - events = append(events, ccipevents.Event[price_registry.PriceRegistryUsdPerTokenUpdated]{ + events = append(events, ccipdata.Event[price_registry.PriceRegistryUsdPerTokenUpdated]{ Data: price_registry.PriceRegistryUsdPerTokenUpdated{ Token: up.Token, Value: up.Value, @@ -1247,7 +1247,7 @@ func TestCommitReportingPlugin_getLatestTokenPriceUpdates(t *testing.T) { }) } destEvents.On("GetTokenPriceUpdatesCreatedAfter", ctx, priceRegAddr, mock.Anything, 0).Return(events, nil) - p.config.destEvents = destEvents + p.config.destReader = destEvents p.inflightReports = newInflightCommitReportsContainer(time.Minute) if len(tc.inflightUpdates) > 0 { diff --git a/core/services/ocr2/plugins/ccip/execution_batch_building.go b/core/services/ocr2/plugins/ccip/execution_batch_building.go index fd7a414ade..f97c5325ed 100644 --- a/core/services/ocr2/plugins/ccip/execution_batch_building.go +++ b/core/services/ocr2/plugins/ccip/execution_batch_building.go @@ -12,7 +12,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/ocr2/plugins/ccip/abihelpers" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipevents" + "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" ) @@ -22,9 +22,9 @@ func getProofData( lggr logger.Logger, hashLeaf hashlib.LeafHasherInterface[[32]byte], onRampAddress common.Address, - sourceEventsClient ccipevents.Client, + sourceEventsClient ccipdata.Reader, interval commit_store.CommitStoreInterval, -) (sendReqsInRoot []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], leaves [][32]byte, tree *merklemulti.Tree[[32]byte], err error) { +) (sendReqsInRoot []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], leaves [][32]byte, tree *merklemulti.Tree[[32]byte], err error) { sendReqs, err := sourceEventsClient.GetSendRequestsBetweenSeqNums( ctx, onRampAddress, @@ -97,7 +97,7 @@ func validateSeqNumbers(serviceCtx context.Context, commitStore commit_store.Com } // Gets the commit report from the saved logs for a given sequence number. -func getCommitReportForSeqNum(ctx context.Context, destEvents ccipevents.Client, commitStore commit_store.CommitStoreInterface, seqNum uint64) (commit_store.CommitStoreCommitReport, error) { +func getCommitReportForSeqNum(ctx context.Context, destEvents ccipdata.Reader, commitStore commit_store.CommitStoreInterface, seqNum uint64) (commit_store.CommitStoreCommitReport, error) { acceptedReports, err := destEvents.GetAcceptedCommitReportsGteSeqNum(ctx, commitStore.Address(), seqNum, 0) if err != nil { return commit_store.CommitStoreCommitReport{}, err diff --git a/core/services/ocr2/plugins/ccip/execution_plugin.go b/core/services/ocr2/plugins/ccip/execution_plugin.go index dc1e7748fc..9bdfbbf028 100644 --- a/core/services/ocr2/plugins/ccip/execution_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_plugin.go @@ -15,7 +15,7 @@ import ( libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipevents" + "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/oraclelib" @@ -112,8 +112,8 @@ func NewExecutionServices(lggr logger.Logger, jb job.Job, chainSet evm.LegacyCha lggr: execLggr, sourceLP: sourceChain.LogPoller(), destLP: destChain.LogPoller(), - sourceEvents: ccipevents.NewLogPollerClient(sourceChain.LogPoller(), execLggr, sourceChain.Client()), - destEvents: ccipevents.NewLogPollerClient(destChain.LogPoller(), execLggr, destChain.Client()), + sourceEvents: ccipdata.NewLogPollerReader(sourceChain.LogPoller(), execLggr, sourceChain.Client()), + destEvents: ccipdata.NewLogPollerReader(destChain.LogPoller(), execLggr, destChain.Client()), onRamp: onRamp, offRamp: offRamp, commitStore: commitStore, diff --git a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go index a2d16f0aa7..0250b2f272 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go @@ -32,7 +32,7 @@ import ( "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/cache" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipevents" + "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/observability" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -54,8 +54,8 @@ var ( type ExecutionPluginConfig struct { lggr logger.Logger sourceLP, destLP logpoller.LogPoller - sourceEvents ccipevents.Client - destEvents ccipevents.Client + sourceEvents ccipdata.Reader + destEvents ccipdata.Reader onRamp evm_2_evm_onramp.EVM2EVMOnRampInterface offRamp evm_2_evm_offramp.EVM2EVMOffRampInterface commitStore commit_store.CommitStoreInterface @@ -736,7 +736,7 @@ func (r *ExecutionReportingPlugin) getReportsWithSendRequests( // use errgroup to fetch send request logs and executed sequence numbers in parallel eg := &errgroup.Group{} - var sendRequests []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + var sendRequests []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] eg.Go(func() error { sendReqs, err := r.config.sourceEvents.GetSendRequestsBetweenSeqNums( ctx, @@ -1126,7 +1126,7 @@ func getTokensPrices(ctx context.Context, feeTokens []common.Address, priceRegis func getUnexpiredCommitReports( ctx context.Context, - destEvents ccipevents.Client, + destEvents ccipdata.Reader, commitStore commit_store.CommitStoreInterface, permissionExecutionThreshold time.Duration, ) ([]commit_store.CommitStoreCommitReport, error) { 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 a22236803b..6f87a7f83b 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -37,7 +37,7 @@ import ( "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/cache" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipevents" + "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/utils" @@ -52,8 +52,8 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { name string commitStoreDown bool inflightReports []InflightInternalExecutionReport - unexpiredReports []ccipevents.Event[commit_store.CommitStoreReportAccepted] - sendRequests []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + unexpiredReports []ccipdata.Event[commit_store.CommitStoreReportAccepted] + sendRequests []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] executedSeqNums []uint64 blessedRoots [][32]byte senderNonce uint64 @@ -69,7 +69,7 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { name: "happy flow", commitStoreDown: false, inflightReports: []InflightInternalExecutionReport{}, - unexpiredReports: []ccipevents.Event[commit_store.CommitStoreReportAccepted]{ + unexpiredReports: []ccipdata.Event[commit_store.CommitStoreReportAccepted]{ { Data: commit_store.CommitStoreReportAccepted{ Report: commit_store.CommitStoreCommitReport{ @@ -87,7 +87,7 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { IsEnabled: false, }, senderNonce: 9, - sendRequests: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + sendRequests: []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ { Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 10}, @@ -131,13 +131,13 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { offRamp.On("GetSenderNonce", mock.Anything, mock.Anything).Return(tc.senderNonce, nil).Maybe() p.config.offRamp = offRamp - destEvents := ccipevents.NewMockClient(t) + destEvents := ccipdata.NewMockReader(t) destEvents.On("GetAcceptedCommitReportsGteTimestamp", ctx, commitStoreAddr, mock.Anything, 0). Return(tc.unexpiredReports, nil).Maybe() destEvents.On("LatestBlock", ctx).Return(int64(1234), nil).Maybe() - var executionEvents []ccipevents.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] + var executionEvents []ccipdata.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] for _, seqNum := range tc.executedSeqNums { - executionEvents = append(executionEvents, ccipevents.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]{ + executionEvents = append(executionEvents, ccipdata.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]{ Data: evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged{SequenceNumber: seqNum}, }) } @@ -150,7 +150,7 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { onRamp.On("Address").Return(onRampAddr).Maybe() p.config.onRamp = onRamp - sourceEvents := ccipevents.NewMockClient(t) + sourceEvents := ccipdata.NewMockReader(t) sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, mock.Anything, mock.Anything, 0). Return(tc.sendRequests, nil).Maybe() p.config.sourceEvents = sourceEvents @@ -376,10 +376,10 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { commitStore.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(math.MaxInt64), nil) p.config.commitStore = commitStore - destEvents := ccipevents.NewMockClient(t) + destEvents := ccipdata.NewMockReader(t) destEvents.On("GetAcceptedCommitReportsGteSeqNum", ctx, commitStoreAddress, observations[0].SeqNr, 0). - Return([]ccipevents.Event[commit_store.CommitStoreReportAccepted]{ + Return([]ccipdata.Event[commit_store.CommitStoreReportAccepted]{ { Data: commit_store.CommitStoreReportAccepted{ Report: commit_store.CommitStoreCommitReport{ @@ -400,9 +400,9 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { onRamp.On("Address").Return(onRampAddr) p.config.onRamp = onRamp - sendReqs := make([]ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], len(observations)) + sendReqs := make([]ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], len(observations)) for i := range observations { - sendReqs[i] = ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + sendReqs[i] = ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{Message: evm_2_evm_onramp.InternalEVM2EVMMessage{ SourceChainSelector: math.MaxUint64, SequenceNumber: uint64(i + 1), @@ -419,7 +419,7 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { }}, } } - sourceEvents := ccipevents.NewMockClient(t) + sourceEvents := ccipdata.NewMockReader(t) sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, observations[0].SeqNr, observations[len(observations)-1].SeqNr, 0).Return(sendReqs, nil) p.config.sourceEvents = sourceEvents @@ -961,7 +961,7 @@ func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { reports []commit_store.CommitStoreCommitReport expQueryMin uint64 // expected min/max used in the query to get ccipevents expQueryMax uint64 - onchainEvents []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + onchainEvents []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] destLatestBlock int64 destExecutedSeqNums []uint64 @@ -988,7 +988,7 @@ func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { }, expQueryMin: 1, expQueryMax: 3, - onchainEvents: []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ + onchainEvents: []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested]{ {Data: evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{ Message: evm_2_evm_onramp.InternalEVM2EVMMessage{SequenceNumber: 1}, }}, @@ -1055,18 +1055,18 @@ func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { offRamp.On("Address").Return(offRampAddr).Maybe() p.config.offRamp = offRamp - sourceEvents := ccipevents.NewMockClient(t) + sourceEvents := ccipdata.NewMockReader(t) sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, tc.expQueryMin, tc.expQueryMax, 0). Return(tc.onchainEvents, nil).Maybe() p.config.sourceEvents = sourceEvents - destEvents := ccipevents.NewMockClient(t) + destEvents := ccipdata.NewMockReader(t) destEvents.On("LatestBlock", ctx).Return(tc.destLatestBlock, nil).Maybe() - var executedEvents []ccipevents.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] + var executedEvents []ccipdata.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] for _, executedSeqNum := range tc.destExecutedSeqNums { - executedEvents = append(executedEvents, ccipevents.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]{ + executedEvents = append(executedEvents, ccipdata.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]{ Data: evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged{SequenceNumber: executedSeqNum}, - BlockMeta: ccipevents.BlockMeta{BlockNumber: tc.destLatestBlock - 10}, + BlockMeta: ccipdata.BlockMeta{BlockNumber: tc.destLatestBlock - 10}, }) } destEvents.On("GetExecutionStateChangesBetweenSeqNums", ctx, offRampAddr, tc.expQueryMin, tc.expQueryMax, 0).Return(executedEvents, nil).Maybe() diff --git a/core/services/ocr2/plugins/ccip/internal/ccipevents/logpoller.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller.go similarity index 90% rename from core/services/ocr2/plugins/ccip/internal/ccipevents/logpoller.go rename to core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller.go index ab6d14e6a0..4473e72703 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipevents/logpoller.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller.go @@ -1,4 +1,4 @@ -package ccipevents +package ccipdata import ( "context" @@ -23,10 +23,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) -var _ Client = &LogPollerClient{} +var _ Reader = &LogPollerReader{} -// LogPollerClient implements the Client interface by using a logPoller instance to fetch the events. -type LogPollerClient struct { +// LogPollerReader implements the Reader interface by using a logPoller instance to fetch the events. +type LogPollerReader struct { lp logpoller.LogPoller lggr logger.Logger client evmclient.Client @@ -34,15 +34,15 @@ type LogPollerClient struct { dependencyCache sync.Map } -func NewLogPollerClient(lp logpoller.LogPoller, lggr logger.Logger, client evmclient.Client) *LogPollerClient { - return &LogPollerClient{ +func NewLogPollerReader(lp logpoller.LogPoller, lggr logger.Logger, client evmclient.Client) *LogPollerReader { + return &LogPollerReader{ lp: lp, lggr: lggr, client: client, } } -func (c *LogPollerClient) GetSendRequestsGteSeqNum(ctx context.Context, onRampAddress common.Address, seqNum uint64, checkFinalityTags bool, confs int) (sendReqs []Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], err error) { +func (c *LogPollerReader) GetSendRequestsGteSeqNum(ctx context.Context, onRampAddress common.Address, seqNum uint64, checkFinalityTags bool, confs int) (sendReqs []Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], err error) { onRamp, err := c.loadOnRamp(onRampAddress) if err != nil { return nil, err @@ -108,7 +108,7 @@ func (c *LogPollerClient) GetSendRequestsGteSeqNum(ctx context.Context, onRampAd ) } -func (c *LogPollerClient) GetSendRequestsBetweenSeqNums(ctx context.Context, onRampAddress common.Address, seqNumMin, seqNumMax uint64, confs int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error) { +func (c *LogPollerReader) GetSendRequestsBetweenSeqNums(ctx context.Context, onRampAddress common.Address, seqNumMin, seqNumMax uint64, confs int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error) { onRamp, err := c.loadOnRamp(onRampAddress) if err != nil { return nil, err @@ -135,7 +135,7 @@ func (c *LogPollerClient) GetSendRequestsBetweenSeqNums(ctx context.Context, onR ) } -func (c *LogPollerClient) GetTokenPriceUpdatesCreatedAfter(ctx context.Context, priceRegistryAddress common.Address, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerTokenUpdated], error) { +func (c *LogPollerReader) GetTokenPriceUpdatesCreatedAfter(ctx context.Context, priceRegistryAddress common.Address, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerTokenUpdated], error) { priceRegistry, err := c.loadPriceRegistry(priceRegistryAddress) if err != nil { return nil, err @@ -161,7 +161,7 @@ func (c *LogPollerClient) GetTokenPriceUpdatesCreatedAfter(ctx context.Context, ) } -func (c *LogPollerClient) GetGasPriceUpdatesCreatedAfter(ctx context.Context, priceRegistryAddress common.Address, chainSelector uint64, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerUnitGasUpdated], error) { +func (c *LogPollerReader) GetGasPriceUpdatesCreatedAfter(ctx context.Context, priceRegistryAddress common.Address, chainSelector uint64, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerUnitGasUpdated], error) { priceRegistry, err := c.loadPriceRegistry(priceRegistryAddress) if err != nil { return nil, err @@ -189,7 +189,7 @@ func (c *LogPollerClient) GetGasPriceUpdatesCreatedAfter(ctx context.Context, pr ) } -func (c *LogPollerClient) GetExecutionStateChangesBetweenSeqNums(ctx context.Context, offRampAddress common.Address, seqNumMin, seqNumMax uint64, confs int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error) { +func (c *LogPollerReader) GetExecutionStateChangesBetweenSeqNums(ctx context.Context, offRampAddress common.Address, seqNumMin, seqNumMax uint64, confs int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error) { offRamp, err := c.loadOffRamp(offRampAddress) if err != nil { return nil, err @@ -217,11 +217,11 @@ func (c *LogPollerClient) GetExecutionStateChangesBetweenSeqNums(ctx context.Con ) } -func (c *LogPollerClient) LatestBlock(ctx context.Context) (int64, error) { +func (c *LogPollerReader) LatestBlock(ctx context.Context) (int64, error) { return c.lp.LatestBlock(pg.WithParentCtx(ctx)) } -func (c *LogPollerClient) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, commitStoreAddress common.Address, seqNum uint64, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { +func (c *LogPollerReader) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, commitStoreAddress common.Address, seqNum uint64, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { commitStore, err := c.loadCommitStore(commitStoreAddress) if err != nil { return nil, err @@ -248,7 +248,7 @@ func (c *LogPollerClient) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, ) } -func (c *LogPollerClient) GetAcceptedCommitReportsGteTimestamp(ctx context.Context, commitStoreAddress common.Address, ts time.Time, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { +func (c *LogPollerReader) GetAcceptedCommitReportsGteTimestamp(ctx context.Context, commitStoreAddress common.Address, ts time.Time, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { commitStore, err := c.loadCommitStore(commitStoreAddress) if err != nil { return nil, err @@ -295,7 +295,7 @@ func parseLogs[T any](logs []logpoller.Log, lggr logger.Logger, parseFunc func(l return reqs, nil } -func (c *LogPollerClient) loadOnRamp(addr common.Address) (*evm_2_evm_onramp.EVM2EVMOnRampFilterer, error) { +func (c *LogPollerReader) loadOnRamp(addr common.Address) (*evm_2_evm_onramp.EVM2EVMOnRampFilterer, error) { onRamp, exists := loadCachedDependency[*evm_2_evm_onramp.EVM2EVMOnRampFilterer](&c.dependencyCache, addr) if exists { return onRamp, nil @@ -310,7 +310,7 @@ func (c *LogPollerClient) loadOnRamp(addr common.Address) (*evm_2_evm_onramp.EVM return onRamp, nil } -func (c *LogPollerClient) loadPriceRegistry(addr common.Address) (*price_registry.PriceRegistryFilterer, error) { +func (c *LogPollerReader) loadPriceRegistry(addr common.Address) (*price_registry.PriceRegistryFilterer, error) { priceRegistry, exists := loadCachedDependency[*price_registry.PriceRegistryFilterer](&c.dependencyCache, addr) if exists { return priceRegistry, nil @@ -325,7 +325,7 @@ func (c *LogPollerClient) loadPriceRegistry(addr common.Address) (*price_registr return priceRegistry, nil } -func (c *LogPollerClient) loadOffRamp(addr common.Address) (*evm_2_evm_offramp.EVM2EVMOffRampFilterer, error) { +func (c *LogPollerReader) loadOffRamp(addr common.Address) (*evm_2_evm_offramp.EVM2EVMOffRampFilterer, error) { offRamp, exists := loadCachedDependency[*evm_2_evm_offramp.EVM2EVMOffRampFilterer](&c.dependencyCache, addr) if exists { return offRamp, nil @@ -340,7 +340,7 @@ func (c *LogPollerClient) loadOffRamp(addr common.Address) (*evm_2_evm_offramp.E return offRamp, nil } -func (c *LogPollerClient) loadCommitStore(addr common.Address) (*commit_store.CommitStoreFilterer, error) { +func (c *LogPollerReader) loadCommitStore(addr common.Address) (*commit_store.CommitStoreFilterer, error) { commitStore, exists := loadCachedDependency[*commit_store.CommitStoreFilterer](&c.dependencyCache, addr) if exists { return commitStore, nil diff --git a/core/services/ocr2/plugins/ccip/internal/ccipevents/logpoller_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller_test.go similarity index 96% rename from core/services/ocr2/plugins/ccip/internal/ccipevents/logpoller_test.go rename to core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller_test.go index 27de1c09ab..42cb107c64 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipevents/logpoller_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/logpoller_test.go @@ -1,4 +1,4 @@ -package ccipevents +package ccipdata import ( "context" @@ -20,7 +20,7 @@ import ( ) func TestLogPollerClient_loadDependency(t *testing.T) { - c := &LogPollerClient{} + c := &LogPollerReader{} someAddr := utils.RandomAddress() @@ -90,7 +90,7 @@ func TestLogPollerClient_GetSendRequestsGteSeqNum(t *testing.T) { mock.Anything, ).Return([]logpoller.Log{}, nil) - c := &LogPollerClient{lp: lp} + c := &LogPollerReader{lp: lp} events, err := c.GetSendRequestsGteSeqNum( context.Background(), onRampAddr, @@ -119,7 +119,7 @@ func TestLogPollerClient_GetSendRequestsGteSeqNum(t *testing.T) { cl := evmClientMocks.NewClient(t) cl.On("HeaderByNumber", mock.Anything, mock.Anything).Return(h, nil) - c := &LogPollerClient{lp: lp, client: cl} + c := &LogPollerReader{lp: lp, client: cl} events, err := c.GetSendRequestsGteSeqNum( context.Background(), onRampAddr, diff --git a/core/services/ocr2/plugins/ccip/internal/ccipevents/mock.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/mock.go similarity index 91% rename from core/services/ocr2/plugins/ccip/internal/ccipevents/mock.go rename to core/services/ocr2/plugins/ccip/internal/ccipdata/mock.go index 73d8bd9173..4389ed2fcb 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipevents/mock.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/mock.go @@ -1,6 +1,6 @@ // Code generated by mockery v2.28.1. DO NOT EDIT. -package ccipevents +package ccipdata import ( common "github.com/ethereum/go-ethereum/common" @@ -19,13 +19,13 @@ import ( time "time" ) -// MockClient is an autogenerated mock type for the Client type -type MockClient struct { +// MockReader is an autogenerated mock type for the Reader type +type MockReader struct { mock.Mock } // GetAcceptedCommitReportsGteSeqNum provides a mock function with given fields: ctx, commitStoreAddress, seqNum, confs -func (_m *MockClient) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, commitStoreAddress common.Address, seqNum uint64, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { +func (_m *MockReader) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, commitStoreAddress common.Address, seqNum uint64, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { ret := _m.Called(ctx, commitStoreAddress, seqNum, confs) var r0 []Event[commit_store.CommitStoreReportAccepted] @@ -51,7 +51,7 @@ func (_m *MockClient) GetAcceptedCommitReportsGteSeqNum(ctx context.Context, com } // GetAcceptedCommitReportsGteTimestamp provides a mock function with given fields: ctx, commitStoreAddress, ts, confs -func (_m *MockClient) GetAcceptedCommitReportsGteTimestamp(ctx context.Context, commitStoreAddress common.Address, ts time.Time, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { +func (_m *MockReader) GetAcceptedCommitReportsGteTimestamp(ctx context.Context, commitStoreAddress common.Address, ts time.Time, confs int) ([]Event[commit_store.CommitStoreReportAccepted], error) { ret := _m.Called(ctx, commitStoreAddress, ts, confs) var r0 []Event[commit_store.CommitStoreReportAccepted] @@ -77,7 +77,7 @@ func (_m *MockClient) GetAcceptedCommitReportsGteTimestamp(ctx context.Context, } // GetExecutionStateChangesBetweenSeqNums provides a mock function with given fields: ctx, offRamp, seqNumMin, seqNumMax, confs -func (_m *MockClient) GetExecutionStateChangesBetweenSeqNums(ctx context.Context, offRamp common.Address, seqNumMin uint64, seqNumMax uint64, confs int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error) { +func (_m *MockReader) GetExecutionStateChangesBetweenSeqNums(ctx context.Context, offRamp common.Address, seqNumMin uint64, seqNumMax uint64, confs int) ([]Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged], error) { ret := _m.Called(ctx, offRamp, seqNumMin, seqNumMax, confs) var r0 []Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] @@ -103,7 +103,7 @@ func (_m *MockClient) GetExecutionStateChangesBetweenSeqNums(ctx context.Context } // GetGasPriceUpdatesCreatedAfter provides a mock function with given fields: ctx, priceRegistry, chainSelector, ts, confs -func (_m *MockClient) GetGasPriceUpdatesCreatedAfter(ctx context.Context, priceRegistry common.Address, chainSelector uint64, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerUnitGasUpdated], error) { +func (_m *MockReader) GetGasPriceUpdatesCreatedAfter(ctx context.Context, priceRegistry common.Address, chainSelector uint64, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerUnitGasUpdated], error) { ret := _m.Called(ctx, priceRegistry, chainSelector, ts, confs) var r0 []Event[price_registry.PriceRegistryUsdPerUnitGasUpdated] @@ -129,7 +129,7 @@ func (_m *MockClient) GetGasPriceUpdatesCreatedAfter(ctx context.Context, priceR } // GetSendRequestsBetweenSeqNums provides a mock function with given fields: ctx, onRamp, seqNumMin, seqNumMax, confs -func (_m *MockClient) GetSendRequestsBetweenSeqNums(ctx context.Context, onRamp common.Address, seqNumMin uint64, seqNumMax uint64, confs int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error) { +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] @@ -155,7 +155,7 @@ func (_m *MockClient) GetSendRequestsBetweenSeqNums(ctx context.Context, onRamp } // GetSendRequestsGteSeqNum provides a mock function with given fields: ctx, onRamp, seqNum, checkFinalityTags, confs -func (_m *MockClient) GetSendRequestsGteSeqNum(ctx context.Context, onRamp common.Address, seqNum uint64, checkFinalityTags bool, confs int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error) { +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] @@ -181,7 +181,7 @@ func (_m *MockClient) GetSendRequestsGteSeqNum(ctx context.Context, onRamp commo } // GetTokenPriceUpdatesCreatedAfter provides a mock function with given fields: ctx, priceRegistry, ts, confs -func (_m *MockClient) GetTokenPriceUpdatesCreatedAfter(ctx context.Context, priceRegistry common.Address, ts time.Time, confs int) ([]Event[price_registry.PriceRegistryUsdPerTokenUpdated], error) { +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) var r0 []Event[price_registry.PriceRegistryUsdPerTokenUpdated] @@ -207,7 +207,7 @@ func (_m *MockClient) GetTokenPriceUpdatesCreatedAfter(ctx context.Context, pric } // LatestBlock provides a mock function with given fields: ctx -func (_m *MockClient) LatestBlock(ctx context.Context) (int64, error) { +func (_m *MockReader) LatestBlock(ctx context.Context) (int64, error) { ret := _m.Called(ctx) var r0 int64 @@ -230,14 +230,14 @@ func (_m *MockClient) LatestBlock(ctx context.Context) (int64, error) { return r0, r1 } -type mockConstructorTestingTNewMockClient interface { +type mockConstructorTestingTNewMockReader interface { mock.TestingT Cleanup(func()) } -// NewMockClient creates a new instance of MockClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMockClient(t mockConstructorTestingTNewMockClient) *MockClient { - mock := &MockClient{} +// NewMockReader creates a new instance of MockReader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockReader(t mockConstructorTestingTNewMockReader) *MockReader { + mock := &MockReader{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipevents/client.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go similarity index 94% rename from core/services/ocr2/plugins/ccip/internal/ccipevents/client.go rename to core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go index 723a8346c3..4f43187e67 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipevents/client.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go @@ -1,4 +1,4 @@ -package ccipevents +package ccipdata import ( "context" @@ -22,10 +22,10 @@ type BlockMeta struct { BlockNumber int64 } -// Client can be used to fetch CCIP related parsed on-chain events. +// Client can be used to fetch CCIP related parsed on-chain data. // -//go:generate mockery --quiet --name Client --output . --filename mock.go --inpackage --case=underscore -type Client interface { +//go:generate mockery --quiet --name Reader --output . --filename mock.go --inpackage --case=underscore +type Reader 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. GetSendRequestsGteSeqNum(ctx context.Context, onRamp common.Address, seqNum uint64, checkFinalityTags bool, confs int) ([]Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], error) diff --git a/core/services/ocr2/plugins/ccip/plugins_common.go b/core/services/ocr2/plugins/ccip/plugins_common.go index 9fed840bd9..93e3f94f38 100644 --- a/core/services/ocr2/plugins/ccip/plugins_common.go +++ b/core/services/ocr2/plugins/ccip/plugins_common.go @@ -20,7 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp_1_1_0" "github.com/smartcontractkit/chainlink/v2/core/logger" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipevents" + "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/observability" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -141,7 +141,7 @@ func leavesFromIntervals( lggr logger.Logger, interval commit_store.CommitStoreInterval, hasher hashlib.LeafHasherInterface[[32]byte], - sendReqs []ccipevents.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], + sendReqs []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], ) ([][32]byte, error) { var seqNrs []uint64 for _, req := range sendReqs { From e754fe5f2d712d0b83d3f9d48407bc0e874dd49d Mon Sep 17 00:00:00 2001 From: dimkouv Date: Mon, 18 Sep 2023 11:52:01 +0300 Subject: [PATCH 24/25] add fake implementations --- .../ocr2/plugins/ccip/commit_plugin_test.go | 12 +- .../ccip/commit_reporting_plugin_test.go | 56 +++---- .../plugins/ccip/execution_plugin_test.go | 30 ++-- .../ccip/execution_reporting_plugin_test.go | 139 ++++++------------ .../plugins/ccip/internal/cache/cache_test.go | 4 - .../ccip/internal/cache/snoozed_roots_test.go | 2 +- .../ccip/internal/cache/tokens_test.go | 21 +-- .../plugins/ccip/testhelpers/commitstore.go | 89 +++++++++++ .../ocr2/plugins/ccip/testhelpers/offramp.go | 105 +++++++++++++ .../ocr2/plugins/ccip/testhelpers/onramp.go | 56 +++++++ .../plugins/ccip/testhelpers/priceregistry.go | 52 +++++++ 11 files changed, 390 insertions(+), 176 deletions(-) create mode 100644 core/services/ocr2/plugins/ccip/testhelpers/commitstore.go create mode 100644 core/services/ocr2/plugins/ccip/testhelpers/offramp.go create mode 100644 core/services/ocr2/plugins/ccip/testhelpers/onramp.go create mode 100644 core/services/ocr2/plugins/ccip/testhelpers/priceregistry.go diff --git a/core/services/ocr2/plugins/ccip/commit_plugin_test.go b/core/services/ocr2/plugins/ccip/commit_plugin_test.go index a9dc54e94a..9a4af21a50 100644 --- a/core/services/ocr2/plugins/ccip/commit_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_plugin_test.go @@ -17,6 +17,7 @@ import ( "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/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" ) func TestGetCommitPluginFilterNamesFromSpec(t *testing.T) { @@ -87,13 +88,10 @@ func TestGetCommitPluginFilterNames(t *testing.T) { onRampAddr := common.HexToAddress("0xdafea492d9c6733ae3d56b7ed1adb60692c98bc2") priceRegAddr := common.HexToAddress("0xdafea492d9c6733ae3d56b7ed1adb60692c98bc3") offRampAddr := common.HexToAddress("0xDAFeA492D9c6733Ae3D56b7eD1AdB60692C98BC4") - mockCommitStore := mock_contracts.NewCommitStoreInterface(t) - mockCommitStore.On("GetStaticConfig", mock.Anything).Return(commit_store.CommitStoreStaticConfig{ - OnRamp: onRampAddr, - }, nil) - mockCommitStore.On("GetDynamicConfig", mock.Anything).Return(commit_store.CommitStoreDynamicConfig{ - PriceRegistry: priceRegAddr, - }, nil) + + mockCommitStore, _ := testhelpers.NewFakeCommitStore(t, 1) + mockCommitStore.SetStaticConfig(commit_store.CommitStoreStaticConfig{OnRamp: onRampAddr}) + mockCommitStore.SetDynamicConfig(commit_store.CommitStoreDynamicConfig{PriceRegistry: priceRegAddr}) srcLP := mocklp.NewLogPoller(t) dstLP := mocklp.NewLogPoller(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 63a3457f98..1b3180919f 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -29,7 +29,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" - mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" @@ -38,6 +37,7 @@ import ( "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/services/ocr2/plugins/ccip/testhelpers" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -99,11 +99,8 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { onRampAddress := utils.RandomAddress() sourceFinalityDepth := 10 - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("IsUnpausedAndARMHealthy", mock.Anything).Return(!tc.commitStoreIsPaused, nil) - if tc.commitStoreSeqNum > 0 { - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(tc.commitStoreSeqNum, nil) - } + commitStore, _ := testhelpers.NewFakeCommitStore(t, tc.commitStoreSeqNum) + commitStore.SetPaused(tc.commitStoreIsPaused) sourceEvents := ccipdata.NewMockReader(t) if len(tc.sendReqs) > 0 { @@ -159,6 +156,7 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { } func TestCommitReportingPlugin_Report(t *testing.T) { + testCases := []struct { name string observations []CommitObservation @@ -232,14 +230,12 @@ func TestCommitReportingPlugin_Report(t *testing.T) { } ctx := testutils.Context(t) - destPriceRegistryAddress := utils.RandomAddress() onRampAddress := utils.RandomAddress() sourceChainSelector := rand.Int() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - destPriceRegistry := mock_contracts.NewPriceRegistryInterface(t) - destPriceRegistry.On("Address").Return(destPriceRegistryAddress) + destPriceRegistry, destPriceRegistryAddress := testhelpers.NewFakePriceRegistry(t) destEvents := ccipdata.NewMockReader(t) destEvents.On("GetGasPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, uint64(sourceChainSelector), mock.Anything, 0).Return(tc.gasPriceUpdates, nil) @@ -317,8 +313,7 @@ func TestCommitReportingPlugin_ShouldAcceptFinalizedReport(t *testing.T) { t.Run("stale report should not be accepted", func(t *testing.T) { onChainSeqNum := uint64(100) - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + commitStore, _ := testhelpers.NewFakeCommitStore(t, onChainSeqNum) p := newPlugin() p.config.commitStore = commitStore @@ -341,8 +336,7 @@ func TestCommitReportingPlugin_ShouldAcceptFinalizedReport(t *testing.T) { t.Run("non-stale report should be accepted and added inflight", func(t *testing.T) { onChainSeqNum := uint64(100) - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + commitStore, _ := testhelpers.NewFakeCommitStore(t, onChainSeqNum) p := newPlugin() p.config.commitStore = commitStore @@ -391,14 +385,14 @@ func TestCommitReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { ctx := testutils.Context(t) p := &CommitReportingPlugin{} - commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore, _ := testhelpers.NewFakeCommitStore(t, 0) p.config.commitStore = commitStore p.inflightReports = newInflightCommitReportsContainer(time.Minute) p.lggr = logger.TestLogger(t) t.Run("should transmit when report is not stale", func(t *testing.T) { onChainSeqNum := uint64(100) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + commitStore.SetNextSequenceNumber(onChainSeqNum) // not-stale since report interval is not behind on chain seq num report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum, Max: onChainSeqNum + 10} encodedReport, err := abihelpers.EncodeCommitReport(report) @@ -410,7 +404,7 @@ func TestCommitReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { t.Run("should not transmit when report is stale", func(t *testing.T) { onChainSeqNum := uint64(100) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(onChainSeqNum, nil).Once() + commitStore.SetNextSequenceNumber(onChainSeqNum) // stale since report interval is behind on chain seq num report.Interval = commit_store.CommitStoreInterval{Min: onChainSeqNum - 2, Max: onChainSeqNum + 10} encodedReport, err := abihelpers.EncodeCommitReport(report) @@ -427,8 +421,6 @@ func TestCommitReportingPlugin_ShouldTransmitAcceptedReport(t *testing.T) { } func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) { - t.Parallel() - const defaultSourceChainSelector = 10 // we reuse this value across all test cases feeToken1 := common.HexToAddress("0xa") feeToken2 := common.HexToAddress("0xb") @@ -654,8 +646,6 @@ func TestCommitReportingPlugin_calculatePriceUpdates(t *testing.T) { } func TestCommitReportingPlugin_generatePriceUpdates(t *testing.T) { - t.Parallel() - val1e18 := func(val int64) *big.Int { return new(big.Int).Mul(big.NewInt(1e18), big.NewInt(val)) } const nTokens = 10 @@ -866,9 +856,8 @@ func TestCommitReportingPlugin_generatePriceUpdates(t *testing.T) { func TestCommitReportingPlugin_nextMinSeqNum(t *testing.T) { lggr := logger.TestLogger(t) - commitStore := mock_contracts.CommitStoreInterface{} - cp := CommitReportingPlugin{config: CommitPluginConfig{commitStore: &commitStore}, inflightReports: newInflightCommitReportsContainer(time.Hour)} root1 := utils.Keccak256Fixed(hexutil.MustDecode("0xaa")) + var tt = []struct { onChainMin uint64 inflight []commit_store.CommitStoreCommitReport @@ -904,7 +893,8 @@ func TestCommitReportingPlugin_nextMinSeqNum(t *testing.T) { }, } for _, tc := range tt { - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(tc.onChainMin, nil) + commitStore, _ := testhelpers.NewFakeCommitStore(t, tc.onChainMin) + cp := CommitReportingPlugin{config: CommitPluginConfig{commitStore: commitStore}, inflightReports: newInflightCommitReportsContainer(time.Hour)} epochAndRound := uint64(1) for _, rep := range tc.inflight { rc := rep @@ -927,7 +917,7 @@ func TestCommitReportingPlugin_isStaleReport(t *testing.T) { merkleRoot2 := utils.Keccak256Fixed([]byte("some merkle root 2")) t.Run("empty report", func(t *testing.T) { - commitStore := mock_contracts.NewCommitStoreInterface(t) + commitStore, _ := testhelpers.NewFakeCommitStore(t, 1) r := &CommitReportingPlugin{config: CommitPluginConfig{commitStore: commitStore}} isStale := r.isStaleReport(ctx, lggr, commit_store.CommitStoreCommitReport{}, false, types.ReportTimestamp{}) assert.True(t, isStale) @@ -935,9 +925,7 @@ func TestCommitReportingPlugin_isStaleReport(t *testing.T) { t.Run("merkle root", func(t *testing.T) { const expNextSeqNum = uint64(9) - - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(expNextSeqNum, nil) + commitStore, _ := testhelpers.NewFakeCommitStore(t, expNextSeqNum) r := &CommitReportingPlugin{ config: CommitPluginConfig{commitStore: commitStore}, @@ -1030,8 +1018,7 @@ func TestCommitReportingPlugin_calculateMinMaxSequenceNumbers(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { p := &CommitReportingPlugin{} - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(tc.commitStoreSeqNum, nil) + commitStore, _ := testhelpers.NewFakeCommitStore(t, tc.commitStoreSeqNum) p.config.commitStore = commitStore p.inflightReports = newInflightCommitReportsContainer(time.Minute) @@ -1119,8 +1106,7 @@ func TestCommitReportingPlugin_getLatestGasPriceUpdate(t *testing.T) { p := &CommitReportingPlugin{} p.inflightReports = newInflightCommitReportsContainer(time.Minute) p.lggr = lggr - destPriceRegistry := mock_contracts.NewPriceRegistryInterface(t) - destPriceRegistry.On("Address").Return(utils.RandomAddress()).Maybe() + destPriceRegistry, _ := testhelpers.NewFakePriceRegistry(t) p.destPriceRegistry = destPriceRegistry if tc.inflightGasPriceUpdate != nil { @@ -1230,9 +1216,7 @@ func TestCommitReportingPlugin_getLatestTokenPriceUpdates(t *testing.T) { t.Run(tc.name, func(t *testing.T) { p := &CommitReportingPlugin{} - priceRegAddr := utils.RandomAddress() - priceReg := mock_contracts.NewPriceRegistryInterface(t) - priceReg.On("Address").Return(priceRegAddr) + priceReg, priceRegAddr := testhelpers.NewFakePriceRegistry(t) p.destPriceRegistry = priceReg destEvents := ccipdata.NewMockReader(t) @@ -1302,8 +1286,6 @@ func Test_commitReportSize(t *testing.T) { } func Test_calculateIntervalConsensus(t *testing.T) { - t.Parallel() - tests := []struct { name string intervals []commit_store.CommitStoreInterval @@ -1347,8 +1329,6 @@ func Test_calculateIntervalConsensus(t *testing.T) { } func Test_calculateUsdPer1e18TokenAmount(t *testing.T) { - t.Parallel() - tests := []struct { name string price *big.Int diff --git a/core/services/ocr2/plugins/ccip/execution_plugin_test.go b/core/services/ocr2/plugins/ccip/execution_plugin_test.go index 6ab5a0098e..4fc7b2a747 100644 --- a/core/services/ocr2/plugins/ccip/execution_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_plugin_test.go @@ -2,7 +2,6 @@ package ccip import ( "context" - "fmt" "testing" "github.com/ethereum/go-ethereum/common" @@ -13,9 +12,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" "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" - mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/job" - ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" ) func TestGetExecutionPluginFilterNamesFromSpec(t *testing.T) { @@ -66,29 +64,19 @@ func TestGetExecutionPluginFilterNamesFromSpec(t *testing.T) { } func TestGetExecutionPluginFilterNames(t *testing.T) { - specContractID := common.HexToAddress("0xdafea492d9c6733ae3d56b7ed1adb60692c98bc1") // off-ramp addr - onRampAddr := common.HexToAddress("0xdafea492d9c6733ae3d56b7ed1adb60692c98bc2") commitStoreAddr := common.HexToAddress("0xdafea492d9c6733ae3d56b7ed1adb60692c98bc3") srcPriceRegAddr := common.HexToAddress("0xdafea492d9c6733ae3d56b7ed1adb60692c98bc9") dstPriceRegAddr := common.HexToAddress("0xdafea492d9c6733ae3d56b7ed1adb60692c98b19") - mockOffRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) - mockOffRamp.On("Address").Return(specContractID) - mockOffRamp.On("GetDynamicConfig", mock.Anything).Return( - evm_2_evm_offramp.EVM2EVMOffRampDynamicConfig{ - PriceRegistry: dstPriceRegAddr, - }, nil) + mockOffRamp, offRampAddr := testhelpers.NewFakeOffRamp(t) + mockOffRamp.SetDynamicConfig(evm_2_evm_offramp.EVM2EVMOffRampDynamicConfig{PriceRegistry: dstPriceRegAddr}) - mockOnRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) - mockOnRamp.On("TypeAndVersion", mock.Anything).Return(fmt.Sprintf("%s %s", ccipconfig.EVM2EVMOnRamp, "1.2.0"), nil) - mockOnRamp.On("GetDynamicConfig", mock.Anything).Return( - evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{ - PriceRegistry: srcPriceRegAddr, - }, nil) + mockOnRamp, onRampAddr := testhelpers.NewFakeOnRamp(t) + mockOnRamp.SetDynamicCfg(evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{PriceRegistry: srcPriceRegAddr}) srcLP := mocklp.NewLogPoller(t) srcFilters := []string{ - "Exec ccip sends - 0xdafea492D9c6733aE3d56B7ED1aDb60692C98bc2", + "Exec ccip sends - " + onRampAddr.String(), "Fee token added - 0xdAFea492D9c6733aE3d56B7ed1ADb60692c98bC9", "Fee token removed - 0xdAFea492D9c6733aE3d56B7ed1ADb60692c98bC9", } @@ -99,9 +87,9 @@ func TestGetExecutionPluginFilterNames(t *testing.T) { dstLP := mocklp.NewLogPoller(t) dstFilters := []string{ "Exec report accepts - 0xdafEa492d9C6733aE3D56b7eD1aDb60692c98bc3", - "Exec execution state changes - 0xdafeA492d9c6733Ae3d56B7ed1AdB60692C98bC1", - "Token pool added - 0xdafeA492d9c6733Ae3d56B7ed1AdB60692C98bC1", - "Token pool removed - 0xdafeA492d9c6733Ae3d56B7ed1AdB60692C98bC1", + "Exec execution state changes - " + offRampAddr.String(), + "Token pool added - " + offRampAddr.String(), + "Token pool removed - " + offRampAddr.String(), "Fee token added - 0xdaFEa492D9C6733Ae3D56b7ed1adB60692C98b19", "Fee token removed - 0xdaFEa492D9C6733Ae3D56b7ed1adB60692C98b19", } 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 6f87a7f83b..063ae0d08c 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -49,26 +49,26 @@ import ( func TestExecutionReportingPlugin_Observation(t *testing.T) { testCases := []struct { - name string - commitStoreDown bool - inflightReports []InflightInternalExecutionReport - unexpiredReports []ccipdata.Event[commit_store.CommitStoreReportAccepted] - sendRequests []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] - executedSeqNums []uint64 - blessedRoots [][32]byte - senderNonce uint64 - rateLimiterState evm_2_evm_offramp.RateLimiterTokenBucket - expErr bool + name string + commitStorePaused bool + inflightReports []InflightInternalExecutionReport + unexpiredReports []ccipdata.Event[commit_store.CommitStoreReportAccepted] + sendRequests []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] + executedSeqNums []uint64 + blessedRoots map[[32]byte]bool + senderNonce uint64 + rateLimiterState evm_2_evm_offramp.RateLimiterTokenBucket + expErr bool }{ { - name: "commit store is down", - commitStoreDown: true, - expErr: true, + name: "commit store is down", + commitStorePaused: true, + expErr: true, }, { - name: "happy flow", - commitStoreDown: false, - inflightReports: []InflightInternalExecutionReport{}, + name: "happy flow", + commitStorePaused: false, + inflightReports: []InflightInternalExecutionReport{}, unexpiredReports: []ccipdata.Event[commit_store.CommitStoreReportAccepted]{ { Data: commit_store.CommitStoreReportAccepted{ @@ -80,8 +80,8 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { }, }, }, - blessedRoots: [][32]byte{ - {123}, + blessedRoots: map[[32]byte]bool{ + [32]byte{123}: true, }, rateLimiterState: evm_2_evm_offramp.RateLimiterTokenBucket{ IsEnabled: false, @@ -115,20 +115,13 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { p.inflightReports.reports = tc.inflightReports p.lggr = logger.TestLogger(t) - commitStoreAddr := utils.RandomAddress() - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("IsUnpausedAndARMHealthy", mock.Anything).Return(!tc.commitStoreDown, nil) - commitStore.On("Address").Return(commitStoreAddr).Maybe() - for _, root := range tc.blessedRoots { - commitStore.On("IsBlessed", mock.Anything, root).Return(true, nil) - } + commitStore, commitStoreAddr := testhelpers.NewFakeCommitStore(t, 1) + commitStore.SetPaused(tc.commitStorePaused) + commitStore.SetBlessedRoots(tc.blessedRoots) p.config.commitStore = commitStore - offRampAddr := utils.RandomAddress() - offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) - offRamp.On("Address").Return(offRampAddr).Maybe() - offRamp.On("CurrentRateLimiterState", mock.Anything).Return(tc.rateLimiterState, nil).Maybe() - offRamp.On("GetSenderNonce", mock.Anything, mock.Anything).Return(tc.senderNonce, nil).Maybe() + offRamp, offRampAddr := testhelpers.NewFakeOffRamp(t) + offRamp.SetRateLimiterState(tc.rateLimiterState) p.config.offRamp = offRamp destEvents := ccipdata.NewMockReader(t) @@ -145,9 +138,7 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { Return(executionEvents, nil).Maybe() p.config.destEvents = destEvents - onRampAddr := utils.RandomAddress() - onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) - onRamp.On("Address").Return(onRampAddr).Maybe() + onRamp, onRampAddr := testhelpers.NewFakeOnRamp(t) p.config.onRamp = onRamp sourceEvents := ccipdata.NewMockReader(t) @@ -162,16 +153,10 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { }, nil).Maybe() p.cachedDestTokens = cachedDestTokens - priceRegistryAddr := utils.RandomAddress() - priceRegistry := mock_contracts.NewPriceRegistryInterface(t) - priceRegistry.On("Address").Return(priceRegistryAddr).Maybe() - priceRegistry.On("GetTokenPrices", mock.Anything, mock.Anything). - Return([]price_registry.InternalTimestampedPackedUint224{ - { - Value: big.NewInt(123), - Timestamp: uint32(time.Now().Unix()), - }, - }, nil).Maybe() + priceRegistry, _ := testhelpers.NewFakePriceRegistry(t) + priceRegistry.SetTokenPrices([]price_registry.InternalTimestampedPackedUint224{ + {Value: big.NewInt(123), Timestamp: uint32(time.Now().Unix())}, + }) p.destPriceRegistry = priceRegistry p.config.sourcePriceRegistry = priceRegistry @@ -230,10 +215,8 @@ func TestExecutionReportingPlugin_Report(t *testing.T) { p.lggr = logger.TestLogger(t) p.F = tc.f - commitStoreAddr := utils.RandomAddress() - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything).Return(tc.committedSeqNum+1, nil).Maybe() - commitStore.On("Address").Return(commitStoreAddr, nil).Maybe() + commitStore, _ := testhelpers.NewFakeCommitStore(t, tc.committedSeqNum) + p.config.commitStore = commitStore observations := make([]types.AttributedObservation, len(tc.observations)) @@ -277,7 +260,7 @@ func TestExecutionReportingPlugin_ShouldAcceptFinalizedReport(t *testing.T) { encodedReport, err := abihelpers.EncodeExecutionReport(report) require.NoError(t, err) - mockOffRamp := &mock_contracts.EVM2EVMOffRampInterface{} + mockOffRamp, _ := testhelpers.NewFakeOffRamp(t) plugin := ExecutionReportingPlugin{ config: ExecutionPluginConfig{ offRamp: mockOffRamp, @@ -368,11 +351,7 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { p := &ExecutionReportingPlugin{} p.lggr = logger.TestLogger(t) - commitStoreAddress := utils.RandomAddress() - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("Address").Return(commitStoreAddress) - commitStore.On("GetExpectedNextSequenceNumber", mock.Anything). - Return(executionReport.Messages[len(executionReport.Messages)-1].SequenceNumber+1, nil) + commitStore, commitStoreAddress := testhelpers.NewFakeCommitStore(t, executionReport.Messages[len(executionReport.Messages)-1].SequenceNumber+1) commitStore.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(math.MaxInt64), nil) p.config.commitStore = commitStore @@ -395,9 +374,7 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { p.config.leafHasher = leafHasher123{} - onRampAddr := utils.RandomAddress() - onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) - onRamp.On("Address").Return(onRampAddr) + onRamp, onRampAddr := testhelpers.NewFakeOnRamp(t) p.config.onRamp = onRamp sendReqs := make([]ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], len(observations)) @@ -431,7 +408,7 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { func TestExecutionReportingPlugin_buildBatch(t *testing.T) { c, _ := testhelpers.SetupChain(t) - mockOffRamp := mock_contracts.EVM2EVMOffRampInterface{} + offRamp, _ := testhelpers.NewFakeOffRamp(t) // We do this just to have the parsing available. onRamp, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(common.HexToAddress("0x1"), c) require.NoError(t, err) @@ -442,9 +419,8 @@ func TestExecutionReportingPlugin_buildBatch(t *testing.T) { srcNative := common.HexToAddress("0xc") plugin := ExecutionReportingPlugin{ config: ExecutionPluginConfig{ - offRamp: &mockOffRamp, - // We use a real onRamp for parsing - onRamp: onRamp, + offRamp: offRamp, + onRamp: onRamp, }, destWrappedNative: destNative, offchainConfig: ccipconfig.ExecOffchainConfig{ @@ -662,10 +638,7 @@ func TestExecutionReportingPlugin_buildBatch(t *testing.T) { for _, tc := range tt { tc := tc t.Run(tc.name, func(t *testing.T) { - for sender, nonce := range tc.offRampNoncesBySender { - mockOffRamp.On("GetSenderNonce", mock.Anything, sender).Return(nonce, nil) - } - + offRamp.SetSenderNonces(tc.offRampNoncesBySender) seqNrs := plugin.buildBatch( lggr, commitReportWithSendRequests{sendRequestsWithMeta: tc.reqs}, @@ -871,12 +844,8 @@ func TestExecutionReportingPlugin_destPoolRateLimits(t *testing.T) { p := &ExecutionReportingPlugin{} p.lggr = lggr - offRampAddr := utils.RandomAddress() - offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) - for destTk, pool := range tc.destPools { - offRamp.On("GetPoolByDestToken", mock.Anything, destTk).Return(pool, nil) - } - offRamp.On("Address").Return(offRampAddr) + offRamp, offRampAddr := testhelpers.NewFakeOffRamp(t) + offRamp.SetTokenPools(tc.destPools) p.config.offRamp = offRamp p.customTokenPoolFactory = func(ctx context.Context, poolAddress common.Address, _ bind.ContractBackend) (custom_token_pool.CustomTokenPoolInterface, error) { @@ -1045,14 +1014,10 @@ func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { p := &ExecutionReportingPlugin{} p.lggr = lggr - onRampAddr := utils.RandomAddress() - onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) - onRamp.On("Address").Return(onRampAddr).Maybe() + onRamp, onRampAddr := testhelpers.NewFakeOnRamp(t) p.config.onRamp = onRamp - offRampAddr := utils.RandomAddress() - offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) - offRamp.On("Address").Return(offRampAddr).Maybe() + offRamp, offRampAddr := testhelpers.NewFakeOffRamp(t) p.config.offRamp = offRamp sourceEvents := ccipdata.NewMockReader(t) @@ -1106,17 +1071,10 @@ func TestExecutionReportingPluginFactory_UpdateLogPollerFilters(t *testing.T) { destLP := lpMocks.NewLogPoller(t) sourceLP := lpMocks.NewLogPoller(t) - onRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) - onRamp.On("Address").Return(utils.RandomAddress(), nil) - - sourcePriceRegistry := mock_contracts.NewPriceRegistryInterface(t) - sourcePriceRegistry.On("Address").Return(utils.RandomAddress(), nil) - - commitStore := mock_contracts.NewCommitStoreInterface(t) - commitStore.On("Address").Return(utils.RandomAddress(), nil) - - offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) - offRamp.On("Address").Return(utils.RandomAddress(), nil) + onRamp, _ := testhelpers.NewFakeOnRamp(t) + sourcePriceRegistry, _ := testhelpers.NewFakePriceRegistry(t) + commitStore, _ := testhelpers.NewFakeCommitStore(t, 1) + offRamp, _ := testhelpers.NewFakeOffRamp(t) destPriceRegistryAddr := utils.RandomAddress() @@ -1391,11 +1349,8 @@ func Test_getTokensPrices(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - priceReg := mock_contracts.NewPriceRegistryInterface(t) - - priceReg.On("GetTokenPrices", mock.Anything, append(tc.feeTokens, tc.tokens...)). - Return(tc.retPrices, nil) - priceReg.On("Address").Return(common.HexToAddress("1234"), nil) + priceReg, _ := testhelpers.NewFakePriceRegistry(t) + priceReg.SetTokenPrices(tc.retPrices) prices, err := getTokensPrices(context.Background(), tc.feeTokens, priceReg, tc.tokens) if tc.expErr { diff --git a/core/services/ocr2/plugins/ccip/internal/cache/cache_test.go b/core/services/ocr2/plugins/ccip/internal/cache/cache_test.go index e37b09b95e..373b6cbf36 100644 --- a/core/services/ocr2/plugins/ccip/internal/cache/cache_test.go +++ b/core/services/ocr2/plugins/ccip/internal/cache/cache_test.go @@ -120,10 +120,6 @@ func TestGet_ConcurrentAccess(t *testing.T) { // 1 init block + 100 iterations require.Equal(t, int64(101), contract.lastChangeBlock) - - // Make sure that recent value is stored in cache - val := contract.copyCachedValue() - require.Equal(t, "value_99", val) } func newCachedContract(lp logpoller.LogPoller, cacheValue string, originValue []string, lastChangeBlock int64) *CachedChain[string] { diff --git a/core/services/ocr2/plugins/ccip/internal/cache/snoozed_roots_test.go b/core/services/ocr2/plugins/ccip/internal/cache/snoozed_roots_test.go index f3813df575..07ea14d32e 100644 --- a/core/services/ocr2/plugins/ccip/internal/cache/snoozed_roots_test.go +++ b/core/services/ocr2/plugins/ccip/internal/cache/snoozed_roots_test.go @@ -34,7 +34,7 @@ func TestEvictingElements(t *testing.T) { k1 := [32]byte{1} c.Snooze(k1) - time.Sleep(1 * time.Second) + time.Sleep(10 * time.Millisecond) assert.False(t, c.IsSnoozed(k1)) } diff --git a/core/services/ocr2/plugins/ccip/internal/cache/tokens_test.go b/core/services/ocr2/plugins/ccip/internal/cache/tokens_test.go index b562c44563..657e7d5fa1 100644 --- a/core/services/ocr2/plugins/ccip/internal/cache/tokens_test.go +++ b/core/services/ocr2/plugins/ccip/internal/cache/tokens_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -117,14 +118,12 @@ func TestCallOrigin(t *testing.T) { dst2 := common.HexToAddress("21") testCases := []struct { - name string - srcTokens []common.Address - srcToDst map[common.Address]common.Address - expErr bool + name string + srcToDst map[common.Address]common.Address + expErr bool }{ { - name: "base", - srcTokens: []common.Address{src1, src2}, + name: "base", srcToDst: map[common.Address]common.Address{ src1: dst1, src2: dst2, @@ -132,8 +131,7 @@ func TestCallOrigin(t *testing.T) { expErr: false, }, { - name: "dup dst token", - srcTokens: []common.Address{src1, src2}, + name: "dup dst token", srcToDst: map[common.Address]common.Address{ src1: dst1, src2: dst1, @@ -144,11 +142,8 @@ func TestCallOrigin(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - offRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) - offRamp.On("GetSupportedTokens", mock.Anything).Return(tc.srcTokens, nil) - for src, dst := range tc.srcToDst { - offRamp.On("GetDestinationToken", mock.Anything, src).Return(dst, nil) - } + offRamp, _ := testhelpers.NewFakeOffRamp(t) + offRamp.SetSourceToDestTokens(tc.srcToDst) o := supportedTokensOrigin{offRamp: offRamp} srcToDst, err := o.CallOrigin(context.Background()) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/commitstore.go b/core/services/ocr2/plugins/ccip/testhelpers/commitstore.go new file mode 100644 index 0000000000..b41104c44f --- /dev/null +++ b/core/services/ocr2/plugins/ccip/testhelpers/commitstore.go @@ -0,0 +1,89 @@ +package testhelpers + +import ( + "sync" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "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/utils" +) + +type FakeCommitStore struct { + *mock_contracts.CommitStoreInterface + + isPaused bool + blessedRoots map[[32]byte]bool + staticCfg commit_store.CommitStoreStaticConfig + dynamicCfg commit_store.CommitStoreDynamicConfig + nextSeqNum uint64 + + mu sync.RWMutex +} + +func NewFakeCommitStore(t *testing.T, nextSeqNum uint64) (*FakeCommitStore, common.Address) { + addr := utils.RandomAddress() + mockCommitStore := mock_contracts.NewCommitStoreInterface(t) + mockCommitStore.On("Address").Return(addr).Maybe() + + commitStore := &FakeCommitStore{CommitStoreInterface: mockCommitStore} + commitStore.SetPaused(false) + commitStore.SetNextSequenceNumber(nextSeqNum) + + return commitStore, addr +} + +func (cs *FakeCommitStore) SetPaused(isPaused bool) { + setCommitStoreVal(cs, func(cs *FakeCommitStore) { cs.isPaused = isPaused }) +} + +func (cs *FakeCommitStore) IsUnpausedAndARMHealthy(opts *bind.CallOpts) (bool, error) { + return getCommitStoreVal(cs, func(cs *FakeCommitStore) bool { return !cs.isPaused }), nil +} + +func (cs *FakeCommitStore) SetBlessedRoots(roots map[[32]byte]bool) { + setCommitStoreVal(cs, func(cs *FakeCommitStore) { cs.blessedRoots = roots }) +} + +func (cs *FakeCommitStore) IsBlessed(opts *bind.CallOpts, root [32]byte) (bool, error) { + return getCommitStoreVal(cs, func(cs *FakeCommitStore) bool { return cs.blessedRoots[root] }), nil +} + +func (cs *FakeCommitStore) SetStaticConfig(cfg commit_store.CommitStoreStaticConfig) { + setCommitStoreVal(cs, func(cs *FakeCommitStore) { cs.staticCfg = cfg }) +} + +func (cs *FakeCommitStore) GetStaticConfig(opts *bind.CallOpts) (commit_store.CommitStoreStaticConfig, error) { + return getCommitStoreVal(cs, func(cs *FakeCommitStore) commit_store.CommitStoreStaticConfig { return cs.staticCfg }), nil +} + +func (cs *FakeCommitStore) SetDynamicConfig(cfg commit_store.CommitStoreDynamicConfig) { + setCommitStoreVal(cs, func(cs *FakeCommitStore) { cs.dynamicCfg = cfg }) +} + +func (cs *FakeCommitStore) GetDynamicConfig(opts *bind.CallOpts) (commit_store.CommitStoreDynamicConfig, error) { + return getCommitStoreVal(cs, func(cs *FakeCommitStore) commit_store.CommitStoreDynamicConfig { return cs.dynamicCfg }), nil +} + +func (cs *FakeCommitStore) SetNextSequenceNumber(seqNum uint64) { + setCommitStoreVal(cs, func(cs *FakeCommitStore) { cs.nextSeqNum = seqNum }) +} + +func (cs *FakeCommitStore) GetExpectedNextSequenceNumber(opts *bind.CallOpts) (uint64, error) { + return getCommitStoreVal(cs, func(cs *FakeCommitStore) uint64 { return cs.nextSeqNum }), nil +} + +func getCommitStoreVal[T any](cs *FakeCommitStore, getter func(cs *FakeCommitStore) T) T { + cs.mu.RLock() + defer cs.mu.RUnlock() + return getter(cs) +} + +func setCommitStoreVal(cs *FakeCommitStore, setter func(cs *FakeCommitStore)) { + cs.mu.Lock() + defer cs.mu.Unlock() + setter(cs) +} diff --git a/core/services/ocr2/plugins/ccip/testhelpers/offramp.go b/core/services/ocr2/plugins/ccip/testhelpers/offramp.go new file mode 100644 index 0000000000..16b572b5c0 --- /dev/null +++ b/core/services/ocr2/plugins/ccip/testhelpers/offramp.go @@ -0,0 +1,105 @@ +package testhelpers + +import ( + "sync" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" + mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type FakeOffRamp struct { + *mock_contracts.EVM2EVMOffRampInterface + + rateLimiterState evm_2_evm_offramp.RateLimiterTokenBucket + senderNonces map[common.Address]uint64 + tokenToPool map[common.Address]common.Address + dynamicConfig evm_2_evm_offramp.EVM2EVMOffRampDynamicConfig + sourceToDestTokens map[common.Address]common.Address + + mu sync.RWMutex +} + +func NewFakeOffRamp(t *testing.T) (*FakeOffRamp, common.Address) { + addr := utils.RandomAddress() + mockOffRamp := mock_contracts.NewEVM2EVMOffRampInterface(t) + mockOffRamp.On("Address").Return(addr).Maybe() + + offRamp := &FakeOffRamp{EVM2EVMOffRampInterface: mockOffRamp} + return offRamp, addr +} + +func (o *FakeOffRamp) CurrentRateLimiterState(opts *bind.CallOpts) (evm_2_evm_offramp.RateLimiterTokenBucket, error) { + return getOffRampVal(o, func(o *FakeOffRamp) (evm_2_evm_offramp.RateLimiterTokenBucket, error) { return o.rateLimiterState, nil }) +} + +func (o *FakeOffRamp) SetRateLimiterState(state evm_2_evm_offramp.RateLimiterTokenBucket) { + setOffRampVal(o, func(o *FakeOffRamp) { o.rateLimiterState = state }) +} + +func (o *FakeOffRamp) GetSenderNonce(opts *bind.CallOpts, sender common.Address) (uint64, error) { + return getOffRampVal(o, func(o *FakeOffRamp) (uint64, error) { return o.senderNonces[sender], nil }) +} + +func (o *FakeOffRamp) SetSenderNonces(senderNonces map[common.Address]uint64) { + setOffRampVal(o, func(o *FakeOffRamp) { o.senderNonces = senderNonces }) +} + +func (o *FakeOffRamp) GetPoolByDestToken(opts *bind.CallOpts, destToken common.Address) (common.Address, error) { + return getOffRampVal(o, func(o *FakeOffRamp) (common.Address, error) { return o.tokenToPool[destToken], nil }) +} + +func (o *FakeOffRamp) SetTokenPools(tokenToPool map[common.Address]common.Address) { + setOffRampVal(o, func(o *FakeOffRamp) { o.tokenToPool = tokenToPool }) +} + +func (o *FakeOffRamp) GetDynamicConfig(opts *bind.CallOpts) (evm_2_evm_offramp.EVM2EVMOffRampDynamicConfig, error) { + return getOffRampVal(o, func(o *FakeOffRamp) (evm_2_evm_offramp.EVM2EVMOffRampDynamicConfig, error) { + return o.dynamicConfig, nil + }) +} + +func (o *FakeOffRamp) SetDynamicConfig(cfg evm_2_evm_offramp.EVM2EVMOffRampDynamicConfig) { + setOffRampVal(o, func(o *FakeOffRamp) { o.dynamicConfig = cfg }) +} + +func (o *FakeOffRamp) SetSourceToDestTokens(m map[common.Address]common.Address) { + setOffRampVal(o, func(o *FakeOffRamp) { o.sourceToDestTokens = m }) +} + +func (o *FakeOffRamp) GetSupportedTokens(opts *bind.CallOpts) ([]common.Address, error) { + return getOffRampVal(o, func(o *FakeOffRamp) ([]common.Address, error) { + tks := make([]common.Address, 0, len(o.sourceToDestTokens)) + for tk := range o.sourceToDestTokens { + tks = append(tks, tk) + } + return tks, nil + }) +} + +func (o *FakeOffRamp) GetDestinationToken(opts *bind.CallOpts, sourceToken common.Address) (common.Address, error) { + return getOffRampVal(o, func(o *FakeOffRamp) (common.Address, error) { + addr, exists := o.sourceToDestTokens[sourceToken] + if !exists { + return common.Address{}, errors.New("token does not exist") + } + return addr, nil + }) +} + +func getOffRampVal[T any](o *FakeOffRamp, getter func(o *FakeOffRamp) (T, error)) (T, error) { + o.mu.RLock() + defer o.mu.RUnlock() + return getter(o) +} + +func setOffRampVal(o *FakeOffRamp, setter func(o *FakeOffRamp)) { + o.mu.Lock() + defer o.mu.Unlock() + setter(o) +} diff --git a/core/services/ocr2/plugins/ccip/testhelpers/onramp.go b/core/services/ocr2/plugins/ccip/testhelpers/onramp.go new file mode 100644 index 0000000000..bd51b2ccd2 --- /dev/null +++ b/core/services/ocr2/plugins/ccip/testhelpers/onramp.go @@ -0,0 +1,56 @@ +package testhelpers + +import ( + "fmt" + "sync" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" + mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" + ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type FakeOnRamp struct { + *mock_contracts.EVM2EVMOnRampInterface + + dynamicConfig evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig + + mu sync.RWMutex +} + +func NewFakeOnRamp(t *testing.T) (*FakeOnRamp, common.Address) { + addr := utils.RandomAddress() + mockOnRamp := mock_contracts.NewEVM2EVMOnRampInterface(t) + mockOnRamp.On("Address").Return(addr).Maybe() + + onRamp := &FakeOnRamp{EVM2EVMOnRampInterface: mockOnRamp} + return onRamp, addr +} + +func (o *FakeOnRamp) TypeAndVersion(opts *bind.CallOpts) (string, error) { + return fmt.Sprintf("%s %s", ccipconfig.EVM2EVMOnRamp, "1.2.0"), nil +} + +func (o *FakeOnRamp) SetDynamicCfg(cfg evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig) { + setOnRampVal(o, func(o *FakeOnRamp) { o.dynamicConfig = cfg }) +} + +func (o *FakeOnRamp) GetDynamicConfig(opts *bind.CallOpts) (evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig, error) { + return getOnRampVal(o, func(o *FakeOnRamp) (evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig, error) { return o.dynamicConfig, nil }) +} + +func getOnRampVal[T any](o *FakeOnRamp, getter func(o *FakeOnRamp) (T, error)) (T, error) { + o.mu.RLock() + defer o.mu.RUnlock() + return getter(o) +} + +func setOnRampVal(o *FakeOnRamp, setter func(o *FakeOnRamp)) { + o.mu.Lock() + defer o.mu.Unlock() + setter(o) +} diff --git a/core/services/ocr2/plugins/ccip/testhelpers/priceregistry.go b/core/services/ocr2/plugins/ccip/testhelpers/priceregistry.go new file mode 100644 index 0000000000..5b127e685c --- /dev/null +++ b/core/services/ocr2/plugins/ccip/testhelpers/priceregistry.go @@ -0,0 +1,52 @@ +package testhelpers + +import ( + "sync" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" + mock_contracts "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/mocks" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type FakePriceRegistry struct { + *mock_contracts.PriceRegistryInterface + + tokenPrices []price_registry.InternalTimestampedPackedUint224 + + mu sync.RWMutex +} + +func NewFakePriceRegistry(t *testing.T) (*FakePriceRegistry, common.Address) { + addr := utils.RandomAddress() + mockPriceRegistry := mock_contracts.NewPriceRegistryInterface(t) + mockPriceRegistry.On("Address").Return(addr).Maybe() + + priceRegistry := &FakePriceRegistry{PriceRegistryInterface: mockPriceRegistry} + return priceRegistry, addr +} + +func (p *FakePriceRegistry) SetTokenPrices(prices []price_registry.InternalTimestampedPackedUint224) { + setPriceRegistryVal(p, func(p *FakePriceRegistry) { p.tokenPrices = prices }) +} + +func (p *FakePriceRegistry) GetTokenPrices(opts *bind.CallOpts, tokens []common.Address) ([]price_registry.InternalTimestampedPackedUint224, error) { + return getPriceRegistryVal(p, func(p *FakePriceRegistry) ([]price_registry.InternalTimestampedPackedUint224, error) { + return p.tokenPrices, nil + }) +} + +func getPriceRegistryVal[T any](p *FakePriceRegistry, getter func(p *FakePriceRegistry) (T, error)) (T, error) { + p.mu.RLock() + defer p.mu.RUnlock() + return getter(p) +} + +func setPriceRegistryVal(p *FakePriceRegistry, setter func(p *FakePriceRegistry)) { + p.mu.Lock() + defer p.mu.Unlock() + setter(p) +} From 008782c7b6356cf16cd9901873dd1911644ae167 Mon Sep 17 00:00:00 2001 From: dimkouv Date: Mon, 18 Sep 2023 13:57:26 +0300 Subject: [PATCH 25/25] rename 'events' to 'reader' --- .../ccip/commit_reporting_plugin_test.go | 38 ++++++++-------- .../plugins/ccip/execution_batch_building.go | 8 ++-- .../ocr2/plugins/ccip/execution_plugin.go | 6 +-- .../ccip/execution_reporting_plugin.go | 20 ++++----- .../ccip/execution_reporting_plugin_test.go | 43 +++++++++---------- 5 files changed, 57 insertions(+), 58 deletions(-) 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 70a39463e7..8d7e0ab51b 100644 --- a/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/commit_reporting_plugin_test.go @@ -103,9 +103,9 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { commitStore, _ := testhelpers.NewFakeCommitStore(t, tc.commitStoreSeqNum) commitStore.SetPaused(tc.commitStoreIsPaused) - sourceEvents := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockReader(t) if len(tc.sendReqs) > 0 { - sourceEvents.On("GetSendRequestsGteSeqNum", ctx, onRampAddress, tc.commitStoreSeqNum, false, sourceFinalityDepth). + sourceReader.On("GetSendRequestsGteSeqNum", ctx, onRampAddress, tc.commitStoreSeqNum, false, sourceFinalityDepth). Return(tc.sendReqs, nil) } @@ -135,7 +135,7 @@ func TestCommitReportingPlugin_Observation(t *testing.T) { p.config.commitStore = commitStore p.config.onRampAddress = onRampAddress p.offchainConfig.SourceFinalityDepth = uint32(sourceFinalityDepth) - p.config.sourceReader = sourceEvents + p.config.sourceReader = sourceReader p.tokenDecimalsCache = tokenDecimalsCache p.config.priceGetter = priceGet p.config.sourceFeeEstimator = sourceFeeEst @@ -238,21 +238,21 @@ func TestCommitReportingPlugin_Report(t *testing.T) { t.Run(tc.name, func(t *testing.T) { destPriceRegistry, destPriceRegistryAddress := testhelpers.NewFakePriceRegistry(t) - destEvents := ccipdata.NewMockReader(t) - destEvents.On("GetGasPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, uint64(sourceChainSelector), mock.Anything, 0).Return(tc.gasPriceUpdates, nil) - destEvents.On("GetTokenPriceUpdatesCreatedAfter", ctx, destPriceRegistryAddress, mock.Anything, 0).Return(tc.tokenPriceUpdates, nil) + destReader := ccipdata.NewMockReader(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) - sourceEvents := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockReader(t) if len(tc.sendRequests) > 0 { - sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddress, tc.expSeqNumRange.Min, tc.expSeqNumRange.Max, 0).Return(tc.sendRequests, nil) + sourceReader.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddress, tc.expSeqNumRange.Min, tc.expSeqNumRange.Max, 0).Return(tc.sendRequests, nil) } p := &CommitReportingPlugin{} p.lggr = logger.TestLogger(t) p.inflightReports = newInflightCommitReportsContainer(time.Minute) p.destPriceRegistry = destPriceRegistry - p.config.destReader = destEvents - p.config.sourceReader = sourceEvents + p.config.destReader = destReader + p.config.sourceReader = sourceReader p.config.onRampAddress = onRampAddress p.config.sourceChainSelector = uint64(sourceChainSelector) p.config.leafHasher = &leafHasher123{} @@ -1034,7 +1034,7 @@ func TestCommitReportingPlugin_calculateMinMaxSequenceNumbers(t *testing.T) { } } - sourceEvents := ccipdata.NewMockReader(t) + sourceReader := ccipdata.NewMockReader(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]{ @@ -1043,8 +1043,8 @@ func TestCommitReportingPlugin_calculateMinMaxSequenceNumbers(t *testing.T) { }, }) } - sourceEvents.On("GetSendRequestsGteSeqNum", ctx, mock.Anything, tc.expQueryMin, false, 0).Return(sendReqs, nil) - p.config.sourceReader = sourceEvents + sourceReader.On("GetSendRequestsGteSeqNum", ctx, mock.Anything, tc.expQueryMin, false, 0).Return(sendReqs, nil) + p.config.sourceReader = sourceReader minSeqNum, maxSeqNum, err := p.calculateMinMaxSequenceNumbers(ctx, lggr) if tc.expErr { @@ -1133,9 +1133,9 @@ func TestCommitReportingPlugin_getLatestGasPriceUpdate(t *testing.T) { }, }) } - destEvents := ccipdata.NewMockReader(t) - destEvents.On("GetGasPriceUpdatesCreatedAfter", ctx, mock.Anything, uint64(0), mock.Anything, 0).Return(events, nil) - p.config.destReader = destEvents + destReader := ccipdata.NewMockReader(t) + destReader.On("GetGasPriceUpdatesCreatedAfter", ctx, mock.Anything, uint64(0), mock.Anything, 0).Return(events, nil) + p.config.destReader = destReader } priceUpdate, err := p.getLatestGasPriceUpdate(ctx, time.Now(), tc.checkInflight) @@ -1220,7 +1220,7 @@ func TestCommitReportingPlugin_getLatestTokenPriceUpdates(t *testing.T) { priceReg, priceRegAddr := testhelpers.NewFakePriceRegistry(t) p.destPriceRegistry = priceReg - destEvents := ccipdata.NewMockReader(t) + destReader := ccipdata.NewMockReader(t) var events []ccipdata.Event[price_registry.PriceRegistryUsdPerTokenUpdated] for _, up := range tc.priceRegistryUpdates { events = append(events, ccipdata.Event[price_registry.PriceRegistryUsdPerTokenUpdated]{ @@ -1231,8 +1231,8 @@ func TestCommitReportingPlugin_getLatestTokenPriceUpdates(t *testing.T) { }, }) } - destEvents.On("GetTokenPriceUpdatesCreatedAfter", ctx, priceRegAddr, mock.Anything, 0).Return(events, nil) - p.config.destReader = destEvents + destReader.On("GetTokenPriceUpdatesCreatedAfter", ctx, priceRegAddr, mock.Anything, 0).Return(events, nil) + p.config.destReader = destReader p.inflightReports = newInflightCommitReportsContainer(time.Minute) if len(tc.inflightUpdates) > 0 { diff --git a/core/services/ocr2/plugins/ccip/execution_batch_building.go b/core/services/ocr2/plugins/ccip/execution_batch_building.go index f97c5325ed..ee02ccc311 100644 --- a/core/services/ocr2/plugins/ccip/execution_batch_building.go +++ b/core/services/ocr2/plugins/ccip/execution_batch_building.go @@ -22,10 +22,10 @@ func getProofData( lggr logger.Logger, hashLeaf hashlib.LeafHasherInterface[[32]byte], onRampAddress common.Address, - sourceEventsClient ccipdata.Reader, + sourceReader ccipdata.Reader, interval commit_store.CommitStoreInterval, ) (sendReqsInRoot []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested], leaves [][32]byte, tree *merklemulti.Tree[[32]byte], err error) { - sendReqs, err := sourceEventsClient.GetSendRequestsBetweenSeqNums( + sendReqs, err := sourceReader.GetSendRequestsBetweenSeqNums( ctx, onRampAddress, interval.Min, @@ -97,8 +97,8 @@ func validateSeqNumbers(serviceCtx context.Context, commitStore commit_store.Com } // Gets the commit report from the saved logs for a given sequence number. -func getCommitReportForSeqNum(ctx context.Context, destEvents ccipdata.Reader, commitStore commit_store.CommitStoreInterface, seqNum uint64) (commit_store.CommitStoreCommitReport, error) { - acceptedReports, err := destEvents.GetAcceptedCommitReportsGteSeqNum(ctx, commitStore.Address(), seqNum, 0) +func getCommitReportForSeqNum(ctx context.Context, destReader ccipdata.Reader, commitStore commit_store.CommitStoreInterface, seqNum uint64) (commit_store.CommitStoreCommitReport, error) { + acceptedReports, err := destReader.GetAcceptedCommitReportsGteSeqNum(ctx, commitStore.Address(), seqNum, 0) if err != nil { return commit_store.CommitStoreCommitReport{}, err } diff --git a/core/services/ocr2/plugins/ccip/execution_plugin.go b/core/services/ocr2/plugins/ccip/execution_plugin.go index 9bdfbbf028..d09ae5fc8e 100644 --- a/core/services/ocr2/plugins/ccip/execution_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_plugin.go @@ -112,8 +112,8 @@ func NewExecutionServices(lggr logger.Logger, jb job.Job, chainSet evm.LegacyCha lggr: execLggr, sourceLP: sourceChain.LogPoller(), destLP: destChain.LogPoller(), - sourceEvents: ccipdata.NewLogPollerReader(sourceChain.LogPoller(), execLggr, sourceChain.Client()), - destEvents: ccipdata.NewLogPollerReader(destChain.LogPoller(), execLggr, destChain.Client()), + sourceReader: ccipdata.NewLogPollerReader(sourceChain.LogPoller(), execLggr, sourceChain.Client()), + destReader: ccipdata.NewLogPollerReader(destChain.LogPoller(), execLggr, destChain.Client()), onRamp: onRamp, offRamp: offRamp, commitStore: commitStore, @@ -130,7 +130,7 @@ func NewExecutionServices(lggr logger.Logger, jb job.Job, chainSet evm.LegacyCha return nil, err } - argsNoPlugin.ReportingPluginFactory = promwrapper.NewPromFactory(wrappedPluginFactory, "CCIPExecution", string(spec.Relay), destChain.ID()) + argsNoPlugin.ReportingPluginFactory = promwrapper.NewPromFactory(wrappedPluginFactory, "CCIPExecution", spec.Relay, destChain.ID()) argsNoPlugin.Logger = relaylogger.NewOCRWrapper(execLggr, true, logError) oracle, err := libocr2.NewOracle(argsNoPlugin) if err != nil { diff --git a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go index 0250b2f272..1c3a6da7fe 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin.go @@ -54,8 +54,8 @@ var ( type ExecutionPluginConfig struct { lggr logger.Logger sourceLP, destLP logpoller.LogPoller - sourceEvents ccipdata.Reader - destEvents ccipdata.Reader + sourceReader ccipdata.Reader + destReader ccipdata.Reader onRamp evm_2_evm_onramp.EVM2EVMOnRampInterface offRamp evm_2_evm_offramp.EVM2EVMOffRampInterface commitStore commit_store.CommitStoreInterface @@ -238,7 +238,7 @@ type evm2EVMOnRampCCIPSendRequestedWithMeta struct { func (r *ExecutionReportingPlugin) getExecutableObservations(ctx context.Context, lggr logger.Logger, timestamp types.ReportTimestamp, inflight []InflightInternalExecutionReport) ([]ObservedMessage, error) { unexpiredReports, err := getUnexpiredCommitReports( ctx, - r.config.destEvents, + r.config.destReader, r.config.commitStore, r.onchainConfig.PermissionLessExecutionThresholdDuration(), ) @@ -441,7 +441,7 @@ func (r *ExecutionReportingPlugin) sourceDestinationTokens(ctx context.Context) // before. It doesn't matter if the executed succeeded, since we don't retry previous // attempts even if they failed. Value in the map indicates whether the log is finalized or not. func (r *ExecutionReportingPlugin) getExecutedSeqNrsInRange(ctx context.Context, min, max uint64, latestBlock int64) (map[uint64]bool, error) { - stateChanges, err := r.config.destEvents.GetExecutionStateChangesBetweenSeqNums( + stateChanges, err := r.config.destReader.GetExecutionStateChangesBetweenSeqNums( ctx, r.config.offRamp.Address(), min, @@ -738,7 +738,7 @@ func (r *ExecutionReportingPlugin) getReportsWithSendRequests( var sendRequests []ccipdata.Event[evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested] eg.Go(func() error { - sendReqs, err := r.config.sourceEvents.GetSendRequestsBetweenSeqNums( + sendReqs, err := r.config.sourceReader.GetSendRequestsBetweenSeqNums( ctx, r.config.onRamp.Address(), intervalMin, @@ -754,7 +754,7 @@ func (r *ExecutionReportingPlugin) getReportsWithSendRequests( var executedSeqNums map[uint64]bool eg.Go(func() error { - latestBlock, err := r.config.destEvents.LatestBlock(ctx) + latestBlock, err := r.config.destReader.LatestBlock(ctx) if err != nil { return err } @@ -823,13 +823,13 @@ func (r *ExecutionReportingPlugin) buildReport(ctx context.Context, lggr logger. if err := validateSeqNumbers(ctx, r.config.commitStore, observedMessages); err != nil { return nil, err } - commitReport, err := getCommitReportForSeqNum(ctx, r.config.destEvents, r.config.commitStore, observedMessages[0].SeqNr) + commitReport, err := getCommitReportForSeqNum(ctx, r.config.destReader, r.config.commitStore, observedMessages[0].SeqNr) if err != nil { return nil, err } lggr.Infow("Building execution report", "observations", observedMessages, "merkleRoot", hexutil.Encode(commitReport.MerkleRoot[:]), "report", commitReport) - sendReqsInRoot, leaves, tree, err := getProofData(ctx, lggr, r.config.leafHasher, r.config.onRamp.Address(), r.config.sourceEvents, commitReport.Interval) + sendReqsInRoot, leaves, tree, err := getProofData(ctx, lggr, r.config.leafHasher, r.config.onRamp.Address(), r.config.sourceReader, commitReport.Interval) if err != nil { return nil, err } @@ -1126,11 +1126,11 @@ func getTokensPrices(ctx context.Context, feeTokens []common.Address, priceRegis func getUnexpiredCommitReports( ctx context.Context, - destEvents ccipdata.Reader, + destReader ccipdata.Reader, commitStore commit_store.CommitStoreInterface, permissionExecutionThreshold time.Duration, ) ([]commit_store.CommitStoreCommitReport, error) { - acceptedReports, err := destEvents.GetAcceptedCommitReportsGteTimestamp( + acceptedReports, err := destReader.GetAcceptedCommitReportsGteTimestamp( ctx, commitStore.Address(), time.Now().Add(-permissionExecutionThreshold), 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 063ae0d08c..c5b9722b3f 100644 --- a/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go +++ b/core/services/ocr2/plugins/ccip/execution_reporting_plugin_test.go @@ -124,27 +124,27 @@ func TestExecutionReportingPlugin_Observation(t *testing.T) { offRamp.SetRateLimiterState(tc.rateLimiterState) p.config.offRamp = offRamp - destEvents := ccipdata.NewMockReader(t) - destEvents.On("GetAcceptedCommitReportsGteTimestamp", ctx, commitStoreAddr, mock.Anything, 0). + destReader := ccipdata.NewMockReader(t) + destReader.On("GetAcceptedCommitReportsGteTimestamp", ctx, commitStoreAddr, mock.Anything, 0). Return(tc.unexpiredReports, nil).Maybe() - destEvents.On("LatestBlock", ctx).Return(int64(1234), nil).Maybe() + destReader.On("LatestBlock", ctx).Return(int64(1234), nil).Maybe() var executionEvents []ccipdata.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged] for _, seqNum := range tc.executedSeqNums { executionEvents = append(executionEvents, ccipdata.Event[evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged]{ Data: evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged{SequenceNumber: seqNum}, }) } - destEvents.On("GetExecutionStateChangesBetweenSeqNums", ctx, offRampAddr, mock.Anything, mock.Anything, 0). + destReader.On("GetExecutionStateChangesBetweenSeqNums", ctx, offRampAddr, mock.Anything, mock.Anything, 0). Return(executionEvents, nil).Maybe() - p.config.destEvents = destEvents + p.config.destReader = destReader onRamp, onRampAddr := testhelpers.NewFakeOnRamp(t) p.config.onRamp = onRamp - sourceEvents := ccipdata.NewMockReader(t) - sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, mock.Anything, mock.Anything, 0). + sourceReader := ccipdata.NewMockReader(t) + sourceReader.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, mock.Anything, mock.Anything, 0). Return(tc.sendRequests, nil).Maybe() - p.config.sourceEvents = sourceEvents + p.config.sourceReader = sourceReader cachedDestTokens := cache.NewMockAutoSync[cache.CachedTokens](t) cachedDestTokens.On("Get", ctx).Return(cache.CachedTokens{ @@ -355,9 +355,8 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { commitStore.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(math.MaxInt64), nil) p.config.commitStore = commitStore - destEvents := ccipdata.NewMockReader(t) - - destEvents.On("GetAcceptedCommitReportsGteSeqNum", ctx, commitStoreAddress, observations[0].SeqNr, 0). + destReader := ccipdata.NewMockReader(t) + destReader.On("GetAcceptedCommitReportsGteSeqNum", ctx, commitStoreAddress, observations[0].SeqNr, 0). Return([]ccipdata.Event[commit_store.CommitStoreReportAccepted]{ { Data: commit_store.CommitStoreReportAccepted{ @@ -370,7 +369,7 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { }, }, }, nil) - p.config.destEvents = destEvents + p.config.destReader = destReader p.config.leafHasher = leafHasher123{} @@ -396,10 +395,10 @@ func TestExecutionReportingPlugin_buildReport(t *testing.T) { }}, } } - sourceEvents := ccipdata.NewMockReader(t) - sourceEvents.On("GetSendRequestsBetweenSeqNums", + sourceReader := ccipdata.NewMockReader(t) + sourceReader.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, observations[0].SeqNr, observations[len(observations)-1].SeqNr, 0).Return(sendReqs, nil) - p.config.sourceEvents = sourceEvents + p.config.sourceReader = sourceReader execReport, err := p.buildReport(ctx, p.lggr, observations) assert.NoError(t, err) @@ -1020,13 +1019,13 @@ func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { offRamp, offRampAddr := testhelpers.NewFakeOffRamp(t) p.config.offRamp = offRamp - sourceEvents := ccipdata.NewMockReader(t) - sourceEvents.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, tc.expQueryMin, tc.expQueryMax, 0). + sourceReader := ccipdata.NewMockReader(t) + sourceReader.On("GetSendRequestsBetweenSeqNums", ctx, onRampAddr, tc.expQueryMin, tc.expQueryMax, 0). Return(tc.onchainEvents, nil).Maybe() - p.config.sourceEvents = sourceEvents + p.config.sourceReader = sourceReader - destEvents := ccipdata.NewMockReader(t) - destEvents.On("LatestBlock", ctx).Return(tc.destLatestBlock, nil).Maybe() + 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]{ @@ -1034,8 +1033,8 @@ func TestExecutionReportingPlugin_getReportsWithSendRequests(t *testing.T) { BlockMeta: ccipdata.BlockMeta{BlockNumber: tc.destLatestBlock - 10}, }) } - destEvents.On("GetExecutionStateChangesBetweenSeqNums", ctx, offRampAddr, tc.expQueryMin, tc.expQueryMax, 0).Return(executedEvents, nil).Maybe() - p.config.destEvents = destEvents + destReader.On("GetExecutionStateChangesBetweenSeqNums", ctx, offRampAddr, tc.expQueryMin, tc.expQueryMax, 0).Return(executedEvents, nil).Maybe() + p.config.destReader = destReader populatedReports, err := p.getReportsWithSendRequests(ctx, tc.reports) if tc.expErr {