diff --git a/core/chains/evm/chain.go b/core/chains/evm/chain.go index 6a948a4cdd0..8704d0c1fc8 100644 --- a/core/chains/evm/chain.go +++ b/core/chains/evm/chain.go @@ -142,22 +142,34 @@ type AppConfig interface { type ChainRelayExtenderConfig struct { Logger logger.Logger - DB *sqlx.DB KeyStore keystore.Eth - *RelayerConfig + ChainOpts } -// options for the relayer factory. -// TODO BCF-2508 clean up configuration of chain and relayer after BCF-2440 -// the factory wants to own the logger and db -// the factory creates extenders, which need the same and more opts -type RelayerConfig struct { +func (c ChainRelayExtenderConfig) Validate() error { + err := c.ChainOpts.Validate() + if c.Logger == nil { + err = errors.Join(err, errors.New("nil Logger")) + } + if c.KeyStore == nil { + err = errors.Join(err, errors.New("nil Keystore")) + } + + if err != nil { + err = fmt.Errorf("invalid ChainRelayerExtenderConfig: %w", err) + } + return err +} + +type ChainOpts struct { AppConfig AppConfig EventBroadcaster pg.EventBroadcaster MailMon *utils.MailboxMonitor GasEstimator gas.EvmFeeEstimator + *sqlx.DB + // TODO BCF-2513 remove test code from the API // Gen-functions are useful for dependency injection by tests GenEthClient func(*big.Int) client.Client @@ -168,7 +180,31 @@ type RelayerConfig struct { GenGasEstimator func(*big.Int) gas.EvmFeeEstimator } +func (o ChainOpts) Validate() error { + var err error + if o.AppConfig == nil { + err = errors.Join(err, errors.New("nil AppConfig")) + } + if o.EventBroadcaster == nil { + err = errors.Join(err, errors.New("nil EventBroadcaster")) + } + if o.MailMon == nil { + err = errors.Join(err, errors.New("nil MailMon")) + } + if o.DB == nil { + err = errors.Join(err, errors.New("nil DB")) + } + if err != nil { + err = fmt.Errorf("invalid ChainOpts: %w", err) + } + return err +} + func NewTOMLChain(ctx context.Context, chain *toml.EVMConfig, opts ChainRelayExtenderConfig) (Chain, error) { + err := opts.Validate() + if err != nil { + return nil, err + } chainID := chain.ChainID l := opts.Logger.With("evmChainID", chainID.String()) if !chain.IsEnabled() { @@ -458,14 +494,3 @@ func newPrimary(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr return evmclient.NewNode(cfg, noNewHeadsThreshold, lggr, (url.URL)(*n.WSURL), (*url.URL)(n.HTTPURL), *n.Name, id, chainID, *n.Order), nil } - -func (opts *ChainRelayExtenderConfig) Check() error { - if opts.Logger == nil { - return errors.New("logger must be non-nil") - } - if opts.AppConfig == nil { - return errors.New("config must be non-nil") - } - - return nil -} diff --git a/core/chains/evm/chain_test.go b/core/chains/evm/chain_test.go index 41f498b3e76..09395ff4c97 100644 --- a/core/chains/evm/chain_test.go +++ b/core/chains/evm/chain_test.go @@ -4,11 +4,15 @@ import ( "math/big" "testing" + "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestLegacyChains(t *testing.T) { @@ -25,3 +29,50 @@ func TestLegacyChains(t *testing.T) { assert.Equal(t, c, got) } + +func TestChainOpts_Validate(t *testing.T) { + type fields struct { + AppConfig evm.AppConfig + EventBroadcaster pg.EventBroadcaster + MailMon *utils.MailboxMonitor + DB *sqlx.DB + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "valid", + fields: fields{ + AppConfig: configtest.NewTestGeneralConfig(t), + EventBroadcaster: pg.NewNullEventBroadcaster(), + MailMon: &utils.MailboxMonitor{}, + DB: pgtest.NewSqlxDB(t), + }, + }, + { + name: "invalid", + fields: fields{ + AppConfig: nil, + EventBroadcaster: nil, + MailMon: nil, + DB: nil, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := evm.ChainOpts{ + AppConfig: tt.fields.AppConfig, + EventBroadcaster: tt.fields.EventBroadcaster, + MailMon: tt.fields.MailMon, + DB: tt.fields.DB, + } + if err := o.Validate(); (err != nil) != tt.wantErr { + t.Errorf("ChainOpts.Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 2a771a83587..20ad3dec107 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -153,15 +153,13 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G // create the relayer-chain interoperators from application configuration relayerFactory := chainlink.RelayerFactory{ Logger: appLggr, - DB: db, - QConfig: cfg.Database(), LoopRegistry: loopRegistry, GRPCOpts: grpcOpts, } evmFactoryCfg := chainlink.EVMFactoryConfig{ CSAETHKeystore: keyStore, - RelayerConfig: &evm.RelayerConfig{AppConfig: cfg, EventBroadcaster: eventBroadcaster, MailMon: mailMon}, + ChainOpts: evm.ChainOpts{AppConfig: cfg, EventBroadcaster: eventBroadcaster, MailMon: mailMon, DB: db}, } // evm always enabled for backward compatibility // TODO BCF-2510 this needs to change in order to clear the path for EVM extraction diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 8d2b5f1fabf..691e5faa923 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -42,13 +42,11 @@ import ( func genTestEVMRelayers(t *testing.T, opts evm.ChainRelayExtenderConfig, ks evmrelayer.CSAETHKeystore) *chainlink.CoreRelayerChainInteroperators { f := chainlink.RelayerFactory{ Logger: opts.Logger, - DB: opts.DB, - QConfig: opts.AppConfig.Database(), LoopRegistry: plugins.NewLoopRegistry(opts.Logger), } relayers, err := chainlink.NewCoreRelayerChainInteroperators(chainlink.InitEVM(testutils.Context(t), f, chainlink.EVMFactoryConfig{ - RelayerConfig: opts.RelayerConfig, + ChainOpts: opts.ChainOpts, CSAETHKeystore: ks, })) if err != nil { @@ -87,12 +85,12 @@ func TestShell_RunNodeWithPasswords(t *testing.T) { opts := evm.ChainRelayExtenderConfig{ Logger: lggr, - DB: db, KeyStore: keyStore.Eth(), - RelayerConfig: &evm.RelayerConfig{ + ChainOpts: evm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: &utils.MailboxMonitor{}, + DB: db, }, } testRelayers := genTestEVMRelayers(t, opts, keyStore) @@ -191,13 +189,12 @@ func TestShell_RunNodeWithAPICredentialsFile(t *testing.T) { lggr := logger.TestLogger(t) opts := evm.ChainRelayExtenderConfig{ Logger: lggr, - DB: db, KeyStore: keyStore.Eth(), - RelayerConfig: &evm.RelayerConfig{ + ChainOpts: evm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), - - MailMon: &utils.MailboxMonitor{}, + MailMon: &utils.MailboxMonitor{}, + DB: db, }, } testRelayers := genTestEVMRelayers(t, opts, keyStore) diff --git a/core/cmd/shell_test.go b/core/cmd/shell_test.go index 45192392f6d..c74a0067a68 100644 --- a/core/cmd/shell_test.go +++ b/core/cmd/shell_test.go @@ -370,8 +370,6 @@ func TestSetupSolanaRelayer(t *testing.T) { rf := chainlink.RelayerFactory{ Logger: lggr, - DB: pgtest.NewSqlxDB(t), - QConfig: tConfig.Database(), LoopRegistry: reg, } @@ -457,8 +455,6 @@ func TestSetupStarkNetRelayer(t *testing.T) { }) rf := chainlink.RelayerFactory{ Logger: lggr, - DB: pgtest.NewSqlxDB(t), - QConfig: tConfig.Database(), LoopRegistry: reg, } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 1c5d1ed5bbb..671d4072816 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -386,17 +386,16 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn relayerFactory := chainlink.RelayerFactory{ Logger: lggr, - DB: db, - QConfig: cfg.Database(), LoopRegistry: loopRegistry, GRPCOpts: loop.GRPCOpts{}, } evmOpts := chainlink.EVMFactoryConfig{ - RelayerConfig: &evm.RelayerConfig{ + ChainOpts: evm.ChainOpts{ AppConfig: cfg, EventBroadcaster: eventBroadcaster, MailMon: mailMon, + DB: db, }, CSAETHKeystore: keyStore, } @@ -423,6 +422,8 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn Keystore: keyStore.Cosmos(), CosmosConfigs: cfg.CosmosConfigs(), EventBroadcaster: eventBroadcaster, + DB: db, + QConfig: cfg.Database(), } initOps = append(initOps, chainlink.InitCosmos(testCtx, relayerFactory, cosmosCfg)) } diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index 62696f75d96..2a86101d0d8 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -82,13 +82,13 @@ func NewChainRelayExtOpts(t testing.TB, testopts TestChainOpts) evm.ChainRelayEx require.NotNil(t, testopts.KeyStore) opts := evm.ChainRelayExtenderConfig{ Logger: logger.TestLogger(t), - DB: testopts.DB, KeyStore: testopts.KeyStore, - RelayerConfig: &evm.RelayerConfig{ + ChainOpts: evm.ChainOpts{ AppConfig: testopts.GeneralConfig, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: testopts.MailMon, GasEstimator: testopts.GasEstimator, + DB: testopts.DB, }, } opts.GenEthClient = func(*big.Int) evmclient.Client { diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go index 1a0dbadd266..6513184d9ec 100644 --- a/core/services/chainlink/relayer_chain_interoperators.go +++ b/core/services/chainlink/relayer_chain_interoperators.go @@ -90,9 +90,9 @@ func NewCoreRelayerChainInteroperators(initFuncs ...CoreRelayerChainInitFunc) (* srvs: make([]services.ServiceCtx, 0), } for _, initFn := range initFuncs { - err2 := initFn(cr) - if err2 != nil { - return nil, err2 + err := initFn(cr) + if err != nil { + return nil, err } } return cr, nil diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index 9cfe354fe91..527dacd56d8 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -174,8 +174,6 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { factory := chainlink.RelayerFactory{ Logger: lggr, - DB: db, - QConfig: cfg.Database(), LoopRegistry: plugins.NewLoopRegistry(lggr), GRPCOpts: loop.GRPCOpts{}, } @@ -207,10 +205,11 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { {name: "2 evm chains with 3 nodes", initFuncs: []chainlink.CoreRelayerChainInitFunc{ chainlink.InitEVM(testctx, factory, chainlink.EVMFactoryConfig{ - RelayerConfig: &evm.RelayerConfig{ + ChainOpts: evm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: &utils.MailboxMonitor{}, + DB: db, }, CSAETHKeystore: keyStore, }), @@ -262,7 +261,9 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { chainlink.InitCosmos(testctx, factory, chainlink.CosmosFactoryConfig{ Keystore: keyStore.Cosmos(), CosmosConfigs: cfg.CosmosConfigs(), - EventBroadcaster: pg.NewNullEventBroadcaster()}), + EventBroadcaster: pg.NewNullEventBroadcaster(), + DB: db, + QConfig: cfg.Database()}), }, expectedCosmosChainCnt: 2, expectedCosmosNodeCnt: 2, @@ -279,10 +280,11 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Keystore: keyStore.Solana(), SolanaConfigs: cfg.SolanaConfigs()}), chainlink.InitEVM(testctx, factory, chainlink.EVMFactoryConfig{ - RelayerConfig: &evm.RelayerConfig{ + ChainOpts: evm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: &utils.MailboxMonitor{}, + DB: db, }, CSAETHKeystore: keyStore, }), @@ -293,6 +295,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Keystore: keyStore.Cosmos(), CosmosConfigs: cfg.CosmosConfigs(), EventBroadcaster: pg.NewNullEventBroadcaster(), + DB: db, + QConfig: cfg.Database(), }), }, expectedEVMChainCnt: 2, diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 0a0d653f5fc..c0541f4384d 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -2,9 +2,11 @@ package chainlink import ( "context" + "errors" "fmt" "github.com/pelletier/go-toml/v2" + "github.com/smartcontractkit/sqlx" pkgcosmos "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" @@ -27,14 +29,12 @@ import ( type RelayerFactory struct { logger.Logger - *sqlx.DB - pg.QConfig *plugins.LoopRegistry loop.GRPCOpts } type EVMFactoryConfig struct { - *evm.RelayerConfig + evm.ChainOpts evmrelay.CSAETHKeystore } @@ -45,10 +45,9 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m // override some common opts with the factory values. this seems weird... maybe other signatures should change, or this should take a different type... ccOpts := evm.ChainRelayExtenderConfig{ - Logger: r.Logger.Named("EVM"), - DB: r.DB, - KeyStore: config.CSAETHKeystore.Eth(), - RelayerConfig: config.RelayerConfig, + Logger: r.Logger.Named("EVM"), + KeyStore: config.CSAETHKeystore.Eth(), + ChainOpts: config.ChainOpts, } evmRelayExtenders, err := evmrelay.NewChainRelayerExtenders(ctx, ccOpts) @@ -58,15 +57,28 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(evmRelayExtenders) for _, ext := range evmRelayExtenders.Slice() { relayID := relay.ID{Network: relay.EVM, ChainID: relay.ChainID(ext.Chain().ID().String())} - chain, err := legacyChains.Get(relayID.ChainID) - if err != nil { - return nil, err + chain, err2 := legacyChains.Get(relayID.ChainID) + if err2 != nil { + return nil, err2 + } + + relayerOpts := evmrelay.RelayerOpts{ + DB: ccOpts.DB, + QConfig: ccOpts.AppConfig.Database(), + CSAETHKeystore: config.CSAETHKeystore, + EventBroadcaster: ccOpts.EventBroadcaster, } - relayer := evmrelay.NewLoopRelayServerAdapter(evmrelay.NewRelayer(ccOpts.DB, chain, r.QConfig, ccOpts.Logger, config.CSAETHKeystore, ccOpts.EventBroadcaster), ext) - relayers[relayID] = relayer + relayer, err2 := evmrelay.NewRelayer(ccOpts.Logger, chain, relayerOpts) + if err2 != nil { + err = errors.Join(err, err2) + continue + } + + relayers[relayID] = evmrelay.NewLoopRelayServerAdapter(relayer, ext) } - return relayers, nil + // always return err because it is accumulating individual errors + return relayers, err } type SolanaFactoryConfig struct { @@ -209,9 +221,39 @@ type CosmosFactoryConfig struct { Keystore keystore.Cosmos cosmos.CosmosConfigs EventBroadcaster pg.EventBroadcaster + *sqlx.DB + pg.QConfig +} + +func (c CosmosFactoryConfig) Validate() error { + var err error + if c.Keystore == nil { + err = errors.Join(err, fmt.Errorf("nil Keystore")) + } + if len(c.CosmosConfigs) == 0 { + err = errors.Join(err, fmt.Errorf("no CosmosConfigs provided")) + } + if c.EventBroadcaster == nil { + err = errors.Join(err, fmt.Errorf("nil EventBroadcaster")) + } + if c.DB == nil { + err = errors.Join(err, fmt.Errorf("nil DB")) + } + if c.QConfig == nil { + err = errors.Join(err, fmt.Errorf("nil QConfig")) + } + + if err != nil { + err = fmt.Errorf("invalid CosmosFactoryConfig: %w", err) + } + return err } func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConfig) (map[relay.ID]cosmos.LoopRelayerChainer, error) { + err := config.Validate() + if err != nil { + return nil, fmt.Errorf("cannot create Cosmos relayer: %w", err) + } relayers := make(map[relay.ID]cosmos.LoopRelayerChainer) var ( @@ -224,9 +266,9 @@ func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConf relayId := relay.ID{Network: relay.Cosmos, ChainID: relay.ChainID(*chainCfg.ChainID)} opts := cosmos.ChainOpts{ - QueryConfig: r.QConfig, + QueryConfig: config.QConfig, Logger: lggr.Named(relayId.ChainID), - DB: r.DB, + DB: config.DB, KeyStore: loopKs, EventBroadcaster: config.EventBroadcaster, } diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index db6497d11f6..85f14e407f8 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -180,7 +180,7 @@ func setupTestServiceCfg(t *testing.T, overrideCfg func(c *chainlink.Config, s * keyStore := new(ksmocks.Master) scopedConfig := evmtest.NewChainScopedConfig(t, gcfg) ethKeyStore := cltest.NewKeyStore(t, db, gcfg.Database()).Eth() - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{GeneralConfig: gcfg, + relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: gcfg, HeadTracker: headtracker.NullTracker, KeyStore: ethKeyStore}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) keyStore.On("Eth").Return(ethKeyStore) diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index 631f7ebfee9..7dbf811f783 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -284,7 +284,14 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) chain := evmtest.MustGetDefaultChain(t, legacyChains) - evmRelayer := evmrelayer.NewRelayer(testopts.DB, chain, testopts.GeneralConfig.Database(), lggr, keyStore, pg.NewNullEventBroadcaster()) + evmRelayer, err := evmrelayer.NewRelayer(lggr, chain, evmrelayer.RelayerOpts{ + DB: db, + QConfig: testopts.GeneralConfig.Database(), + CSAETHKeystore: keyStore, + EventBroadcaster: pg.NewNullEventBroadcaster(), + }) + assert.NoError(t, err) + testRelayGetter := &relayGetter{ e: relayExtenders.Slice()[0], r: evmRelayer, @@ -306,7 +313,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { jobOCR2VRF.Type: delegateOCR2, }, db, lggr, nil) - err := spawner.CreateJob(jobOCR2VRF) + err = spawner.CreateJob(jobOCR2VRF) require.NoError(t, err) jobSpecID := jobOCR2VRF.ID delegateOCR2.jobID = jobOCR2VRF.ID diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index f1731b9c438..6ae2052c408 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -3,13 +3,14 @@ package evm import ( "context" "encoding/json" + "errors" "fmt" "strings" "sync" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" + pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median/evmreportcodec" @@ -61,17 +62,49 @@ type CSAETHKeystore interface { Eth() keystore.Eth } -func NewRelayer(db *sqlx.DB, chain evm.Chain, cfg pg.QConfig, lggr logger.Logger, ks CSAETHKeystore, eventBroadcaster pg.EventBroadcaster) *Relayer { +type RelayerOpts struct { + *sqlx.DB + pg.QConfig + CSAETHKeystore + pg.EventBroadcaster +} + +func (c RelayerOpts) Validate() error { + var err error + if c.DB == nil { + err = errors.Join(err, errors.New("nil DB")) + } + if c.QConfig == nil { + err = errors.Join(err, errors.New("nil QConfig")) + } + if c.CSAETHKeystore == nil { + err = errors.Join(err, errors.New("nil Keystore")) + } + if c.EventBroadcaster == nil { + err = errors.Join(err, errors.New("nil Eventbroadcaster")) + } + + if err != nil { + err = fmt.Errorf("invalid RelayerOpts: %w", err) + } + return err +} + +func NewRelayer(lggr logger.Logger, chain evm.Chain, opts RelayerOpts) (*Relayer, error) { + err := opts.Validate() + if err != nil { + return nil, fmt.Errorf("cannot create evm relayer: %w", err) + } lggr = lggr.Named("Relayer") return &Relayer{ - db: db, + db: opts.DB, chain: chain, lggr: lggr, - ks: ks, + ks: opts.CSAETHKeystore, mercuryPool: wsrpc.NewPool(lggr), - eventBroadcaster: eventBroadcaster, - pgCfg: cfg, - } + eventBroadcaster: opts.EventBroadcaster, + pgCfg: opts.QConfig, + }, nil } func (r *Relayer) Name() string { @@ -107,11 +140,11 @@ func (r *Relayer) NewMercuryProvider(rargs relaytypes.RelayArgs, pargs relaytype var mercuryConfig mercuryconfig.PluginConfig if err := json.Unmarshal(pargs.PluginConfig, &mercuryConfig); err != nil { - return nil, errors.WithStack(err) + return nil, pkgerrors.WithStack(err) } if relayConfig.FeedID == nil { - return nil, errors.New("FeedID must be specified") + return nil, pkgerrors.New("FeedID must be specified") } feedID := mercuryutils.FeedID(*relayConfig.FeedID) @@ -120,15 +153,15 @@ func (r *Relayer) NewMercuryProvider(rargs relaytypes.RelayArgs, pargs relaytype } configWatcher, err := newConfigProvider(r.lggr, r.chain, relayOpts, r.eventBroadcaster) if err != nil { - return nil, errors.WithStack(err) + return nil, pkgerrors.WithStack(err) } if !relayConfig.EffectiveTransmitterID.Valid { - return nil, errors.New("EffectiveTransmitterID must be specified") + return nil, pkgerrors.New("EffectiveTransmitterID must be specified") } privKey, err := r.ks.CSA().Get(relayConfig.EffectiveTransmitterID.String) if err != nil { - return nil, errors.Wrap(err, "failed to get CSA key for mercury connection") + return nil, pkgerrors.Wrap(err, "failed to get CSA key for mercury connection") } client, err := r.mercuryPool.Checkout(context.Background(), privKey, mercuryConfig.ServerPubKey, mercuryConfig.ServerURL()) @@ -190,7 +223,7 @@ func FilterNamesFromRelayArgs(args relaytypes.RelayArgs) (filterNames []string, } var relayConfig types.RelayConfig if err = json.Unmarshal(args.RelayConfig, &relayConfig); err != nil { - return nil, errors.WithStack(err) + return nil, pkgerrors.WithStack(err) } if relayConfig.FeedID != nil { @@ -289,13 +322,13 @@ func (c *configWatcher) ContractConfigTracker() ocrtypes.ContractConfigTracker { func newConfigProvider(lggr logger.Logger, chain evm.Chain, opts *types.RelayOpts, eventBroadcaster pg.EventBroadcaster) (*configWatcher, error) { if !common.IsHexAddress(opts.ContractID) { - return nil, errors.Errorf("invalid contractID, expected hex address") + return nil, pkgerrors.Errorf("invalid contractID, expected hex address") } aggregatorAddress := common.HexToAddress(opts.ContractID) contractABI, err := abi.JSON(strings.NewReader(ocr2aggregator.OCR2AggregatorMetaData.ABI)) if err != nil { - return nil, errors.Wrap(err, "could not get contract ABI JSON") + return nil, pkgerrors.Wrap(err, "could not get contract ABI JSON") } var cp types.ConfigPoller @@ -347,23 +380,23 @@ func newContractTransmitter(lggr logger.Logger, rargs relaytypes.RelayArgs, tran var fromAddresses []common.Address sendingKeys := relayConfig.SendingKeys if !relayConfig.EffectiveTransmitterID.Valid { - return nil, errors.New("EffectiveTransmitterID must be specified") + return nil, pkgerrors.New("EffectiveTransmitterID must be specified") } effectiveTransmitterAddress := common.HexToAddress(relayConfig.EffectiveTransmitterID.String) sendingKeysLength := len(sendingKeys) if sendingKeysLength == 0 { - return nil, errors.New("no sending keys provided") + return nil, pkgerrors.New("no sending keys provided") } // If we are using multiple sending keys, then a forwarder is needed to rotate transmissions. // Ensure that this forwarder is not set to a local sending key, and ensure our sending keys are enabled. for _, s := range sendingKeys { if sendingKeysLength > 1 && s == effectiveTransmitterAddress.String() { - return nil, errors.New("the transmitter is a local sending key with transaction forwarding enabled") + return nil, pkgerrors.New("the transmitter is a local sending key with transaction forwarding enabled") } if err := ethKeystore.CheckEnabled(common.HexToAddress(s), configWatcher.chain.Config().EVM().ChainID()); err != nil { - return nil, errors.Wrap(err, "one of the sending keys given is not enabled") + return nil, pkgerrors.Wrap(err, "one of the sending keys given is not enabled") } fromAddresses = append(fromAddresses, common.HexToAddress(s)) } @@ -394,7 +427,7 @@ func newContractTransmitter(lggr logger.Logger, rargs relaytypes.RelayArgs, tran ) if err != nil { - return nil, errors.Wrap(err, "failed to create transmitter") + return nil, pkgerrors.Wrap(err, "failed to create transmitter") } return NewOCRContractTransmitter( @@ -415,7 +448,7 @@ func newPipelineContractTransmitter(lggr logger.Logger, rargs relaytypes.RelayAr } if !relayConfig.EffectiveTransmitterID.Valid { - return nil, errors.New("EffectiveTransmitterID must be specified") + return nil, pkgerrors.New("EffectiveTransmitterID must be specified") } effectiveTransmitterAddress := common.HexToAddress(relayConfig.EffectiveTransmitterID.String) transmitterAddress := common.HexToAddress(transmitterID) diff --git a/core/services/relay/evm/evm_test.go b/core/services/relay/evm/evm_test.go new file mode 100644 index 00000000000..df7cd8eb818 --- /dev/null +++ b/core/services/relay/evm/evm_test.go @@ -0,0 +1,68 @@ +package evm_test + +import ( + "testing" + + "github.com/smartcontractkit/sqlx" + "github.com/stretchr/testify/assert" + + configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" +) + +func TestRelayerOpts_Validate(t *testing.T) { + cfg := configtest.NewTestGeneralConfig(t) + type fields struct { + DB *sqlx.DB + QConfig pg.QConfig + CSAETHKeystore evm.CSAETHKeystore + EventBroadcaster pg.EventBroadcaster + } + tests := []struct { + name string + fields fields + wantErrContains string + }{ + { + name: "all invalid", + fields: fields{ + DB: nil, + QConfig: nil, + CSAETHKeystore: nil, + EventBroadcaster: nil, + }, + wantErrContains: `nil DB +nil QConfig +nil Keystore +nil Eventbroadcaster`, + }, + { + name: "missing db, keystore", + fields: fields{ + DB: nil, + QConfig: cfg.Database(), + CSAETHKeystore: nil, + EventBroadcaster: pg.NewNullEventBroadcaster(), + }, + wantErrContains: `nil DB +nil Keystore`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := evm.RelayerOpts{ + DB: tt.fields.DB, + QConfig: tt.fields.QConfig, + CSAETHKeystore: tt.fields.CSAETHKeystore, + EventBroadcaster: tt.fields.EventBroadcaster, + } + err := c.Validate() + if tt.wantErrContains != "" { + assert.Contains(t, err.Error(), tt.wantErrContains) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/core/services/relay/evm/relayer_extender.go b/core/services/relay/evm/relayer_extender.go index 592c9bacee2..ce638d10cf9 100644 --- a/core/services/relay/evm/relayer_extender.go +++ b/core/services/relay/evm/relayer_extender.go @@ -118,7 +118,7 @@ func (s *ChainRelayerExt) Ready() (err error) { } func NewChainRelayerExtenders(ctx context.Context, opts evmchain.ChainRelayExtenderConfig) (*ChainRelayerExtenders, error) { - if err := opts.Check(); err != nil { + if err := opts.Validate(); err != nil { return nil, err } @@ -143,16 +143,15 @@ func NewChainRelayerExtenders(ctx context.Context, opts evmchain.ChainRelayExten cid := enabled[i].ChainID.String() privOpts := evmchain.ChainRelayExtenderConfig{ - Logger: opts.Logger.Named(cid), - RelayerConfig: opts.RelayerConfig, - DB: opts.DB, - KeyStore: opts.KeyStore, + Logger: opts.Logger.Named(cid), + ChainOpts: opts.ChainOpts, + KeyStore: opts.KeyStore, } privOpts.Logger.Infow(fmt.Sprintf("Loading chain %s", cid), "evmChainID", cid) chain, err2 := evmchain.NewTOMLChain(ctx, enabled[i], privOpts) if err2 != nil { - err = multierr.Combine(err, err2) + err = multierr.Combine(err, fmt.Errorf("failed to create chain %s: %w", cid, err2)) continue } @@ -161,5 +160,6 @@ func NewChainRelayerExtenders(ctx context.Context, opts evmchain.ChainRelayExten } result = append(result, s) } - return newChainRelayerExtsFromSlice(result, opts.AppConfig), nil + // always return because it's accumulating errors + return newChainRelayerExtsFromSlice(result, opts.AppConfig), err }