From 76999e58a4b2f52a5a21f842bd015d7094c2168e Mon Sep 17 00:00:00 2001 From: ferglor <19188060+ferglor@users.noreply.github.com> Date: Wed, 30 Aug 2023 20:39:52 +0100 Subject: [PATCH] Read logs for block number and trigger config (#10354) * Read logs for block number and trigger config * Use indexed logs * Cleanup * Reduce confs * Update test coverage * Continue if id cannot be parsed * Please the linter * Batch the log trigger upkeep refresh * Batch the upkeep refresh * No longer use errgroup * Dont error on a single batch, add a processing delay, add comments * Update tests * Add a todo * Refresh active upkeeps before batching * update comment * Only take max values * Filter log upkeeps at a higher level * Refresh after collecting the log upkeep IDs, generate hashes --- .../evm21/logprovider/provider_life_cycle.go | 1 + .../ocr2/plugins/ocr2keeper/evm21/registry.go | 107 ++++- .../evm21/registry_check_pipeline_test.go | 5 + .../plugins/ocr2keeper/evm21/registry_test.go | 380 +++++++++++++++++- 4 files changed, 477 insertions(+), 16 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go index 758d6e4b744..23b999ee800 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go @@ -19,6 +19,7 @@ var ( ) func (p *logEventProvider) RefreshActiveUpkeeps(ids ...*big.Int) ([]*big.Int, error) { + // Exploratory: investigate how we can batch the refresh if len(ids) == 0 { return nil, nil } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go index 99f229b0b71..53316423f71 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go @@ -13,10 +13,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" coreTypes "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" - "github.com/patrickmn/go-cache" + "github.com/pkg/errors" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "go.uber.org/multierr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -37,6 +37,9 @@ const ( defaultAllowListExpiration = 20 * time.Minute // allowListCleanupInterval decides when the expired items in allowList cache will be deleted. allowListCleanupInterval = 5 * time.Minute + // TODO decide on a value for this + indexedLogsConfirmations = 10 + logTriggerRefreshBatchSize = 32 ) var ( @@ -270,34 +273,108 @@ func (r *EvmRegistry) refreshActiveUpkeeps() error { } r.active.Reset(ids...) - return r.refreshLogTriggerUpkeeps(ids) -} - -// refreshLogTriggerUpkeeps refreshes the active upkeep ids for log trigger upkeeps -// -// TODO: check for updated config for log trigger upkeeps and update it, currently we ignore them. -func (r *EvmRegistry) refreshLogTriggerUpkeeps(ids []*big.Int) error { - logTriggerIDs := make([]*big.Int, 0) + var logTriggerIDs []*big.Int for _, id := range ids { uid := &ocr2keepers.UpkeepIdentifier{} if ok := uid.FromBigInt(id); !ok { r.lggr.Warnf("failed to parse upkeep id %s", id.String()) + continue } switch core.GetUpkeepType(*uid) { case ocr2keepers.LogTrigger: logTriggerIDs = append(logTriggerIDs, id) - default: } } + newUpkeeps, err := r.logEventProvider.RefreshActiveUpkeeps(logTriggerIDs...) if err != nil { return fmt.Errorf("failed to refresh active upkeep ids in log event provider: %w", err) } + + return r.refreshLogTriggerUpkeeps(newUpkeeps) +} + +// refreshLogTriggerUpkeeps refreshes the active upkeep ids for log trigger upkeeps +// +// TODO: check for updated config for log trigger upkeeps and update it, currently we ignore them. +func (r *EvmRegistry) refreshLogTriggerUpkeeps(ids []*big.Int) error { + var err error + for i := 0; i < len(ids); i += logTriggerRefreshBatchSize { + end := i + logTriggerRefreshBatchSize + if end > len(ids) { + end = len(ids) + } + idBatch := ids[i:end] + + if batchErr := r.refreshLogTriggerUpkeepsBatch(idBatch); batchErr != nil { + multierr.AppendInto(&err, batchErr) + } + + time.Sleep(500 * time.Millisecond) + } + + return err +} + +func (r *EvmRegistry) refreshLogTriggerUpkeepsBatch(logTriggerIDs []*big.Int) error { + var logTriggerHashes []common.Hash + for _, id := range logTriggerIDs { + logTriggerHashes = append(logTriggerHashes, common.BigToHash(id)) + } + + unpausedLogs, err := r.poller.IndexedLogs(iregistry21.IKeeperRegistryMasterUpkeepUnpaused{}.Topic(), r.addr, 1, logTriggerHashes, indexedLogsConfirmations, pg.WithParentCtx(r.ctx)) + if err != nil { + return err + } + configSetLogs, err := r.poller.IndexedLogs(iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{}.Topic(), r.addr, 1, logTriggerHashes, indexedLogsConfirmations, pg.WithParentCtx(r.ctx)) + if err != nil { + return err + } + + logs := append(unpausedLogs, configSetLogs...) + + configSetBlockNumbers := map[string]uint64{} + unpausedBlockNumbers := map[string]uint64{} + perUpkeepConfig := map[string][]byte{} + + for _, log := range logs { + rawLog := log.ToGethLog() + abilog, err := r.registry.ParseLog(rawLog) + if err != nil { + return err + } + switch l := abilog.(type) { + case *iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet: + if rawLog.BlockNumber > configSetBlockNumbers[l.Id.String()] { + configSetBlockNumbers[l.Id.String()] = rawLog.BlockNumber + perUpkeepConfig[l.Id.String()] = l.TriggerConfig + } + case *iregistry21.IKeeperRegistryMasterUpkeepUnpaused: + if rawLog.BlockNumber > unpausedBlockNumbers[l.Id.String()] { + unpausedBlockNumbers[l.Id.String()] = rawLog.BlockNumber + } + } + } + var merr error - for _, id := range newUpkeeps { - // TODO: find the ConfigSet/UpkeepUnpaused events for this upkeep and pass cfg and block number - // block number should be taken from UpkeepUnpaused if it's block is higher than ConfigSet - if err := r.updateTriggerConfig(id, nil, 0); err != nil { + for _, id := range logTriggerIDs { + logBlock, ok := configSetBlockNumbers[id.String()] + if !ok { + r.lggr.Warnf("unable to find config set block number for %s", id.String()) + continue + } + + config, ok := perUpkeepConfig[id.String()] + if !ok { + r.lggr.Warnf("unable to find per upkeep config for %s", id.String()) + continue + } + + // In case an upkeep was paused then unpaused after a config set event, start the config from the unpaused block number + if unpausedBlockNumbers[id.String()] > logBlock { + logBlock = unpausedBlockNumbers[id.String()] + } + if err := r.updateTriggerConfig(id, config, logBlock); err != nil { merr = goerrors.Join(merr, fmt.Errorf("failed to update trigger config for upkeep id %s: %w", id.String(), err)) } } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go index ec691d190ef..69364396e8b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go @@ -208,12 +208,17 @@ func TestRegistry_VerifyCheckBlock(t *testing.T) { type mockLogPoller struct { logpoller.LogPoller GetBlocksRangeFn func(ctx context.Context, numbers []uint64, qopts ...pg.QOpt) ([]logpoller.LogPollerBlock, error) + IndexedLogsFn func(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) } func (p *mockLogPoller) GetBlocksRange(ctx context.Context, numbers []uint64, qopts ...pg.QOpt) ([]logpoller.LogPollerBlock, error) { return p.GetBlocksRangeFn(ctx, numbers, qopts...) } +func (p *mockLogPoller) IndexedLogs(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) { + return p.IndexedLogsFn(eventSig, address, topicIndex, topicValues, confs, qopts...) +} + func TestRegistry_VerifyLogExists(t *testing.T) { lggr := logger.TestLogger(t) upkeepId := ocr2keepers.UpkeepIdentifier{} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go index e17145ff81e..1816667be7f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go @@ -1,18 +1,30 @@ package evm import ( + "errors" "fmt" "math/big" "testing" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - + coreTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + types3 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -182,3 +194,369 @@ func TestPollLogs(t *testing.T) { }) } } + +func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { + for _, tc := range []struct { + name string + ids []*big.Int + logEventProvider logprovider.LogEventProvider + poller logpoller.LogPoller + registry Registry + packer encoding.Packer + expectsErr bool + wantErr error + }{ + { + name: "an error is returned when fetching indexed logs for IKeeperRegistryMasterUpkeepUnpaused errors", + ids: []*big.Int{ + core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + }, + logEventProvider: &mockLogEventProvider{ + RefreshActiveUpkeepsFn: func(ids ...*big.Int) ([]*big.Int, error) { + // of the ids specified in the test, only one is a valid log trigger upkeep + assert.Equal(t, 1, len(ids)) + return ids, nil + }, + }, + poller: &mockLogPoller{ + IndexedLogsFn: func(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) { + if eventSig == (iregistry21.IKeeperRegistryMasterUpkeepUnpaused{}.Topic()) { + return nil, errors.New("indexed logs boom") + } + return nil, nil + }, + }, + expectsErr: true, + wantErr: errors.New("indexed logs boom"), + }, + { + name: "an error is returned when fetching indexed logs for IKeeperRegistryMasterUpkeepTriggerConfigSet errors", + ids: []*big.Int{ + core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types.ConditionTrigger, "abc").BigInt(), + big.NewInt(-1), + }, + logEventProvider: &mockLogEventProvider{ + RefreshActiveUpkeepsFn: func(ids ...*big.Int) ([]*big.Int, error) { + // of the ids specified in the test, only one is a valid log trigger upkeep + assert.Equal(t, 1, len(ids)) + return ids, nil + }, + }, + poller: &mockLogPoller{ + IndexedLogsFn: func(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) { + if eventSig == (iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{}.Topic()) { + return nil, errors.New("indexed logs boom") + } + return nil, nil + }, + }, + expectsErr: true, + wantErr: errors.New("indexed logs boom"), + }, + { + name: "an error is returned when parsing the logs using the registry errors", + ids: []*big.Int{ + core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types.ConditionTrigger, "abc").BigInt(), + big.NewInt(-1), + }, + logEventProvider: &mockLogEventProvider{ + RefreshActiveUpkeepsFn: func(ids ...*big.Int) ([]*big.Int, error) { + // of the ids specified in the test, only one is a valid log trigger upkeep + assert.Equal(t, 1, len(ids)) + return ids, nil + }, + }, + poller: &mockLogPoller{ + IndexedLogsFn: func(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) { + return []logpoller.Log{ + {}, + }, nil + }, + }, + registry: &mockRegistry{ + ParseLogFn: func(log coreTypes.Log) (generated.AbigenLog, error) { + return nil, errors.New("parse log boom") + }, + }, + expectsErr: true, + wantErr: errors.New("parse log boom"), + }, + { + name: "an error is returned when registering the filter errors", + ids: []*big.Int{ + core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types.ConditionTrigger, "abc").BigInt(), + big.NewInt(-1), + }, + logEventProvider: &mockLogEventProvider{ + RefreshActiveUpkeepsFn: func(ids ...*big.Int) ([]*big.Int, error) { + // of the ids specified in the test, only one is a valid log trigger upkeep + assert.Equal(t, 1, len(ids)) + return ids, nil + }, + RegisterFilterFn: func(opts logprovider.FilterOptions) error { + return errors.New("register filter boom") + }, + }, + poller: &mockLogPoller{ + IndexedLogsFn: func(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) { + return []logpoller.Log{ + { + BlockNumber: 1, + }, + { + BlockNumber: 2, + }, + }, nil + }, + }, + registry: &mockRegistry{ + ParseLogFn: func(log coreTypes.Log) (generated.AbigenLog, error) { + if log.BlockNumber == 1 { + return &iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{ + TriggerConfig: []byte{1, 2, 3}, + Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + }, nil + } + return &iregistry21.IKeeperRegistryMasterUpkeepUnpaused{ + Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + }, nil + }, + GetUpkeepTriggerConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return nil, nil + }, + }, + packer: &mockPacker{ + UnpackLogTriggerConfigFn: func(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) { + return automation_utils_2_1.LogTriggerConfig{}, nil + }, + }, + expectsErr: true, + wantErr: errors.New("failed to update trigger config for upkeep id 452312848583266388373324160190187140521564213162920931037143039228013182976: failed to register log filter: register filter boom"), + }, + { + name: "log trigger upkeeps are refreshed without error", + ids: []*big.Int{ + core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + core.GenUpkeepID(types.LogTrigger, "def").BigInt(), + core.GenUpkeepID(types.ConditionTrigger, "abc").BigInt(), + big.NewInt(-1), + }, + logEventProvider: &mockLogEventProvider{ + RefreshActiveUpkeepsFn: func(ids ...*big.Int) ([]*big.Int, error) { + // of the ids specified in the test, only two are a valid log trigger upkeep + assert.Equal(t, 2, len(ids)) + return ids, nil + }, + RegisterFilterFn: func(opts logprovider.FilterOptions) error { + return nil + }, + }, + poller: &mockLogPoller{ + IndexedLogsFn: func(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) { + return []logpoller.Log{ + { + BlockNumber: 2, + }, + { + BlockNumber: 1, + }, + }, nil + }, + }, + registry: &mockRegistry{ + ParseLogFn: func(log coreTypes.Log) (generated.AbigenLog, error) { + if log.BlockNumber == 1 { + return &iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{ + Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + TriggerConfig: []byte{1, 2, 3}, + }, nil + } + return &iregistry21.IKeeperRegistryMasterUpkeepUnpaused{ + Id: core.GenUpkeepID(types.LogTrigger, "def").BigInt(), + }, nil + }, + GetUpkeepTriggerConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return nil, nil + }, + }, + packer: &mockPacker{ + UnpackLogTriggerConfigFn: func(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) { + return automation_utils_2_1.LogTriggerConfig{}, nil + }, + }, + }, + { + name: "log trigger upkeeps are refreshed in batch without error", + ids: func() []*big.Int { + res := []*big.Int{} + for i := 0; i < logTriggerRefreshBatchSize*3; i++ { + res = append(res, core.GenUpkeepID(types.LogTrigger, fmt.Sprintf("%d", i)).BigInt()) + } + return res + }(), + logEventProvider: &mockLogEventProvider{ + RefreshActiveUpkeepsFn: func(ids ...*big.Int) ([]*big.Int, error) { + assert.Equal(t, logTriggerRefreshBatchSize, len(ids)) + return ids, nil + }, + RegisterFilterFn: func(opts logprovider.FilterOptions) error { + return nil + }, + }, + poller: &mockLogPoller{ + IndexedLogsFn: func(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) { + return []logpoller.Log{ + { + BlockNumber: 2, + }, + { + BlockNumber: 1, + }, + }, nil + }, + }, + registry: &mockRegistry{ + ParseLogFn: func(log coreTypes.Log) (generated.AbigenLog, error) { + if log.BlockNumber == 1 { + return &iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{ + Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + TriggerConfig: []byte{1, 2, 3}, + }, nil + } + return &iregistry21.IKeeperRegistryMasterUpkeepUnpaused{ + Id: core.GenUpkeepID(types.LogTrigger, "def").BigInt(), + }, nil + }, + GetUpkeepTriggerConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return nil, nil + }, + }, + packer: &mockPacker{ + UnpackLogTriggerConfigFn: func(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) { + return automation_utils_2_1.LogTriggerConfig{}, nil + }, + }, + }, + { + name: "log trigger upkeeps are refreshed in batch, with a partial batch without error", + ids: func() []*big.Int { + res := []*big.Int{} + for i := 0; i < logTriggerRefreshBatchSize+3; i++ { + res = append(res, core.GenUpkeepID(types.LogTrigger, fmt.Sprintf("%d", i)).BigInt()) + } + return res + }(), + logEventProvider: &mockLogEventProvider{ + RefreshActiveUpkeepsFn: func(ids ...*big.Int) ([]*big.Int, error) { + if len(ids) != logTriggerRefreshBatchSize { + assert.Equal(t, 3, len(ids)) + } + return ids, nil + }, + RegisterFilterFn: func(opts logprovider.FilterOptions) error { + return nil + }, + }, + poller: &mockLogPoller{ + IndexedLogsFn: func(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs int, qopts ...pg.QOpt) ([]logpoller.Log, error) { + return []logpoller.Log{ + { + BlockNumber: 2, + }, + { + BlockNumber: 1, + }, + }, nil + }, + }, + registry: &mockRegistry{ + ParseLogFn: func(log coreTypes.Log) (generated.AbigenLog, error) { + if log.BlockNumber == 1 { + return &iregistry21.IKeeperRegistryMasterUpkeepTriggerConfigSet{ + Id: core.GenUpkeepID(types.LogTrigger, "abc").BigInt(), + TriggerConfig: []byte{1, 2, 3}, + }, nil + } + return &iregistry21.IKeeperRegistryMasterUpkeepUnpaused{ + Id: core.GenUpkeepID(types.LogTrigger, "def").BigInt(), + }, nil + }, + GetUpkeepTriggerConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return nil, nil + }, + }, + packer: &mockPacker{ + UnpackLogTriggerConfigFn: func(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) { + return automation_utils_2_1.LogTriggerConfig{}, nil + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + lggr := logger.TestLogger(t) + var hb types3.HeadBroadcaster + var lp logpoller.LogPoller + + bs := NewBlockSubscriber(hb, lp, lggr) + + registry := &EvmRegistry{ + addr: common.BigToAddress(big.NewInt(1)), + poller: tc.poller, + logEventProvider: tc.logEventProvider, + chLog: make(chan logpoller.Log, 10), + bs: bs, + registry: tc.registry, + packer: tc.packer, + lggr: lggr, + } + + err := registry.refreshLogTriggerUpkeeps(tc.ids) + if tc.expectsErr { + assert.Error(t, err) + assert.Equal(t, err.Error(), tc.wantErr.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +type mockLogEventProvider struct { + logprovider.LogEventProvider + RefreshActiveUpkeepsFn func(ids ...*big.Int) ([]*big.Int, error) + RegisterFilterFn func(opts logprovider.FilterOptions) error +} + +func (p *mockLogEventProvider) RefreshActiveUpkeeps(ids ...*big.Int) ([]*big.Int, error) { + return p.RefreshActiveUpkeepsFn(ids...) +} + +func (p *mockLogEventProvider) RegisterFilter(opts logprovider.FilterOptions) error { + return p.RegisterFilterFn(opts) +} + +type mockRegistry struct { + Registry + GetUpkeepTriggerConfigFn func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) + ParseLogFn func(log coreTypes.Log) (generated.AbigenLog, error) +} + +func (r *mockRegistry) ParseLog(log coreTypes.Log) (generated.AbigenLog, error) { + return r.ParseLogFn(log) +} + +func (r *mockRegistry) GetUpkeepTriggerConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return r.GetUpkeepTriggerConfigFn(opts, upkeepId) +} + +type mockPacker struct { + encoding.Packer + UnpackLogTriggerConfigFn func(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) +} + +func (p *mockPacker) UnpackLogTriggerConfig(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) { + return p.UnpackLogTriggerConfigFn(raw) +}