Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[KS-430] Provide an OracleFactory to StandardCapabilities #14305

Draft
wants to merge 14 commits into
base: develop
Choose a base branch
from
Draft
4 changes: 2 additions & 2 deletions core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/prometheus/client_golang v1.17.0
github.com/shopspring/decimal v1.4.0
github.com/smartcontractkit/chainlink-automation v1.0.4
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240829145110-4a45c426fbe8
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240902092205-6c13b641e59c
github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000
github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7
github.com/spf13/cobra v1.8.0
Expand Down Expand Up @@ -243,7 +243,6 @@ require (
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
Expand Down Expand Up @@ -277,6 +276,7 @@ require (
github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 // indirect
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 // indirect
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect
github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect
github.com/smartcontractkit/wsrpc v0.7.3 // indirect
Expand Down
8 changes: 4 additions & 4 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1190,8 +1190,8 @@ github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8um
github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240828115624-442f1cff195b h1:v1RnZVfUoHIm/lwIqRAH4eDRNTu+N+AtQE5Ik4U9hsU=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240828115624-442f1cff195b/go.mod h1:Z9lQ5t20kRk28pzRLnqAJZUVOw8E6/siA3P3MLyKqoM=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240829145110-4a45c426fbe8 h1:MOFuL1J4/rRcR0x09qSlOsKIiq4I7YzbZcQ421KqUZA=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240829145110-4a45c426fbe8/go.mod h1:TJSY2ETKiXLRPvGHNO7Dp1tlpFIPSCWwN3iIdrsadIE=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240902092205-6c13b641e59c h1:dX68Us+R9M2OM4mJovOsat4DxKmkVjTkA/LYqpsl8yY=
github.com/smartcontractkit/chainlink-common v0.2.2-0.20240902092205-6c13b641e59c/go.mod h1:qtkr71Qrx62qLQyG/UyqJaMeIlVwnu7zoYsc92PYFEI=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 h1:NBQLtqk8zsyY4qTJs+NElI3aDFTcAo83JHvqD04EvB0=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45/go.mod h1:LV0h7QBQUpoC2UUi6TcUvcIFm1xjP/DtEcqV8+qeLUs=
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 h1:KH6tpCw5hu8u6UTtgll7a8mE4sIbHCbmtzHJdKuRwBw=
Expand All @@ -1202,8 +1202,8 @@ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457
github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f/go.mod h1:Ml88TJTwZCj6yHDkAEN/EhxVutzSlk+kDZgfibRIqF0=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 h1:HyLTySm7BR+oNfZqDTkVJ25wnmcTtxBBD31UkFL+kEM=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU=
github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU=
github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0=
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs=
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA=
github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7 h1:e38V5FYE7DA1JfKXeD5Buo/7lczALuVXlJ8YNTAUxcw=
github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7/go.mod h1:fb1ZDVXACvu4frX3APHZaEBp0xi1DIm34DcA0CwTsZM=
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE=
Expand Down
21 changes: 13 additions & 8 deletions core/services/chainlink/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,14 +438,6 @@ func NewApplication(opts ApplicationOpts) (Application, error) {
pipelineRunner,
cfg.JobPipeline(),
),
job.StandardCapabilities: standardcapabilities.NewDelegate(
globalLogger,
opts.DS, jobORM,
opts.CapabilitiesRegistry,
loopRegistrarConfig,
telemetryManager,
pipelineRunner,
opts.RelayerChainInteroperators),
}
webhookJobRunner = delegates[job.Webhook].(*webhook.Delegate).WebhookJobRunner()
)
Expand Down Expand Up @@ -485,6 +477,19 @@ func NewApplication(opts ApplicationOpts) (Application, error) {
return nil, fmt.Errorf("P2P stack required for OCR or OCR2")
}

// If peer wrapper is initialized, Oracle Factory dependency will be available to standard capabilities
delegates[job.StandardCapabilities] = standardcapabilities.NewDelegate(
globalLogger,
opts.DS, jobORM,
opts.CapabilitiesRegistry,
loopRegistrarConfig,
telemetryManager,
pipelineRunner,
opts.RelayerChainInteroperators,
keyStore.OCR2(),
peerWrapper,
)

if cfg.OCR().Enabled() {
delegates[job.OffchainReporting] = ocr.NewDelegate(
opts.DS,
Expand Down
11 changes: 6 additions & 5 deletions core/services/job/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -896,11 +896,12 @@ func (w *WorkflowSpec) Validate() error {
}

type StandardCapabilitiesSpec struct {
ID int32
CreatedAt time.Time `toml:"-"`
UpdatedAt time.Time `toml:"-"`
Command string `toml:"command"`
Config string `toml:"config"`
ID int32
CreatedAt time.Time `toml:"-"`
UpdatedAt time.Time `toml:"-"`
Command string `toml:"command"`
Config string `toml:"config"`
OracleFactory JSONConfig `toml:"oracleFactory"`
Copy link
Contributor

@cedric-cordenier cedric-cordenier Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DeividasK OracleFactoryConfig?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put a comment here to document where we define what's expected in OracleConfig?

}

func (w *StandardCapabilitiesSpec) GetID() string {
Expand Down
133 changes: 133 additions & 0 deletions core/services/ocr2/plugins/generic/oraclefactory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package generic

import (
"context"
"encoding/json"
"fmt"

"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/smartcontractkit/libocr/commontypes"
ocr "github.com/smartcontractkit/libocr/offchainreporting2plus"
"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types"

"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/job"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key"
"github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon"
"github.com/smartcontractkit/chainlink/v2/core/services/telemetry"

"github.com/smartcontractkit/chainlink-common/pkg/types/core"
)

type oracleFactoryConfig struct {
Enabled bool
TraceLogging bool
BootstrapPeers []commontypes.BootstrapperLocator
}

func NewOracleFactoryConfig(config job.JSONConfig) (*oracleFactoryConfig, error) {
var ofc struct {
Enabled bool `json:"enabled"`
TraceLogging bool `json:"traceLogging"`
BootstrapPeers []string `json:"bootstrapPeers"`
}
err := json.Unmarshal(config.Bytes(), &ofc)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal oracle factory config")
}

if !ofc.Enabled {
return &oracleFactoryConfig{}, nil
}

// If Oracle Factory is enabled, it must have at least one bootstrap peer
if len(ofc.BootstrapPeers) == 0 {
return nil, errors.New("no bootstrap peers found")
}

bootstrapPeers, err := ocrcommon.ParseBootstrapPeers(ofc.BootstrapPeers)
if err != nil {
return nil, errors.Wrap(err, "failed to parse bootstrap peers")
}

return &oracleFactoryConfig{
Enabled: true,
TraceLogging: ofc.TraceLogging,
BootstrapPeers: bootstrapPeers,
}, nil
}

type oracleFactory struct {
database ocr3types.Database
jobID int32
jobName string
jobORM job.ORM
kb ocr2key.KeyBundle
lggr logger.Logger
config *oracleFactoryConfig
peerWrapper *ocrcommon.SingletonPeerWrapper
}

type OracleFactoryParams struct {
Database ocr3types.Database
JobID int32
JobName string
JobORM job.ORM
Kb ocr2key.KeyBundle
Logger logger.Logger
Config *oracleFactoryConfig
PeerWrapper *ocrcommon.SingletonPeerWrapper
}

func NewOracleFactory(params OracleFactoryParams) (core.OracleFactory, error) {
return &oracleFactory{
database: params.Database,
jobID: params.JobID,
jobName: params.JobName,
jobORM: params.JobORM,
kb: params.Kb,
lggr: params.Logger,
config: params.Config,
peerWrapper: params.PeerWrapper,
}, nil
}

func (of *oracleFactory) NewOracle(ctx context.Context, args core.OracleArgs) (core.Oracle, error) {
oracle, err := ocr.NewOracle(ocr.OCR3OracleArgs[[]byte]{
LocalConfig: args.LocalConfig,
ContractConfigTracker: args.ContractConfigTracker,
ContractTransmitter: args.ContractTransmitter,
OffchainConfigDigester: args.OffchainConfigDigester,
ReportingPluginFactory: args.ReportingPluginFactoryService,
BinaryNetworkEndpointFactory: of.peerWrapper.Peer2,
V2Bootstrappers: of.config.BootstrapPeers,
Database: of.database,
Logger: ocrcommon.NewOCRWrapper(of.lggr, of.config.TraceLogging, func(ctx context.Context, msg string) {
logger.Sugared(of.lggr).ErrorIf(of.jobORM.RecordError(ctx, of.jobID, msg), "unable to record error")
}),
// TODO?
MonitoringEndpoint: &telemetry.NoopAgent{},
OffchainKeyring: of.kb,
OnchainKeyring: ocrcommon.NewOCR3OnchainKeyringAdapter(of.kb),
MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"job_name": of.jobName}, prometheus.DefaultRegisterer),
})

if err != nil {
return nil, fmt.Errorf("%w: failed to create new OCR oracle", err)
}

return &adaptedOracle{oracle: oracle}, nil
}

type adaptedOracle struct {
oracle ocr.Oracle
}

func (a *adaptedOracle) Start(ctx context.Context) error {
return a.oracle.Start()
}

func (a *adaptedOracle) Close(ctx context.Context) error {
return a.oracle.Close()
}
52 changes: 49 additions & 3 deletions core/services/standardcapabilities/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/types/core"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/job"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/generic"
"github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon"
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
"github.com/smartcontractkit/chainlink/v2/core/services/telemetry"
"github.com/smartcontractkit/chainlink/v2/plugins"
Expand All @@ -34,15 +37,23 @@ type Delegate struct {
monitoringEndpointGen telemetry.MonitoringEndpointGenerator
pipelineRunner pipeline.Runner
relayers RelayGetter
ocrKs keystore.OCR2
peerWrapper *ocrcommon.SingletonPeerWrapper

isNewlyCreatedJob bool
}

func NewDelegate(logger logger.Logger, ds sqlutil.DataSource, jobORM job.ORM, registry core.CapabilitiesRegistry,
cfg plugins.RegistrarConfig, monitoringEndpointGen telemetry.MonitoringEndpointGenerator, pipelineRunner pipeline.Runner,
relayers RelayGetter) *Delegate {
relayers RelayGetter,
ocrKs keystore.OCR2,
peerWrapper *ocrcommon.SingletonPeerWrapper,
) *Delegate {
return &Delegate{logger: logger, ds: ds, jobORM: jobORM, registry: registry, cfg: cfg, monitoringEndpointGen: monitoringEndpointGen, pipelineRunner: pipelineRunner,
relayers: relayers, isNewlyCreatedJob: false}
relayers: relayers, isNewlyCreatedJob: false,
ocrKs: ocrKs,
peerWrapper: peerWrapper,
}
}

func (d *Delegate) JobType() job.Type {
Expand All @@ -67,8 +78,38 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) ([]job.Ser
return nil, fmt.Errorf("failed to create relayer set: %w", err)
}

keyBundles, err := d.ocrKs.GetAll()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% certain this will work, but we can try it

if err != nil {
return nil, err
}
if len(keyBundles) > 1 {
return nil, fmt.Errorf("expected only one OCR key bundle, but found found: %d", len(keyBundles))
}

oracleFactoryConfig, err := generic.NewOracleFactoryConfig(spec.StandardCapabilitiesSpec.OracleFactory)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal oracle factory config")
}

if oracleFactoryConfig.Enabled && d.peerWrapper == nil {
return nil, errors.New("P2P stack required for Oracle Factory")
}

oracleFactory, err := generic.NewOracleFactory(generic.OracleFactoryParams{
Logger: log,
JobORM: d.jobORM,
JobID: spec.ID,
JobName: spec.Name.ValueOrZero(),
Database: ocr2.NewDB(d.ds, spec.ID, 0, log),
Kb: keyBundles[0],
Config: oracleFactoryConfig,
PeerWrapper: d.peerWrapper,
})
if err != nil {
return nil, fmt.Errorf("failed to create oracle factory: %w", err)
}
standardCapability := newStandardCapabilities(log, spec.StandardCapabilitiesSpec, d.cfg, telemetryService, kvStore, d.registry, errorLog,
pr, relayerSet)
pr, relayerSet, oracleFactory)

return []job.ServiceCtx{standardCapability}, nil
}
Expand Down Expand Up @@ -107,6 +148,11 @@ func ValidatedStandardCapabilitiesSpec(tomlString string) (job.Job, error) {
return jb, errors.Errorf("standard capabilities command must be set")
}

_, err = generic.NewOracleFactoryConfig(jb.StandardCapabilitiesSpec.OracleFactory)
if err != nil {
return jb, errors.Wrap(err, "failed to unmarshal oracle factory config")
}

return jb, nil
}

Expand Down
Loading
Loading