Skip to content

Commit

Permalink
LOOPP plugin config validation service (#12430)
Browse files Browse the repository at this point in the history
* Initial draft

* Initial draft

* Update go mod

* Fix go sum

* Pin to chainlink-common PR

* Add tests

* Add unregister

* Fix lint

* Implement feedback

* Pin to latest version of chainlink-common PR

* Pin to latest version of chainlink-common PR

* Pin to latest version of chainlink-common PR

* Fix typo

* Add comment for further work once BCF-3126 is implemented

* Pin to latest version of common PR

* Bump to latest chainlink-common
  • Loading branch information
george-dorin authored Apr 4, 2024
1 parent d4dd1ec commit 71b5437
Show file tree
Hide file tree
Showing 31 changed files with 291 additions and 104 deletions.
8 changes: 4 additions & 4 deletions core/internal/features/ocr2/features_ocr2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ typeABI = '''
'''
`
}
ocrJob, err := validate.ValidatedOracleSpecToml(apps[i].Config.OCR2(), apps[i].Config.Insecure(), fmt.Sprintf(`
ocrJob, err := validate.ValidatedOracleSpecToml(testutils.Context(t), apps[i].Config.OCR2(), apps[i].Config.Insecure(), fmt.Sprintf(`
type = "offchainreporting2"
relay = "evm"
schemaVersion = 1
Expand Down Expand Up @@ -488,7 +488,7 @@ juelsPerFeeCoinSource = """
answer1 [type=median index=0];
"""
juelsPerFeeCoinCacheDuration = "1m"
`, ocrContractAddress, kbs[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i, blockBeforeConfig.Number().Int64(), chainReaderSpec, fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i))
`, ocrContractAddress, kbs[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i, blockBeforeConfig.Number().Int64(), chainReaderSpec, fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i), nil)
require.NoError(t, err)
err = apps[i].AddJobV2(testutils.Context(t), &ocrJob)
require.NoError(t, err)
Expand Down Expand Up @@ -793,7 +793,7 @@ chainID = 1337
URL: models.WebURL(*u),
}))

ocrJob, err := validate.ValidatedOracleSpecToml(apps[i].Config.OCR2(), apps[i].Config.Insecure(), fmt.Sprintf(`
ocrJob, err := validate.ValidatedOracleSpecToml(testutils.Context(t), apps[i].Config.OCR2(), apps[i].Config.Insecure(), fmt.Sprintf(`
type = "offchainreporting2"
relay = "evm"
schemaVersion = 1
Expand Down Expand Up @@ -841,7 +841,7 @@ juelsPerFeeCoinSource = """
answer1 [type=median index=0];
"""
juelsPerFeeCoinCacheDuration = "1m"
`, ocrContractAddress, kbs[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i, fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i))
`, ocrContractAddress, kbs[i].ID(), transmitters[i], fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i, fmt.Sprintf("bridge%d", i), i, slowServers[i].URL, i), nil)
require.NoError(t, err)
err = apps[i].AddJobV2(testutils.Context(t), &ocrJob)
require.NoError(t, err)
Expand Down
20 changes: 20 additions & 0 deletions core/internal/mocks/application.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/prometheus/client_golang v1.17.0
github.com/shopspring/decimal v1.3.1
github.com/smartcontractkit/chainlink-automation v1.0.2
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240402105740-0be47ab9cf63
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240404141006-77085a02ce25
github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868
github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000
github.com/smartcontractkit/libocr v0.0.0-20240326191951-2bbe9382d052
Expand Down
4 changes: 2 additions & 2 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1187,8 +1187,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq
github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE=
github.com/smartcontractkit/chainlink-automation v1.0.2 h1:xsfyuswL15q2YBGQT3qn2SBz6fnSKiSW7XZ8IZQLpnI=
github.com/smartcontractkit/chainlink-automation v1.0.2/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240402105740-0be47ab9cf63 h1:wX78l6lMQ6hfwqpOkavD/IyXqBDZ8MZOhhBE9z15Sd0=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240402105740-0be47ab9cf63/go.mod h1:kstYjAGqBswdZpl7YkSPeXBDVwaY1VaR6tUMPWl8ykA=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240404141006-77085a02ce25 h1:fY2wMtlr/VQxPyVVQdi1jFvQHi0VbDnGGVXzLKOZTOY=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240404141006-77085a02ce25/go.mod h1:kstYjAGqBswdZpl7YkSPeXBDVwaY1VaR6tUMPWl8ykA=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc=
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo=
Expand Down
15 changes: 13 additions & 2 deletions core/services/chainlink/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type Application interface {
GetExternalInitiatorManager() webhook.ExternalInitiatorManager
GetRelayers() RelayerChainInteroperators
GetLoopRegistry() *plugins.LoopRegistry
GetLoopRegistrarConfig() plugins.RegistrarConfig

// V2 Jobs (TOML specified)
JobSpawner() job.Spawner
Expand Down Expand Up @@ -150,6 +151,7 @@ type ChainlinkApplication struct {
secretGenerator SecretGenerator
profiler *pyroscope.Profiler
loopRegistry *plugins.LoopRegistry
loopRegistrarConfig plugins.RegistrarConfig

started bool
startStopMu sync.Mutex
Expand Down Expand Up @@ -425,10 +427,14 @@ func NewApplication(opts ApplicationOpts) (Application, error) {
} else {
globalLogger.Debug("Off-chain reporting disabled")
}

loopRegistrarConfig := plugins.NewRegistrarConfig(opts.GRPCOpts, opts.LoopRegistry.Register, opts.LoopRegistry.Unregister)

if cfg.OCR2().Enabled() {
globalLogger.Debug("Off-chain reporting v2 enabled")
registrarConfig := plugins.NewRegistrarConfig(opts.GRPCOpts, opts.LoopRegistry.Register)
ocr2DelegateConfig := ocr2.NewDelegateConfig(cfg.OCR2(), cfg.Mercury(), cfg.Threshold(), cfg.Insecure(), cfg.JobPipeline(), cfg.Database(), registrarConfig)

ocr2DelegateConfig := ocr2.NewDelegateConfig(cfg.OCR2(), cfg.Mercury(), cfg.Threshold(), cfg.Insecure(), cfg.JobPipeline(), cfg.Database(), loopRegistrarConfig)

delegates[job.OffchainReporting2] = ocr2.NewDelegate(
sqlxDB,
jobORM,
Expand Down Expand Up @@ -496,6 +502,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) {
legacyEVMChains,
globalLogger,
opts.Version,
loopRegistrarConfig,
)
} else {
feedsService = &feeds.NullService{}
Expand Down Expand Up @@ -534,6 +541,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) {
secretGenerator: opts.SecretGenerator,
profiler: profiler,
loopRegistry: loopRegistry,
loopRegistrarConfig: loopRegistrarConfig,

sqlxDB: opts.SqlxDB,
db: opts.DB,
Expand Down Expand Up @@ -604,6 +612,9 @@ func (app *ChainlinkApplication) StopIfStarted() error {
func (app *ChainlinkApplication) GetLoopRegistry() *plugins.LoopRegistry {
return app.loopRegistry
}
func (app *ChainlinkApplication) GetLoopRegistrarConfig() plugins.RegistrarConfig {
return app.loopRegistrarConfig
}

// Stop allows the application to exit by halting schedules, closing
// logs, and closing the DB connection.
Expand Down
80 changes: 42 additions & 38 deletions core/services/feeds/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/jmoiron/sqlx"

"github.com/smartcontractkit/chainlink-common/pkg/services"
"github.com/smartcontractkit/chainlink/v2/plugins"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
Expand Down Expand Up @@ -102,22 +103,23 @@ type Service interface {
type service struct {
services.StateMachine

orm ORM
jobORM job.ORM
q pg.Q
csaKeyStore keystore.CSA
p2pKeyStore keystore.P2P
ocr1KeyStore keystore.OCR
ocr2KeyStore keystore.OCR2
jobSpawner job.Spawner
insecureCfg InsecureConfig
jobCfg JobConfig
ocrCfg OCRConfig
ocr2cfg OCR2Config
connMgr ConnectionsManager
legacyChains legacyevm.LegacyChainContainer
lggr logger.Logger
version string
orm ORM
jobORM job.ORM
q pg.Q
csaKeyStore keystore.CSA
p2pKeyStore keystore.P2P
ocr1KeyStore keystore.OCR
ocr2KeyStore keystore.OCR2
jobSpawner job.Spawner
insecureCfg InsecureConfig
jobCfg JobConfig
ocrCfg OCRConfig
ocr2cfg OCR2Config
connMgr ConnectionsManager
legacyChains legacyevm.LegacyChainContainer
lggr logger.Logger
version string
loopRegistrarConfig plugins.RegistrarConfig
}

// NewService constructs a new feeds service
Expand All @@ -135,25 +137,27 @@ func NewService(
legacyChains legacyevm.LegacyChainContainer,
lggr logger.Logger,
version string,
rc plugins.RegistrarConfig,
) *service {
lggr = lggr.Named("Feeds")
svc := &service{
orm: orm,
jobORM: jobORM,
q: pg.NewQ(db, lggr, dbCfg),
jobSpawner: jobSpawner,
p2pKeyStore: keyStore.P2P(),
csaKeyStore: keyStore.CSA(),
ocr1KeyStore: keyStore.OCR(),
ocr2KeyStore: keyStore.OCR2(),
insecureCfg: insecureCfg,
jobCfg: jobCfg,
ocrCfg: ocrCfg,
ocr2cfg: ocr2Cfg,
connMgr: newConnectionsManager(lggr),
legacyChains: legacyChains,
lggr: lggr,
version: version,
orm: orm,
jobORM: jobORM,
q: pg.NewQ(db, lggr, dbCfg),
jobSpawner: jobSpawner,
p2pKeyStore: keyStore.P2P(),
csaKeyStore: keyStore.CSA(),
ocr1KeyStore: keyStore.OCR(),
ocr2KeyStore: keyStore.OCR2(),
insecureCfg: insecureCfg,
jobCfg: jobCfg,
ocrCfg: ocrCfg,
ocr2cfg: ocr2Cfg,
connMgr: newConnectionsManager(lggr),
legacyChains: legacyChains,
lggr: lggr,
version: version,
loopRegistrarConfig: rc,
}

return svc
Expand Down Expand Up @@ -534,7 +538,7 @@ type ProposeJobArgs struct {
// belonging to another feeds manager, we do not update it.
func (s *service) ProposeJob(ctx context.Context, args *ProposeJobArgs) (int64, error) {
// Validate the args
if err := s.validateProposeJobArgs(*args); err != nil {
if err := s.validateProposeJobArgs(ctx, *args); err != nil {
return 0, err
}

Expand Down Expand Up @@ -717,7 +721,7 @@ func (s *service) ApproveSpec(ctx context.Context, id int64, force bool) error {
return errors.Wrap(err, "fms rpc client")
}

j, err := s.generateJob(spec.Definition)
j, err := s.generateJob(ctx, spec.Definition)
if err != nil {
return errors.Wrap(err, "could not generate job from spec")
}
Expand Down Expand Up @@ -1121,7 +1125,7 @@ func (s *service) findExistingJobForOCRFlux(j *job.Job, qopts pg.QOpt) (int32, e
}

// generateJob validates and generates a job from a spec.
func (s *service) generateJob(spec string) (*job.Job, error) {
func (s *service) generateJob(ctx context.Context, spec string) (*job.Job, error) {
jobType, err := job.ValidateSpec(spec)
if err != nil {
return nil, errors.Wrap(err, "failed to parse job spec TOML")
Expand All @@ -1138,7 +1142,7 @@ func (s *service) generateJob(spec string) (*job.Job, error) {
if !s.ocr2cfg.Enabled() {
return nil, ErrOCR2Disabled
}
js, err = ocr2.ValidatedOracleSpecToml(s.ocr2cfg, s.insecureCfg, spec)
js, err = ocr2.ValidatedOracleSpecToml(ctx, s.ocr2cfg, s.insecureCfg, spec, s.loopRegistrarConfig)
case job.Bootstrap:
if !s.ocr2cfg.Enabled() {
return nil, ErrOCR2Disabled
Expand Down Expand Up @@ -1297,9 +1301,9 @@ func (s *service) newOCR2ConfigMsg(cfg OCR2ConfigModel) (*pb.OCR2Config, error)
return msg, nil
}

func (s *service) validateProposeJobArgs(args ProposeJobArgs) error {
func (s *service) validateProposeJobArgs(ctx context.Context, args ProposeJobArgs) error {
// Validate the job spec
j, err := s.generateJob(args.Spec)
j, err := s.generateJob(ctx, args.Spec)
if err != nil {
return errors.Wrap(err, "failed to generate a job based on spec")
}
Expand Down
2 changes: 1 addition & 1 deletion core/services/feeds/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func setupTestServiceCfg(t *testing.T, overrideCfg func(c *chainlink.Config, s *
keyStore.On("P2P").Return(p2pKeystore)
keyStore.On("OCR").Return(ocr1Keystore)
keyStore.On("OCR2").Return(ocr2Keystore)
svc := feeds.NewService(orm, jobORM, db, spawner, keyStore, scopedConfig.Insecure(), scopedConfig.JobPipeline(), scopedConfig.OCR(), scopedConfig.OCR2(), scopedConfig.Database(), legacyChains, lggr, "1.0.0")
svc := feeds.NewService(orm, jobORM, db, spawner, keyStore, scopedConfig.Insecure(), scopedConfig.JobPipeline(), scopedConfig.OCR(), scopedConfig.OCR2(), scopedConfig.Database(), legacyChains, lggr, "1.0.0", nil)
svc.SetConnectionsManager(connMgr)

return &TestService{
Expand Down
16 changes: 10 additions & 6 deletions core/services/job/job_orm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ func TestORM_CreateJob_OCR2_DuplicatedContractAddress(t *testing.T) {

_, address := cltest.MustInsertRandomKey(t, keyStore.Eth())

jb, err := ocr2validate.ValidatedOracleSpecToml(config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal())
jb, err := ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal(), nil)
require.NoError(t, err)

const juelsPerFeeCoinSource = `
Expand All @@ -813,7 +813,7 @@ func TestORM_CreateJob_OCR2_DuplicatedContractAddress(t *testing.T) {
err = jobORM.CreateJob(&jb)
require.NoError(t, err)

jb2, err := ocr2validate.ValidatedOracleSpecToml(config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal())
jb2, err := ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal(), nil)
require.NoError(t, err)

jb2.Name = null.StringFrom("Job with same chain id & contract address")
Expand All @@ -823,7 +823,7 @@ func TestORM_CreateJob_OCR2_DuplicatedContractAddress(t *testing.T) {
err = jobORM.CreateJob(&jb2)
require.Error(t, err)

jb3, err := ocr2validate.ValidatedOracleSpecToml(config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal())
jb3, err := ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal(), nil)
require.NoError(t, err)
jb3.Name = null.StringFrom("Job with different chain id & same contract address")
jb3.OCR2OracleSpec.TransmitterID = null.StringFrom(address.String())
Expand Down Expand Up @@ -856,7 +856,7 @@ func TestORM_CreateJob_OCR2_Sending_Keys_Transmitter_Keys_Validations(t *testing

jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database())

jb, err := ocr2validate.ValidatedOracleSpecToml(config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal())
jb, err := ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal(), nil)
require.NoError(t, err)

t.Run("sending keys or transmitterID must be defined", func(t *testing.T) {
Expand Down Expand Up @@ -897,7 +897,7 @@ func TestORM_ValidateKeyStoreMatch(t *testing.T) {
var jb job.Job
{
var err error
jb, err = ocr2validate.ValidatedOracleSpecToml(config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal())
jb, err = ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal(), nil)
require.NoError(t, err)
}

Expand Down Expand Up @@ -1089,7 +1089,7 @@ func Test_FindJob(t *testing.T) {
)
require.NoError(t, err)

jobOCR2, err := ocr2validate.ValidatedOracleSpecToml(config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal())
jobOCR2, err := ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal(), nil)
require.NoError(t, err)
jobOCR2.OCR2OracleSpec.TransmitterID = null.StringFrom(address.String())

Expand All @@ -1104,16 +1104,20 @@ func Test_FindJob(t *testing.T) {
ocr2WithFeedID1 := "0x0001000000000000000000000000000000000000000000000000000000000001"
ocr2WithFeedID2 := "0x0001000000000000000000000000000000000000000000000000000000000002"
jobOCR2WithFeedID1, err := ocr2validate.ValidatedOracleSpecToml(
testutils.Context(t),
config.OCR2(),
config.Insecure(),
fmt.Sprintf(mercuryOracleTOML, cltest.DefaultCSAKey.PublicKeyString(), ocr2WithFeedID1),
nil,
)
require.NoError(t, err)

jobOCR2WithFeedID2, err := ocr2validate.ValidatedOracleSpecToml(
testutils.Context(t),
config.OCR2(),
config.Insecure(),
fmt.Sprintf(mercuryOracleTOML, cltest.DefaultCSAKey.PublicKeyString(), ocr2WithFeedID2),
nil,
)
jobOCR2WithFeedID2.ExternalJobID = uuid.New()
jobOCR2WithFeedID2.Name = null.StringFrom("new name")
Expand Down
Loading

0 comments on commit 71b5437

Please sign in to comment.