From 0932572d3144419afe881aac597f6b56db1441fe Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Mon, 3 Aug 2026 11:14:37 +0100 Subject: [PATCH 1/8] llo: OCR3.1 promwrapper --- core/services/ocr3_1/promwrapper/factory.go | 62 +++++++ core/services/ocr3_1/promwrapper/metrics.go | 104 +++++++++++ core/services/ocr3_1/promwrapper/plugin.go | 171 ++++++++++++++++++ .../ocr3_1/promwrapper/plugin_test.go | 170 +++++++++++++++++ 4 files changed, 507 insertions(+) create mode 100644 core/services/ocr3_1/promwrapper/factory.go create mode 100644 core/services/ocr3_1/promwrapper/metrics.go create mode 100644 core/services/ocr3_1/promwrapper/plugin.go create mode 100644 core/services/ocr3_1/promwrapper/plugin_test.go diff --git a/core/services/ocr3_1/promwrapper/factory.go b/core/services/ocr3_1/promwrapper/factory.go new file mode 100644 index 00000000000..c28a7c78a55 --- /dev/null +++ b/core/services/ocr3_1/promwrapper/factory.go @@ -0,0 +1,62 @@ +package promwrapper + +import ( + "context" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" +) + +var _ ocr3_1types.ReportingPluginFactory[any] = &ReportingPluginFactory[any]{} + +// ReportingPluginFactory wraps an ocr3_1types.ReportingPluginFactory so the +// produced plugin reports prometheus metrics. It is the OCR3.1 counterpart of +// core/services/ocr3/promwrapper.ReportingPluginFactory. +type ReportingPluginFactory[RI any] struct { + origin ocr3_1types.ReportingPluginFactory[RI] + lggr logger.Logger + chainFamily string + chainID string + plugin string +} + +func NewReportingPluginFactory[RI any]( + origin ocr3_1types.ReportingPluginFactory[RI], + lggr logger.Logger, + chainFamily string, + chainID string, + plugin string, +) *ReportingPluginFactory[RI] { + return &ReportingPluginFactory[RI]{ + origin: origin, + lggr: lggr, + chainFamily: chainFamily, + chainID: chainID, + plugin: plugin, + } +} + +func (r ReportingPluginFactory[RI]) NewReportingPlugin(ctx context.Context, config ocr3types.ReportingPluginConfig, bbf ocr3_1types.BlobBroadcastFetcher) (ocr3_1types.ReportingPlugin[RI], ocr3_1types.ReportingPluginInfo, error) { + plugin, info, err := r.origin.NewReportingPlugin(ctx, config, bbf) + if err != nil { + return nil, nil, err + } + r.lggr.Infow("Wrapping OCR3.1 ReportingPlugin with prometheus metrics reporter", + "configDigest", config.ConfigDigest, + "oracleID", config.OracleID, + ) + wrapped := newReportingPlugin( + plugin, + r.chainFamily, + r.chainID, + r.plugin, + config.ConfigDigest.String(), + promOCR3ReportsGenerated, + promOCR3Durations, + promOCR3Sizes, + promOCR3PluginStatus, + ) + return wrapped, info, err +} diff --git a/core/services/ocr3_1/promwrapper/metrics.go b/core/services/ocr3_1/promwrapper/metrics.go new file mode 100644 index 00000000000..cc9f95c3455 --- /dev/null +++ b/core/services/ocr3_1/promwrapper/metrics.go @@ -0,0 +1,104 @@ +// Package promwrapper instruments an OCR3.1 ReportingPlugin with prometheus +// metrics. It is the OCR3.1 counterpart of core/services/ocr3/promwrapper and +// mirrors that package's structure (see also the ocr3 / ocr3_1 split of +// beholderwrapper). +// +// It emits the same ocr3_reporting_plugin_* metric series as the OCR3.0 +// wrapper — the two versions share one metric surface, differentiated by the +// "function" label (which includes the OCR3.1-only phases observationQuorum, +// stateTransition and committed). To keep that surface identical without +// importing the OCR3.0 package, the metric collectors is registered with +// registerOrExisting to avoid runtime issues. +package promwrapper + +import ( + "errors" + "time" + + "github.com/prometheus/client_golang/prometheus" +) + +type functionType string + +const ( + query functionType = "query" + observation functionType = "observation" + validateObservation functionType = "validateObservation" + observationQuorum functionType = "observationQuorum" + stateTransition functionType = "stateTransition" + committed functionType = "committed" + reports functionType = "reports" + shouldAccept functionType = "shouldAccept" + shouldTransmit functionType = "shouldTransmit" +) + +var ( + buckets = []float64{ + float64(10 * time.Millisecond), + float64(50 * time.Millisecond), + float64(100 * time.Millisecond), + float64(200 * time.Millisecond), + float64(500 * time.Millisecond), + float64(700 * time.Millisecond), + float64(time.Second), + float64(2 * time.Second), + float64(5 * time.Second), + float64(10 * time.Second), + float64(20 * time.Second), + float64(30 * time.Second), + } + + promOCR3ReportsGenerated = registerOrExisting(prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "ocr3_reporting_plugin_reports_processed", + Help: "Tracks number of reports processed/generated within by different OCR3 functions", + }, + []string{"chainFamily", "chainID", "plugin", "function"}, + )) + promOCR3Durations = registerOrExisting(prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "ocr3_reporting_plugin_duration", + Help: "The amount of time elapsed during the OCR3 plugin's function", + Buckets: buckets, + }, + []string{"chainFamily", "chainID", "plugin", "function", "success"}, + )) + promOCR3Sizes = registerOrExisting(prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "ocr3_reporting_plugin_data_sizes", + Help: "Tracks the size of the data produced by OCR3 plugin in bytes (e.g. reports, observations etc.)", + }, + []string{"chainFamily", "chainID", "plugin", "function"}, + )) + promOCR3PluginStatus = registerOrExisting(prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "ocr3_reporting_plugin_status", + Help: "Gauge indicating whether plugin is up and running or not", + }, + []string{"chainFamily", "chainID", "plugin", "configDigest"}, + )) +) + +// registerOrExisting registers c on the default registerer, or if an equal +// collector is already registered (e.g. because core/services/ocr3/promwrapper +// is also linked into this binary), returns that existing collector so both +// packages write to a single shared metric series. +func registerOrExisting[C prometheus.Collector](c C) C { + if err := prometheus.DefaultRegisterer.Register(c); err != nil { + var are prometheus.AlreadyRegisteredError + if errors.As(err, &are) { + if existing, ok := are.ExistingCollector.(C); ok { + return existing + } + } + panic(err) + } + return c +} + +func boolToInt(arg bool) int { + if arg { + return 1 + } + return 0 +} diff --git a/core/services/ocr3_1/promwrapper/plugin.go b/core/services/ocr3_1/promwrapper/plugin.go new file mode 100644 index 00000000000..5785101e6ae --- /dev/null +++ b/core/services/ocr3_1/promwrapper/plugin.go @@ -0,0 +1,171 @@ +package promwrapper + +import ( + "context" + "strconv" + "time" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" +) + +var _ ocr3_1types.ReportingPlugin[any] = &reportingPlugin[any]{} + +// reportingPlugin wraps an OCR3.1 ReportingPlugin, instrumenting the same +// prometheus metrics as the OCR3.0 wrapper (durations, data sizes, reports +// generated, up/down status). +type reportingPlugin[RI any] struct { + ocr3_1types.ReportingPlugin[RI] + chainFamily string + chainID string + plugin string + + configDigest string + // Prometheus components for tracking metrics + reportsGenerated *prometheus.CounterVec + durations *prometheus.HistogramVec + sizes *prometheus.CounterVec + status *prometheus.GaugeVec +} + +func newReportingPlugin[RI any]( + origin ocr3_1types.ReportingPlugin[RI], + chainFamily string, + chainID string, + plugin string, + configDigest string, + reportsGenerated *prometheus.CounterVec, + durations *prometheus.HistogramVec, + sizes *prometheus.CounterVec, + status *prometheus.GaugeVec, +) *reportingPlugin[RI] { + return &reportingPlugin[RI]{ + ReportingPlugin: origin, + chainFamily: chainFamily, + chainID: chainID, + plugin: plugin, + configDigest: configDigest, + reportsGenerated: reportsGenerated, + durations: durations, + sizes: sizes, + status: status, + } +} + +func (p *reportingPlugin[RI]) Query(ctx context.Context, seqNr uint64, kvr ocr3_1types.KeyValueStateReader, bbf ocr3_1types.BlobBroadcastFetcher) (ocrtypes.Query, error) { + result, err := withObservedExecution(p, query, func() (ocrtypes.Query, error) { + return p.ReportingPlugin.Query(ctx, seqNr, kvr, bbf) + }) + p.trackSize(query, len(result), err) + return result, err +} + +func (p *reportingPlugin[RI]) Observation(ctx context.Context, seqNr uint64, aq ocrtypes.AttributedQuery, kvr ocr3_1types.KeyValueStateReader, bbf ocr3_1types.BlobBroadcastFetcher) (ocrtypes.Observation, error) { + result, err := withObservedExecution(p, observation, func() (ocrtypes.Observation, error) { + return p.ReportingPlugin.Observation(ctx, seqNr, aq, kvr, bbf) + }) + p.trackSize(observation, len(result), err) + return result, err +} + +func (p *reportingPlugin[RI]) ValidateObservation(ctx context.Context, seqNr uint64, aq ocrtypes.AttributedQuery, ao ocrtypes.AttributedObservation, kvr ocr3_1types.KeyValueStateReader, bf ocr3_1types.BlobFetcher) error { + _, err := withObservedExecution(p, validateObservation, func() (any, error) { + err := p.ReportingPlugin.ValidateObservation(ctx, seqNr, aq, ao, kvr, bf) + return nil, err + }) + return err +} + +func (p *reportingPlugin[RI]) ObservationQuorum(ctx context.Context, seqNr uint64, aq ocrtypes.AttributedQuery, aos []ocrtypes.AttributedObservation, kvr ocr3_1types.KeyValueStateReader, bf ocr3_1types.BlobFetcher) (bool, error) { + return withObservedExecution(p, observationQuorum, func() (bool, error) { + return p.ReportingPlugin.ObservationQuorum(ctx, seqNr, aq, aos, kvr, bf) + }) +} + +func (p *reportingPlugin[RI]) StateTransition(ctx context.Context, seqNr uint64, aq ocrtypes.AttributedQuery, aos []ocrtypes.AttributedObservation, kvrw ocr3_1types.KeyValueStateReadWriter, bf ocr3_1types.BlobFetcher) (ocr3_1types.ReportsPlusPrecursor, error) { + result, err := withObservedExecution(p, stateTransition, func() (ocr3_1types.ReportsPlusPrecursor, error) { + return p.ReportingPlugin.StateTransition(ctx, seqNr, aq, aos, kvrw, bf) + }) + p.trackSize(stateTransition, len(result), err) + return result, err +} + +func (p *reportingPlugin[RI]) Committed(ctx context.Context, seqNr uint64, kvr ocr3_1types.KeyValueStateReader) error { + _, err := withObservedExecution(p, committed, func() (any, error) { + err := p.ReportingPlugin.Committed(ctx, seqNr, kvr) + return nil, err + }) + return err +} + +func (p *reportingPlugin[RI]) Reports(ctx context.Context, seqNr uint64, rpp ocr3_1types.ReportsPlusPrecursor) ([]ocr3types.ReportPlus[RI], error) { + result, err := withObservedExecution(p, reports, func() ([]ocr3types.ReportPlus[RI], error) { + return p.ReportingPlugin.Reports(ctx, seqNr, rpp) + }) + p.trackReports(reports, len(result)) + return result, err +} + +func (p *reportingPlugin[RI]) ShouldAcceptAttestedReport(ctx context.Context, seqNr uint64, reportWithInfo ocr3types.ReportWithInfo[RI]) (bool, error) { + result, err := withObservedExecution(p, shouldAccept, func() (bool, error) { + return p.ReportingPlugin.ShouldAcceptAttestedReport(ctx, seqNr, reportWithInfo) + }) + p.trackReports(shouldAccept, boolToInt(result)) + return result, err +} + +func (p *reportingPlugin[RI]) ShouldTransmitAcceptedReport(ctx context.Context, seqNr uint64, reportWithInfo ocr3types.ReportWithInfo[RI]) (bool, error) { + result, err := withObservedExecution(p, shouldTransmit, func() (bool, error) { + return p.ReportingPlugin.ShouldTransmitAcceptedReport(ctx, seqNr, reportWithInfo) + }) + p.trackReports(shouldTransmit, boolToInt(result)) + return result, err +} + +func (p *reportingPlugin[RI]) Close() error { + p.updateStatus(false) + return p.ReportingPlugin.Close() +} + +func (p *reportingPlugin[RI]) trackReports(function functionType, count int) { + p.reportsGenerated. + WithLabelValues(p.chainFamily, p.chainID, p.plugin, string(function)). + Add(float64(count)) +} + +func (p *reportingPlugin[RI]) updateStatus(status bool) { + p.status. + WithLabelValues(p.chainFamily, p.chainID, p.plugin, p.configDigest). + Set(float64(boolToInt(status))) +} + +func (p *reportingPlugin[RI]) trackSize(function functionType, size int, err error) { + if err != nil { + return + } + p.sizes. + WithLabelValues(p.chainFamily, p.chainID, p.plugin, string(function)). + Add(float64(size)) +} + +func withObservedExecution[RI, R any]( + p *reportingPlugin[RI], + function functionType, + exec func() (R, error), +) (R, error) { + start := time.Now() + result, err := exec() + + success := err == nil + + p.durations. + WithLabelValues(p.chainFamily, p.chainID, p.plugin, string(function), strconv.FormatBool(success)). + Observe(float64(time.Since(start))) + + p.updateStatus(true) + + return result, err +} diff --git a/core/services/ocr3_1/promwrapper/plugin_test.go b/core/services/ocr3_1/promwrapper/plugin_test.go new file mode 100644 index 00000000000..c4a9766fe5b --- /dev/null +++ b/core/services/ocr3_1/promwrapper/plugin_test.go @@ -0,0 +1,170 @@ +package promwrapper + +import ( + "context" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" + io_prometheus_client "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +// Test_Plugin_FunctionLabels guards the OCR3.1 metric labelling: the three +// OCR3.1-only phases must be recorded under their own distinct function labels +// (observationQuorum, stateTransition, committed) and NOT conflated with one +// another. Each method is invoked once and the per-label duration histogram +// sample count is asserted to increment by exactly one. +func Test_Plugin_FunctionLabels(t *testing.T) { + const ( + fam = "evm" + id = "1" + plug = "llo" + ) + funcs := []functionType{query, observation, validateObservation, observationQuorum, stateTransition, committed, reports, shouldAccept, shouldTransmit} + + init := map[functionType]int{} + for _, f := range funcs { + init[f] = counterFromHistogramByLabels(t, promOCR3Durations, fam, id, plug, string(f), "true") + } + + p := newReportingPlugin[uint]( + fakePlugin[uint]{reports: make([]ocr3types.ReportPlus[uint], 2), stateTransitionSize: 4}, + fam, id, plug, "abc", + promOCR3ReportsGenerated, promOCR3Durations, promOCR3Sizes, promOCR3PluginStatus, + ) + + ctx := t.Context() + _, err := p.Query(ctx, 1, nil, nil) + require.NoError(t, err) + _, err = p.Observation(ctx, 1, ocrtypes.AttributedQuery{}, nil, nil) + require.NoError(t, err) + require.NoError(t, p.ValidateObservation(ctx, 1, ocrtypes.AttributedQuery{}, ocrtypes.AttributedObservation{}, nil, nil)) + _, err = p.ObservationQuorum(ctx, 1, ocrtypes.AttributedQuery{}, nil, nil, nil) + require.NoError(t, err) + _, err = p.StateTransition(ctx, 1, ocrtypes.AttributedQuery{}, nil, nil, nil) + require.NoError(t, err) + require.NoError(t, p.Committed(ctx, 1, nil)) + _, err = p.Reports(ctx, 1, nil) + require.NoError(t, err) + _, err = p.ShouldAcceptAttestedReport(ctx, 1, ocr3types.ReportWithInfo[uint]{}) + require.NoError(t, err) + _, err = p.ShouldTransmitAcceptedReport(ctx, 1, ocr3types.ReportWithInfo[uint]{}) + require.NoError(t, err) + + // Every phase recorded exactly one duration sample under its own label. + for _, f := range funcs { + got := counterFromHistogramByLabels(t, promOCR3Durations, fam, id, plug, string(f), "true") - init[f] + require.Equalf(t, 1, got, "duration label %q should increment once", f) + } + + // StateTransition precursor size tracked under the stateTransition label. + require.Equal(t, 4, int(testutil.ToFloat64(promOCR3Sizes.WithLabelValues(fam, id, plug, string(stateTransition))))) + // Reports counter under the reports label. + require.Equal(t, 2, int(testutil.ToFloat64(promOCR3ReportsGenerated.WithLabelValues(fam, id, plug, string(reports))))) +} + +// Test_Factory covers NewReportingPluginFactory + NewReportingPlugin: the +// factory wraps the origin plugin and the wrapper reports metrics. +func Test_Factory(t *testing.T) { + factory := NewReportingPluginFactory[uint]( + fakeFactory[uint]{plugin: fakePlugin[uint]{}}, + logger.TestLogger(t), "aptos", "1", "llo", + ) + + cd := ocrtypes.ConfigDigest{9} + p, info, err := factory.NewReportingPlugin(t.Context(), ocr3types.ReportingPluginConfig{ConfigDigest: cd}, nil) + require.NoError(t, err) + require.NotNil(t, p) + require.NotNil(t, info) + + _, err = p.Query(t.Context(), 1, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, int(testutil.ToFloat64(promOCR3PluginStatus.WithLabelValues("aptos", "1", "llo", cd.String())))) + + require.NoError(t, p.Close()) + require.Equal(t, 0, int(testutil.ToFloat64(promOCR3PluginStatus.WithLabelValues("aptos", "1", "llo", cd.String())))) +} + +func counterFromHistogramByLabels(t *testing.T, histogramVec *prometheus.HistogramVec, labels ...string) int { + observer, err := histogramVec.GetMetricWithLabelValues(labels...) + require.NoError(t, err) + + metricCh := make(chan prometheus.Metric, 1) + observer.(prometheus.Histogram).Collect(metricCh) + close(metricCh) + + metric := <-metricCh + pb := &io_prometheus_client.Metric{} + err = metric.Write(pb) + require.NoError(t, err) + + //nolint:gosec // we don't care about that in tests + return int(pb.GetHistogram().GetSampleCount()) +} + +type fakeFactory[RI any] struct{ plugin fakePlugin[RI] } + +func (f fakeFactory[RI]) NewReportingPlugin(context.Context, ocr3types.ReportingPluginConfig, ocr3_1types.BlobBroadcastFetcher) (ocr3_1types.ReportingPlugin[RI], ocr3_1types.ReportingPluginInfo, error) { + return f.plugin, ocr3_1types.ReportingPluginInfo1{}, nil +} + +type fakePlugin[RI any] struct { + reports []ocr3types.ReportPlus[RI] + observationSize int + stateTransitionSize int + err error +} + +func (f fakePlugin[RI]) Query(context.Context, uint64, ocr3_1types.KeyValueStateReader, ocr3_1types.BlobBroadcastFetcher) (ocrtypes.Query, error) { + return ocrtypes.Query{}, f.err +} + +func (f fakePlugin[RI]) Observation(context.Context, uint64, ocrtypes.AttributedQuery, ocr3_1types.KeyValueStateReader, ocr3_1types.BlobBroadcastFetcher) (ocrtypes.Observation, error) { + if f.err != nil { + return nil, f.err + } + return make([]byte, f.observationSize), nil +} + +func (f fakePlugin[RI]) ValidateObservation(context.Context, uint64, ocrtypes.AttributedQuery, ocrtypes.AttributedObservation, ocr3_1types.KeyValueStateReader, ocr3_1types.BlobFetcher) error { + return f.err +} + +func (f fakePlugin[RI]) ObservationQuorum(context.Context, uint64, ocrtypes.AttributedQuery, []ocrtypes.AttributedObservation, ocr3_1types.KeyValueStateReader, ocr3_1types.BlobFetcher) (bool, error) { + return false, f.err +} + +func (f fakePlugin[RI]) StateTransition(context.Context, uint64, ocrtypes.AttributedQuery, []ocrtypes.AttributedObservation, ocr3_1types.KeyValueStateReadWriter, ocr3_1types.BlobFetcher) (ocr3_1types.ReportsPlusPrecursor, error) { + if f.err != nil { + return nil, f.err + } + return make([]byte, f.stateTransitionSize), nil +} + +func (f fakePlugin[RI]) Committed(context.Context, uint64, ocr3_1types.KeyValueStateReader) error { + return f.err +} + +func (f fakePlugin[RI]) Reports(context.Context, uint64, ocr3_1types.ReportsPlusPrecursor) ([]ocr3types.ReportPlus[RI], error) { + if f.err != nil { + return nil, f.err + } + return f.reports, nil +} + +func (f fakePlugin[RI]) ShouldAcceptAttestedReport(context.Context, uint64, ocr3types.ReportWithInfo[RI]) (bool, error) { + return true, f.err +} + +func (f fakePlugin[RI]) ShouldTransmitAcceptedReport(context.Context, uint64, ocr3types.ReportWithInfo[RI]) (bool, error) { + return true, f.err +} + +func (f fakePlugin[RI]) Close() error { return f.err } From c17e788eb244ca993f52d08521d175857a3d6a4c Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Mon, 3 Aug 2026 11:08:37 +0100 Subject: [PATCH 2/8] llo: OCR3.1 implementation --- core/services/llo/delegate.go | 163 +++++++++++++----- core/services/llo/observation/data_source.go | 64 ++++--- .../llo/observation/data_source_test.go | 66 ++++--- .../llo/observation/observation_context.go | 5 +- core/services/llo/observation/types.go | 6 +- core/services/llo/telem/telemetry.go | 28 +-- core/services/llo/telem/telemetry_test.go | 11 +- core/services/ocr2/delegate.go | 15 ++ 8 files changed, 242 insertions(+), 116 deletions(-) diff --git a/core/services/llo/delegate.go b/core/services/llo/delegate.go index 142a51ff2f1..def9a75b50e 100644 --- a/core/services/llo/delegate.go +++ b/core/services/llo/delegate.go @@ -9,6 +9,7 @@ import ( "github.com/prometheus/client_golang/prometheus" ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" ocr2plus "github.com/smartcontractkit/libocr/offchainreporting2plus" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3shims" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -22,12 +23,14 @@ import ( "github.com/smartcontractkit/chainlink-data-streams/llo/retirement" "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter" llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" + llov31 "github.com/smartcontractkit/chainlink-data-streams/llo/v31" corelogger "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/llo/observation" "github.com/smartcontractkit/chainlink/v2/core/services/llo/telem" "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/promwrapper" + promwrapper31 "github.com/smartcontractkit/chainlink/v2/core/services/ocr3_1/promwrapper" "github.com/smartcontractkit/chainlink/v2/core/services/streams" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" ) @@ -44,8 +47,13 @@ type delegate struct { cfg DelegateConfig reportCodecs map[llotypes.ReportFormat]llocommon.ReportCodec - src llov30.ShouldRetireCache - ds llov30.DataSource + // src is the shared ShouldRetireCache. llov30.ShouldRetireCache and + // llov31.ShouldRetireCache have identical method sets, so this value serves + // both versions. + src llov30.ShouldRetireCache + // ds is the shared LLO data source (llocommon.DataSource); v30 and v31 both + // consume it, lifecycle gating is driven by the round's DSOpts. + ds llocommon.DataSource telem telem.TelemeterService oracles []Closer @@ -86,6 +94,16 @@ type DelegateConfig struct { OnchainKeyring ocr3types.OnchainKeyring[llotypes.ReportInfo] LocalConfig ocr2types.LocalConfig NewOCR3DB func(pluginID int32) ocr3types.Database + + // OCR3.1 (only required when OCR31 is true; see chainlink-data-streams + // llo/config.PluginConfig.OCRVersion) + OCR31 bool + // BinaryNetworkEndpoint2Factory is the OCR3.1 ("2") network endpoint factory + // (peerWrapper.Peer3_1). Required when OCR31 is true. + BinaryNetworkEndpoint2Factory ocr2types.BinaryNetworkEndpoint2Factory + // KeyValueDatabaseFactory provides the replicated per-configDigest key-value + // store the OCR3.1 protocol requires. Required when OCR31 is true. + KeyValueDatabaseFactory ocr3_1types.KeyValueDatabaseFactory } func NewDelegate(cfg DelegateConfig) (job.ServiceCtx, error) { @@ -105,6 +123,14 @@ func NewDelegate(cfg DelegateConfig) (job.ServiceCtx, error) { if cfg.ShouldRetireCache == nil { return nil, errors.New("ShouldRetireCache must not be nil") } + if cfg.OCR31 { + if cfg.KeyValueDatabaseFactory == nil { + return nil, errors.New("KeyValueDatabaseFactory must not be nil when running OCR3.1") + } + if cfg.BinaryNetworkEndpoint2Factory == nil { + return nil, errors.New("BinaryNetworkEndpoint2Factory must not be nil when running OCR3.1") + } + } var codecLggr logger.Logger if cfg.ReportingPluginConfig.VerboseLogging { codecLggr = logger.Named(lggr, "ReportCodecs") @@ -124,11 +150,7 @@ func NewDelegate(cfg DelegateConfig) (job.ServiceCtx, error) { SampleTelemetry: cfg.SampleTelemetry, }) - ds := observation.NewDataSource( - logger.Named(lggr, "DataSource"), - cfg.Registry, - t, - ) + ds := observation.NewDataSource(logger.Named(lggr, "DataSource"), cfg.Registry, t) notifier, ok := cfg.ContractTransmitter.(transmitter.TransmitNotifier) if ok { @@ -166,43 +188,13 @@ func (d *delegate) Start(ctx context.Context) error { // This is a performance optimization }) - oracle, err := ocr2plus.NewOracle(ocr2plus.OCR3OracleArgs2[llotypes.ReportInfo]{ - BinaryNetworkEndpointFactory: d.cfg.BinaryNetworkEndpointFactory, - V2Bootstrappers: d.cfg.V2Bootstrappers, - ContractConfigTracker: configTracker, - ContractTransmitter: d.cfg.ContractTransmitter, - Database: d.cfg.NewOCR3DB(int32(i)), // //nolint:gosec // G115 // impossible due to check on line 119 - LocalConfig: d.cfg.LocalConfig, - Logger: ocrLogger, - MonitoringEndpoint: d.cfg.OCR3MonitoringEndpoint, - OffchainConfigDigester: d.cfg.OffchainConfigDigester, - OffchainKeyring: d.cfg.OffchainKeyring, - OnchainKeyring: ocr3shims.OnchainKeyringAsOnchainKeyring2(d.cfg.OnchainKeyring), - ReportingPluginFactory: promwrapper.NewReportingPluginFactory( - llov30.NewPluginFactory( - llov30.PluginFactoryParams{ - Config: d.cfg.ReportingPluginConfig, - PredecessorRetirementReportCache: psrrc, - ShouldRetireCache: d.src, - RetirementReportCodec: d.cfg.RetirementReportCodec, - ChannelDefinitionCache: d.cfg.ChannelDefinitionCache, - DataSource: d.ds, - Logger: logger.Named(lggr, "ReportingPlugin"), - OnchainConfigCodec: llocommon.EVMOnchainConfigCodec{}, - ReportCodecs: d.reportCodecs, - OutcomeTelemetryCh: d.telem.GetOutcomeTelemetryCh(), - ReportTelemetryCh: d.telem.GetReportTelemetryCh(), - DonID: d.cfg.DonID, - }, - ), - lggr, - "", - d.cfg.ChainID, - "llo", - ), - MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"job_name": d.cfg.JobName.ValueOrZero()}, prometheus.DefaultRegisterer), - }) - + var oracle ocr2plus.Oracle + var err error + if d.cfg.OCR31 { + oracle, err = d.newOracleV31(i, configTracker, lggr, ocrLogger, psrrc) + } else { + oracle, err = d.newOracleV30(i, configTracker, lggr, ocrLogger, psrrc) + } if err != nil { return fmt.Errorf("%w: failed to create new OCR oracle", err) } @@ -216,6 +208,89 @@ func (d *delegate) Start(ctx context.Context) error { }) } +// newOracleV30 builds an OCR3.0 oracle running the llo/v30 reporting plugin. +func (d *delegate) newOracleV30(i int, configTracker ocr2types.ContractConfigTracker, lggr logger.Logger, ocrLogger ocrcommontypes.Logger, psrrc llocommon.PredecessorRetirementReportCache) (ocr2plus.Oracle, error) { + return ocr2plus.NewOracle(ocr2plus.OCR3OracleArgs2[llotypes.ReportInfo]{ + BinaryNetworkEndpointFactory: d.cfg.BinaryNetworkEndpointFactory, + V2Bootstrappers: d.cfg.V2Bootstrappers, + ContractConfigTracker: configTracker, + ContractTransmitter: d.cfg.ContractTransmitter, + Database: d.cfg.NewOCR3DB(int32(i)), //nolint:gosec // G115 // impossible due to ContractConfigTrackers length check + LocalConfig: d.cfg.LocalConfig, + Logger: ocrLogger, + MonitoringEndpoint: d.cfg.OCR3MonitoringEndpoint, + OffchainConfigDigester: d.cfg.OffchainConfigDigester, + OffchainKeyring: d.cfg.OffchainKeyring, + OnchainKeyring: ocr3shims.OnchainKeyringAsOnchainKeyring2(d.cfg.OnchainKeyring), + ReportingPluginFactory: promwrapper.NewReportingPluginFactory( + llov30.NewPluginFactory( + llov30.PluginFactoryParams{ + Config: d.cfg.ReportingPluginConfig, + PredecessorRetirementReportCache: psrrc, + ShouldRetireCache: d.src, + RetirementReportCodec: d.cfg.RetirementReportCodec, + ChannelDefinitionCache: d.cfg.ChannelDefinitionCache, + DataSource: d.ds, + Logger: logger.Named(lggr, "ReportingPlugin"), + OnchainConfigCodec: llocommon.EVMOnchainConfigCodec{}, + ReportCodecs: d.reportCodecs, + OutcomeTelemetryCh: d.telem.GetOutcomeTelemetryCh(), + ReportTelemetryCh: d.telem.GetReportTelemetryCh(), + DonID: d.cfg.DonID, + }, + ), + lggr, + "", + d.cfg.ChainID, + "llo", + ), + MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"job_name": d.cfg.JobName.ValueOrZero()}, prometheus.DefaultRegisterer), + }) +} + +// newOracleV31 builds an OCR3.1 oracle running the llo/v31 reporting plugin. It +// differs from v30 by the OCR3.1 oracle args (OCR3_1OracleArgs2), the "2" +// network endpoint factory, and the required replicated KeyValueDatabaseFactory. +func (d *delegate) newOracleV31(i int, configTracker ocr2types.ContractConfigTracker, lggr logger.Logger, ocrLogger ocrcommontypes.Logger, psrrc llocommon.PredecessorRetirementReportCache) (ocr2plus.Oracle, error) { + factory := promwrapper31.NewReportingPluginFactory( + llov31.NewPluginFactory(llov31.PluginFactoryParams{ + Config: llov31.Config{VerboseLogging: d.cfg.ReportingPluginConfig.VerboseLogging}, + PredecessorRetirementReportCache: psrrc, + ShouldRetireCache: d.src, + RetirementReportCodec: d.cfg.RetirementReportCodec, + ChannelDefinitionCache: d.cfg.ChannelDefinitionCache, + DataSource: d.ds, + Logger: logger.Named(lggr, "ReportingPlugin"), + OnchainConfigCodec: llocommon.EVMOnchainConfigCodec{}, + ReportCodecs: d.reportCodecs, + OutcomeTelemetryCh: d.telem.GetOutcomeTelemetryCh(), + ReportTelemetryCh: d.telem.GetReportTelemetryCh(), + DonID: d.cfg.DonID, + BlobThreshold: 0, // 0 => llov31.DefaultBlobThreshold + }), + lggr, + "", + d.cfg.ChainID, + "llo", + ) + return ocr2plus.NewOracle(ocr2plus.OCR3_1OracleArgs2[llotypes.ReportInfo]{ + BinaryNetworkEndpointFactory: d.cfg.BinaryNetworkEndpoint2Factory, + V2Bootstrappers: d.cfg.V2Bootstrappers, + ContractConfigTracker: configTracker, + ContractTransmitter: d.cfg.ContractTransmitter, + Database: d.cfg.NewOCR3DB(int32(i)), //nolint:gosec // G115 // impossible due to ContractConfigTrackers length check + KeyValueDatabaseFactory: d.cfg.KeyValueDatabaseFactory, + LocalConfig: d.cfg.LocalConfig, + Logger: ocrLogger, + MonitoringEndpoint: d.cfg.OCR3MonitoringEndpoint, + OffchainConfigDigester: d.cfg.OffchainConfigDigester, + OffchainKeyring: d.cfg.OffchainKeyring, + OnchainKeyring: ocr3shims.OnchainKeyringAsOnchainKeyring2(d.cfg.OnchainKeyring), + ReportingPluginFactory: factory, + MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"job_name": d.cfg.JobName.ValueOrZero()}, prometheus.DefaultRegisterer), + }) +} + func (d *delegate) Close() error { return d.StopOnce("LLODelegate", func() (merr error) { for _, oracle := range d.oracles { diff --git a/core/services/llo/observation/data_source.go b/core/services/llo/observation/data_source.go index 9130efdb756..b4220d91243 100644 --- a/core/services/llo/observation/data_source.go +++ b/core/services/llo/observation/data_source.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" + "github.com/smartcontractkit/chainlink/v2/core/services/llo/telem" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/streams" ) @@ -147,8 +147,6 @@ func (e *ObservationFailedError) Unwrap() error { return e.inner } -var _ llov30.DataSource = &dataSource{} - type dataSource struct { wg sync.WaitGroup lggr logger.Logger @@ -165,7 +163,12 @@ type dataSource struct { loopWakeCh chan struct{} } -func NewDataSource(lggr logger.Logger, registry Registry, t Telemeter) llov30.DataSource { +var _ llocommon.DataSource = &dataSource{} + +// NewDataSource returns the shared LLO data source. llo/v30 and llo/v31 both +// consume llocommon.DataSource, so a single implementation serves both OCR +// protocol versions; lifecycle gating is driven by opts.LifeCycleStage(). +func NewDataSource(lggr logger.Logger, registry Registry, t Telemeter) llocommon.DataSource { return newDataSource(lggr, registry, t) } @@ -192,13 +195,38 @@ func (d *dataSource) signalObservationLoopWake() { } } -// Observe starts or refreshes the background observation loop for the plugin's stream set, then fills streamValues -// from the in-memory cache (backed by pipeline observations registered for each stream ID). -func (d *dataSource) Observe(ctx context.Context, streamValues llocommon.StreamValues, opts llov30.DSOpts) error { +// Observe gates the background observation loop on the round's lifecycle stage +// (only a Production instance runs pipeline observations), then fills +// streamValues from the in-memory cache. The stage is carried by opts and is +// derived by each plugin version from its own state (v30 from the previous +// outcome, v31 from the KeyValueState), so a single implementation serves both. +func (d *dataSource) Observe(ctx context.Context, streamValues llocommon.StreamValues, opts llocommon.DSOpts) error { + return d.observe(ctx, streamValues, opts, d.inProduction(opts)) +} + +// inProduction reports whether this OCR instance is the Production instance (the +// only one that should run pipeline observations). +func (d *dataSource) inProduction(opts llocommon.DSOpts) bool { + if opts == nil { + d.lggr.Warnw("Observe: nil opts, treating as not-in-production") + return false + } + if opts.LifeCycleStage() != llocommon.LifeCycleStageProduction { + d.lggr.Debugw("Observe: LLO OCR instance is not in production lifecycle stage", + "configDigest", opts.ConfigDigest().String(), "stage", opts.LifeCycleStage()) + return false + } + return true +} + +// observe starts or refreshes the background observation loop for the plugin's stream set, then fills streamValues +// from the in-memory cache (backed by pipeline observations registered for each stream ID). inProduction gates the +// loop: when false the observable stream set is cleared and no pipelines run this round. +func (d *dataSource) observe(ctx context.Context, streamValues llocommon.StreamValues, opts telem.DSOpts, inProduction bool) error { // Observation loop logic { // setObservableStreams copies stream IDs and deadline into internal state (the plugin's map is not retained). - d.setObservableStreams(ctx, streamValues, opts) + d.setObservableStreams(ctx, streamValues, opts, inProduction) if !d.observationLoopStarted.Load() { loopStartedCh := make(chan struct{}) @@ -283,7 +311,7 @@ func (d *dataSource) startObservationLoop(loopStartedCh chan struct{}) { startTS := time.Now() ctx, cancel := context.WithTimeout(stopChanCtx, osv.observationTimeout) - lggr := logger.With(d.lggr, "observationTimestamp", osv.opts.ObservationTimestamp(), "configDigest", osv.opts.ConfigDigest(), "seqNr", osv.opts.OutCtx().SeqNr) + lggr := logger.With(d.lggr, "observationTimestamp", osv.opts.ObservationTimestamp(), "configDigest", osv.opts.ConfigDigest(), "seqNr", osv.opts.SeqNr()) var mu sync.Mutex var wg sync.WaitGroup @@ -490,30 +518,24 @@ func (d *dataSource) Close() error { } type observableStreamValues struct { - opts llov30.DSOpts + opts telem.DSOpts streamValues llocommon.StreamValues observationTimeout time.Duration } -// setObservableStreams updates the stream set and observation deadline (T) used by the background loop when in production. -func (d *dataSource) setObservableStreams(ctx context.Context, streamValues llocommon.StreamValues, opts llov30.DSOpts) { +// setObservableStreams updates the stream set and observation deadline (T) used by the background loop. When +// inProduction is false (v30 non-production instance) the observable set is left unchanged/empty so no pipelines run. +func (d *dataSource) setObservableStreams(ctx context.Context, streamValues llocommon.StreamValues, opts telem.DSOpts, inProduction bool) { if opts == nil || len(streamValues) == 0 { d.lggr.Warnw("setObservableStreams: no observable streams to set", "opts", opts, "observable_streams", len(streamValues)) return } - outCtx := opts.OutCtx() - outcome, err := opts.OutcomeCodec().Decode(outCtx.PreviousOutcome) - if err != nil { - d.lggr.Errorw("setObservableStreams: failed to decode outcome", "error", err) - return - } - - if outcome.LifeCycleStage != llocommon.LifeCycleStageProduction { + if !inProduction { d.lggr.Debugw( "setObservableStreams: LLO OCR instance is not in production lifecycle stage", - "configDigest", opts.ConfigDigest().String(), "stage", outcome.LifeCycleStage) + "configDigest", opts.ConfigDigest().String()) return } diff --git a/core/services/llo/observation/data_source_test.go b/core/services/llo/observation/data_source_test.go index a2bf5e33bd7..d524ccde621 100644 --- a/core/services/llo/observation/data_source_test.go +++ b/core/services/llo/observation/data_source_test.go @@ -16,7 +16,6 @@ import ( promtest "github.com/prometheus/client_golang/prometheus/testutil" "github.com/shopspring/decimal" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -24,7 +23,6 @@ import ( llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" "github.com/smartcontractkit/chainlink/v2/core/bridges" clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -100,9 +98,9 @@ func makeStreamValues(streamIDs ...llotypes.StreamID) llocommon.StreamValues { type mockOpts struct { verboseLogging bool seqNr uint64 - outCtx ocr3types.OutcomeContext configDigest ocr2types.ConfigDigest observationTimestamp time.Time + lifeCycleStage llotypes.LifeCycleStage } func (m *mockOpts) VerboseLogging() bool { return m.verboseLogging } @@ -112,12 +110,6 @@ func (m *mockOpts) SeqNr() uint64 { } return m.seqNr } -func (m *mockOpts) OutCtx() ocr3types.OutcomeContext { - if m.outCtx.SeqNr == 0 { - return ocr3types.OutcomeContext{SeqNr: 1042, PreviousOutcome: []byte("foo")} - } - return m.outCtx -} func (m *mockOpts) ConfigDigest() ocr2types.ConfigDigest { if m.configDigest.Hex() == "" { return ocr2types.ConfigDigest{6, 5, 4} @@ -130,19 +122,11 @@ func (m *mockOpts) ObservationTimestamp() time.Time { } return m.observationTimestamp } -func (m *mockOpts) OutcomeCodec() llov30.OutcomeCodec { - return mockOutputCodec{} -} - -type mockOutputCodec struct{} - -func (oc mockOutputCodec) Encode(outcome llov30.Outcome) (ocr3types.Outcome, error) { - return ocr3types.Outcome{}, nil -} -func (oc mockOutputCodec) Decode(encoded ocr3types.Outcome) (outcome llov30.Outcome, err error) { - return llov30.Outcome{ - LifeCycleStage: llocommon.LifeCycleStageProduction, - }, nil +func (m *mockOpts) LifeCycleStage() llotypes.LifeCycleStage { + if m.lifeCycleStage == "" { + return llocommon.LifeCycleStageProduction + } + return m.lifeCycleStage } type mockTelemeter struct { @@ -155,19 +139,19 @@ type v3PremiumLegacyPacket struct { run *pipeline.Run trrs pipeline.TaskRunResults streamID uint32 - opts llov30.DSOpts + opts telem.DSOpts val llocommon.StreamValue err error } var _ Telemeter = &mockTelemeter{} -func (m *mockTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llov30.DSOpts, val llocommon.StreamValue, err error) { +func (m *mockTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts telem.DSOpts, val llocommon.StreamValue, err error) { m.mu.Lock() defer m.mu.Unlock() m.v3PremiumLegacyPackets = append(m.v3PremiumLegacyPackets, v3PremiumLegacyPacket{run, trrs, streamID, opts, val, err}) } -func (m *mockTelemeter) MakeObservationScopedTelemetryCh(opts llov30.DSOpts, size int) (ch chan<- any) { +func (m *mockTelemeter) MakeObservationScopedTelemetryCh(opts telem.DSOpts, size int) (ch chan<- any) { m.mu.Lock() defer m.mu.Unlock() m.ch = make(chan any, size) @@ -941,3 +925,35 @@ result3 -> result3_parse -> multiply3; require.NoError(b, err) ds.Close() } + +// Test_DataSource_inProduction covers the lifecycle gate: only a Production +// instance observes; staging/retired/unknown and nil opts do not. +func Test_DataSource_inProduction(t *testing.T) { + t.Parallel() + reg := &mockRegistry{pipelines: make(map[streams.StreamID]*mockPipeline)} + ds := newDataSource(logger.NullLogger, reg, telem.NullTelemeter) + defer ds.Close() + + require.True(t, ds.inProduction(&mockOpts{lifeCycleStage: llocommon.LifeCycleStageProduction})) + require.False(t, ds.inProduction(&mockOpts{lifeCycleStage: llocommon.LifeCycleStageStaging})) + require.False(t, ds.inProduction(&mockOpts{lifeCycleStage: llocommon.LifeCycleStageRetired})) + require.False(t, ds.inProduction(nil)) +} + +// Test_DataSource_StagingDoesNotObserve asserts a non-Production instance runs +// no pipelines and returns unset stream values. +func Test_DataSource_StagingDoesNotObserve(t *testing.T) { + t.Parallel() + reg := &mockRegistry{pipelines: make(map[streams.StreamID]*mockPipeline)} + reg.pipelines[1] = pipelineForStream(1, 1, big.NewInt(42), nil) + ds := newDataSource(logger.NullLogger, reg, telem.NullTelemeter) + defer ds.Close() + + ctx, cancel := context.WithTimeout(t.Context(), observationTimeout) + defer cancel() + vals := makeStreamValues(1) + require.NoError(t, ds.Observe(ctx, vals, &mockOpts{lifeCycleStage: llocommon.LifeCycleStageStaging})) + + require.Nil(t, vals[1], "staging instance must not populate stream values") + require.Zero(t, reg.pipelines[1].runCount.Load(), "staging instance must not run pipelines") +} diff --git a/core/services/llo/observation/observation_context.go b/core/services/llo/observation/observation_context.go index 52c286955b0..d0b80242b58 100644 --- a/core/services/llo/observation/observation_context.go +++ b/core/services/llo/observation/observation_context.go @@ -14,7 +14,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" "github.com/smartcontractkit/chainlink/v2/core/services/llo/telem" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -30,7 +29,7 @@ import ( var _ ObservationContext = (*observationContext)(nil) type ObservationContext interface { //nolint:revive // ObservationContext is the established interface name in this package - Observe(ctx context.Context, streamID streams.StreamID, opts llov30.DSOpts) (val llocommon.StreamValue, err error) + Observe(ctx context.Context, streamID streams.StreamID, opts telem.DSOpts) (val llocommon.StreamValue, err error) } type execution struct { @@ -59,7 +58,7 @@ func newObservationContext(l logger.Logger, r Registry, t Telemeter) *observatio return &observationContext{l, r, t, sync.Mutex{}, make(map[streams.Pipeline]*execution)} } -func (oc *observationContext) Observe(ctx context.Context, streamID streams.StreamID, opts llov30.DSOpts) (val llocommon.StreamValue, err error) { +func (oc *observationContext) Observe(ctx context.Context, streamID streams.StreamID, opts telem.DSOpts) (val llocommon.StreamValue, err error) { run, trrs, err := oc.run(ctx, streamID) observationFinishedAt := time.Now() if err != nil { diff --git a/core/services/llo/observation/types.go b/core/services/llo/observation/types.go index 6b5e1e14de1..656aad3eb3f 100644 --- a/core/services/llo/observation/types.go +++ b/core/services/llo/observation/types.go @@ -4,7 +4,7 @@ import ( "context" llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" + "github.com/smartcontractkit/chainlink/v2/core/services/llo/telem" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/streams" ) @@ -14,8 +14,8 @@ type Registry interface { } type Telemeter interface { - EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llov30.DSOpts, val llocommon.StreamValue, err error) - MakeObservationScopedTelemetryCh(opts llov30.DSOpts, size int) (ch chan<- any) + EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts telem.DSOpts, val llocommon.StreamValue, err error) + MakeObservationScopedTelemetryCh(opts telem.DSOpts, size int) (ch chan<- any) CaptureEATelemetry() bool CaptureObservationTelemetry() bool } diff --git a/core/services/llo/telem/telemetry.go b/core/services/llo/telem/telemetry.go index b956e9126e7..db50172f1bd 100644 --- a/core/services/llo/telem/telemetry.go +++ b/core/services/llo/telem/telemetry.go @@ -15,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodecs/evm" - llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -27,9 +26,14 @@ import ( const adapterLWBAErrorName = "AdapterLWBAError" +// DSOpts is the shared, version-agnostic LLO data-source options (llo/v30 and +// llo/v31 both use llocommon.DSOpts). Aliased here so the telemetry and +// observation paths keep referring to telem.DSOpts. +type DSOpts = llocommon.DSOpts + type Telemeter interface { - EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llov30.DSOpts, val llocommon.StreamValue, err error) - MakeObservationScopedTelemetryCh(opts llov30.DSOpts, size int) (ch chan<- any) + EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val llocommon.StreamValue, err error) + MakeObservationScopedTelemetryCh(opts DSOpts, size int) (ch chan<- any) GetOutcomeTelemetryCh() chan<- *llocommon.LLOOutcomeTelemetry GetReportTelemetryCh() chan<- *llocommon.LLOReportTelemetry CaptureEATelemetry() bool @@ -146,7 +150,7 @@ type telemeter struct { sampler *sampler } -func (t *telemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llov30.DSOpts, val llocommon.StreamValue, err error) { +func (t *telemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val llocommon.StreamValue, err error) { if t.Ready() != nil { // This should never happen, telemeter should always be started BEFORE // the oracle and closed AFTER it @@ -170,7 +174,7 @@ func (t *telemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.Task type telemetryCollectionContext struct { in <-chan any - opts llov30.DSOpts + opts DSOpts } // MakeObservationScopedTelemetryCh reads telem packets from the returned channel and sends them @@ -182,7 +186,7 @@ type telemetryCollectionContext struct { // // It is necessary to make a new channel for every Observation call because it // closes over DSOpts which is scoped to that call only. -func (t *telemeter) MakeObservationScopedTelemetryCh(opts llov30.DSOpts, size int) chan<- any { +func (t *telemeter) MakeObservationScopedTelemetryCh(opts DSOpts, size int) chan<- any { if !t.captureObservationTelemetry && !t.captureEATelemetry { return nil } @@ -362,7 +366,7 @@ func (t *telemeter) enqueueTelemetry(digest string, seqNr uint64, typ synchroniz } } -func (t *telemeter) prepareObservationTelemetry(p any, opts llov30.DSOpts) { +func (t *telemeter) prepareObservationTelemetry(p any, opts DSOpts) { var telemType synchronization.TelemetryType var msg proto.Message switch v := p.(type) { @@ -447,7 +451,7 @@ func (t *telemeter) prepareV3PremiumLegacyTelemetry(d *TelemetryPipeline) { Version: uint32(1000 + mercury.REPORT_V3), // add 1000 to distinguish between legacy feeds, this can be changed if necessary DonId: t.donID, } - epoch, round, err := evm.SeqNrToEpochAndRound(d.opts.OutCtx().SeqNr) + epoch, round, err := evm.SeqNrToEpochAndRound(d.opts.SeqNr()) if err != nil { t.eng.Warnw("Failed to convert sequence number to epoch and round", "err", err) } else { @@ -470,7 +474,7 @@ func (t *telemeter) TrackSeqNr(digest types.ConfigDigest, seqNr uint64) { } type TelemetryObserve struct { - Opts llov30.DSOpts + Opts DSOpts Telemetry any } @@ -478,7 +482,7 @@ type TelemetryPipeline struct { run *pipeline.Run trrs pipeline.TaskRunResults streamID uint32 - opts llov30.DSOpts + opts DSOpts val llocommon.StreamValue dpInvariantViolationDetected bool } @@ -487,9 +491,9 @@ var NullTelemeter TelemeterService = &nullTelemeter{} type nullTelemeter struct{} -func (t *nullTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llov30.DSOpts, val llocommon.StreamValue, err error) { +func (t *nullTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val llocommon.StreamValue, err error) { } -func (t *nullTelemeter) MakeObservationScopedTelemetryCh(opts llov30.DSOpts, size int) (ch chan<- any) { +func (t *nullTelemeter) MakeObservationScopedTelemetryCh(opts DSOpts, size int) (ch chan<- any) { return nil } func (t *nullTelemeter) GetOutcomeTelemetryCh() chan<- *llocommon.LLOOutcomeTelemetry { diff --git a/core/services/llo/telem/telemetry_test.go b/core/services/llo/telem/telemetry_test.go index 682c80374a9..c6036c3d85f 100644 --- a/core/services/llo/telem/telemetry_test.go +++ b/core/services/llo/telem/telemetry_test.go @@ -13,11 +13,10 @@ import ( "google.golang.org/protobuf/proto" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -50,18 +49,14 @@ type mockOpts struct { func (m *mockOpts) VerboseLogging() bool { return m.verboseLogging } func (m *mockOpts) SeqNr() uint64 { return 1042 } -func (m *mockOpts) OutCtx() ocr3types.OutcomeContext { - return ocr3types.OutcomeContext{SeqNr: 1042, PreviousOutcome: ocr3types.Outcome([]byte("foo"))} -} func (m *mockOpts) ConfigDigest() ocr2types.ConfigDigest { return ocr2types.ConfigDigest{6, 5, 4} } func (m *mockOpts) ObservationTimestamp() time.Time { return time.Unix(1737936858, 0) } - -func (m *mockOpts) OutcomeCodec() llov30.OutcomeCodec { - return nil +func (m *mockOpts) LifeCycleStage() llotypes.LifeCycleStage { + return llocommon.LifeCycleStageProduction } const bridgeResponse = `{ diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 72c4eb7dc17..5f3f7f1ff6a 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -1571,7 +1571,22 @@ func (d *Delegate) newServicesLLO( NewOCR3DB: func(pluginID int32) ocr3types.Database { return NewDB(d.ds, spec.ID, pluginID, lggr) }, + + OCR31: pluginCfg.IsOCR31(), } + + // OCR3.1 (llo/v31) additionally requires the "2" network endpoint factory and + // a persistent replicated key-value store, wired here the same way the vault + // and DKG OCR3.1 plugins are (pebble under OCR2().KeyValueStoreRootDir()). + if pluginCfg.IsOCR31() { + fullPath := filepath.Join(d.cfg.OCR2().KeyValueStoreRootDir(), jb.ExternalJobID.String()) + if err = utils.EnsureDirAndMaxPerms(fullPath, os.FileMode(0700)); err != nil { + return nil, fmt.Errorf("failed to create LLO key value store directory: %w", err) + } + cfg.BinaryNetworkEndpoint2Factory = d.peerWrapper.Peer3_1 + cfg.KeyValueDatabaseFactory = kvdb.NewPebbleKeyValueDatabaseFactory(fullPath) + } + oracle, err := llo.NewDelegate(cfg) if err != nil { return nil, err From b60fcebcd4a6b9e8fd5d55d212480e1b5c689f52 Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Mon, 3 Aug 2026 11:10:30 +0100 Subject: [PATCH 3/8] llo: OCR3.1 integration tests --- .../services/ocr2/plugins/llo/helpers_test.go | 6 + .../llo/history_backfill_integration_test.go | 21 +- .../ocr2/plugins/llo/integration_test.go | 200 +++++++++++++++--- 3 files changed, 197 insertions(+), 30 deletions(-) diff --git a/core/services/ocr2/plugins/llo/helpers_test.go b/core/services/ocr2/plugins/llo/helpers_test.go index 683a0a39e56..a36aad19d3b 100644 --- a/core/services/ocr2/plugins/llo/helpers_test.go +++ b/core/services/ocr2/plugins/llo/helpers_test.go @@ -181,10 +181,16 @@ func setupNode( // [OCR2] c.OCR2.Enabled = new(true) c.OCR2.ContractPollInterval = commonconfig.MustNewDuration(100 * time.Millisecond) + // Unique per-node root for the OCR3.1 (llo/v31) pebble key-value store so the + // nodes in this process don't share state and nothing is written to ~/.chainlink-data. + c.OCR2.KeyValueStoreRootDir = new(t.TempDir()) // [P2P] c.P2P.PeerID = new(p2pKey.PeerID()) c.P2P.TraceLogging = new(true) + // Required for OCR3.1 (llo/v31) networking (the "2" endpoint factory needs + // the experimental ragep2p host). Backward-compatible with OCR3.0. + c.P2P.EnableExperimentalRageP2P = new(true) // [P2P.V2] c.P2P.V2.Enabled = new(true) diff --git a/core/services/ocr2/plugins/llo/history_backfill_integration_test.go b/core/services/ocr2/plugins/llo/history_backfill_integration_test.go index d615af19b51..96e6828d256 100644 --- a/core/services/ocr2/plugins/llo/history_backfill_integration_test.go +++ b/core/services/ocr2/plugins/llo/history_backfill_integration_test.go @@ -105,7 +105,19 @@ func quoteBackfillString(benchmark float64) string { func TestIntegration_LLO_history_backfill(t *testing.T) { t.Parallel() + for _, ocr31 := range []bool{false, true} { + name := "OCR3.0/v30" + if ocr31 { + name = "OCR3.1/v31" + } + t.Run(name, func(t *testing.T) { + t.Parallel() + testIntegrationLLOHistoryBackfill(t, ocr31) + }) + } +} +func testIntegrationLLOHistoryBackfill(t *testing.T, ocr31 bool) { const ( salt = 600 donID = uint32(776655) @@ -167,6 +179,9 @@ lloConfigMode = "bluegreen" donID = %d channelDefinitionsContractAddress = "0x%x" channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, configStoreAddress, fromBlock) + if ocr31 { + pluginConfig += "\nocrVersion = \"3.1\"" + } nativeStrm := Stream{ id: streamNative, @@ -208,9 +223,13 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi require.NoError(t, err) backend.Commit() + productionConfigOpts := []OCRConfigOption{WithOracles(oracles), WithOffchainConfig(offchainConfig)} + if ocr31 { + productionConfigOpts = append(productionConfigOpts, WithOCR31()) + } setProductionConfig( t, donID, steve, backend, configurator, configuratorAddress, nodes, - WithOracles(oracles), WithOffchainConfig(offchainConfig), + productionConfigOpts..., ) signerAddresses := make([]common.Address, len(oracles)) diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go index 0068209c5c6..134aeb37e41 100644 --- a/core/services/ocr2/plugins/llo/integration_test.go +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -32,6 +32,7 @@ import ( "github.com/smartcontractkit/freeport" "github.com/smartcontractkit/libocr/offchainreporting2/types" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -225,6 +226,12 @@ type OCRConfig struct { MaxDurationShouldTransmitAcceptedReport time.Duration F int OnchainConfig []byte + + // ocr31 selects the OCR3.1 (llo/v31) config format when true. It changes the + // confighelper used by generateConfig (ocr3_1confighelper => offchainConfigVersion + // 310) so the on-chain config matches what an OCR3.1 node validates. The + // config digest prefix (LLO 0x0009) is identical across OCR3.0/3.1. + ocr31 bool } func makeDefaultOCRConfig() *OCRConfig { @@ -285,6 +292,13 @@ func WithOracles(oracles []confighelper.OracleIdentityExtra) OCRConfigOption { } } +// WithOCR31 switches config generation to the OCR3.1 (llo/v31) format. +func WithOCR31() OCRConfigOption { + return func(cfg *OCRConfig) { + cfg.ocr31 = true + } +} + type OCRConfigOption func(*OCRConfig) func generateConfig(t *testing.T, opts ...OCRConfigOption) (signers []types.OnchainPublicKey, transmitters []types.Account, f uint8, outOnchainConfig []byte, offchainConfigVersion uint64, offchainConfig []byte) { @@ -295,30 +309,85 @@ func generateConfig(t *testing.T, opts ...OCRConfigOption) (signers []types.Onch } t.Logf("Using OCR config: %+v\n", cfg) var err error - signers, transmitters, f, outOnchainConfig, offchainConfigVersion, offchainConfig, err = ocr3confighelper.ContractSetConfigArgsForTests( + if cfg.ocr31 { + signers, transmitters, f, outOnchainConfig, offchainConfigVersion, offchainConfig, err = generateOCR31Config(cfg) + } else { + signers, transmitters, f, outOnchainConfig, offchainConfigVersion, offchainConfig, err = ocr3confighelper.ContractSetConfigArgsForTests( + cfg.DeltaProgress, + cfg.DeltaResend, + cfg.DeltaInitial, + cfg.DeltaRound, + cfg.DeltaGrace, + cfg.DeltaCertifiedCommitRequest, + cfg.DeltaStage, + cfg.RMax, + cfg.S, + cfg.Oracles, + cfg.ReportingPluginConfig, + cfg.MaxDurationInitialization, + cfg.MaxDurationQuery, + cfg.MaxDurationObservation, + cfg.MaxDurationShouldAcceptAttestedReport, + cfg.MaxDurationShouldTransmitAcceptedReport, + cfg.F, + cfg.OnchainConfig, + ) + } + + require.NoError(t, err) + + return +} + +// generateOCR31Config maps the shared OCRConfig to ocr3_1confighelper's OCR3.1 +// argument shape (offchainConfigVersion 310). The v3.0 MaxDurationQuery/Observation +// become OCR3.1 warn durations; DeltaResend/DeltaInitial/DeltaCertifiedCommitRequest +// move into the optional-config struct. +func generateOCR31Config(cfg *OCRConfig) (signers []types.OnchainPublicKey, transmitters []types.Account, f uint8, outOnchainConfig []byte, offchainConfigVersion uint64, offchainConfig []byte, err error) { + // OCR3.1 requires a positive MaxDurationInitialization (unlike OCR3.0 where it + // is optional); default it when the shared config leaves it unset. + maxDurationInitialization := time.Second + if cfg.MaxDurationInitialization != nil && *cfg.MaxDurationInitialization > 0 { + maxDurationInitialization = *cfg.MaxDurationInitialization + } + deltaResend := cfg.DeltaResend + deltaInitial := cfg.DeltaInitial + deltaReportsPlusPrecursorRequest := cfg.DeltaCertifiedCommitRequest + // OCR3.1 requires every warn/max duration to be positive. The shared OCRConfig + // leaves several at 0 (valid for OCR3.0); positive-default them here. + positive := func(d time.Duration) time.Duration { + if d > 0 { + return d + } + return time.Second + } + return ocr3_1confighelper.ContractSetConfigArgsForTests( + ocr3_1confighelper.CheckPublicConfigLevelDefault, + cfg.Oracles, + cfg.F, cfg.DeltaProgress, - cfg.DeltaResend, - cfg.DeltaInitial, cfg.DeltaRound, cfg.DeltaGrace, - cfg.DeltaCertifiedCommitRequest, - cfg.DeltaStage, cfg.RMax, + cfg.DeltaStage, cfg.S, - cfg.Oracles, cfg.ReportingPluginConfig, - cfg.MaxDurationInitialization, - cfg.MaxDurationQuery, - cfg.MaxDurationObservation, - cfg.MaxDurationShouldAcceptAttestedReport, - cfg.MaxDurationShouldTransmitAcceptedReport, - cfg.F, cfg.OnchainConfig, + maxDurationInitialization, + positive(cfg.MaxDurationQuery), // warnDurationQuery + positive(cfg.MaxDurationObservation), // warnDurationObservation + time.Second, // warnDurationValidateObservation + time.Second, // warnDurationObservationQuorum + time.Second, // warnDurationStateTransition + time.Second, // warnDurationCommitted + positive(cfg.MaxDurationShouldAcceptAttestedReport), + positive(cfg.MaxDurationShouldTransmitAcceptedReport), + ocr3_1confighelper.ContractSetConfigArgsOptionalConfig{ + DeltaResend: &deltaResend, + DeltaInitial: &deltaInitial, + DeltaReportsPlusPrecursorRequest: &deltaReportsPlusPrecursorRequest, + }, ) - - require.NoError(t, err) - - return } func setLegacyConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, legacyVerifier *verifier.Verifier, legacyVerifierAddr common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra, inOffchainConfig llocommon.OffchainConfig) ocr2types.ConfigDigest { @@ -666,15 +735,24 @@ func TestIntegration_LLO_multi_formats(t *testing.T) { DefaultMinReportIntervalNanoseconds: 1, }, } + ocrVersions := []struct { + name string + ocr31 bool + }{ + {"OCR3.0/v30", false}, + {"OCR3.1/v31", true}, + } for _, offchainConfig := range offchainConfigs { - t.Run(fmt.Sprintf("offchainConfig=%+v", offchainConfig), func(t *testing.T) { - t.Parallel() - testIntegrationLLOMultiFormats(t, offchainConfig) - }) + for _, ov := range ocrVersions { + t.Run(fmt.Sprintf("%s/offchainConfig=%+v", ov.name, offchainConfig), func(t *testing.T) { + t.Parallel() + testIntegrationLLOMultiFormats(t, offchainConfig, ov.ocr31) + }) + } } } -func testIntegrationLLOMultiFormats(t *testing.T, offchainConfig llocommon.OffchainConfig) { +func testIntegrationLLOMultiFormats(t *testing.T, offchainConfig llocommon.OffchainConfig, ocr31 bool) { testStartTimeStamp := time.Now() expirationWindow := uint32(3600) @@ -1010,6 +1088,9 @@ lloConfigMode = "bluegreen" donID = %d channelDefinitionsContractAddress = "0x%x" channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, configStoreAddress, fromBlock) + if ocr31 { + pluginConfig += "\nocrVersion = \"3.1\"" + } bridgeName := "superbridge" @@ -1197,8 +1278,12 @@ dp -> deribit_funding_interval_hours_parse -> deribit_funding_interval_hours_dec } // Set config on configurator + productionConfigOpts := []OCRConfigOption{WithOracles(oracles), WithOffchainConfig(offchainConfig)} + if ocr31 { + productionConfigOpts = append(productionConfigOpts, WithOCR31()) + } digest := setProductionConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, WithOracles(oracles), WithOffchainConfig(offchainConfig), + t, donID, steve, backend, configurator, configuratorAddress, nodes, productionConfigOpts..., ) // NOTE: Wait for one of each type of report @@ -1831,10 +1916,26 @@ func TestIntegration_LLO_blue_green_lifecycle(t *testing.T) { ProtocolVersion: 0, DefaultMinReportIntervalNanoseconds: 0, EnableObservationCompression: false} - testIntegrationLLOBlueGreenLifecycle(t, offchainConfig) + for _, ocr31 := range []bool{false, true} { + name := "OCR3.0/v30" + if ocr31 { + name = "OCR3.1/v31" + } + t.Run(name, func(t *testing.T) { + t.Parallel() + testIntegrationLLOBlueGreenLifecycle(t, offchainConfig, ocr31) + }) + } } -func testIntegrationLLOBlueGreenLifecycle(t *testing.T, offchainConfig llocommon.OffchainConfig) { +func testIntegrationLLOBlueGreenLifecycle(t *testing.T, offchainConfig llocommon.OffchainConfig, ocr31 bool) { + // withVersion appends WithOCR31() to config options when running the v31 variant. + withVersion := func(opts ...OCRConfigOption) []OCRConfigOption { + if ocr31 { + return append(opts, WithOCR31()) + } + return opts + } clientCSAKeys := make([]csakey.KeyV2, nNodes) clientPubKeys := make([]ed25519.PublicKey, nNodes) @@ -1909,6 +2010,9 @@ lloConfigMode = "bluegreen" donID = %d channelDefinitionsContractAddress = "0x%x" channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, configStoreAddress, fromBlock) + if ocr31 { + pluginConfig += "\nocrVersion = \"3.1\"" + } addOCRJobsEVMPremiumLegacy(t, streams, serverPubKey, serverURL, configuratorAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, pluginConfig, relayType, relayConfig) var blueDigest ocr2types.ConfigDigest @@ -1919,7 +2023,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi { // Set config on configurator blueDigest = setProductionConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, WithOracles(oracles), WithOffchainConfig(offchainConfig), + t, donID, steve, backend, configurator, configuratorAddress, nodes, withVersion(WithOracles(oracles), WithOffchainConfig(offchainConfig))..., ) // NOTE: Wait until blue produces a report @@ -1945,7 +2049,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi { offchainConfig.EnableObservationCompression = true greenDigest = setStagingConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, WithPredecessorConfigDigest(blueDigest), WithOracles(oracles), WithOffchainConfig(offchainConfig), + t, donID, steve, backend, configurator, configuratorAddress, nodes, withVersion(WithPredecessorConfigDigest(blueDigest), WithOracles(oracles), WithOffchainConfig(offchainConfig))..., ) // NOTE: Wait until green produces the first "specimen" report @@ -2084,7 +2188,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi offchainConfig.ProtocolVersion = 1 offchainConfig.DefaultMinReportIntervalNanoseconds = 1 blueDigest = setStagingConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, WithPredecessorConfigDigest(greenDigest), WithOracles(oracles), WithOffchainConfig(offchainConfig), + t, donID, steve, backend, configurator, configuratorAddress, nodes, withVersion(WithPredecessorConfigDigest(greenDigest), WithOracles(oracles), WithOffchainConfig(offchainConfig))..., ) // NOTE: Wait until blue produces the first "specimen" report @@ -2188,7 +2292,19 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi func TestIntegration_LLO_channel_merging_owners_adders(t *testing.T) { t.Parallel() + for _, ocr31 := range []bool{false, true} { + name := "OCR3.0/v30" + if ocr31 { + name = "OCR3.1/v31" + } + t.Run(name, func(t *testing.T) { + t.Parallel() + testIntegrationLLOChannelMerging(t, ocr31) + }) + } +} +func testIntegrationLLOChannelMerging(t *testing.T, ocr31 bool) { offchainConfig := llocommon.OffchainConfig{ ProtocolVersion: 1, DefaultMinReportIntervalNanoseconds: uint64(1 * time.Second), @@ -2270,6 +2386,9 @@ lloConfigMode = "bluegreen" donID = %d channelDefinitionsContractAddress = "0x%x" channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, configStoreAddress, fromBlock) + if ocr31 { + pluginConfig += "\nocrVersion = \"3.1\"" + } // Add stream specs and LLO jobs to all nodes for i, node := range nodes { @@ -2289,8 +2408,12 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi } // Set initial OCR config + mergeConfigOpts := []OCRConfigOption{WithOracles(oracles), WithOffchainConfig(offchainConfig)} + if ocr31 { + mergeConfigOpts = append(mergeConfigOpts, WithOCR31()) + } digest := setProductionConfig( - t, donID, steve, backend, configurator, configuratorAddress, nodes, WithOracles(oracles), WithOffchainConfig(offchainConfig), + t, donID, steve, backend, configurator, configuratorAddress, nodes, mergeConfigOpts..., ) // Track reports by channel ID @@ -2694,7 +2817,19 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi // and no longer transmits reports for that channel. func TestIntegration_LLO_tombstone_stops_observations_and_reports(t *testing.T) { t.Parallel() + for _, ocr31 := range []bool{false, true} { + name := "OCR3.0/v30" + if ocr31 { + name = "OCR3.1/v31" + } + t.Run(name, func(t *testing.T) { + t.Parallel() + testIntegrationLLOTombstone(t, ocr31) + }) + } +} +func testIntegrationLLOTombstone(t *testing.T, ocr31 bool) { const ( salt = 500 donID = uint32(777666) @@ -2749,6 +2884,9 @@ lloConfigMode = "bluegreen" donID = %d channelDefinitionsContractAddress = "0x%x" channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, configStoreAddress, fromBlock) + if ocr31 { + pluginConfig += "\nocrVersion = \"3.1\"" + } var streamACalls, streamBCalls atomic.Uint64 priceA := decimal.NewFromFloat(111.1) @@ -2791,9 +2929,13 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi require.NoError(t, err) backend.Commit() + tombstoneConfigOpts := []OCRConfigOption{WithOracles(oracles), WithOffchainConfig(offchainConfig)} + if ocr31 { + tombstoneConfigOpts = append(tombstoneConfigOpts, WithOCR31()) + } setProductionConfig( t, donID, steve, backend, configurator, configuratorAddress, nodes, - WithOracles(oracles), WithOffchainConfig(offchainConfig), + tombstoneConfigOpts..., ) seenChannels := make(map[uint32]bool) From 24a60a9bf5bb3bb10049f0b0593d3197de5d6945 Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Mon, 3 Aug 2026 11:12:40 +0100 Subject: [PATCH 4/8] llo: OCR3.1, OCR3.0 benchmarks --- .../ocr2/plugins/llo/bench/bench_baseline.txt | 222 +++++++++ .../ocr2/plugins/llo/bench/harness_test.go | 429 ++++++++++++++++++ .../plugins/llo/bench/plugin_bench_test.go | 326 +++++++++++++ core/services/ocr2/plugins/llo/bench/run.sh | 41 ++ 4 files changed, 1018 insertions(+) create mode 100644 core/services/ocr2/plugins/llo/bench/bench_baseline.txt create mode 100644 core/services/ocr2/plugins/llo/bench/harness_test.go create mode 100644 core/services/ocr2/plugins/llo/bench/plugin_bench_test.go create mode 100755 core/services/ocr2/plugins/llo/bench/run.sh diff --git a/core/services/ocr2/plugins/llo/bench/bench_baseline.txt b/core/services/ocr2/plugins/llo/bench/bench_baseline.txt new file mode 100644 index 00000000000..0684e759e16 --- /dev/null +++ b/core/services/ocr2/plugins/llo/bench/bench_baseline.txt @@ -0,0 +1,222 @@ +goos: darwin +goarch: arm64 +pkg: github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/bench +cpu: Apple M5 Max + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ sec/op │ +FullRound/ch=10_str=1/v30-18 34.72µ ± 3% +FullRound/ch=10_str=1/v31-18 39.77µ ± 1% +FullRound/ch=100_str=1/v30-18 315.7µ ± 0% +FullRound/ch=100_str=1/v31-18 397.1µ ± 1% +FullRound/ch=100_str=10/v30-18 2.205m ± 1% +FullRound/ch=100_str=10/v31-18 2.236m ± 0% +FullRound/ch=1000_str=1/v30-18 3.229m ± 0% +FullRound/ch=1000_str=1/v31-18 4.317m ± 0% +FullRound/ch=1000_str=10/v30-18 22.96m ± 1% +FullRound/ch=1000_str=10/v31-18 23.15m ± 0% +Observation/ch=10_str=1/v30-18 6.466µ ± 0% +Observation/ch=10_str=1/v31-18 8.004µ ± 1% +Observation/ch=100_str=1/v30-18 60.00µ ± 1% +Observation/ch=100_str=1/v31-18 81.50µ ± 0% +Observation/ch=100_str=10/v30-18 432.4µ ± 0% +Observation/ch=100_str=10/v31-18 383.4µ ± 0% +Observation/ch=1000_str=1/v30-18 613.5µ ± 0% +Observation/ch=1000_str=1/v31-18 946.5µ ± 1% +Observation/ch=1000_str=10/v30-18 4.225m ± 1% +Observation/ch=1000_str=10/v31-18 3.807m ± 2% +StateAdvance/ch=10_str=1/v30_Outcome-18 19.90µ ± 0% +StateAdvance/ch=10_str=1/v31_StateTransition-18 21.80µ ± 12% +StateAdvance/ch=100_str=1/v30_Outcome-18 183.5µ ± 0% +StateAdvance/ch=100_str=1/v31_StateTransition-18 219.6µ ± 1% +StateAdvance/ch=100_str=10/v30_Outcome-18 1.473m ± 0% +StateAdvance/ch=100_str=10/v31_StateTransition-18 1.481m ± 0% +StateAdvance/ch=1000_str=1/v30_Outcome-18 1.861m ± 0% +StateAdvance/ch=1000_str=1/v31_StateTransition-18 2.335m ± 0% +StateAdvance/ch=1000_str=10/v30_Outcome-18 15.30m ± 1% +StateAdvance/ch=1000_str=10/v31_StateTransition-18 15.55m ± 1% +Reports/ch=10_str=1/v30-18 8.476µ ± 0% +Reports/ch=10_str=1/v31-18 8.777µ ± 0% +Reports/ch=100_str=1/v30-18 75.05µ ± 0% +Reports/ch=100_str=1/v31-18 79.41µ ± 1% +Reports/ch=100_str=10/v30-18 357.8µ ± 0% +Reports/ch=100_str=10/v31-18 383.8µ ± 1% +Reports/ch=1000_str=1/v30-18 756.3µ ± 1% +Reports/ch=1000_str=1/v31-18 819.7µ ± 0% +Reports/ch=1000_str=10/v30-18 3.424m ± 2% +Reports/ch=1000_str=10/v31-18 3.660m ± 2% +geomean 464.1µ + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ obs_B/op │ +FullRound/ch=10_str=1/v30-18 180.0 ± 0% +FullRound/ch=10_str=1/v31-18 172.0 ± 0% +FullRound/ch=100_str=1/v30-18 1.620k ± 0% +FullRound/ch=100_str=1/v31-18 1.612k ± 0% +FullRound/ch=100_str=10/v30-18 16.89k ± 0% +FullRound/ch=100_str=10/v31-18 16.89k ± 0% +FullRound/ch=1000_str=1/v30-18 16.89k ± 0% +FullRound/ch=1000_str=1/v31-18 16.89k ± 0% +FullRound/ch=1000_str=10/v30-18 169.9k ± 0% +FullRound/ch=1000_str=10/v31-18 169.9k ± 0% +geomean 6.727k + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ outcome_B/op │ +FullRound/ch=10_str=1/v30-18 482.0 ± 0% +FullRound/ch=100_str=1/v30-18 4.622k ± 0% +FullRound/ch=100_str=10/v30-18 27.97k ± 0% +FullRound/ch=1000_str=1/v30-18 49.51k ± 0% +FullRound/ch=1000_str=10/v30-18 283.5k ± 0% +geomean 15.43k + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ report_B/op │ +FullRound/ch=10_str=1/v30-18 2.541k ± 0% +FullRound/ch=10_str=1/v31-18 2.541k ± 0% +FullRound/ch=100_str=1/v30-18 25.59k ± 0% +FullRound/ch=100_str=1/v31-18 25.59k ± 0% +FullRound/ch=100_str=10/v30-18 44.49k ± 0% +FullRound/ch=100_str=10/v31-18 44.49k ± 0% +FullRound/ch=1000_str=1/v30-18 257.9k ± 0% +FullRound/ch=1000_str=1/v31-18 257.9k ± 0% +FullRound/ch=1000_str=10/v30-18 446.9k ± 0% +FullRound/ch=1000_str=10/v31-18 446.9k ± 0% +geomean 50.65k + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ reports/op │ +FullRound/ch=10_str=1/v30-18 10.00 ± 0% +FullRound/ch=10_str=1/v31-18 10.00 ± 0% +FullRound/ch=100_str=1/v30-18 100.0 ± 0% +FullRound/ch=100_str=1/v31-18 100.0 ± 0% +FullRound/ch=100_str=10/v30-18 100.0 ± 0% +FullRound/ch=100_str=10/v31-18 100.0 ± 0% +FullRound/ch=1000_str=1/v30-18 1.000k ± 0% +FullRound/ch=1000_str=1/v31-18 1.000k ± 0% +FullRound/ch=1000_str=10/v30-18 1.000k ± 0% +FullRound/ch=1000_str=10/v31-18 1.000k ± 0% +geomean 158.5 + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ B/op │ +FullRound/ch=10_str=1/v30-18 64.18Ki ± 0% +FullRound/ch=10_str=1/v31-18 64.38Ki ± 0% +FullRound/ch=100_str=1/v30-18 617.4Ki ± 0% +FullRound/ch=100_str=1/v31-18 586.4Ki ± 0% +FullRound/ch=100_str=10/v30-18 4.477Mi ± 0% +FullRound/ch=100_str=10/v31-18 3.788Mi ± 0% +FullRound/ch=1000_str=1/v30-18 6.626Mi ± 0% +FullRound/ch=1000_str=1/v31-18 6.669Mi ± 0% +FullRound/ch=1000_str=10/v30-18 43.71Mi ± 0% +FullRound/ch=1000_str=10/v31-18 36.53Mi ± 0% +Observation/ch=10_str=1/v30-18 11.46Ki ± 0% +Observation/ch=10_str=1/v31-18 9.344Ki ± 0% +Observation/ch=100_str=1/v30-18 119.9Ki ± 0% +Observation/ch=100_str=1/v31-18 80.11Ki ± 0% +Observation/ch=100_str=10/v30-18 946.7Ki ± 0% +Observation/ch=100_str=10/v31-18 517.1Ki ± 0% +Observation/ch=1000_str=1/v30-18 1.328Mi ± 0% +Observation/ch=1000_str=1/v31-18 1.029Mi ± 0% +Observation/ch=1000_str=10/v30-18 9.117Mi ± 0% +Observation/ch=1000_str=10/v31-18 4.753Mi ± 0% +StateAdvance/ch=10_str=1/v30_Outcome-18 33.69Ki ± 0% +StateAdvance/ch=10_str=1/v31_StateTransition-18 34.02Ki ± 0% +StateAdvance/ch=100_str=1/v30_Outcome-18 320.3Ki ± 0% +StateAdvance/ch=100_str=1/v31_StateTransition-18 297.4Ki ± 0% +StateAdvance/ch=100_str=10/v30_Outcome-18 2.748Mi ± 0% +StateAdvance/ch=100_str=10/v31_StateTransition-18 2.417Mi ± 0% +StateAdvance/ch=1000_str=1/v30_Outcome-18 3.496Mi ± 0% +StateAdvance/ch=1000_str=1/v31_StateTransition-18 3.435Mi ± 0% +StateAdvance/ch=1000_str=10/v30_Outcome-18 26.46Mi ± 0% +StateAdvance/ch=1000_str=10/v31_StateTransition-18 23.00Mi ± 0% +Reports/ch=10_str=1/v30-18 18.90Ki ± 0% +Reports/ch=10_str=1/v31-18 19.80Ki ± 0% +Reports/ch=100_str=1/v30-18 177.1Ki ± 0% +Reports/ch=100_str=1/v31-18 190.9Ki ± 0% +Reports/ch=100_str=10/v30-18 823.5Ki ± 0% +Reports/ch=100_str=10/v31-18 871.7Ki ± 0% +Reports/ch=1000_str=1/v30-18 1.803Mi ± 0% +Reports/ch=1000_str=1/v31-18 2.040Mi ± 0% +Reports/ch=1000_str=10/v30-18 8.135Mi ± 0% +Reports/ch=1000_str=10/v31-18 8.619Mi ± 0% +geomean 842.7Ki + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ allocs/op │ +FullRound/ch=10_str=1/v30-18 1.209k ± 0% +FullRound/ch=10_str=1/v31-18 1.292k ± 0% +FullRound/ch=100_str=1/v30-18 10.92k ± 0% +FullRound/ch=100_str=1/v31-18 11.05k ± 0% +FullRound/ch=100_str=10/v30-18 86.50k ± 0% +FullRound/ch=100_str=10/v31-18 76.57k ± 0% +FullRound/ch=1000_str=1/v30-18 110.3k ± 0% +FullRound/ch=1000_str=1/v31-18 110.6k ± 0% +FullRound/ch=1000_str=10/v30-18 878.4k ± 0% +FullRound/ch=1000_str=10/v31-18 771.4k ± 0% +Observation/ch=10_str=1/v30-18 205.0 ± 0% +Observation/ch=10_str=1/v31-18 223.0 ± 0% +Observation/ch=100_str=1/v30-18 1.933k ± 0% +Observation/ch=100_str=1/v31-18 1.777k ± 0% +Observation/ch=100_str=10/v30-18 14.61k ± 0% +Observation/ch=100_str=10/v31-18 9.412k ± 0% +Observation/ch=1000_str=1/v30-18 19.07k ± 0% +Observation/ch=1000_str=1/v31-18 17.13k ± 0% +Observation/ch=1000_str=10/v30-18 148.9k ± 0% +Observation/ch=1000_str=10/v31-18 93.33k ± 0% +StateAdvance/ch=10_str=1/v30_Outcome-18 683.0 ± 0% +StateAdvance/ch=10_str=1/v31_StateTransition-18 698.0 ± 0% +StateAdvance/ch=100_str=1/v30_Outcome-18 6.131k ± 0% +StateAdvance/ch=100_str=1/v31_StateTransition-18 5.984k ± 0% +StateAdvance/ch=100_str=10/v30_Outcome-18 55.12k ± 0% +StateAdvance/ch=100_str=10/v31_StateTransition-18 49.95k ± 0% +StateAdvance/ch=1000_str=1/v30_Outcome-18 63.20k ± 0% +StateAdvance/ch=1000_str=1/v31_StateTransition-18 61.32k ± 0% +StateAdvance/ch=1000_str=10/v30_Outcome-18 562.3k ± 0% +StateAdvance/ch=1000_str=10/v31_StateTransition-18 507.0k ± 0% +Reports/ch=10_str=1/v30-18 320.0 ± 0% +Reports/ch=10_str=1/v31-18 320.0 ± 0% +Reports/ch=100_str=1/v30-18 2.855k ± 0% +Reports/ch=100_str=1/v31-18 2.870k ± 0% +Reports/ch=100_str=10/v30-18 16.76k ± 0% +Reports/ch=100_str=10/v31-18 16.78k ± 0% +Reports/ch=1000_str=1/v30-18 28.08k ± 0% +Reports/ch=1000_str=1/v31-18 28.11k ± 0% +Reports/ch=1000_str=10/v30-18 167.1k ± 0% +Reports/ch=1000_str=10/v31-18 167.2k ± 0% +geomean 15.35k + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ kvkeys/op │ +FullRound/ch=10_str=1/v31-18 11.00 ± 0% +FullRound/ch=100_str=1/v31-18 101.0 ± 0% +FullRound/ch=100_str=10/v31-18 101.0 ± 0% +FullRound/ch=1000_str=1/v31-18 1.001k ± 0% +FullRound/ch=1000_str=10/v31-18 1.001k ± 0% +geomean 162.2 + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ kvread_B/op │ +FullRound/ch=10_str=1/v31-18 456.0 ± 0% +FullRound/ch=100_str=1/v31-18 4.236k ± 0% +FullRound/ch=100_str=10/v31-18 16.78k ± 0% +FullRound/ch=1000_str=1/v31-18 43.78k ± 0% +FullRound/ch=1000_str=10/v31-18 169.8k ± 0% +geomean 11.92k + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ kvwrite_B/op │ +FullRound/ch=10_str=1/v31-18 152.0 ± 0% +FullRound/ch=100_str=1/v31-18 1.412k ± 0% +FullRound/ch=100_str=10/v31-18 1.412k ± 0% +FullRound/ch=1000_str=1/v31-18 14.01k ± 0% +FullRound/ch=1000_str=10/v31-18 14.01k ± 0% +geomean 2.264k + + │ core/services/ocr2/plugins/llo/bench/bench_results.txt │ + │ precursor_B/op │ +FullRound/ch=10_str=1/v31-18 482.0 ± 0% +FullRound/ch=100_str=1/v31-18 4.622k ± 0% +FullRound/ch=100_str=10/v31-18 27.97k ± 0% +FullRound/ch=1000_str=1/v31-18 49.51k ± 0% +FullRound/ch=1000_str=10/v31-18 283.5k ± 0% +geomean 15.43k diff --git a/core/services/ocr2/plugins/llo/bench/harness_test.go b/core/services/ocr2/plugins/llo/bench/harness_test.go new file mode 100644 index 00000000000..9b78473e96d --- /dev/null +++ b/core/services/ocr2/plugins/llo/bench/harness_test.go @@ -0,0 +1,429 @@ +// Package bench contains a comparative micro-benchmark between the OCR3.0 LLO +// plugin (chainlink-data-streams/llo/v30) and the OCR3.1 LLO plugin +// (chainlink-data-streams/llo/v31). +// +// The two plugins implement identical LLO application logic on top of different +// OCR protocols. The performance question this benchmark answers is what that +// protocol difference costs at steady state: +// +// - v30 (OCR3.0) carries all of its state in a single Outcome blob that is +// decoded from the previous round and re-encoded every round. Its per-round +// cost therefore scales with the *total* state size (channels + streams). +// - v31 (OCR3.1) keeps its state in a replicated KeyValueState (a pebble +// database in production) and only reads/writes the keys that change. Its +// per-round cost scales with per-round *churn*, not total state. +// +// Both plugins are driven exclusively through their exported ReportingPlugin +// APIs, so nothing in the read-only chainlink-data-streams or libocr modules is +// modified. v31's KeyValueState is backed by libocr's in-memory +// KeyValueDatabase (offchainreporting2plus/ocrintegrationtesthelpers) rather +// than the production pebble factory: pebble commits with fsync every round, +// which dwarfs and obscures the plugin's own work. The in-memory store isolates +// plugin CPU/allocation cost, making it directly comparable to v30's in-memory +// Outcome blob. Neither plugin's oracle-level OCR3 protocol Database +// (core/services/llo/delegate.go) is modeled here. +// +// Scope note: blob offloading (v31 offloads large observation payloads to +// libocr blobs above BlobThreshold) cannot be exercised outside libocr's oracle +// runtime — a BlobHandle cannot be constructed by application code (see the +// comment in v31/plugin_test.go). We therefore disable blob offloading here so +// observations always inline, which also makes the observation-transport cost +// directly comparable to v30 (which is always inline). Blob behavior is covered +// by the integration tests. +package bench + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/shopspring/decimal" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + memkvdb "github.com/smartcontractkit/libocr/offchainreporting2plus/ocrintegrationtesthelpers" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" + llov31 "github.com/smartcontractkit/chainlink-data-streams/llo/v31" + + corello "github.com/smartcontractkit/chainlink/v2/core/services/llo" +) + +// benchConfigDigest is a fixed config digest shared by both plugins. v31 passes +// it to the KeyValueDatabase factory. +var benchConfigDigest = ocrtypes.ConfigDigest{'b', 'e', 'n', 'c', 'h'} + +const ( + // maxDurationObservation bounds the DataSource.Observe context in both + // plugins. It must be > 0 or the observation context is already expired. + maxDurationObservation = 5 * time.Second + // channelsPerRound is the protocol cap on how many channel definitions an + // observation may vote to add per round (MaxObservationUpdateChannelDefinitionsLength). + // Establishing C channels therefore takes ~ceil(C/channelsPerRound) rounds. + channelsPerRound = 5 + // warmupRoundSlack is added on top of the minimum rounds needed to add every + // channel, to allow the last batch to become reportable and to absorb the + // bootstrap round. + warmupRoundSlack = 32 +) + +// --------------------------------------------------------------------------- +// Workload +// --------------------------------------------------------------------------- + +// workload describes the size of the report-production problem: numChannels +// channels, each observing streamsPerChannel distinct streams via the median +// aggregator, emitting a JSON report. +// +// Stream IDs are unique across the whole workload, so the total number of +// distinct observed streams is numChannels*streamsPerChannel. Report format is +// held constant (JSON) because it is orthogonal to the v30-vs-v31 delta: both +// plugins use the identical report codec, so its cost cancels out of the +// comparison. +type workload struct { + numChannels int + streamsPerChannel int +} + +func (w workload) String() string { + return fmt.Sprintf("ch=%d_str=%d", w.numChannels, w.streamsPerChannel) +} + +// channelDefinitions builds the workload's channel definitions and returns the +// full set of stream IDs referenced. +func (w workload) channelDefinitions() (llotypes.ChannelDefinitions, []llotypes.StreamID) { + defs := make(llotypes.ChannelDefinitions, w.numChannels) + var streamIDs []llotypes.StreamID + var sid llotypes.StreamID + for c := 0; c < w.numChannels; c++ { + streams := make([]llotypes.Stream, 0, w.streamsPerChannel) + for s := 0; s < w.streamsPerChannel; s++ { + sid++ + streams = append(streams, llotypes.Stream{StreamID: sid, Aggregator: llotypes.AggregatorMedian}) + streamIDs = append(streamIDs, sid) + } + defs[llotypes.ChannelID(c+1)] = llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatJSON, + Streams: streams, + } + } + return defs, streamIDs +} + +// --------------------------------------------------------------------------- +// Mocks (shared, version-agnostic) +// --------------------------------------------------------------------------- + +type mockChannelDefinitionCache struct{ defs llotypes.ChannelDefinitions } + +func (m *mockChannelDefinitionCache) Definitions(llotypes.ChannelDefinitions) llotypes.ChannelDefinitions { + return m.defs +} +func (m *mockChannelDefinitionCache) Start(context.Context) error { return nil } +func (m *mockChannelDefinitionCache) Close() error { return nil } +func (m *mockChannelDefinitionCache) Ready() error { return nil } +func (m *mockChannelDefinitionCache) HealthReport() map[string]error { return nil } +func (m *mockChannelDefinitionCache) Name() string { return "benchChannelDefinitionCache" } + +// staticDataSource fills every requested stream with a fixed decimal value. +// This keeps the DataSource out of the measured critical path (no I/O, no +// allocation-heavy pipeline) so the benchmark isolates plugin cost. +type staticDataSource struct{ value *llocommon.Decimal } + +func newStaticDataSource() *staticDataSource { + return &staticDataSource{value: llocommon.ToDecimal(decimal.NewFromInt(123456))} +} + +func (d *staticDataSource) Observe(_ context.Context, sv llocommon.StreamValues, _ llocommon.DSOpts) error { + for k := range sv { + sv[k] = d.value + } + return nil +} + +type mockShouldRetireCache struct{} + +func (mockShouldRetireCache) ShouldRetire(ocrtypes.ConfigDigest) (bool, error) { return false, nil } + +type mockOnchainConfigCodec struct{} + +func (mockOnchainConfigCodec) Decode([]byte) (llocommon.OnchainConfig, error) { + return llocommon.OnchainConfig{}, nil +} +func (mockOnchainConfigCodec) Encode(llocommon.OnchainConfig) ([]byte, error) { return nil, nil } + +// --------------------------------------------------------------------------- +// Plugin construction +// --------------------------------------------------------------------------- + +func reportCodecs() map[llotypes.ReportFormat]llocommon.ReportCodec { + // The same production codec set both plugins use (delegate.go). Only the + // JSON codec is exercised by this workload. + return corello.NewReportCodecs(logger.Nop(), 0) +} + +// benchOffchainConfig selects protocol version 1 with a 1ns minimum report +// interval. Version 1 makes both plugins use full nanosecond timestamp +// resolution for the JSON report format (version 0 truncates v30's timestamps +// to whole seconds, which would prevent reporting within a single wall-clock +// second and diverge from v31). The 1ns interval effectively reports every +// round while keeping both plugins on identical reportability rules. +func benchOffchainConfig() []byte { + b, err := llocommon.OffchainConfig{ + ProtocolVersion: 1, + DefaultMinReportIntervalNanoseconds: 1, + }.Encode() + if err != nil { + panic(err) + } + return b +} + +func pluginConfig(n, f int) ocr3types.ReportingPluginConfig { + return ocr3types.ReportingPluginConfig{ + ConfigDigest: benchConfigDigest, + N: n, + F: f, + MaxDurationObservation: maxDurationObservation, + OffchainConfig: benchOffchainConfig(), + } +} + +func buildV30(tb testing.TB, defs llotypes.ChannelDefinitions, n, f int) ocr3types.ReportingPlugin[llotypes.ReportInfo] { + tb.Helper() + factory := llov30.NewPluginFactory(llov30.PluginFactoryParams{ + Config: llov30.Config{VerboseLogging: false}, + ShouldRetireCache: mockShouldRetireCache{}, + RetirementReportCodec: llocommon.StandardRetirementReportCodec{}, + ChannelDefinitionCache: &mockChannelDefinitionCache{defs: defs}, + DataSource: newStaticDataSource(), + Logger: logger.Nop(), + OnchainConfigCodec: mockOnchainConfigCodec{}, + ReportCodecs: reportCodecs(), + }) + p, _, err := factory.NewReportingPlugin(context.Background(), pluginConfig(n, f)) + require.NoError(tb, err) + return p +} + +func buildV31(tb testing.TB, defs llotypes.ChannelDefinitions, n, f int) (ocr3_1types.ReportingPlugin[llotypes.ReportInfo], ocr3_1types.KeyValueDatabase) { + tb.Helper() + factory := llov31.NewPluginFactory(llov31.PluginFactoryParams{ + Config: llov31.Config{VerboseLogging: false}, + ShouldRetireCache: mockShouldRetireCache{}, + RetirementReportCodec: llocommon.StandardRetirementReportCodec{}, + ChannelDefinitionCache: &mockChannelDefinitionCache{defs: defs}, + DataSource: newStaticDataSource(), + Logger: logger.Nop(), + OnchainConfigCodec: mockOnchainConfigCodec{}, + ReportCodecs: reportCodecs(), + // Negative disables blob offloading; observations always inline. See the + // package-level scope note. + BlobThreshold: -1, + }) + p, _, err := factory.NewReportingPlugin(context.Background(), pluginConfig(n, f), nil) + require.NoError(tb, err) + + // libocr's in-memory KeyValueDatabase (the same helper v31's integration + // tests use): a btree behind the production KeyValueDatabaseFactory + // interface, whose Commit applies to memory with no WAL/fsync. This isolates + // the plugin's CPU/allocation cost from storage-engine cost, so the v31 + // numbers are directly comparable to v30's in-memory Outcome blob. + dbFactory := memkvdb.NewStatelessInMemoryKeyValueDatabaseFactory() + db, err := dbFactory.NewKeyValueDatabase(benchConfigDigest) + require.NoError(tb, err) + tb.Cleanup(func() { _ = db.Close() }) + return p, db +} + +// --------------------------------------------------------------------------- +// Round drivers +// --------------------------------------------------------------------------- + +func attributedObservation(observer int, obs []byte) ocrtypes.AttributedObservation { + return ocrtypes.AttributedObservation{Observer: commontypes.OracleID(observer), Observation: obs} +} + +// replicate builds n AttributedObservations from a single serialized +// observation. Real oracles observe slightly different values; identical copies +// are representative for a benchmark and keep the workload deterministic (the +// aggregation still processes n observations per stream). +func replicate(obs []byte, n int) []ocrtypes.AttributedObservation { + aos := make([]ocrtypes.AttributedObservation, 0, n) + for i := 0; i < n; i++ { + aos = append(aos, attributedObservation(i, obs)) + } + return aos +} + +// v30Round drives one full v30 round (Observation → Outcome → Reports) against +// a fixed previous outcome. It returns the produced outcome, the reports, and +// the serialized observation. +func v30Round(tb testing.TB, p ocr3types.ReportingPlugin[llotypes.ReportInfo], seqNr uint64, prevOutcome []byte, n int) (ocr3types.Outcome, []ocr3types.ReportPlus[llotypes.ReportInfo], []byte) { + ctx := context.Background() + outctx := ocr3types.OutcomeContext{SeqNr: seqNr, PreviousOutcome: prevOutcome} + obs, err := p.Observation(ctx, outctx, nil) + require.NoError(tb, err) + aos := replicate(obs, n) + outcome, err := p.Outcome(ctx, outctx, nil, aos) + require.NoError(tb, err) + reports, err := p.Reports(ctx, seqNr, outcome) + require.NoError(tb, err) + return outcome, reports, obs +} + +// v31Round drives one full v31 round (Observation → StateTransition → Reports) +// against the KeyValueDatabase, mirroring libocr's per-seqNr transaction +// lifecycle: Observation reads a snapshot of the state committed after seqNr-1; +// StateTransition mutates a batch that is committed to advance the state to +// seqNr. It mutates db. +func v31Round(tb testing.TB, p ocr3_1types.ReportingPlugin[llotypes.ReportInfo], db ocr3_1types.KeyValueDatabase, seqNr uint64, n int) ([]ocr3types.ReportPlus[llotypes.ReportInfo], []byte) { + ctx := context.Background() + + var obs []byte + if seqNr > 1 { + rtx, err := db.NewReadTransaction() + require.NoError(tb, err) + obs, err = p.Observation(ctx, seqNr, ocrtypes.AttributedQuery{}, rtx, nil) + rtx.Discard() + require.NoError(tb, err) + } + aos := bootOrReplicate(obs, n, seqNr) + + wtx, err := db.NewReadWriteTransaction() + require.NoError(tb, err) + prec, err := p.StateTransition(ctx, seqNr, ocrtypes.AttributedQuery{}, aos, wtx, nil) + if err != nil { + wtx.Discard() + require.NoError(tb, err) + } + require.NoError(tb, wtx.Commit()) + + reports, err := p.Reports(ctx, seqNr, prec) + require.NoError(tb, err) + return reports, obs +} + +// bootOrReplicate returns the bootstrap observation set (empty observations) +// for the first round, otherwise n copies of obs. +func bootOrReplicate(obs []byte, n int, seqNr uint64) []ocrtypes.AttributedObservation { + if seqNr == 1 { + // First round: empty observations, only 2f+1 are needed; n is fine. + return replicate(nil, n) + } + return replicate(obs, n) +} + +// --------------------------------------------------------------------------- +// Warmup +// --------------------------------------------------------------------------- + +// warmupRounds returns the round budget needed to establish `channels` +// channels (5 per round) plus slack for reportability and bootstrap. +func warmupRounds(channels int) uint64 { + return uint64(channels/channelsPerRound + warmupRoundSlack) +} + +// warmV30 drives rounds until all `channels` channels are established and +// reportable, then returns the previous-outcome and seqNr that reliably yield a +// full report set. That (prevOutcome, seqNr) pair is reused for every measured +// iteration: the stored watermarks are in the past, so each measured round +// (with a fresh wall-clock observation timestamp) reports every channel. +func warmV30(tb testing.TB, p ocr3types.ReportingPlugin[llotypes.ReportInfo], n, channels int) (steadyPrev ocr3types.Outcome, steadySeq uint64) { + tb.Helper() + // seqNr 1 bootstraps with empty observations and no previous outcome. + prev, _, _ := v30Round(tb, p, 1, nil, n) + maxRounds := warmupRounds(channels) + for seqNr := uint64(2); seqNr <= maxRounds; seqNr++ { + outcome, reports, _ := v30Round(tb, p, seqNr, prev, n) + if len(reports) >= channels { + return prev, seqNr + } + prev = outcome + } + tb.Fatalf("v30 did not reach %d reportable channels within %d warmup rounds", channels, maxRounds) + return nil, 0 +} + +// warmV31 drives rounds until all `channels` channels are established and +// reportable, returning the next seqNr to use. Unlike v30, v31 must chain +// (state lives in the mutated KeyValueDatabase), so measured iterations continue from +// this seqNr. +func warmV31(tb testing.TB, p ocr3_1types.ReportingPlugin[llotypes.ReportInfo], db ocr3_1types.KeyValueDatabase, n, channels int) (nextSeq uint64) { + tb.Helper() + maxRounds := warmupRounds(channels) + for seqNr := uint64(1); seqNr <= maxRounds; seqNr++ { + reports, _ := v31Round(tb, p, db, seqNr, n) + if len(reports) >= channels { + return seqNr + 1 + } + } + tb.Fatalf("v31 did not reach %d reportable channels within %d warmup rounds", channels, maxRounds) + return 0 +} + +// reportBytes sums the serialized report payloads. +func reportBytes(reports []ocr3types.ReportPlus[llotypes.ReportInfo]) int { + var total int + for _, r := range reports { + total += len(r.ReportWithInfo.Report) + } + return total +} + +// --------------------------------------------------------------------------- +// Counting KV wrappers (size instrumentation, not used on the hot path) +// --------------------------------------------------------------------------- + +// countingRW wraps a KeyValueState read-write transaction and tallies the volume of +// KeyValueState I/O a single v31 round performs. It is used only for the +// one-off size probe, never inside the timed loop, so its counter overhead +// does not pollute latency measurements. +type countingRW struct { + inner ocr3_1types.KeyValueStateReadWriter + reads, readBytes, writes, writeBytes, deletes, keys int +} + +func (c *countingRW) Read(key []byte) ([]byte, error) { + v, err := c.inner.Read(key) + c.reads++ + c.readBytes += len(v) + return v, err +} + +func (c *countingRW) Write(key, value []byte) error { + c.writes++ + c.keys++ + c.writeBytes += len(key) + len(value) + return c.inner.Write(key, value) +} + +func (c *countingRW) Delete(key []byte) error { + c.deletes++ + c.keys++ + return c.inner.Delete(key) +} + +var _ ocr3_1types.KeyValueStateReadWriter = (*countingRW)(nil) + +// countingReader wraps a read transaction and tallies read volume. +type countingReader struct { + inner ocr3_1types.KeyValueStateReader + reads, readBytes int +} + +func (c *countingReader) Read(key []byte) ([]byte, error) { + v, err := c.inner.Read(key) + c.reads++ + c.readBytes += len(v) + return v, err +} + +var _ ocr3_1types.KeyValueStateReader = (*countingReader)(nil) diff --git a/core/services/ocr2/plugins/llo/bench/plugin_bench_test.go b/core/services/ocr2/plugins/llo/bench/plugin_bench_test.go new file mode 100644 index 00000000000..d3c0c0363c4 --- /dev/null +++ b/core/services/ocr2/plugins/llo/bench/plugin_bench_test.go @@ -0,0 +1,326 @@ +package bench + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" +) + +const ( + benchN = 4 // total oracles + benchF = 1 // fault tolerance (2f+1 = 3 quorum) +) + +// benchWorkloads is the scaling matrix. Total observed streams per round is +// numChannels*streamsPerChannel and is kept at or below the protocol limit +// (MaxObservationStreamValuesLength = 10_000). +var benchWorkloads = []workload{ + {numChannels: 10, streamsPerChannel: 1}, + {numChannels: 100, streamsPerChannel: 1}, + {numChannels: 100, streamsPerChannel: 10}, + {numChannels: 1000, streamsPerChannel: 1}, + {numChannels: 1000, streamsPerChannel: 10}, +} + +// --------------------------------------------------------------------------- +// Correctness gate +// --------------------------------------------------------------------------- + +// TestParity asserts that, for the same workload, both plugins reach a +// report-producing steady state and emit the same number of reports in the +// same format. This guards the benchmark: if the two drivers diverge, the +// latency numbers are not comparing like for like. +func TestParity(t *testing.T) { + for _, w := range benchWorkloads { + w := w + t.Run(w.String(), func(t *testing.T) { + defs, _ := w.channelDefinitions() + + p30 := buildV30(t, defs, benchN, benchF) + prev, seq := warmV30(t, p30, benchN, w.numChannels) + _, reports30, _ := v30Round(t, p30, seq, prev, benchN) + + p31, db := buildV31(t, defs, benchN, benchF) + seq31 := warmV31(t, p31, db, benchN, w.numChannels) + reports31, _ := v31Round(t, p31, db, seq31, benchN) + + require.NotEmpty(t, reports30, "v30 produced no reports") + require.Equal(t, w.numChannels, len(reports30), "v30 should report every channel") + require.Equal(t, len(reports30), len(reports31), "v30 and v31 must produce the same number of reports") + + for i := range reports30 { + require.Equal(t, llotypes.ReportFormatJSON, reports30[i].ReportWithInfo.Info.ReportFormat) + } + for i := range reports31 { + require.Equal(t, llotypes.ReportFormatJSON, reports31[i].ReportWithInfo.Info.ReportFormat) + } + }) + } +} + +// --------------------------------------------------------------------------- +// Full-round benchmark +// --------------------------------------------------------------------------- + +// BenchmarkFullRound measures a complete steady-state round for each plugin: +// Observation → (Outcome | StateTransition) → Reports. Besides ns/op and the +// -benchmem allocation metrics, it reports per-round size characteristics: +// - v30: outcome_B (the re-serialized state blob), obs_B, report_B, reports +// - v31: precursor_B, obs_B, report_B, reports, and KeyValueState I/O volume +// (kvread_B, kvwrite_B, kvkeys) — the incremental state cost v30 lacks. +func BenchmarkFullRound(b *testing.B) { + for _, w := range benchWorkloads { + w := w + defs, _ := w.channelDefinitions() + + b.Run(w.String()+"/v30", func(b *testing.B) { + p := buildV30(b, defs, benchN, benchF) + prev, seq := warmV30(b, p, benchN, w.numChannels) + + // Size probe (untimed). Reported after the loop because + // b.ResetTimer() deletes user metrics. + outcome, reports, obs := v30Round(b, p, seq, prev, benchN) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v30Round(b, p, seq, prev, benchN) + } + b.StopTimer() + b.ReportMetric(float64(len(outcome)), "outcome_B/op") + b.ReportMetric(float64(len(obs)), "obs_B/op") + b.ReportMetric(float64(reportBytes(reports)), "report_B/op") + b.ReportMetric(float64(len(reports)), "reports/op") + }) + + b.Run(w.String()+"/v31", func(b *testing.B) { + p, db := buildV31(b, defs, benchN, benchF) + seq := warmV31(b, p, db, benchN, w.numChannels) + + // Size/IO probe (untimed). Reported after the loop because + // b.ResetTimer() deletes user metrics. + m := probeV31(b, p, db, seq, benchN) + seq++ + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v31Round(b, p, db, seq, benchN) + seq++ + } + b.StopTimer() + b.ReportMetric(float64(m.precBytes), "precursor_B/op") + b.ReportMetric(float64(m.obsBytes), "obs_B/op") + b.ReportMetric(float64(m.reportBytesTotal), "report_B/op") + b.ReportMetric(float64(m.reports), "reports/op") + b.ReportMetric(float64(m.kvReadBytes), "kvread_B/op") + b.ReportMetric(float64(m.kvWriteBytes), "kvwrite_B/op") + b.ReportMetric(float64(m.kvKeys), "kvkeys/op") + }) + } +} + +// --------------------------------------------------------------------------- +// Per-stage benchmarks (localize where the cost is spent) +// --------------------------------------------------------------------------- + +// BenchmarkObservation measures only the Observation stage (data-source gather +// + observation encode; for v31 also the KeyValueState read). +func BenchmarkObservation(b *testing.B) { + for _, w := range benchWorkloads { + w := w + defs, _ := w.channelDefinitions() + + b.Run(w.String()+"/v30", func(b *testing.B) { + p := buildV30(b, defs, benchN, benchF) + prev, seq := warmV30(b, p, benchN, w.numChannels) + outctx := ocr3types.OutcomeContext{SeqNr: seq, PreviousOutcome: prev} + ctx := context.Background() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := p.Observation(ctx, outctx, nil); err != nil { + b.Fatal(err) + } + } + }) + + b.Run(w.String()+"/v31", func(b *testing.B) { + p, db := buildV31(b, defs, benchN, benchF) + seq := warmV31(b, p, db, benchN, w.numChannels) + ctx := context.Background() + rtx, err := db.NewReadTransaction() + require.NoError(b, err) + defer rtx.Discard() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := p.Observation(ctx, seq, ocrtypes.AttributedQuery{}, rtx, nil); err != nil { + b.Fatal(err) + } + } + }) + } +} + +// BenchmarkStateAdvance measures the state-generation stage: v30's Outcome +// (decode previous outcome, aggregate, re-encode full outcome) versus v31's +// StateTransition (incremental KeyValueState reads/writes) followed by the +// KeyValueDatabase commit that libocr performs after every StateTransition. +func BenchmarkStateAdvance(b *testing.B) { + for _, w := range benchWorkloads { + w := w + defs, _ := w.channelDefinitions() + + b.Run(w.String()+"/v30_Outcome", func(b *testing.B) { + p := buildV30(b, defs, benchN, benchF) + prev, seq := warmV30(b, p, benchN, w.numChannels) + ctx := context.Background() + outctx := ocr3types.OutcomeContext{SeqNr: seq, PreviousOutcome: prev} + obs, err := p.Observation(ctx, outctx, nil) + require.NoError(b, err) + aos := replicate(obs, benchN) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := p.Outcome(ctx, outctx, nil, aos); err != nil { + b.Fatal(err) + } + } + }) + + b.Run(w.String()+"/v31_StateTransition", func(b *testing.B) { + p, db := buildV31(b, defs, benchN, benchF) + seq := warmV31(b, p, db, benchN, w.numChannels) + ctx := context.Background() + rtx, err := db.NewReadTransaction() + require.NoError(b, err) + obs, err := p.Observation(ctx, seq, ocrtypes.AttributedQuery{}, rtx, nil) + rtx.Discard() + require.NoError(b, err) + aos := replicate(obs, benchN) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + wtx, err := db.NewReadWriteTransaction() + if err != nil { + b.Fatal(err) + } + if _, err := p.StateTransition(ctx, seq, ocrtypes.AttributedQuery{}, aos, wtx, nil); err != nil { + b.Fatal(err) + } + if err := wtx.Commit(); err != nil { + b.Fatal(err) + } + seq++ + } + }) + } +} + +// BenchmarkReports measures only the Reports stage (turning a committed +// outcome/precursor into signed report payloads). +func BenchmarkReports(b *testing.B) { + for _, w := range benchWorkloads { + w := w + defs, _ := w.channelDefinitions() + + b.Run(w.String()+"/v30", func(b *testing.B) { + p := buildV30(b, defs, benchN, benchF) + prev, seq := warmV30(b, p, benchN, w.numChannels) + outcome, reports, _ := v30Round(b, p, seq, prev, benchN) + require.NotEmpty(b, reports) + ctx := context.Background() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := p.Reports(ctx, seq, outcome); err != nil { + b.Fatal(err) + } + } + }) + + b.Run(w.String()+"/v31", func(b *testing.B) { + p, db := buildV31(b, defs, benchN, benchF) + seq := warmV31(b, p, db, benchN, w.numChannels) + // Produce a committed precursor to feed Reports repeatedly. + ctx := context.Background() + rtx, err := db.NewReadTransaction() + require.NoError(b, err) + obs, err := p.Observation(ctx, seq, ocrtypes.AttributedQuery{}, rtx, nil) + rtx.Discard() + require.NoError(b, err) + wtx, err := db.NewReadWriteTransaction() + require.NoError(b, err) + prec, err := p.StateTransition(ctx, seq, ocrtypes.AttributedQuery{}, replicate(obs, benchN), wtx, nil) + require.NoError(b, err) + require.NoError(b, wtx.Commit()) + reports, err := p.Reports(ctx, seq, prec) + require.NoError(b, err) + require.NotEmpty(b, reports) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := p.Reports(ctx, seq, prec); err != nil { + b.Fatal(err) + } + } + }) + } +} + +// --------------------------------------------------------------------------- +// v31 size probe +// --------------------------------------------------------------------------- + +type v31Sizes struct { + precBytes int + obsBytes int + reportBytesTotal int + reports int + kvReadBytes int + kvWriteBytes int + kvKeys int +} + +// probeV31 runs one instrumented round to capture per-round size and +// KeyValueState I/O characteristics. It commits, advancing the state by one +// seqNr. +func probeV31(tb testing.TB, p ocr3_1types.ReportingPlugin[llotypes.ReportInfo], db ocr3_1types.KeyValueDatabase, seqNr uint64, n int) v31Sizes { + tb.Helper() + ctx := context.Background() + + rtx, err := db.NewReadTransaction() + require.NoError(tb, err) + cr := &countingReader{inner: rtx} + obs, err := p.Observation(ctx, seqNr, ocrtypes.AttributedQuery{}, cr, nil) + rtx.Discard() + require.NoError(tb, err) + + wtx, err := db.NewReadWriteTransaction() + require.NoError(tb, err) + crw := &countingRW{inner: wtx} + prec, err := p.StateTransition(ctx, seqNr, ocrtypes.AttributedQuery{}, replicate(obs, n), crw, nil) + require.NoError(tb, err) + require.NoError(tb, wtx.Commit()) + + reports, err := p.Reports(ctx, seqNr, prec) + require.NoError(tb, err) + + return v31Sizes{ + precBytes: len(prec), + obsBytes: len(obs), + reportBytesTotal: reportBytes(reports), + reports: len(reports), + kvReadBytes: cr.readBytes + crw.readBytes, + kvWriteBytes: crw.writeBytes, + kvKeys: crw.keys, + } +} diff --git a/core/services/ocr2/plugins/llo/bench/run.sh b/core/services/ocr2/plugins/llo/bench/run.sh new file mode 100755 index 00000000000..6b305c47cdd --- /dev/null +++ b/core/services/ocr2/plugins/llo/bench/run.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# +# Runs the v30-vs-v31 LLO plugin benchmark matrix and (if benchstat is present) +# prints a summarized table with per-benchmark variance. +# +# Usage: +# ./run.sh [bench-regex] [count] [benchtime] +# +# Examples: +# ./run.sh # full matrix, count=6, benchtime=1s +# ./run.sh BenchmarkFullRound 10 2s # just the full-round bench, more samples +# ./run.sh 'FullRound/ch=10' 6 200x # a single workload, fixed iterations +# +# Read the /v30 and /v31 rows for the same workload side by side; the v31 rows +# additionally carry precursor_B, kvread_B, kvwrite_B and kvkeys per op. +set -euo pipefail + +BENCH="${1:-Benchmark}" +COUNT="${2:-6}" +BENCHTIME="${3:-1s}" + +cd "$(dirname "$0")" + +OUT="bench_results.txt" + +echo "running: -bench '${BENCH}' -count=${COUNT} -benchtime=${BENCHTIME}" +go test . \ + -run '^$' \ + -bench "${BENCH}" \ + -benchmem \ + -count="${COUNT}" \ + -benchtime="${BENCHTIME}" \ + -timeout=60m | tee "${OUT}" + +echo +if command -v benchstat >/dev/null 2>&1; then + echo "=== benchstat (${OUT}) ===" + benchstat "${OUT}" +else + echo "benchstat not found; install with: go install golang.org/x/perf/cmd/benchstat@latest" +fi From 529a43da1816253ada7a8176a514d8b214173844 Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Mon, 3 Aug 2026 15:04:56 +0100 Subject: [PATCH 5/8] ocr3_1 metric namespace --- core/services/ocr3_1/promwrapper/factory.go | 1 - core/services/ocr3_1/promwrapper/metrics.go | 61 +++++++------------ .../ocr3_1/promwrapper/plugin_test.go | 6 +- 3 files changed, 25 insertions(+), 43 deletions(-) diff --git a/core/services/ocr3_1/promwrapper/factory.go b/core/services/ocr3_1/promwrapper/factory.go index c28a7c78a55..5d1aeb72405 100644 --- a/core/services/ocr3_1/promwrapper/factory.go +++ b/core/services/ocr3_1/promwrapper/factory.go @@ -4,7 +4,6 @@ import ( "context" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ) diff --git a/core/services/ocr3_1/promwrapper/metrics.go b/core/services/ocr3_1/promwrapper/metrics.go index cc9f95c3455..4768c31d566 100644 --- a/core/services/ocr3_1/promwrapper/metrics.go +++ b/core/services/ocr3_1/promwrapper/metrics.go @@ -3,19 +3,17 @@ // mirrors that package's structure (see also the ocr3 / ocr3_1 split of // beholderwrapper). // -// It emits the same ocr3_reporting_plugin_* metric series as the OCR3.0 -// wrapper — the two versions share one metric surface, differentiated by the -// "function" label (which includes the OCR3.1-only phases observationQuorum, -// stateTransition and committed). To keep that surface identical without -// importing the OCR3.0 package, the metric collectors is registered with -// registerOrExisting to avoid runtime issues. +// Metrics are emitted under their own ocr3_1_reporting_plugin_* names (distinct +// from the OCR3.0 wrapper's ocr3_reporting_plugin_* series) so the two protocol +// versions are independently observable and so the two packages never contend +// for the same collector registration. package promwrapper import ( - "errors" "time" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" ) type functionType string @@ -48,54 +46,37 @@ var ( float64(30 * time.Second), } - promOCR3ReportsGenerated = registerOrExisting(prometheus.NewCounterVec( + promOCR3ReportsGenerated = promauto.NewCounterVec( prometheus.CounterOpts{ - Name: "ocr3_reporting_plugin_reports_processed", - Help: "Tracks number of reports processed/generated within by different OCR3 functions", + Name: "ocr3_1_reporting_plugin_reports_processed", + Help: "Tracks number of reports processed/generated by different OCR3.1 functions", }, []string{"chainFamily", "chainID", "plugin", "function"}, - )) - promOCR3Durations = registerOrExisting(prometheus.NewHistogramVec( + ) + promOCR3Durations = promauto.NewHistogramVec( prometheus.HistogramOpts{ - Name: "ocr3_reporting_plugin_duration", - Help: "The amount of time elapsed during the OCR3 plugin's function", + Name: "ocr3_1_reporting_plugin_duration", + Help: "The amount of time elapsed during the OCR3.1 plugin's function", Buckets: buckets, }, []string{"chainFamily", "chainID", "plugin", "function", "success"}, - )) - promOCR3Sizes = registerOrExisting(prometheus.NewCounterVec( + ) + promOCR3Sizes = promauto.NewCounterVec( prometheus.CounterOpts{ - Name: "ocr3_reporting_plugin_data_sizes", - Help: "Tracks the size of the data produced by OCR3 plugin in bytes (e.g. reports, observations etc.)", + Name: "ocr3_1_reporting_plugin_data_sizes", + Help: "Tracks the size of the data produced by the OCR3.1 plugin in bytes (e.g. reports, observations etc.)", }, []string{"chainFamily", "chainID", "plugin", "function"}, - )) - promOCR3PluginStatus = registerOrExisting(prometheus.NewGaugeVec( + ) + promOCR3PluginStatus = promauto.NewGaugeVec( prometheus.GaugeOpts{ - Name: "ocr3_reporting_plugin_status", - Help: "Gauge indicating whether plugin is up and running or not", + Name: "ocr3_1_reporting_plugin_status", + Help: "Gauge indicating whether the OCR3.1 plugin is up and running or not", }, []string{"chainFamily", "chainID", "plugin", "configDigest"}, - )) + ) ) -// registerOrExisting registers c on the default registerer, or if an equal -// collector is already registered (e.g. because core/services/ocr3/promwrapper -// is also linked into this binary), returns that existing collector so both -// packages write to a single shared metric series. -func registerOrExisting[C prometheus.Collector](c C) C { - if err := prometheus.DefaultRegisterer.Register(c); err != nil { - var are prometheus.AlreadyRegisteredError - if errors.As(err, &are) { - if existing, ok := are.ExistingCollector.(C); ok { - return existing - } - } - panic(err) - } - return c -} - func boolToInt(arg bool) int { if arg { return 1 diff --git a/core/services/ocr3_1/promwrapper/plugin_test.go b/core/services/ocr3_1/promwrapper/plugin_test.go index c4a9766fe5b..6a3ae563fdc 100644 --- a/core/services/ocr3_1/promwrapper/plugin_test.go +++ b/core/services/ocr3_1/promwrapper/plugin_test.go @@ -22,6 +22,7 @@ import ( // another. Each method is invoked once and the per-label duration histogram // sample count is asserted to increment by exactly one. func Test_Plugin_FunctionLabels(t *testing.T) { + t.Parallel() const ( fam = "evm" id = "1" @@ -34,7 +35,7 @@ func Test_Plugin_FunctionLabels(t *testing.T) { init[f] = counterFromHistogramByLabels(t, promOCR3Durations, fam, id, plug, string(f), "true") } - p := newReportingPlugin[uint]( + p := newReportingPlugin( fakePlugin[uint]{reports: make([]ocr3types.ReportPlus[uint], 2), stateTransitionSize: 4}, fam, id, plug, "abc", promOCR3ReportsGenerated, promOCR3Durations, promOCR3Sizes, promOCR3PluginStatus, @@ -73,7 +74,8 @@ func Test_Plugin_FunctionLabels(t *testing.T) { // Test_Factory covers NewReportingPluginFactory + NewReportingPlugin: the // factory wraps the origin plugin and the wrapper reports metrics. func Test_Factory(t *testing.T) { - factory := NewReportingPluginFactory[uint]( + t.Parallel() + factory := NewReportingPluginFactory( fakeFactory[uint]{plugin: fakePlugin[uint]{}}, logger.TestLogger(t), "aptos", "1", "llo", ) From 62de929bc58bbc1552a9dbb879ebbf5e84f63c40 Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Mon, 3 Aug 2026 15:05:35 +0100 Subject: [PATCH 6/8] llo: linting fixes --- core/services/llo/delegate.go | 12 ++++++------ core/services/llo/observation/data_source.go | 3 ++- .../ocr2/plugins/llo/bench/harness_test.go | 17 ++++++++--------- .../ocr2/plugins/llo/bench/plugin_bench_test.go | 14 +++++--------- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/core/services/llo/delegate.go b/core/services/llo/delegate.go index def9a75b50e..736a9a377e0 100644 --- a/core/services/llo/delegate.go +++ b/core/services/llo/delegate.go @@ -7,12 +7,6 @@ import ( "strconv" "github.com/prometheus/client_golang/prometheus" - ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" - ocr2plus "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3shims" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -24,6 +18,12 @@ import ( "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter" llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" llov31 "github.com/smartcontractkit/chainlink-data-streams/llo/v31" + ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" + ocr2plus "github.com/smartcontractkit/libocr/offchainreporting2plus" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3shims" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" corelogger "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/llo/observation/data_source.go b/core/services/llo/observation/data_source.go index b4220d91243..0d5b9a7fea7 100644 --- a/core/services/llo/observation/data_source.go +++ b/core/services/llo/observation/data_source.go @@ -208,7 +208,8 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llocommon.StreamV // only one that should run pipeline observations). func (d *dataSource) inProduction(opts llocommon.DSOpts) bool { if opts == nil { - d.lggr.Warnw("Observe: nil opts, treating as not-in-production") + // setObservableStreams logs the nil-opts case; stay silent here to avoid + // a duplicate warning per round. return false } if opts.LifeCycleStage() != llocommon.LifeCycleStageProduction { diff --git a/core/services/ocr2/plugins/llo/bench/harness_test.go b/core/services/ocr2/plugins/llo/bench/harness_test.go index 9b78473e96d..6bbc79c1fd8 100644 --- a/core/services/ocr2/plugins/llo/bench/harness_test.go +++ b/core/services/ocr2/plugins/llo/bench/harness_test.go @@ -41,17 +41,16 @@ import ( "github.com/shopspring/decimal" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - memkvdb "github.com/smartcontractkit/libocr/offchainreporting2plus/ocrintegrationtesthelpers" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-common/pkg/logger" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" llov31 "github.com/smartcontractkit/chainlink-data-streams/llo/v31" + "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + memkvdb "github.com/smartcontractkit/libocr/offchainreporting2plus/ocrintegrationtesthelpers" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" corello "github.com/smartcontractkit/chainlink/v2/core/services/llo" ) @@ -248,7 +247,7 @@ func buildV31(tb testing.TB, defs llotypes.ChannelDefinitions, n, f int) (ocr3_1 // --------------------------------------------------------------------------- func attributedObservation(observer int, obs []byte) ocrtypes.AttributedObservation { - return ocrtypes.AttributedObservation{Observer: commontypes.OracleID(observer), Observation: obs} + return ocrtypes.AttributedObservation{Observer: commontypes.OracleID(observer), Observation: obs} //nolint:gosec // G115: observer is a small oracle index } // replicate builds n AttributedObservations from a single serialized @@ -257,7 +256,7 @@ func attributedObservation(observer int, obs []byte) ocrtypes.AttributedObservat // aggregation still processes n observations per stream). func replicate(obs []byte, n int) []ocrtypes.AttributedObservation { aos := make([]ocrtypes.AttributedObservation, 0, n) - for i := 0; i < n; i++ { + for i := range n { aos = append(aos, attributedObservation(i, obs)) } return aos @@ -328,7 +327,7 @@ func bootOrReplicate(obs []byte, n int, seqNr uint64) []ocrtypes.AttributedObser // warmupRounds returns the round budget needed to establish `channels` // channels (5 per round) plus slack for reportability and bootstrap. func warmupRounds(channels int) uint64 { - return uint64(channels/channelsPerRound + warmupRoundSlack) + return uint64(channels/channelsPerRound + warmupRoundSlack) //nolint:gosec // G115: small non-negative round budget } // warmV30 drives rounds until all `channels` channels are established and diff --git a/core/services/ocr2/plugins/llo/bench/plugin_bench_test.go b/core/services/ocr2/plugins/llo/bench/plugin_bench_test.go index d3c0c0363c4..8f0c560d784 100644 --- a/core/services/ocr2/plugins/llo/bench/plugin_bench_test.go +++ b/core/services/ocr2/plugins/llo/bench/plugin_bench_test.go @@ -6,11 +6,10 @@ import ( "github.com/stretchr/testify/require" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" ) const ( @@ -38,9 +37,10 @@ var benchWorkloads = []workload{ // same format. This guards the benchmark: if the two drivers diverge, the // latency numbers are not comparing like for like. func TestParity(t *testing.T) { + t.Parallel() for _, w := range benchWorkloads { - w := w t.Run(w.String(), func(t *testing.T) { + t.Parallel() defs, _ := w.channelDefinitions() p30 := buildV30(t, defs, benchN, benchF) @@ -52,8 +52,8 @@ func TestParity(t *testing.T) { reports31, _ := v31Round(t, p31, db, seq31, benchN) require.NotEmpty(t, reports30, "v30 produced no reports") - require.Equal(t, w.numChannels, len(reports30), "v30 should report every channel") - require.Equal(t, len(reports30), len(reports31), "v30 and v31 must produce the same number of reports") + require.Len(t, reports30, w.numChannels, "v30 should report every channel") + require.Len(t, reports31, len(reports30), "v30 and v31 must produce the same number of reports") for i := range reports30 { require.Equal(t, llotypes.ReportFormatJSON, reports30[i].ReportWithInfo.Info.ReportFormat) @@ -77,7 +77,6 @@ func TestParity(t *testing.T) { // (kvread_B, kvwrite_B, kvkeys) — the incremental state cost v30 lacks. func BenchmarkFullRound(b *testing.B) { for _, w := range benchWorkloads { - w := w defs, _ := w.channelDefinitions() b.Run(w.String()+"/v30", func(b *testing.B) { @@ -135,7 +134,6 @@ func BenchmarkFullRound(b *testing.B) { // + observation encode; for v31 also the KeyValueState read). func BenchmarkObservation(b *testing.B) { for _, w := range benchWorkloads { - w := w defs, _ := w.channelDefinitions() b.Run(w.String()+"/v30", func(b *testing.B) { @@ -176,7 +174,6 @@ func BenchmarkObservation(b *testing.B) { // KeyValueDatabase commit that libocr performs after every StateTransition. func BenchmarkStateAdvance(b *testing.B) { for _, w := range benchWorkloads { - w := w defs, _ := w.channelDefinitions() b.Run(w.String()+"/v30_Outcome", func(b *testing.B) { @@ -229,7 +226,6 @@ func BenchmarkStateAdvance(b *testing.B) { // outcome/precursor into signed report payloads). func BenchmarkReports(b *testing.B) { for _, w := range benchWorkloads { - w := w defs, _ := w.channelDefinitions() b.Run(w.String()+"/v30", func(b *testing.B) { From d508d53ff41bc3d7a65df04879a7e0faf9b490a2 Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Fri, 7 Aug 2026 19:55:37 +0100 Subject: [PATCH 7/8] llo: package reorg and experimental dev/v31 --- core/config/app_config.go | 4 +- core/config/toml/types.go | 2 +- ...02_provision_streams_trigger_capability.go | 6 +- core/services/chainlink/config_general.go | 4 +- core/services/chainlink/config_mercury.go | 2 +- core/services/chainlink/config_test.go | 2 +- .../chainlink/mocks/general_config.go | 14 +- core/services/chainlink/relayer_factory.go | 2 +- core/services/llo/cleanup_test.go | 2 +- core/services/llo/delegate.go | 21 +-- core/services/llo/keyring.go | 2 +- core/services/llo/observation/cache.go | 18 +-- core/services/llo/observation/cache_test.go | 40 ++--- core/services/llo/observation/data_source.go | 29 ++-- .../llo/observation/data_source_test.go | 150 +++++++++--------- .../llo/observation/observation_context.go | 42 ++--- .../observation/observation_context_test.go | 38 ++--- core/services/llo/observation/types.go | 4 +- core/services/llo/report_codecs.go | 13 +- core/services/llo/telem/sampling.go | 6 +- core/services/llo/telem/sampling_test.go | 14 +- core/services/llo/telem/telemetry.go | 41 ++--- core/services/llo/telem/telemetry_test.go | 76 ++++----- core/services/ocr2/delegate.go | 20 +-- .../ocr2/plugins/llo/bench/harness_test.go | 27 ++-- .../llo/history_backfill_integration_test.go | 6 +- .../ocr2/plugins/llo/integration_test.go | 99 ++++++------ ...annel_definition_cache_integration_test.go | 2 +- core/services/ocr2/validate/validate.go | 2 +- core/services/relay/dummy/relayer.go | 8 +- plugins/loop_registry.go | 6 +- plugins/loop_registry_test.go | 2 +- 32 files changed, 355 insertions(+), 349 deletions(-) diff --git a/core/config/app_config.go b/core/config/app_config.go index 7965d096c73..d8cb8df626b 100644 --- a/core/config/app_config.go +++ b/core/config/app_config.go @@ -7,7 +7,7 @@ import ( pkgerrors "github.com/pkg/errors" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" ) var ( @@ -48,7 +48,7 @@ type AppConfig interface { JobDistributor() JobDistributor JobPipeline() JobPipeline Log() Log - Mercury() de.Mercury + Mercury() dataengine.Mercury OCR() OCR OCR2() OCR2 P2P() P2P diff --git a/core/config/toml/types.go b/core/config/toml/types.go index 7f9e3d4bb5a..bdc5eef34d2 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -20,7 +20,7 @@ import ( "github.com/smartcontractkit/chainlink-common/keystore/corekeys" "github.com/smartcontractkit/chainlink-common/keystore/corekeys/p2pkey" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" - mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink-evm/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/build" diff --git a/core/scripts/keystone/src/02_provision_streams_trigger_capability.go b/core/scripts/keystone/src/02_provision_streams_trigger_capability.go index 091e18a56e7..e5e66b59c67 100644 --- a/core/scripts/keystone/src/02_provision_streams_trigger_capability.go +++ b/core/scripts/keystone/src/02_provision_streams_trigger_capability.go @@ -39,7 +39,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" focr "github.com/smartcontractkit/chainlink-deployments-framework/offchain/ocr" "github.com/smartcontractkit/chainlink-evm/gethwrappers/llo-feeds/generated/channel_config_store" "github.com/smartcontractkit/chainlink-evm/gethwrappers/llo-feeds/generated/configurator" @@ -554,14 +554,14 @@ func generateLLOOCR3Config(nca []NodeKeys) LLOOCR3Config { f := uint8(1) // LLO onchain config: production config has no predecessor. - onchainConfig, err := (&llocommon.EVMOnchainConfigCodec{}).Encode(llocommon.OnchainConfig{ + onchainConfig, err := (&lloprotocol.EVMOnchainConfigCodec{}).Encode(lloprotocol.OnchainConfig{ Version: 1, PredecessorConfigDigest: nil, }) helpers.PanicErr(err) // LLO reporting plugin (offchain) config. - reportingPluginConfig, err := llocommon.OffchainConfig{ + reportingPluginConfig, err := lloprotocol.OffchainConfig{ ProtocolVersion: 1, DefaultMinReportIntervalNanoseconds: uint64(time.Second), EnableObservationCompression: true, diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 77e72388d1b..1f7eacdd5f4 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-common/keystore/corekeys" "github.com/smartcontractkit/chainlink-common/keystore/corekeys/p2pkey" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" - "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" evmcfg "github.com/smartcontractkit/chainlink-evm/pkg/config/toml" coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" @@ -549,7 +549,7 @@ func (g *generalConfig) Prometheus() coreconfig.Prometheus { return &prometheusConfig{s: g.secrets.Prometheus} } -func (g *generalConfig) Mercury() de.Mercury { +func (g *generalConfig) Mercury() dataengine.Mercury { return &mercuryConfig{c: g.c.Mercury, s: g.secrets.Mercury} } diff --git a/core/services/chainlink/config_mercury.go b/core/services/chainlink/config_mercury.go index eb0ad72fb83..cd707bcf74c 100644 --- a/core/services/chainlink/config_mercury.go +++ b/core/services/chainlink/config_mercury.go @@ -4,7 +4,7 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/types" - mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink/v2/core/config/toml" ) diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 1ca79e550ba..840cdbc4f46 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -23,7 +23,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" - mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink-framework/multinode" "github.com/smartcontractkit/chainlink-evm/pkg/assets" diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go index 4a66c5f3f4c..a1a857ced3a 100644 --- a/core/services/chainlink/mocks/general_config.go +++ b/core/services/chainlink/mocks/general_config.go @@ -6,7 +6,7 @@ import ( config "github.com/smartcontractkit/chainlink/v2/core/config" chainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - de "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + dataengine "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" mock "github.com/stretchr/testify/mock" @@ -1477,19 +1477,19 @@ func (_c *GeneralConfig_LogConfiguration_Call) RunAndReturn(run func(config.Logf } // Mercury provides a mock function with no fields -func (_m *GeneralConfig) Mercury() de.Mercury { +func (_m *GeneralConfig) Mercury() dataengine.Mercury { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for Mercury") } - var r0 de.Mercury - if rf, ok := ret.Get(0).(func() de.Mercury); ok { + var r0 dataengine.Mercury + if rf, ok := ret.Get(0).(func() dataengine.Mercury); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(de.Mercury) + r0 = ret.Get(0).(dataengine.Mercury) } } @@ -1513,12 +1513,12 @@ func (_c *GeneralConfig_Mercury_Call) Run(run func()) *GeneralConfig_Mercury_Cal return _c } -func (_c *GeneralConfig_Mercury_Call) Return(_a0 de.Mercury) *GeneralConfig_Mercury_Call { +func (_c *GeneralConfig_Mercury_Call) Return(_a0 dataengine.Mercury) *GeneralConfig_Mercury_Call { _c.Call.Return(_a0) return _c } -func (_c *GeneralConfig_Mercury_Call) RunAndReturn(run func() de.Mercury) *GeneralConfig_Mercury_Call { +func (_c *GeneralConfig_Mercury_Call) RunAndReturn(run func() dataengine.Mercury) *GeneralConfig_Mercury_Call { _c.Call.Return(run) return _c } diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 7a87e0a275c..d47dfbd520b 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" coretypes "github.com/smartcontractkit/chainlink-common/pkg/types/core" "github.com/smartcontractkit/chainlink-data-streams/llo/retirement" - mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink-data-streams/mercury/wsrpc" "github.com/smartcontractkit/chainlink-evm/pkg/chains/legacyevm" evmtoml "github.com/smartcontractkit/chainlink-evm/pkg/config/toml" diff --git a/core/services/llo/cleanup_test.go b/core/services/llo/cleanup_test.go index 58f4e6b7a19..82749f80b39 100644 --- a/core/services/llo/cleanup_test.go +++ b/core/services/llo/cleanup_test.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink-evm/pkg/llo" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" ) diff --git a/core/services/llo/delegate.go b/core/services/llo/delegate.go index 736a9a377e0..8074a278283 100644 --- a/core/services/llo/delegate.go +++ b/core/services/llo/delegate.go @@ -13,11 +13,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + llodatasource "github.com/smartcontractkit/chainlink-data-streams/llo/datasource" + llov31 "github.com/smartcontractkit/chainlink-data-streams/llo/dev/v31" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink-data-streams/llo/retirement" "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter" llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" - llov31 "github.com/smartcontractkit/chainlink-data-streams/llo/v31" ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" ocr2plus "github.com/smartcontractkit/libocr/offchainreporting2plus" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" @@ -45,15 +46,15 @@ type delegate struct { services.StateMachine cfg DelegateConfig - reportCodecs map[llotypes.ReportFormat]llocommon.ReportCodec + reportCodecs map[llotypes.ReportFormat]lloprotocol.ReportCodec // src is the shared ShouldRetireCache. llov30.ShouldRetireCache and // llov31.ShouldRetireCache have identical method sets, so this value serves // both versions. src llov30.ShouldRetireCache - // ds is the shared LLO data source (llocommon.DataSource); v30 and v31 both + // ds is the shared LLO data source (llodatasource.DataSource); v30 and v31 both // consume it, lifecycle gating is driven by the round's DSOpts. - ds llocommon.DataSource + ds llodatasource.DataSource telem telem.TelemeterService oracles []Closer @@ -74,7 +75,7 @@ type DelegateConfig struct { ChannelDefinitionCache llotypes.ChannelDefinitionCache ReportingPluginConfig llov30.Config RetirementReportCache retirement.RetirementReportCache - RetirementReportCodec llocommon.RetirementReportCodec + RetirementReportCodec lloprotocol.RetirementReportCodec ShouldRetireCache llov30.ShouldRetireCache PluginMonitoringEndpoint telemetry.MultitypeMonitoringEndpoint DonID uint32 @@ -209,7 +210,7 @@ func (d *delegate) Start(ctx context.Context) error { } // newOracleV30 builds an OCR3.0 oracle running the llo/v30 reporting plugin. -func (d *delegate) newOracleV30(i int, configTracker ocr2types.ContractConfigTracker, lggr logger.Logger, ocrLogger ocrcommontypes.Logger, psrrc llocommon.PredecessorRetirementReportCache) (ocr2plus.Oracle, error) { +func (d *delegate) newOracleV30(i int, configTracker ocr2types.ContractConfigTracker, lggr logger.Logger, ocrLogger ocrcommontypes.Logger, psrrc lloprotocol.PredecessorRetirementReportCache) (ocr2plus.Oracle, error) { return ocr2plus.NewOracle(ocr2plus.OCR3OracleArgs2[llotypes.ReportInfo]{ BinaryNetworkEndpointFactory: d.cfg.BinaryNetworkEndpointFactory, V2Bootstrappers: d.cfg.V2Bootstrappers, @@ -232,7 +233,7 @@ func (d *delegate) newOracleV30(i int, configTracker ocr2types.ContractConfigTra ChannelDefinitionCache: d.cfg.ChannelDefinitionCache, DataSource: d.ds, Logger: logger.Named(lggr, "ReportingPlugin"), - OnchainConfigCodec: llocommon.EVMOnchainConfigCodec{}, + OnchainConfigCodec: lloprotocol.EVMOnchainConfigCodec{}, ReportCodecs: d.reportCodecs, OutcomeTelemetryCh: d.telem.GetOutcomeTelemetryCh(), ReportTelemetryCh: d.telem.GetReportTelemetryCh(), @@ -251,7 +252,7 @@ func (d *delegate) newOracleV30(i int, configTracker ocr2types.ContractConfigTra // newOracleV31 builds an OCR3.1 oracle running the llo/v31 reporting plugin. It // differs from v30 by the OCR3.1 oracle args (OCR3_1OracleArgs2), the "2" // network endpoint factory, and the required replicated KeyValueDatabaseFactory. -func (d *delegate) newOracleV31(i int, configTracker ocr2types.ContractConfigTracker, lggr logger.Logger, ocrLogger ocrcommontypes.Logger, psrrc llocommon.PredecessorRetirementReportCache) (ocr2plus.Oracle, error) { +func (d *delegate) newOracleV31(i int, configTracker ocr2types.ContractConfigTracker, lggr logger.Logger, ocrLogger ocrcommontypes.Logger, psrrc lloprotocol.PredecessorRetirementReportCache) (ocr2plus.Oracle, error) { factory := promwrapper31.NewReportingPluginFactory( llov31.NewPluginFactory(llov31.PluginFactoryParams{ Config: llov31.Config{VerboseLogging: d.cfg.ReportingPluginConfig.VerboseLogging}, @@ -261,7 +262,7 @@ func (d *delegate) newOracleV31(i int, configTracker ocr2types.ContractConfigTra ChannelDefinitionCache: d.cfg.ChannelDefinitionCache, DataSource: d.ds, Logger: logger.Named(lggr, "ReportingPlugin"), - OnchainConfigCodec: llocommon.EVMOnchainConfigCodec{}, + OnchainConfigCodec: lloprotocol.EVMOnchainConfigCodec{}, ReportCodecs: d.reportCodecs, OutcomeTelemetryCh: d.telem.GetOutcomeTelemetryCh(), ReportTelemetryCh: d.telem.GetReportTelemetryCh(), diff --git a/core/services/llo/keyring.go b/core/services/llo/keyring.go index e7a3f11314f..6c01faf2cdf 100644 --- a/core/services/llo/keyring.go +++ b/core/services/llo/keyring.go @@ -11,7 +11,7 @@ import ( "golang.org/x/exp/maps" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodecs/evm" + "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodec/evm" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/utils/crypto" diff --git a/core/services/llo/observation/cache.go b/core/services/llo/observation/cache.go index 4574ff7d28c..7a1924587fc 100644 --- a/core/services/llo/observation/cache.go +++ b/core/services/llo/observation/cache.go @@ -9,7 +9,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" ) var ( @@ -44,9 +44,9 @@ var ( // StreamValueCache is used by dataSource to decouple the read/write paths for stream values. type StreamValueCache interface { - Get(id llotypes.StreamID) (llocommon.StreamValue, time.Time) - UpdateStreamValues(streamValues llocommon.StreamValues) - AddMany(values map[llotypes.StreamID]llocommon.StreamValue, ttl time.Duration) + Get(id llotypes.StreamID) (lloprotocol.StreamValue, time.Time) + UpdateStreamValues(streamValues lloprotocol.StreamValues) + AddMany(values map[llotypes.StreamID]lloprotocol.StreamValue, ttl time.Duration) Close() error } @@ -69,7 +69,7 @@ type Cache struct { } type item struct { - value llocommon.StreamValue + value lloprotocol.StreamValue expiresAt time.Time writtenAt time.Time // wall clock at Add/AddMany; used for cache_hit_entry_age_ms } @@ -122,7 +122,7 @@ func NewCache(cleanupInterval time.Duration) *Cache { } // Add adds a stream value to the cache. -func (c *Cache) Add(id llotypes.StreamID, value llocommon.StreamValue, ttl time.Duration) { +func (c *Cache) Add(id llotypes.StreamID, value lloprotocol.StreamValue, ttl time.Duration) { now := time.Now() var expiresAt time.Time if ttl > 0 { @@ -133,7 +133,7 @@ func (c *Cache) Add(id llotypes.StreamID, value llocommon.StreamValue, ttl time. c.values[id] = item{value: value, expiresAt: expiresAt, writtenAt: now} } -func (c *Cache) AddMany(values map[llotypes.StreamID]llocommon.StreamValue, ttl time.Duration) { +func (c *Cache) AddMany(values map[llotypes.StreamID]lloprotocol.StreamValue, ttl time.Duration) { now := time.Now() var expiresAt time.Time if ttl > 0 { @@ -148,7 +148,7 @@ func (c *Cache) AddMany(values map[llotypes.StreamID]llocommon.StreamValue, ttl // UpdateStreamValues mutates streamValues in-place for zero-allocation reads. // Emits cache hit/miss metrics async. -func (c *Cache) UpdateStreamValues(streamValues llocommon.StreamValues) { +func (c *Cache) UpdateStreamValues(streamValues lloprotocol.StreamValues) { events := make([]metricEvent, 0, len(streamValues)) c.mu.RLock() @@ -177,7 +177,7 @@ func (c *Cache) UpdateStreamValues(streamValues llocommon.StreamValues) { c.sendMetrics(events) } -func (c *Cache) Get(id llotypes.StreamID) (llocommon.StreamValue, time.Time) { +func (c *Cache) Get(id llotypes.StreamID) (lloprotocol.StreamValue, time.Time) { c.mu.RLock() defer c.mu.RUnlock() item, ok := c.values[id] diff --git a/core/services/llo/observation/cache_test.go b/core/services/llo/observation/cache_test.go index e277b41f583..cf73d7f9f5b 100644 --- a/core/services/llo/observation/cache_test.go +++ b/core/services/llo/observation/cache_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" ) type mockStreamValue struct { @@ -45,8 +45,8 @@ func (m *mockStreamValue) UnmarshalText(data []byte) error { return nil } -func (m *mockStreamValue) Type() llocommon.LLOStreamValue_Type { - return llocommon.LLOStreamValue_TimestampedStreamValue +func (m *mockStreamValue) Type() lloprotocol.LLOStreamValue_Type { + return lloprotocol.LLOStreamValue_TimestampedStreamValue } func TestNewCache(t *testing.T) { @@ -89,7 +89,7 @@ func TestCache_AddMany(t *testing.T) { cache := NewCache(0) defer cache.Close() ttl := time.Second - values := map[llotypes.StreamID]llocommon.StreamValue{ + values := map[llotypes.StreamID]lloprotocol.StreamValue{ 1: &mockStreamValue{value: []byte{1}}, 2: &mockStreamValue{value: []byte{2}}, 3: &mockStreamValue{value: []byte{3}}, @@ -106,7 +106,7 @@ func TestCache_AddMany(t *testing.T) { t.Parallel() cache := NewCache(0) defer cache.Close() - cache.AddMany(map[llotypes.StreamID]llocommon.StreamValue{}, time.Second) + cache.AddMany(map[llotypes.StreamID]lloprotocol.StreamValue{}, time.Second) val, _ := cache.Get(1) assert.Nil(t, val) }) @@ -115,7 +115,7 @@ func TestCache_AddMany(t *testing.T) { t.Parallel() cache := NewCache(0) defer cache.Close() - cache.AddMany(map[llotypes.StreamID]llocommon.StreamValue{ + cache.AddMany(map[llotypes.StreamID]lloprotocol.StreamValue{ 42: &mockStreamValue{value: []byte{42}}, }, time.Minute) got, _ := cache.Get(42) @@ -127,7 +127,7 @@ func TestCache_AddMany(t *testing.T) { cache := NewCache(0) defer cache.Close() cache.Add(1, &mockStreamValue{value: []byte{0}}, time.Second) - cache.AddMany(map[llotypes.StreamID]llocommon.StreamValue{ + cache.AddMany(map[llotypes.StreamID]lloprotocol.StreamValue{ 1: &mockStreamValue{value: []byte{100}}, }, time.Second) got, _ := cache.Get(1) @@ -141,13 +141,13 @@ func TestCache_UpdateStreamValues(t *testing.T) { t.Parallel() cache := NewCache(0) defer cache.Close() - cache.AddMany(map[llotypes.StreamID]llocommon.StreamValue{ + cache.AddMany(map[llotypes.StreamID]lloprotocol.StreamValue{ 1: &mockStreamValue{value: []byte{1}}, 2: &mockStreamValue{value: []byte{2}}, 3: &mockStreamValue{value: []byte{3}}, }, time.Second) - streamValues := llocommon.StreamValues{1: nil, 2: nil, 3: nil} + streamValues := lloprotocol.StreamValues{1: nil, 2: nil, 3: nil} cache.UpdateStreamValues(streamValues) assert.Equal(t, &mockStreamValue{value: []byte{1}}, streamValues[1]) @@ -161,7 +161,7 @@ func TestCache_UpdateStreamValues(t *testing.T) { defer cache.Close() cache.Add(1, &mockStreamValue{value: []byte{1}}, time.Second) - streamValues := llocommon.StreamValues{1: nil, 2: nil, 99: nil} + streamValues := lloprotocol.StreamValues{1: nil, 2: nil, 99: nil} cache.UpdateStreamValues(streamValues) assert.Equal(t, &mockStreamValue{value: []byte{1}}, streamValues[1]) @@ -174,7 +174,7 @@ func TestCache_UpdateStreamValues(t *testing.T) { cache := NewCache(0) defer cache.Close() cache.Add(1, &mockStreamValue{value: []byte{1}}, time.Second) - streamValues := llocommon.StreamValues{} + streamValues := lloprotocol.StreamValues{} cache.UpdateStreamValues(streamValues) assert.Empty(t, streamValues) }) @@ -186,7 +186,7 @@ func TestCache_UpdateStreamValues(t *testing.T) { cache.Add(1, &mockStreamValue{value: []byte{1}}, time.Nanosecond*100) time.Sleep(time.Millisecond) - streamValues := llocommon.StreamValues{1: nil} + streamValues := lloprotocol.StreamValues{1: nil} cache.UpdateStreamValues(streamValues) assert.Nil(t, streamValues[1]) }) @@ -197,7 +197,7 @@ func TestCache_UpdateStreamValues(t *testing.T) { defer cache.Close() cache.Add(1, &mockStreamValue{value: []byte{100}}, time.Second) - streamValues := llocommon.StreamValues{1: &mockStreamValue{value: []byte{0}}} + streamValues := lloprotocol.StreamValues{1: &mockStreamValue{value: []byte{0}}} cache.UpdateStreamValues(streamValues) assert.Equal(t, &mockStreamValue{value: []byte{100}}, streamValues[1]) }) @@ -209,11 +209,11 @@ func TestCache_UpdateStreamValues_RecordsHitEntryAge(t *testing.T) { //nolint:pa cache := NewCache(0) defer cache.Close() - cache.AddMany(map[llotypes.StreamID]llocommon.StreamValue{ + cache.AddMany(map[llotypes.StreamID]lloprotocol.StreamValue{ 1: &mockStreamValue{value: []byte{1}}, }, time.Hour) - streamValues := llocommon.StreamValues{1: nil} + streamValues := lloprotocol.StreamValues{1: nil} cache.UpdateStreamValues(streamValues) var m io_prometheus_client.Metric @@ -232,9 +232,9 @@ func TestCache_Add_Get(t *testing.T) { tests := []struct { name string streamID llotypes.StreamID - value llocommon.StreamValue + value lloprotocol.StreamValue ttl time.Duration - wantValue llocommon.StreamValue + wantValue lloprotocol.StreamValue beforeGet func(cache *Cache) }{ { @@ -435,7 +435,7 @@ func TestCache_ConcurrentAddMany(t *testing.T) { go func(id uint32) { defer wg.Done() for b := range numBatches { - batch := make(map[llotypes.StreamID]llocommon.StreamValue, batchSize) + batch := make(map[llotypes.StreamID]lloprotocol.StreamValue, batchSize) for j := range batchSize { streamID := id*numBatches*batchSize + b*batchSize + j batch[streamID] = &mockStreamValue{value: []byte{byte(id % 256)}} @@ -473,7 +473,7 @@ func TestCache_ConcurrentAddManyUpdateStreamValues(t *testing.T) { go func(id uint32) { defer wg.Done() for iter := range numIterations { - batch := make(map[llotypes.StreamID]llocommon.StreamValue, batchSize) + batch := make(map[llotypes.StreamID]lloprotocol.StreamValue, batchSize) for j := range batchSize { streamID := id*batchSize + j batch[streamID] = &mockStreamValue{value: []byte{byte(iter)}} @@ -487,7 +487,7 @@ func TestCache_ConcurrentAddManyUpdateStreamValues(t *testing.T) { go func(id uint32) { defer wg.Done() for range numIterations { - sv := make(llocommon.StreamValues, batchSize) + sv := make(lloprotocol.StreamValues, batchSize) for j := range batchSize { sv[id*batchSize+j] = nil } diff --git a/core/services/llo/observation/data_source.go b/core/services/llo/observation/data_source.go index 0d5b9a7fea7..da4be374ee2 100644 --- a/core/services/llo/observation/data_source.go +++ b/core/services/llo/observation/data_source.go @@ -16,7 +16,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + llodatasource "github.com/smartcontractkit/chainlink-data-streams/llo/datasource" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink/v2/core/services/llo/telem" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/streams" @@ -163,12 +164,12 @@ type dataSource struct { loopWakeCh chan struct{} } -var _ llocommon.DataSource = &dataSource{} +var _ llodatasource.DataSource = &dataSource{} // NewDataSource returns the shared LLO data source. llo/v30 and llo/v31 both -// consume llocommon.DataSource, so a single implementation serves both OCR +// consume llodatasource.DataSource, so a single implementation serves both OCR // protocol versions; lifecycle gating is driven by opts.LifeCycleStage(). -func NewDataSource(lggr logger.Logger, registry Registry, t Telemeter) llocommon.DataSource { +func NewDataSource(lggr logger.Logger, registry Registry, t Telemeter) llodatasource.DataSource { return newDataSource(lggr, registry, t) } @@ -200,19 +201,19 @@ func (d *dataSource) signalObservationLoopWake() { // streamValues from the in-memory cache. The stage is carried by opts and is // derived by each plugin version from its own state (v30 from the previous // outcome, v31 from the KeyValueState), so a single implementation serves both. -func (d *dataSource) Observe(ctx context.Context, streamValues llocommon.StreamValues, opts llocommon.DSOpts) error { +func (d *dataSource) Observe(ctx context.Context, streamValues lloprotocol.StreamValues, opts llodatasource.DSOpts) error { return d.observe(ctx, streamValues, opts, d.inProduction(opts)) } // inProduction reports whether this OCR instance is the Production instance (the // only one that should run pipeline observations). -func (d *dataSource) inProduction(opts llocommon.DSOpts) bool { +func (d *dataSource) inProduction(opts llodatasource.DSOpts) bool { if opts == nil { // setObservableStreams logs the nil-opts case; stay silent here to avoid // a duplicate warning per round. return false } - if opts.LifeCycleStage() != llocommon.LifeCycleStageProduction { + if opts.LifeCycleStage() != lloprotocol.LifeCycleStageProduction { d.lggr.Debugw("Observe: LLO OCR instance is not in production lifecycle stage", "configDigest", opts.ConfigDigest().String(), "stage", opts.LifeCycleStage()) return false @@ -223,7 +224,7 @@ func (d *dataSource) inProduction(opts llocommon.DSOpts) bool { // observe starts or refreshes the background observation loop for the plugin's stream set, then fills streamValues // from the in-memory cache (backed by pipeline observations registered for each stream ID). inProduction gates the // loop: when false the observable stream set is cleared and no pipelines run this round. -func (d *dataSource) observe(ctx context.Context, streamValues llocommon.StreamValues, opts telem.DSOpts, inProduction bool) error { +func (d *dataSource) observe(ctx context.Context, streamValues lloprotocol.StreamValues, opts telem.DSOpts, inProduction bool) error { // Observation loop logic { // setObservableStreams copies stream IDs and deadline into internal state (the plugin's map is not retained). @@ -349,7 +350,7 @@ func (d *dataSource) startObservationLoop(loopStartedCh chan struct{}) { wg.Add(1) go func(streamIDs []streams.StreamID) { defer wg.Done() - local := make(llocommon.StreamValues, len(streamIDs)) + local := make(lloprotocol.StreamValues, len(streamIDs)) var hadErr bool for _, sid := range streamIDs { local[sid] = nil @@ -454,8 +455,8 @@ type streamsRefreshPlan struct { // in groups; the worker observe list is built from p.StreamIDs() filtered to keys present in streamValues so we never // run Observe for pipeline siblings the plugin did not request this round. Unregistered drivers go to missingStreamIDs; // each increments promMissingStreamCount and triggers a single Warn when missingStreamIDs is non-empty. -func (d *dataSource) buildStreamsRefreshPlan(streamValues llocommon.StreamValues, observationTimeout time.Duration, lggr logger.Logger) streamsRefreshPlan { - candidatesValues := make(llocommon.StreamValues, len(streamValues)) +func (d *dataSource) buildStreamsRefreshPlan(streamValues lloprotocol.StreamValues, observationTimeout time.Duration, lggr logger.Logger) streamsRefreshPlan { + candidatesValues := make(lloprotocol.StreamValues, len(streamValues)) for streamID := range streamValues { // Plugin-scope keys that need refresh become drivers; pipelines are collected below and scoped to these keys. if val, expiresAt := d.cache.Get(streamID); val != nil { @@ -520,13 +521,13 @@ func (d *dataSource) Close() error { type observableStreamValues struct { opts telem.DSOpts - streamValues llocommon.StreamValues + streamValues lloprotocol.StreamValues observationTimeout time.Duration } // setObservableStreams updates the stream set and observation deadline (T) used by the background loop. When // inProduction is false (v30 non-production instance) the observable set is left unchanged/empty so no pipelines run. -func (d *dataSource) setObservableStreams(ctx context.Context, streamValues llocommon.StreamValues, opts telem.DSOpts, inProduction bool) { +func (d *dataSource) setObservableStreams(ctx context.Context, streamValues lloprotocol.StreamValues, opts telem.DSOpts, inProduction bool) { if opts == nil || len(streamValues) == 0 { d.lggr.Warnw("setObservableStreams: no observable streams to set", "opts", opts, "observable_streams", len(streamValues)) @@ -542,7 +543,7 @@ func (d *dataSource) setObservableStreams(ctx context.Context, streamValues lloc osv := &observableStreamValues{ opts: opts, - streamValues: make(llocommon.StreamValues, len(streamValues)), + streamValues: make(lloprotocol.StreamValues, len(streamValues)), observationTimeout: 250 * time.Millisecond, } diff --git a/core/services/llo/observation/data_source_test.go b/core/services/llo/observation/data_source_test.go index d524ccde621..5b6b4bf5ad1 100644 --- a/core/services/llo/observation/data_source_test.go +++ b/core/services/llo/observation/data_source_test.go @@ -22,7 +22,7 @@ import ( "gopkg.in/guregu/null.v4" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink/v2/core/bridges" clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -80,15 +80,15 @@ func pipelineForStream(streamID streams.StreamID, runID int64, res *big.Int, err return p } -func makeStreamValues(streamIDs ...llotypes.StreamID) llocommon.StreamValues { +func makeStreamValues(streamIDs ...llotypes.StreamID) lloprotocol.StreamValues { if len(streamIDs) == 0 { - return llocommon.StreamValues{ + return lloprotocol.StreamValues{ 1: nil, 2: nil, 3: nil, } } - vals := llocommon.StreamValues{} + vals := lloprotocol.StreamValues{} for _, streamID := range streamIDs { vals[streamID] = nil } @@ -124,7 +124,7 @@ func (m *mockOpts) ObservationTimestamp() time.Time { } func (m *mockOpts) LifeCycleStage() llotypes.LifeCycleStage { if m.lifeCycleStage == "" { - return llocommon.LifeCycleStageProduction + return lloprotocol.LifeCycleStageProduction } return m.lifeCycleStage } @@ -140,13 +140,13 @@ type v3PremiumLegacyPacket struct { trrs pipeline.TaskRunResults streamID uint32 opts telem.DSOpts - val llocommon.StreamValue + val lloprotocol.StreamValue err error } var _ Telemeter = &mockTelemeter{} -func (m *mockTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts telem.DSOpts, val llocommon.StreamValue, err error) { +func (m *mockTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts telem.DSOpts, val lloprotocol.StreamValue, err error) { m.mu.Lock() defer m.mu.Unlock() m.v3PremiumLegacyPackets = append(m.v3PremiumLegacyPackets, v3PremiumLegacyPacket{run, trrs, streamID, opts, val, err}) @@ -158,17 +158,17 @@ func (m *mockTelemeter) MakeObservationScopedTelemetryCh(opts telem.DSOpts, size return m.ch } -func (m *mockTelemeter) GetOutcomeTelemetryCh() chan<- *llocommon.LLOOutcomeTelemetry { +func (m *mockTelemeter) GetOutcomeTelemetryCh() chan<- *lloprotocol.LLOOutcomeTelemetry { return nil } -func (m *mockTelemeter) GetReportTelemetryCh() chan<- *llocommon.LLOReportTelemetry { return nil } -func (m *mockTelemeter) CaptureEATelemetry() bool { return true } -func (m *mockTelemeter) CaptureObservationTelemetry() bool { return true } +func (m *mockTelemeter) GetReportTelemetryCh() chan<- *lloprotocol.LLOReportTelemetry { return nil } +func (m *mockTelemeter) CaptureEATelemetry() bool { return true } +func (m *mockTelemeter) CaptureObservationTelemetry() bool { return true } var observationTimeout = 500 * time.Millisecond type addManyCall struct { - values map[llotypes.StreamID]llocommon.StreamValue + values map[llotypes.StreamID]lloprotocol.StreamValue ttl time.Duration } @@ -184,8 +184,8 @@ func newMockCache(inner StreamValueCache) *mockCache { // AddMany is a spy for the StreamValueCache.AddMany method. // It records the values and ttl passed to it and then calls the underlying StreamValueCache.AddMany method. -func (s *mockCache) AddMany(values map[llotypes.StreamID]llocommon.StreamValue, ttl time.Duration) { - snapshot := make(map[llotypes.StreamID]llocommon.StreamValue, len(values)) +func (s *mockCache) AddMany(values map[llotypes.StreamID]lloprotocol.StreamValue, ttl time.Duration) { + snapshot := make(map[llotypes.StreamID]lloprotocol.StreamValue, len(values)) maps.Copy(snapshot, values) s.mu.Lock() s.addCalls = append(s.addCalls, addManyCall{values: snapshot, ttl: ttl}) @@ -239,10 +239,10 @@ func Test_DataSource(t *testing.T) { err := ds.Observe(ctx, vals, opts) require.NoError(t, err) - assert.Equal(t, llocommon.StreamValues{ - 1: llocommon.ToDecimal(decimal.NewFromInt(2181)), - 2: llocommon.ToDecimal(decimal.NewFromInt(40602)), - 3: llocommon.ToDecimal(decimal.NewFromInt(15)), + assert.Equal(t, lloprotocol.StreamValues{ + 1: lloprotocol.ToDecimal(decimal.NewFromInt(2181)), + 2: lloprotocol.ToDecimal(decimal.NewFromInt(40602)), + 3: lloprotocol.ToDecimal(decimal.NewFromInt(15)), }, vals, "vals: %v", vals) ds.Close() }) @@ -265,9 +265,9 @@ func Test_DataSource(t *testing.T) { err := ds.Observe(ctx, vals, opts) require.NoError(t, err) - assert.Equal(t, llocommon.StreamValues{ + assert.Equal(t, lloprotocol.StreamValues{ 11: nil, - 12: llocommon.ToDecimal(decimal.NewFromInt(40602)), + 12: lloprotocol.ToDecimal(decimal.NewFromInt(40602)), 13: nil, }, vals, "vals: %v", vals) ds.Close() @@ -297,10 +297,10 @@ func Test_DataSource(t *testing.T) { ds.Close() require.NoError(t, err) - assert.Equal(t, llocommon.StreamValues{ - 21: llocommon.ToDecimal(decimal.NewFromInt(2181)), - 22: llocommon.ToDecimal(decimal.NewFromInt(40602)), - 23: llocommon.ToDecimal(decimal.NewFromInt(15)), + assert.Equal(t, lloprotocol.StreamValues{ + 21: lloprotocol.ToDecimal(decimal.NewFromInt(2181)), + 22: lloprotocol.ToDecimal(decimal.NewFromInt(40602)), + 23: lloprotocol.ToDecimal(decimal.NewFromInt(15)), }, vals, "vals: %v", vals) // Get only the last 3 packets, as those would be the result of the first round of observations. @@ -318,7 +318,7 @@ func Test_DataSource(t *testing.T) { assert.Len(t, pkt.trrs, 1) assert.Equal(t, 21, int(pkt.streamID)) assert.Equal(t, opts, pkt.opts) - assert.Equal(t, "2181", pkt.val.(*llocommon.Decimal).String()) + assert.Equal(t, "2181", pkt.val.(*lloprotocol.Decimal).String()) require.NoError(t, pkt.err) telems := []any{} @@ -337,7 +337,7 @@ func Test_DataSource(t *testing.T) { require.IsType(t, &telem.LLOObservationTelemetry{}, telems[0]) obsTelem := telems[0].(*telem.LLOObservationTelemetry) assert.Equal(t, uint32(21), obsTelem.StreamId) - assert.Equal(t, int32(llocommon.LLOStreamValue_Decimal), obsTelem.StreamValueType) + assert.Equal(t, int32(lloprotocol.LLOStreamValue_Decimal), obsTelem.StreamValueType) assert.Equal(t, "00000000020885", hex.EncodeToString(obsTelem.StreamValueBinary)) assert.Equal(t, "2181", obsTelem.StreamValueText) assert.Nil(t, obsTelem.ObservationError) @@ -366,9 +366,9 @@ func Test_DataSource(t *testing.T) { err := ds.Observe(ctx, vals, opts) require.NoError(t, err) - assert.Equal(t, llocommon.StreamValues{ + assert.Equal(t, lloprotocol.StreamValues{ 31: nil, - 32: llocommon.ToDecimal(decimal.NewFromInt(40602)), + 32: lloprotocol.ToDecimal(decimal.NewFromInt(40602)), 33: nil, }, vals, "vals: %v", vals) @@ -399,7 +399,7 @@ func Test_DataSource(t *testing.T) { reg.pipelines[20001] = pipelineForStream(20001, 2, big.NewInt(40602), nil) reg.mu.Unlock() - vals := llocommon.StreamValues{ + vals := lloprotocol.StreamValues{ 10001: nil, 20001: nil, 30001: nil, @@ -411,9 +411,9 @@ func Test_DataSource(t *testing.T) { require.NoError(t, err) // Verify initial values - assert.Equal(t, llocommon.StreamValues{ - 10001: llocommon.ToDecimal(decimal.NewFromInt(2181)), - 20001: llocommon.ToDecimal(decimal.NewFromInt(40602)), + assert.Equal(t, lloprotocol.StreamValues{ + 10001: lloprotocol.ToDecimal(decimal.NewFromInt(2181)), + 20001: lloprotocol.ToDecimal(decimal.NewFromInt(40602)), 30001: nil, }, vals) @@ -424,7 +424,7 @@ func Test_DataSource(t *testing.T) { reg.mu.Unlock() // Second observation should use cached values - vals = llocommon.StreamValues{ + vals = lloprotocol.StreamValues{ 10001: nil, 20001: nil, 30001: nil, @@ -435,9 +435,9 @@ func Test_DataSource(t *testing.T) { require.NoError(t, err) // Should still have original values from cache - assert.Equal(t, llocommon.StreamValues{ - 10001: llocommon.ToDecimal(decimal.NewFromInt(2181)), - 20001: llocommon.ToDecimal(decimal.NewFromInt(40602)), + assert.Equal(t, lloprotocol.StreamValues{ + 10001: lloprotocol.ToDecimal(decimal.NewFromInt(2181)), + 20001: lloprotocol.ToDecimal(decimal.NewFromInt(40602)), 30001: nil, }, vals) }) @@ -451,7 +451,7 @@ func Test_DataSource(t *testing.T) { reg.mu.Lock() reg.pipelines[50002] = pipelineForStream(50002, 1, big.NewInt(100), nil) reg.mu.Unlock() - vals := llocommon.StreamValues{50002: nil} + vals := lloprotocol.StreamValues{50002: nil} ctx, cancel := context.WithTimeout(mainCtx, observationTimeout) defer cancel() @@ -467,13 +467,13 @@ func Test_DataSource(t *testing.T) { time.Sleep(observationTimeout * 3) // Second observation should use new value - vals = llocommon.StreamValues{50002: nil} + vals = lloprotocol.StreamValues{50002: nil} ctx2, cancel := context.WithTimeout(mainCtx, observationTimeout*5) defer cancel() err = ds.Observe(ctx2, vals, opts) require.NoError(t, err) - assert.Equal(t, llocommon.StreamValues{50002: llocommon.ToDecimal(decimal.NewFromInt(200))}, vals) + assert.Equal(t, lloprotocol.StreamValues{50002: lloprotocol.ToDecimal(decimal.NewFromInt(200))}, vals) }) t.Run("handles concurrent cache access", func(t *testing.T) { @@ -488,7 +488,7 @@ func Test_DataSource(t *testing.T) { reg.mu.Unlock() // First observation to cache - vals := llocommon.StreamValues{1: nil} + vals := lloprotocol.StreamValues{1: nil} ctx, cancel := context.WithTimeout(mainCtx, observationTimeout) defer cancel() @@ -499,10 +499,10 @@ func Test_DataSource(t *testing.T) { var wg sync.WaitGroup for range 10 { wg.Go(func() { - vals := llocommon.StreamValues{1: nil} + vals := lloprotocol.StreamValues{1: nil} err := ds.Observe(ctx, vals, opts) assert.NoError(t, err) - assert.Equal(t, llocommon.StreamValues{1: llocommon.ToDecimal(decimal.NewFromInt(100))}, vals) + assert.Equal(t, lloprotocol.StreamValues{1: lloprotocol.ToDecimal(decimal.NewFromInt(100))}, vals) }) } wg.Wait() @@ -534,7 +534,7 @@ func Test_DataSource(t *testing.T) { err := ds.Observe(ctx, vals, opts) require.NoError(t, err) - assert.Equal(t, llocommon.StreamValues{1: nil, 2: nil, 3: nil}, vals) + assert.Equal(t, lloprotocol.StreamValues{1: nil, 2: nil, 3: nil}, vals) mc.mu.Lock() for _, call := range mc.addCalls { @@ -564,10 +564,10 @@ func Test_DataSource(t *testing.T) { err = ds.Observe(ctx2, vals2, opts) require.NoError(t, err) - expectedCycle2 := llocommon.StreamValues{ - 1: llocommon.ToDecimal(decimal.NewFromFloat(111.0)), - 2: llocommon.ToDecimal(decimal.NewFromFloat(222.0)), - 3: llocommon.ToDecimal(decimal.NewFromFloat(333.0)), + expectedCycle2 := lloprotocol.StreamValues{ + 1: lloprotocol.ToDecimal(decimal.NewFromFloat(111.0)), + 2: lloprotocol.ToDecimal(decimal.NewFromFloat(222.0)), + 3: lloprotocol.ToDecimal(decimal.NewFromFloat(333.0)), } assert.Equal(t, expectedCycle2, vals2, "cycle 2: expected a value from fixedPipeline") @@ -613,13 +613,13 @@ func Test_DataSource(t *testing.T) { reg.mu.Unlock() time.Sleep(observationTimeout * 3) - vals = llocommon.StreamValues{1: nil} + vals = lloprotocol.StreamValues{1: nil} ctx2, cancel := context.WithTimeout(mainCtx, observationTimeout*5) defer cancel() err = ds.Observe(ctx2, vals, opts) require.NoError(t, err) - assert.Equal(t, llocommon.StreamValues{1: llocommon.ToDecimal(decimal.NewFromInt(100))}, vals) + assert.Equal(t, lloprotocol.StreamValues{1: lloprotocol.ToDecimal(decimal.NewFromInt(100))}, vals) }) }) @@ -712,11 +712,11 @@ func Test_buildStreamsRefreshPlan(t *testing.T) { t.Parallel() cache := NewCache(0) staleTTL := 1 * time.Millisecond - cache.Add(1, llocommon.ToDecimal(decimal.NewFromInt(100)), staleTTL) - cache.Add(2, llocommon.ToDecimal(decimal.NewFromInt(200)), staleTTL) - cache.Add(3, llocommon.ToDecimal(decimal.NewFromInt(300)), staleTTL) + cache.Add(1, lloprotocol.ToDecimal(decimal.NewFromInt(100)), staleTTL) + cache.Add(2, lloprotocol.ToDecimal(decimal.NewFromInt(200)), staleTTL) + cache.Add(3, lloprotocol.ToDecimal(decimal.NewFromInt(300)), staleTTL) ds := &dataSource{lggr: lggr, registry: reg, cache: cache} - sv := llocommon.StreamValues{1: nil, 2: nil, 3: nil} + sv := lloprotocol.StreamValues{1: nil, 2: nil, 3: nil} result := ds.buildStreamsRefreshPlan(sv, timeout, lggr).streamIDsToRefresh @@ -729,11 +729,11 @@ func Test_buildStreamsRefreshPlan(t *testing.T) { t.Run("all streams fresh in cache, returns none", func(t *testing.T) { t.Parallel() cache := NewCache(0) - cache.Add(1, llocommon.ToDecimal(decimal.NewFromInt(100)), time.Hour) - cache.Add(2, llocommon.ToDecimal(decimal.NewFromInt(200)), time.Hour) - cache.Add(3, llocommon.ToDecimal(decimal.NewFromInt(300)), time.Hour) + cache.Add(1, lloprotocol.ToDecimal(decimal.NewFromInt(100)), time.Hour) + cache.Add(2, lloprotocol.ToDecimal(decimal.NewFromInt(200)), time.Hour) + cache.Add(3, lloprotocol.ToDecimal(decimal.NewFromInt(300)), time.Hour) ds := &dataSource{lggr: lggr, registry: reg, cache: cache} - sv := llocommon.StreamValues{1: nil, 2: nil, 3: nil} + sv := lloprotocol.StreamValues{1: nil, 2: nil, 3: nil} result := ds.buildStreamsRefreshPlan(sv, timeout, lggr).streamIDsToRefresh @@ -743,11 +743,11 @@ func Test_buildStreamsRefreshPlan(t *testing.T) { t.Run("one stale driver lists only stale IDs; worker observes all requested streams on that pipeline", func(t *testing.T) { t.Parallel() cache := NewCache(0) - cache.Add(1, llocommon.ToDecimal(decimal.NewFromInt(100)), time.Hour) - cache.Add(2, llocommon.ToDecimal(decimal.NewFromInt(200)), 1*time.Millisecond) - cache.Add(3, llocommon.ToDecimal(decimal.NewFromInt(300)), time.Hour) + cache.Add(1, lloprotocol.ToDecimal(decimal.NewFromInt(100)), time.Hour) + cache.Add(2, lloprotocol.ToDecimal(decimal.NewFromInt(200)), 1*time.Millisecond) + cache.Add(3, lloprotocol.ToDecimal(decimal.NewFromInt(300)), time.Hour) ds := &dataSource{lggr: lggr, registry: reg, cache: cache} - sv := llocommon.StreamValues{1: nil, 2: nil, 3: nil} + sv := lloprotocol.StreamValues{1: nil, 2: nil, 3: nil} plan := ds.buildStreamsRefreshPlan(sv, timeout, lggr) @@ -761,11 +761,11 @@ func Test_buildStreamsRefreshPlan(t *testing.T) { t.Run("staleStreamIDs lists only stale keys; groups intersect pipeline with plugin scope", func(t *testing.T) { t.Parallel() cache := NewCache(0) - cache.Add(1, llocommon.ToDecimal(decimal.NewFromInt(100)), 1*time.Millisecond) - cache.Add(2, llocommon.ToDecimal(decimal.NewFromInt(200)), time.Hour) + cache.Add(1, lloprotocol.ToDecimal(decimal.NewFromInt(100)), 1*time.Millisecond) + cache.Add(2, lloprotocol.ToDecimal(decimal.NewFromInt(200)), time.Hour) // pipeline has {1,2,3}, but only {1,2} in plugin scope ds := &dataSource{lggr: lggr, registry: reg, cache: cache} - sv := llocommon.StreamValues{1: nil, 2: nil} // stream 3 not requested + sv := lloprotocol.StreamValues{1: nil, 2: nil} // stream 3 not requested plan := ds.buildStreamsRefreshPlan(sv, timeout, lggr) @@ -780,7 +780,7 @@ func Test_buildStreamsRefreshPlan(t *testing.T) { t.Run("stream not in registry is stale driver only; no pipeline worker", func(t *testing.T) { t.Parallel() ds := &dataSource{lggr: lggr, registry: reg, cache: NewCache(0)} - sv := llocommon.StreamValues{999: nil} // plugin requested streamId not yet in registry + sv := lloprotocol.StreamValues{999: nil} // plugin requested streamId not yet in registry plan := ds.buildStreamsRefreshPlan(sv, timeout, lggr) @@ -792,7 +792,7 @@ func Test_buildStreamsRefreshPlan(t *testing.T) { t.Run("empty streamValues returns empty set", func(t *testing.T) { t.Parallel() ds := &dataSource{lggr: lggr, registry: reg, cache: NewCache(0)} - sv := llocommon.StreamValues{} + sv := lloprotocol.StreamValues{} result := ds.buildStreamsRefreshPlan(sv, timeout, lggr).streamIDsToRefresh @@ -803,13 +803,13 @@ func Test_buildStreamsRefreshPlan(t *testing.T) { t.Parallel() cache := NewCache(0) // Pipeline {10}: all fresh - cache.Add(10, llocommon.ToDecimal(decimal.NewFromInt(100)), time.Hour) + cache.Add(10, lloprotocol.ToDecimal(decimal.NewFromInt(100)), time.Hour) // Pipeline {20,21}: stream 20 stale, stream 21 fresh - cache.Add(20, llocommon.ToDecimal(decimal.NewFromInt(2000)), 1*time.Millisecond) - cache.Add(21, llocommon.ToDecimal(decimal.NewFromInt(2100)), time.Hour) + cache.Add(20, lloprotocol.ToDecimal(decimal.NewFromInt(2000)), 1*time.Millisecond) + cache.Add(21, lloprotocol.ToDecimal(decimal.NewFromInt(2100)), time.Hour) ds := &dataSource{lggr: lggr, registry: reg, cache: cache} - sv := llocommon.StreamValues{10: nil, 20: nil, 21: nil} + sv := lloprotocol.StreamValues{10: nil, 20: nil, 21: nil} plan := ds.buildStreamsRefreshPlan(sv, timeout, lggr) @@ -915,7 +915,7 @@ result3 -> result3_parse -> multiply3; } ds := newDataSource(lggr, r, telem.NullTelemeter) - vals := make(map[llotypes.StreamID]llocommon.StreamValue) + vals := make(map[llotypes.StreamID]lloprotocol.StreamValue) for i := uint32(0); i < 4*n; i++ { vals[i] = nil } @@ -934,9 +934,9 @@ func Test_DataSource_inProduction(t *testing.T) { ds := newDataSource(logger.NullLogger, reg, telem.NullTelemeter) defer ds.Close() - require.True(t, ds.inProduction(&mockOpts{lifeCycleStage: llocommon.LifeCycleStageProduction})) - require.False(t, ds.inProduction(&mockOpts{lifeCycleStage: llocommon.LifeCycleStageStaging})) - require.False(t, ds.inProduction(&mockOpts{lifeCycleStage: llocommon.LifeCycleStageRetired})) + require.True(t, ds.inProduction(&mockOpts{lifeCycleStage: lloprotocol.LifeCycleStageProduction})) + require.False(t, ds.inProduction(&mockOpts{lifeCycleStage: lloprotocol.LifeCycleStageStaging})) + require.False(t, ds.inProduction(&mockOpts{lifeCycleStage: lloprotocol.LifeCycleStageRetired})) require.False(t, ds.inProduction(nil)) } @@ -952,7 +952,7 @@ func Test_DataSource_StagingDoesNotObserve(t *testing.T) { ctx, cancel := context.WithTimeout(t.Context(), observationTimeout) defer cancel() vals := makeStreamValues(1) - require.NoError(t, ds.Observe(ctx, vals, &mockOpts{lifeCycleStage: llocommon.LifeCycleStageStaging})) + require.NoError(t, ds.Observe(ctx, vals, &mockOpts{lifeCycleStage: lloprotocol.LifeCycleStageStaging})) require.Nil(t, vals[1], "staging instance must not populate stream values") require.Zero(t, reg.pipelines[1].runCount.Load(), "staging instance must not run pipelines") diff --git a/core/services/llo/observation/observation_context.go b/core/services/llo/observation/observation_context.go index d0b80242b58..3d6917c0684 100644 --- a/core/services/llo/observation/observation_context.go +++ b/core/services/llo/observation/observation_context.go @@ -13,7 +13,7 @@ import ( "github.com/shopspring/decimal" "github.com/smartcontractkit/chainlink-common/pkg/logger" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink/v2/core/services/llo/telem" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -29,7 +29,7 @@ import ( var _ ObservationContext = (*observationContext)(nil) type ObservationContext interface { //nolint:revive // ObservationContext is the established interface name in this package - Observe(ctx context.Context, streamID streams.StreamID, opts telem.DSOpts) (val llocommon.StreamValue, err error) + Observe(ctx context.Context, streamID streams.StreamID, opts telem.DSOpts) (val lloprotocol.StreamValue, err error) } type execution struct { @@ -58,7 +58,7 @@ func newObservationContext(l logger.Logger, r Registry, t Telemeter) *observatio return &observationContext{l, r, t, sync.Mutex{}, make(map[streams.Pipeline]*execution)} } -func (oc *observationContext) Observe(ctx context.Context, streamID streams.StreamID, opts telem.DSOpts) (val llocommon.StreamValue, err error) { +func (oc *observationContext) Observe(ctx context.Context, streamID streams.StreamID, opts telem.DSOpts) (val lloprotocol.StreamValue, err error) { run, trrs, err := oc.run(ctx, streamID) observationFinishedAt := time.Now() if err != nil { @@ -133,20 +133,20 @@ func (oc *observationContext) Observe(ctx context.Context, streamID streams.Stre return } -func resultToStreamValue(val any) (llocommon.StreamValue, error) { +func resultToStreamValue(val any) (lloprotocol.StreamValue, error) { switch v := val.(type) { case decimal.Decimal: - return llocommon.ToDecimal(v), nil + return lloprotocol.ToDecimal(v), nil case float64: - return llocommon.ToDecimal(decimal.NewFromFloat(v)), nil + return lloprotocol.ToDecimal(decimal.NewFromFloat(v)), nil case int64: - return llocommon.ToDecimal(decimal.NewFromInt(v)), nil + return lloprotocol.ToDecimal(decimal.NewFromInt(v)), nil case pipeline.ObjectParam: switch v.Type { case pipeline.DecimalType: - return llocommon.ToDecimal(decimal.Decimal(v.DecimalValue)), nil + return lloprotocol.ToDecimal(decimal.Decimal(v.DecimalValue)), nil default: - return nil, fmt.Errorf("don't know how to convert pipeline.ObjectParam with type %d to llocommon.StreamValue", v.Type) + return nil, fmt.Errorf("don't know how to convert pipeline.ObjectParam with type %d to lloprotocol.StreamValue", v.Type) } case map[string]any: sv, err := resultMapToStreamValue(v) @@ -155,13 +155,13 @@ func resultToStreamValue(val any) (llocommon.StreamValue, error) { } return sv, nil default: - return nil, fmt.Errorf("don't know how to convert pipeline output result of type %T to llocommon.StreamValue (got: %v)", val, val) + return nil, fmt.Errorf("don't know how to convert pipeline output result of type %T to lloprotocol.StreamValue (got: %v)", val, val) } } // Converts arbitrary JSON (parsed to map) to a StreamValue -func resultMapToStreamValue(m map[string]any) (llocommon.StreamValue, error) { - var streamValueType llocommon.LLOStreamValue_Type +func resultMapToStreamValue(m map[string]any) (lloprotocol.StreamValue, error) { + var streamValueType lloprotocol.LLOStreamValue_Type { raw, exists := m["streamValueType"] if !exists { @@ -171,13 +171,13 @@ func resultMapToStreamValue(m map[string]any) (llocommon.StreamValue, error) { if !ok { return nil, fmt.Errorf("expected 'streamValueType' to be a int64, got: %T", raw) } - if rawInt64 < 0 || rawInt64 > math.MaxUint32 || rawInt64 >= int64(llocommon.LLOStreamValue_Type(len(llocommon.LLOStreamValue_Type_name))) { //nolint:gosec // G115 // won't overflow + if rawInt64 < 0 || rawInt64 > math.MaxUint32 || rawInt64 >= int64(lloprotocol.LLOStreamValue_Type(len(lloprotocol.LLOStreamValue_Type_name))) { //nolint:gosec // G115 // won't overflow return nil, fmt.Errorf("invalid streamValueType: %v", rawInt64) } - streamValueType = llocommon.LLOStreamValue_Type(rawInt64) //nolint:gosec // G115 // won't overflow due to check above + streamValueType = lloprotocol.LLOStreamValue_Type(rawInt64) //nolint:gosec // G115 // won't overflow due to check above } switch streamValueType { - case llocommon.LLOStreamValue_TimestampedStreamValue: + case lloprotocol.LLOStreamValue_TimestampedStreamValue: r, err := resultMapToTimestampedStreamValue(m) if err != nil { return nil, fmt.Errorf("failed to parse TimestampedStreamValue: %w", err) @@ -197,7 +197,7 @@ func resultMapToStreamValue(m map[string]any) (llocommon.StreamValue, error) { // }, // "result": "123.456" // } -func resultMapToTimestampedStreamValue(m map[string]any) (*llocommon.TimestampedStreamValue, error) { +func resultMapToTimestampedStreamValue(m map[string]any) (*lloprotocol.TimestampedStreamValue, error) { ts, ok := m["timestamps"].(map[string]any) if !ok { return nil, errors.New("expected a key labeled 'timestamps' as map[string]interface{}") @@ -230,9 +230,9 @@ func resultMapToTimestampedStreamValue(m map[string]any) (*llocommon.Timestamped return nil, fmt.Errorf("observedAtMillis too large, got: %d", observedAtMillis) } - return &llocommon.TimestampedStreamValue{ + return &lloprotocol.TimestampedStreamValue{ ObservedAtNanoseconds: observedAtMillis * 1e6, // convert ms to ns - StreamValue: llocommon.ToDecimal(svd), + StreamValue: lloprotocol.ToDecimal(svd), }, nil } @@ -271,7 +271,7 @@ func toUint64(v any) (uint64, error) { } // extractFinalResultAsStreamValue extracts a final StreamValue from a TaskRunResults -func extractFinalResultAsStreamValue(trrs pipeline.TaskRunResults) (llocommon.StreamValue, error) { +func extractFinalResultAsStreamValue(trrs pipeline.TaskRunResults) (lloprotocol.StreamValue, error) { // pipeline.TaskRunResults comes ordered asc by index, this is guaranteed // by the pipeline executor finaltrrs := trrs.Terminals() @@ -290,7 +290,7 @@ func extractFinalResultAsStreamValue(trrs pipeline.TaskRunResults) (llocommon.St if err != nil { return nil, fmt.Errorf("failed to parse BenchmarkPrice: %w", err) } - return llocommon.ToDecimal(val), nil + return lloprotocol.ToDecimal(val), nil case 3: // Expect ordering of Benchmark, Bid, Ask results := make([]decimal.Decimal, 3) @@ -305,7 +305,7 @@ func extractFinalResultAsStreamValue(trrs pipeline.TaskRunResults) (llocommon.St } results[i] = val } - return &llocommon.Quote{ + return &lloprotocol.Quote{ Benchmark: results[0], Bid: results[1], Ask: results[2], diff --git a/core/services/llo/observation/observation_context_test.go b/core/services/llo/observation/observation_context_test.go index e44600644c6..3fbe84e0db1 100644 --- a/core/services/llo/observation/observation_context_test.go +++ b/core/services/llo/observation/observation_context_test.go @@ -23,7 +23,7 @@ import ( "gopkg.in/guregu/null.v4" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" clnull "github.com/smartcontractkit/chainlink-common/pkg/utils/null" @@ -118,7 +118,7 @@ func TestObservationContext_Observe(t *testing.T) { //nolint:paralleltest // sub val, err := oc.Observe(ctx, streamID2, opts) require.NoError(t, err) - assert.Equal(t, "12.34", val.(*llocommon.Decimal).String()) + assert.Equal(t, "12.34", val.(*lloprotocol.Decimal).String()) }) t.Run("returns error in case of erroring pipeline", func(t *testing.T) { //nolint:paralleltest // shares ObservationContext setup _, err := oc.Observe(ctx, streamID3, opts) @@ -127,22 +127,22 @@ func TestObservationContext_Observe(t *testing.T) { //nolint:paralleltest // sub t.Run("returns values for multiple stream IDs within the same job based on streamID tag with a single pipeline execution", func(t *testing.T) { //nolint:paralleltest // shares ObservationContext setup val, err := oc.Observe(ctx, streamID4, opts) require.NoError(t, err) - assert.Equal(t, "12.34", val.(*llocommon.Decimal).String()) + assert.Equal(t, "12.34", val.(*lloprotocol.Decimal).String()) val, err = oc.Observe(ctx, streamID5, opts) require.NoError(t, err) - assert.Equal(t, "56.78", val.(*llocommon.Decimal).String()) + assert.Equal(t, "56.78", val.(*lloprotocol.Decimal).String()) val, err = oc.Observe(ctx, streamID6, opts) require.NoError(t, err) - assert.Equal(t, "90.12", val.(*llocommon.Decimal).String()) + assert.Equal(t, "90.12", val.(*lloprotocol.Decimal).String()) assert.Equal(t, int32(1), multiPipelineDecimal.runCount.Load()) // returns cached values on subsequent calls val, err = oc.Observe(ctx, streamID6, opts) require.NoError(t, err) - assert.Equal(t, "90.12", val.(*llocommon.Decimal).String()) + assert.Equal(t, "90.12", val.(*lloprotocol.Decimal).String()) assert.Equal(t, int32(1), multiPipelineDecimal.runCount.Load()) }) @@ -150,25 +150,25 @@ func TestObservationContext_Observe(t *testing.T) { //nolint:paralleltest // sub val, err := oc.Observe(ctx, streamID7, opts) require.NoError(t, err) - assert.Equal(t, "1.23", val.(*llocommon.Decimal).String()) + assert.Equal(t, "1.23", val.(*lloprotocol.Decimal).String()) }) t.Run("returns value from int64 value", func(t *testing.T) { //nolint:paralleltest // shares ObservationContext setup val, err := oc.Observe(ctx, streamID8, opts) require.NoError(t, err) - assert.Equal(t, "5", val.(*llocommon.Decimal).String()) + assert.Equal(t, "5", val.(*lloprotocol.Decimal).String()) }) t.Run("partial extraction failure in multi-stream pipeline", func(t *testing.T) { //nolint:paralleltest // shares ObservationContext setup val, err := oc.Observe(ctx, streamID9, opts) require.NoError(t, err) - assert.Equal(t, "100.5", val.(*llocommon.Decimal).String()) + assert.Equal(t, "100.5", val.(*lloprotocol.Decimal).String()) _, err = oc.Observe(ctx, streamID10, opts) require.Error(t, err, "unparseable value should fail extraction") val, err = oc.Observe(ctx, streamID11, opts) require.NoError(t, err) - assert.Equal(t, "200.5", val.(*llocommon.Decimal).String()) + assert.Equal(t, "200.5", val.(*lloprotocol.Decimal).String()) assert.Equal(t, int32(1), multiPipelinePartialFail.runCount.Load()) }) @@ -311,21 +311,21 @@ result3 -> result3_parse -> multiply3; val, err := oc.Observe(ctx, streams.StreamID(1), opts) require.NoError(t, err) - assert.Equal(t, "900.0022", val.(*llocommon.Decimal).String()) + assert.Equal(t, "900.0022", val.(*lloprotocol.Decimal).String()) val, err = oc.Observe(ctx, streams.StreamID(2), opts) require.NoError(t, err) - assert.Equal(t, "123.456", val.(*llocommon.Decimal).String()) + assert.Equal(t, "123.456", val.(*lloprotocol.Decimal).String()) val, err = oc.Observe(ctx, streams.StreamID(3), opts) require.NoError(t, err) - assert.Equal(t, "124.456", val.(*llocommon.Decimal).String()) + assert.Equal(t, "124.456", val.(*lloprotocol.Decimal).String()) val, err = oc.Observe(ctx, jobStreamID, opts) require.NoError(t, err) - assert.Equal(t, &llocommon.Quote{ + assert.Equal(t, &lloprotocol.Quote{ Bid: decimal.NewFromFloat32(123.456), Benchmark: decimal.NewFromFloat32(900.0022), Ask: decimal.NewFromFloat32(124.456), - }, val.(*llocommon.Quote)) + }, val.(*lloprotocol.Quote)) }) } @@ -363,7 +363,7 @@ func TestObservationContext_Observe_concurrentAtomicOutput(t *testing.T) { type result struct { strmID uint32 - val llocommon.StreamValue + val lloprotocol.StreamValue err error } @@ -389,9 +389,9 @@ func TestObservationContext_Observe_concurrentAtomicOutput(t *testing.T) { require.NoError(t, r.err, "pipeline %d, stream %d", i, r.strmID) require.NotNil(t, r.val, "pipeline %d, stream %d: nil value", i, r.strmID) } - assert.Equal(t, strconv.Itoa(i*10+1), group[0].val.(*llocommon.Decimal).String(), "pipeline %d sid1", i) - assert.Equal(t, strconv.Itoa(i*10+2), group[1].val.(*llocommon.Decimal).String(), "pipeline %d sid2", i) - assert.Equal(t, strconv.Itoa(i*10+3), group[2].val.(*llocommon.Decimal).String(), "pipeline %d sid3", i) + assert.Equal(t, strconv.Itoa(i*10+1), group[0].val.(*lloprotocol.Decimal).String(), "pipeline %d sid1", i) + assert.Equal(t, strconv.Itoa(i*10+2), group[1].val.(*lloprotocol.Decimal).String(), "pipeline %d sid2", i) + assert.Equal(t, strconv.Itoa(i*10+3), group[2].val.(*lloprotocol.Decimal).String(), "pipeline %d sid3", i) assert.Equal(t, int32(1), pipelines[i].runCount.Load(), "pipeline %d should have run exactly once", i) } } diff --git a/core/services/llo/observation/types.go b/core/services/llo/observation/types.go index 656aad3eb3f..58b42a8d9ea 100644 --- a/core/services/llo/observation/types.go +++ b/core/services/llo/observation/types.go @@ -3,7 +3,7 @@ package observation import ( "context" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink/v2/core/services/llo/telem" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/streams" @@ -14,7 +14,7 @@ type Registry interface { } type Telemeter interface { - EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts telem.DSOpts, val llocommon.StreamValue, err error) + EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts telem.DSOpts, val lloprotocol.StreamValue, err error) MakeObservationScopedTelemetryCh(opts telem.DSOpts, size int) (ch chan<- any) CaptureEATelemetry() bool CaptureObservationTelemetry() bool diff --git a/core/services/llo/report_codecs.go b/core/services/llo/report_codecs.go index adc9cd82ad4..03d694d4443 100644 --- a/core/services/llo/report_codecs.go +++ b/core/services/llo/report_codecs.go @@ -3,20 +3,21 @@ package llo import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodecs/evm" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" + lloreportcodec "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodec" + "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodec/evm" ) // NOTE: All supported codecs must be specified here -func NewReportCodecs(lggr logger.Logger, donID uint32) map[llotypes.ReportFormat]llocommon.ReportCodec { - codecs := make(map[llotypes.ReportFormat]llocommon.ReportCodec) +func NewReportCodecs(lggr logger.Logger, donID uint32) map[llotypes.ReportFormat]lloprotocol.ReportCodec { + codecs := make(map[llotypes.ReportFormat]lloprotocol.ReportCodec) - codecs[llotypes.ReportFormatJSON] = llocommon.JSONReportCodec{} + codecs[llotypes.ReportFormatJSON] = lloreportcodec.JSONReportCodec{} codecs[llotypes.ReportFormatEVMPremiumLegacy] = evm.NewReportCodecPremiumLegacy(lggr, donID) codecs[llotypes.ReportFormatEVMABIEncodeUnpacked] = evm.NewReportCodecEVMABIEncodeUnpacked(lggr, donID) codecs[llotypes.ReportFormatEVMABIEncodeUnpackedExpr] = evm.NewReportCodecEVMABIEncodeUnpackedExpr(lggr, donID) codecs[llotypes.ReportFormatEVMStreamlined] = evm.NewReportCodecStreamlined(lggr) - codecs[llotypes.ReportFormatHistoryBackfill] = llocommon.ReportCodecHistoryBackfill{} + codecs[llotypes.ReportFormatHistoryBackfill] = lloprotocol.ReportCodecHistoryBackfill{} return codecs } diff --git a/core/services/llo/telem/sampling.go b/core/services/llo/telem/sampling.go index ce2604248df..51b5e2465e2 100644 --- a/core/services/llo/telem/sampling.go +++ b/core/services/llo/telem/sampling.go @@ -12,7 +12,7 @@ import ( "google.golang.org/protobuf/proto" "github.com/smartcontractkit/chainlink-common/pkg/logger" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" ) @@ -134,7 +134,7 @@ func fingerprint(typ synchronization.TelemetryType, msg proto.Message) (string, } return strings.Join(traits, samplerDelimiter), nanosToSec(m.ObservationTimestamp), nil case synchronization.LLOOutcome: - m, ok := msg.(*llocommon.LLOOutcomeTelemetry) + m, ok := msg.(*lloprotocol.LLOOutcomeTelemetry) if !ok || m == nil { return "", 0, errors.New("invalid telemetry type, expected LLOOutcomeTelemetry") } @@ -144,7 +144,7 @@ func fingerprint(typ synchronization.TelemetryType, msg proto.Message) (string, } return strings.Join(traits, samplerDelimiter), nanosToSec(int64(m.ObservationTimestampNanoseconds)), nil //nolint:gosec // G115 case synchronization.LLOReport: - m, ok := msg.(*llocommon.LLOReportTelemetry) + m, ok := msg.(*lloprotocol.LLOReportTelemetry) if !ok || m == nil { return "", 0, errors.New("invalid telemetry type, expected LLOReportTelemetry") } diff --git a/core/services/llo/telem/sampling_test.go b/core/services/llo/telem/sampling_test.go index e5fe1812311..ca123a59fde 100644 --- a/core/services/llo/telem/sampling_test.go +++ b/core/services/llo/telem/sampling_test.go @@ -13,7 +13,7 @@ import ( "google.golang.org/protobuf/proto" "github.com/smartcontractkit/chainlink-common/pkg/logger" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" ) @@ -51,7 +51,7 @@ func TestFingerprint(t *testing.T) { }, { name: "successful outcome", - msg: &llocommon.LLOOutcomeTelemetry{ + msg: &lloprotocol.LLOOutcomeTelemetry{ DonId: donID, ConfigDigest: configDigest, ObservationTimestampNanoseconds: uint64(ot.UnixNano()), @@ -63,7 +63,7 @@ func TestFingerprint(t *testing.T) { }, { name: "successful report", - msg: &llocommon.LLOReportTelemetry{ + msg: &lloprotocol.LLOReportTelemetry{ DonId: donID, ChannelId: channelID, ConfigDigest: configDigest, @@ -121,12 +121,12 @@ func TestSample(t *testing.T) { samplr.StartPruningLoop(ctx, &sync.WaitGroup{}) t0 := time.Unix(1600000000, 0) - msg0 := &llocommon.LLOOutcomeTelemetry{ + msg0 := &lloprotocol.LLOOutcomeTelemetry{ DonId: 2, ConfigDigest: []byte("digest"), ObservationTimestampNanoseconds: uint64(t0.UnixNano()), } - msg1 := &llocommon.LLOOutcomeTelemetry{ + msg1 := &lloprotocol.LLOOutcomeTelemetry{ DonId: 2, ConfigDigest: []byte("digest"), ObservationTimestampNanoseconds: uint64(t0.Add(50 * time.Millisecond).UnixNano()), @@ -153,7 +153,7 @@ func TestPruningLoop(t *testing.T) { samplr.prunePeriod = time.Second samplr.StartPruningLoop(ctx, &sync.WaitGroup{}) - msg := &llocommon.LLOOutcomeTelemetry{ + msg := &lloprotocol.LLOOutcomeTelemetry{ DonId: 2, ConfigDigest: []byte("digest"), ObservationTimestampNanoseconds: uint64(time.Now().UnixNano()), @@ -168,7 +168,7 @@ func TestPruningLoop(t *testing.T) { return flag != nil } - msg2 := &llocommon.LLOOutcomeTelemetry{ + msg2 := &lloprotocol.LLOOutcomeTelemetry{ DonId: 2, ConfigDigest: []byte("digest"), ObservationTimestampNanoseconds: uint64(time.Now().Add(10 * time.Second).UnixNano()), diff --git a/core/services/llo/telem/telemetry.go b/core/services/llo/telem/telemetry.go index db50172f1bd..e728639eee5 100644 --- a/core/services/llo/telem/telemetry.go +++ b/core/services/llo/telem/telemetry.go @@ -13,8 +13,9 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodecs/evm" + llodatasource "github.com/smartcontractkit/chainlink-data-streams/llo/datasource" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" + "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodec/evm" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -27,15 +28,15 @@ import ( const adapterLWBAErrorName = "AdapterLWBAError" // DSOpts is the shared, version-agnostic LLO data-source options (llo/v30 and -// llo/v31 both use llocommon.DSOpts). Aliased here so the telemetry and +// llo/v31 both use llodatasource.DSOpts). Aliased here so the telemetry and // observation paths keep referring to telem.DSOpts. -type DSOpts = llocommon.DSOpts +type DSOpts = llodatasource.DSOpts type Telemeter interface { - EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val llocommon.StreamValue, err error) + EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val lloprotocol.StreamValue, err error) MakeObservationScopedTelemetryCh(opts DSOpts, size int) (ch chan<- any) - GetOutcomeTelemetryCh() chan<- *llocommon.LLOOutcomeTelemetry - GetReportTelemetryCh() chan<- *llocommon.LLOReportTelemetry + GetOutcomeTelemetryCh() chan<- *lloprotocol.LLOOutcomeTelemetry + GetReportTelemetryCh() chan<- *lloprotocol.LLOReportTelemetry CaptureEATelemetry() bool CaptureObservationTelemetry() bool TrackSeqNr(digest types.ConfigDigest, seqNr uint64) @@ -86,10 +87,10 @@ func newTelemeter(params TelemeterParams) *telemeter { sampler: newSampler(logger.Sugared(params.Logger), params.SampleTelemetry), } if params.CaptureOutcomeTelemetry { - t.chOutcomeTelemetry = make(chan *llocommon.LLOOutcomeTelemetry, 100) // only one per round so 100 buffer should be more than enough even for very fast rounds + t.chOutcomeTelemetry = make(chan *lloprotocol.LLOOutcomeTelemetry, 100) // only one per round so 100 buffer should be more than enough even for very fast rounds } if params.CaptureReportTelemetry { - t.chReportTelemetry = make(chan *llocommon.LLOReportTelemetry, (2+2)*llocommon.MaxReportCount) // 2 instances+2x size safety buffer + t.chReportTelemetry = make(chan *lloprotocol.LLOReportTelemetry, (2+2)*lloprotocol.MaxReportCount) // 2 instances+2x size safety buffer } t.Service, t.eng = services.Config{ Name: "LLOTelemeterService", @@ -132,8 +133,8 @@ type telemeter struct { captureEATelemetry bool captureObservationTelemetry bool chch chan telemetryCollectionContext - chOutcomeTelemetry chan *llocommon.LLOOutcomeTelemetry - chReportTelemetry chan *llocommon.LLOReportTelemetry + chOutcomeTelemetry chan *lloprotocol.LLOOutcomeTelemetry + chReportTelemetry chan *lloprotocol.LLOReportTelemetry currentSeqNrMu sync.Mutex currentSeqNr map[string]uint64 @@ -150,7 +151,7 @@ type telemeter struct { sampler *sampler } -func (t *telemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val llocommon.StreamValue, err error) { +func (t *telemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val lloprotocol.StreamValue, err error) { if t.Ready() != nil { // This should never happen, telemeter should always be started BEFORE // the oracle and closed AFTER it @@ -208,11 +209,11 @@ func (t *telemeter) MakeObservationScopedTelemetryCh(opts DSOpts, size int) chan return ch } -func (t *telemeter) GetOutcomeTelemetryCh() chan<- *llocommon.LLOOutcomeTelemetry { +func (t *telemeter) GetOutcomeTelemetryCh() chan<- *lloprotocol.LLOOutcomeTelemetry { return t.chOutcomeTelemetry } -func (t *telemeter) GetReportTelemetryCh() chan<- *llocommon.LLOReportTelemetry { +func (t *telemeter) GetReportTelemetryCh() chan<- *lloprotocol.LLOReportTelemetry { return t.chReportTelemetry } @@ -414,10 +415,10 @@ func (t *telemeter) prepareV3PremiumLegacyTelemetry(d *TelemetryPipeline) { var benchmarkPrice, bidPrice, askPrice int64 var bp, bid, ask string switch v := d.val.(type) { - case *llocommon.Decimal: + case *lloprotocol.Decimal: benchmarkPrice = v.Decimal().IntPart() bp = v.Decimal().String() - case *llocommon.Quote: + case *lloprotocol.Quote: benchmarkPrice = v.Benchmark.IntPart() bp = v.Benchmark.String() bidPrice = v.Bid.IntPart() @@ -483,7 +484,7 @@ type TelemetryPipeline struct { trrs pipeline.TaskRunResults streamID uint32 opts DSOpts - val llocommon.StreamValue + val lloprotocol.StreamValue dpInvariantViolationDetected bool } @@ -491,15 +492,15 @@ var NullTelemeter TelemeterService = &nullTelemeter{} type nullTelemeter struct{} -func (t *nullTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val llocommon.StreamValue, err error) { +func (t *nullTelemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts DSOpts, val lloprotocol.StreamValue, err error) { } func (t *nullTelemeter) MakeObservationScopedTelemetryCh(opts DSOpts, size int) (ch chan<- any) { return nil } -func (t *nullTelemeter) GetOutcomeTelemetryCh() chan<- *llocommon.LLOOutcomeTelemetry { +func (t *nullTelemeter) GetOutcomeTelemetryCh() chan<- *lloprotocol.LLOOutcomeTelemetry { return nil } -func (t *nullTelemeter) GetReportTelemetryCh() chan<- *llocommon.LLOReportTelemetry { +func (t *nullTelemeter) GetReportTelemetryCh() chan<- *lloprotocol.LLOReportTelemetry { return nil } func (t *nullTelemeter) CaptureEATelemetry() bool { diff --git a/core/services/llo/telem/telemetry_test.go b/core/services/llo/telem/telemetry_test.go index c6036c3d85f..b3d772c2145 100644 --- a/core/services/llo/telem/telemetry_test.go +++ b/core/services/llo/telem/telemetry_test.go @@ -16,7 +16,7 @@ import ( ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -56,7 +56,7 @@ func (m *mockOpts) ObservationTimestamp() time.Time { return time.Unix(1737936858, 0) } func (m *mockOpts) LifeCycleStage() llotypes.LifeCycleStage { - return llocommon.LifeCycleStageProduction + return lloprotocol.LifeCycleStageProduction } const bridgeResponse = `{ @@ -183,7 +183,7 @@ func Test_Telemeter_v3PremiumLegacy(t *testing.T) { MonitoringEndpoint: m, DonID: donID, }) - val := llocommon.ToDecimal(decimal.NewFromFloat32(102.12)) + val := lloprotocol.ToDecimal(decimal.NewFromFloat32(102.12)) servicetest.Run(t, tm) tm.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, val, nil) tm.TrackSeqNr(opts.ConfigDigest(), opts.SeqNr()) @@ -239,7 +239,7 @@ func Test_Telemeter_v3PremiumLegacy(t *testing.T) { MonitoringEndpoint: m, DonID: donID, }) - val := &llocommon.Quote{Bid: decimal.NewFromFloat32(102.12), Benchmark: decimal.NewFromFloat32(103.32), Ask: decimal.NewFromFloat32(104.25)} + val := &lloprotocol.Quote{Bid: decimal.NewFromFloat32(102.12), Benchmark: decimal.NewFromFloat32(103.32), Ask: decimal.NewFromFloat32(104.25)} servicetest.Run(t, tm) tm.EnqueueV3PremiumLegacy(run, trrs, streamID, opts, val, nil) time.Sleep(10 * time.Millisecond) @@ -417,7 +417,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { assert.Nil(t, ch) }) - t.Run("transmits *llocommon.LLOOutcomeTelemetry", func(t *testing.T) { + t.Run("transmits *lloprotocol.LLOOutcomeTelemetry", func(t *testing.T) { t.Parallel() m := &mockMonitoringEndpoint{chTypedLogs: make(chan typedLog, 100)} tm := newTelemeter(TelemeterParams{ @@ -433,7 +433,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { t.Run("zero values", func(t *testing.T) { opts := &mockOpts{} cd := opts.ConfigDigest() - orig := &llocommon.LLOOutcomeTelemetry{SeqNr: opts.SeqNr(), ConfigDigest: cd[:]} + orig := &lloprotocol.LLOOutcomeTelemetry{SeqNr: opts.SeqNr(), ConfigDigest: cd[:]} ch <- orig // Wait until the telemetry is buffered. @@ -447,7 +447,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { tLog := <-m.chTypedLogs assert.Equal(t, synchronization.LLOOutcome, tLog.telemType) - decoded := &llocommon.LLOOutcomeTelemetry{} + decoded := &lloprotocol.LLOOutcomeTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) assert.Empty(t, decoded.LifeCycleStage) assert.Zero(t, decoded.ObservationTimestampNanoseconds) @@ -462,13 +462,13 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { t.Run("with values", func(t *testing.T) { opts := &mockOpts{} cd := opts.ConfigDigest() - orig := &llocommon.LLOOutcomeTelemetry{ + orig := &lloprotocol.LLOOutcomeTelemetry{ LifeCycleStage: "foo", ObservationTimestampNanoseconds: 2, - ChannelDefinitions: map[uint32]*llocommon.LLOChannelDefinitionProto{ + ChannelDefinitions: map[uint32]*lloprotocol.LLOChannelDefinitionProto{ 3: { ReportFormat: 4, - Streams: []*llocommon.LLOStreamDefinition{ + Streams: []*lloprotocol.LLOStreamDefinition{ { StreamID: 5, Aggregator: 6, @@ -480,9 +480,9 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { ValidAfterNanoseconds: map[uint32]uint64{ 8: 9, }, - StreamAggregates: map[uint32]*llocommon.LLOAggregatorStreamValue{ + StreamAggregates: map[uint32]*lloprotocol.LLOAggregatorStreamValue{ 10: { - AggregatorValues: map[uint32]*llocommon.LLOStreamValue{ + AggregatorValues: map[uint32]*lloprotocol.LLOStreamValue{ 11: { Type: 12, Value: []byte{13}, @@ -507,7 +507,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { tLog := <-m.chTypedLogs assert.Equal(t, synchronization.LLOOutcome, tLog.telemType) - decoded := &llocommon.LLOOutcomeTelemetry{} + decoded := &lloprotocol.LLOOutcomeTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) assert.Equal(t, "foo", decoded.LifeCycleStage) assert.Equal(t, uint64(2), decoded.ObservationTimestampNanoseconds) @@ -521,7 +521,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { assert.Equal(t, uint64(9), decoded.ValidAfterNanoseconds[8]) assert.Len(t, decoded.StreamAggregates, 1) assert.Len(t, decoded.StreamAggregates[10].AggregatorValues, 1) - assert.Equal(t, llocommon.LLOStreamValue_Type(12), decoded.StreamAggregates[10].AggregatorValues[11].Type) + assert.Equal(t, lloprotocol.LLOStreamValue_Type(12), decoded.StreamAggregates[10].AggregatorValues[11].Type) assert.Equal(t, []byte{13}, decoded.StreamAggregates[10].AggregatorValues[11].Value) assert.Equal(t, opts.SeqNr(), decoded.SeqNr) assert.Equal(t, cd[:], decoded.ConfigDigest) @@ -550,7 +550,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { cd := opts.ConfigDigest() // First outcome (from failed epoch) - epoch1Outcome := &llocommon.LLOOutcomeTelemetry{ + epoch1Outcome := &lloprotocol.LLOOutcomeTelemetry{ LifeCycleStage: "production", ObservationTimestampNanoseconds: 1000000001, SeqNr: opts.SeqNr(), @@ -566,7 +566,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { }) // Second outcome (from committed epoch) — different observation timestamp - epoch2Outcome := &llocommon.LLOOutcomeTelemetry{ + epoch2Outcome := &lloprotocol.LLOOutcomeTelemetry{ LifeCycleStage: "production", ObservationTimestampNanoseconds: 2000000002, SeqNr: opts.SeqNr(), @@ -583,7 +583,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { return false } // Wait until the buffer contains the second outcome - msg := buf[0].msg.(*llocommon.LLOOutcomeTelemetry) + msg := buf[0].msg.(*lloprotocol.LLOOutcomeTelemetry) return msg.ObservationTimestampNanoseconds == 2000000002 }) @@ -598,7 +598,7 @@ func Test_Telemeter_outcomeTelemetry(t *testing.T) { tLog := <-m.chTypedLogs assert.Equal(t, synchronization.LLOOutcome, tLog.telemType) - decoded := &llocommon.LLOOutcomeTelemetry{} + decoded := &lloprotocol.LLOOutcomeTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) // The flushed outcome should be from the second (committed) epoch @@ -632,7 +632,7 @@ func Test_Telemeter_reportTelemetry(t *testing.T) { assert.Nil(t, ch) }) - t.Run("transmits *llocommon.LLOReportTelemetry", func(t *testing.T) { + t.Run("transmits *lloprotocol.LLOReportTelemetry", func(t *testing.T) { t.Parallel() m := &mockMonitoringEndpoint{chTypedLogs: make(chan typedLog, 100)} tm := newTelemeter(TelemeterParams{ @@ -648,7 +648,7 @@ func Test_Telemeter_reportTelemetry(t *testing.T) { t.Run("zero values", func(t *testing.T) { opts := &mockOpts{} cd := opts.ConfigDigest() - orig := &llocommon.LLOReportTelemetry{SeqNr: opts.SeqNr(), ConfigDigest: cd[:]} + orig := &lloprotocol.LLOReportTelemetry{SeqNr: opts.SeqNr(), ConfigDigest: cd[:]} ch <- orig // Wait until the telemetry is buffered. @@ -662,7 +662,7 @@ func Test_Telemeter_reportTelemetry(t *testing.T) { tLog := <-m.chTypedLogs assert.Equal(t, synchronization.LLOReport, tLog.telemType) - decoded := &llocommon.LLOReportTelemetry{} + decoded := &lloprotocol.LLOReportTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) assert.Zero(t, decoded.ChannelId) assert.Zero(t, decoded.ValidAfterNanoseconds) @@ -679,19 +679,19 @@ func Test_Telemeter_reportTelemetry(t *testing.T) { t.Run("with values", func(t *testing.T) { opts := &mockOpts{} cd := opts.ConfigDigest() - orig := &llocommon.LLOReportTelemetry{ + orig := &lloprotocol.LLOReportTelemetry{ ChannelId: 1, ValidAfterNanoseconds: 2, ObservationTimestampNanoseconds: 3, ReportFormat: 4, Specimen: true, - StreamDefinitions: []*llocommon.LLOStreamDefinition{ + StreamDefinitions: []*lloprotocol.LLOStreamDefinition{ { StreamID: 5, Aggregator: 6, }, }, - StreamValues: []*llocommon.LLOStreamValue{ + StreamValues: []*lloprotocol.LLOStreamValue{ { Type: 7, Value: []byte{8}, @@ -714,7 +714,7 @@ func Test_Telemeter_reportTelemetry(t *testing.T) { tLog := <-m.chTypedLogs assert.Equal(t, synchronization.LLOReport, tLog.telemType) - decoded := &llocommon.LLOReportTelemetry{} + decoded := &lloprotocol.LLOReportTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) assert.Equal(t, uint32(1), decoded.ChannelId) assert.Equal(t, uint64(2), decoded.ValidAfterNanoseconds) @@ -725,7 +725,7 @@ func Test_Telemeter_reportTelemetry(t *testing.T) { assert.Equal(t, uint32(5), decoded.StreamDefinitions[0].StreamID) assert.Equal(t, uint32(6), decoded.StreamDefinitions[0].Aggregator) assert.Len(t, decoded.StreamValues, 1) - assert.Equal(t, llocommon.LLOStreamValue_Type(7), decoded.StreamValues[0].Type) + assert.Equal(t, lloprotocol.LLOStreamValue_Type(7), decoded.StreamValues[0].Type) assert.Equal(t, []byte{8}, decoded.StreamValues[0].Value) assert.Equal(t, []byte{9}, decoded.ChannelOpts) assert.Equal(t, opts.SeqNr(), decoded.SeqNr) @@ -754,7 +754,7 @@ func Test_Telemeter_reportTelemetry(t *testing.T) { // Send 3 reports for different channels, all with the same seqNr for i := uint32(1); i <= 3; i++ { - ch <- &llocommon.LLOReportTelemetry{ + ch <- &lloprotocol.LLOReportTelemetry{ ChannelId: i, SeqNr: opts.SeqNr(), ConfigDigest: cd[:], @@ -781,7 +781,7 @@ func Test_Telemeter_reportTelemetry(t *testing.T) { for range 3 { tLog := <-m.chTypedLogs assert.Equal(t, synchronization.LLOReport, tLog.telemType) - decoded := &llocommon.LLOReportTelemetry{} + decoded := &lloprotocol.LLOReportTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) receivedChannels = append(receivedChannels, decoded.ChannelId) } @@ -854,7 +854,7 @@ func Test_Telemeter_outcomeTelemetry_samplingAtFlushTime(t *testing.T) { // Spread observation timestamps within the same wall-clock // second (different nanos, same second bucket). obsTs := uint64(secStart + int64(i)*int64(10*time.Millisecond)) - ch <- &llocommon.LLOOutcomeTelemetry{ + ch <- &lloprotocol.LLOOutcomeTelemetry{ LifeCycleStage: "production", ObservationTimestampNanoseconds: obsTs, SeqNr: seqNr, @@ -884,7 +884,7 @@ func Test_Telemeter_outcomeTelemetry_samplingAtFlushTime(t *testing.T) { select { case tLog := <-m.chTypedLogs: assert.Equal(t, synchronization.LLOOutcome, tLog.telemType) - decoded := &llocommon.LLOOutcomeTelemetry{} + decoded := &lloprotocol.LLOOutcomeTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) received = append(received, decoded.ObservationTimestampNanoseconds) case <-time.After(testutils.WaitTimeout(t)): @@ -897,7 +897,7 @@ func Test_Telemeter_outcomeTelemetry_samplingAtFlushTime(t *testing.T) { // remaining survivors that fell in already-seen second buckets. select { case extra := <-m.chTypedLogs: - decoded := &llocommon.LLOOutcomeTelemetry{} + decoded := &lloprotocol.LLOOutcomeTelemetry{} require.NoError(t, proto.Unmarshal(extra.log, decoded)) t.Fatalf("expected no more outcome messages, got one with ts=%d", decoded.ObservationTimestampNanoseconds) case <-time.After(100 * time.Millisecond): @@ -925,7 +925,7 @@ func Test_Telemeter_outcomeTelemetry_samplingAtFlushTime(t *testing.T) { for i := range outcomesPerSecond { seqNr := baseSeqNr + uint64(s*outcomesPerSecond+i) obsTs := uint64(secStart + int64(i)*int64(10*time.Millisecond)) - ch <- &llocommon.LLOOutcomeTelemetry{ + ch <- &lloprotocol.LLOOutcomeTelemetry{ LifeCycleStage: "production", ObservationTimestampNanoseconds: obsTs, SeqNr: seqNr, @@ -1014,7 +1014,7 @@ func Test_Telemeter_reportTelemetry_samplingAtFlushTime(t *testing.T) { // Each seqNr emits a report per channel — mimics the // Reports() call shape in LLO (one report per channel). for _, channelID := range channels { - ch <- &llocommon.LLOReportTelemetry{ + ch <- &lloprotocol.LLOReportTelemetry{ ChannelId: channelID, ObservationTimestampNanoseconds: obsTs, SeqNr: seqNr, @@ -1050,7 +1050,7 @@ func Test_Telemeter_reportTelemetry_samplingAtFlushTime(t *testing.T) { select { case tLog := <-m.chTypedLogs: assert.Equal(t, synchronization.LLOReport, tLog.telemType) - decoded := &llocommon.LLOReportTelemetry{} + decoded := &lloprotocol.LLOReportTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) seen[decoded.ChannelId]++ case <-time.After(testutils.WaitTimeout(t)): @@ -1064,7 +1064,7 @@ func Test_Telemeter_reportTelemetry_samplingAtFlushTime(t *testing.T) { select { case extra := <-m.chTypedLogs: - decoded := &llocommon.LLOReportTelemetry{} + decoded := &lloprotocol.LLOReportTelemetry{} require.NoError(t, proto.Unmarshal(extra.log, decoded)) t.Fatalf("expected no more report messages, got one with channel=%d ts=%d", decoded.ChannelId, decoded.ObservationTimestampNanoseconds) @@ -1094,7 +1094,7 @@ func Test_Telemeter_reportTelemetry_samplingAtFlushTime(t *testing.T) { seqNr := baseSeqNr + uint64(s*seqNrsPerSecond+i) obsTs := uint64(secStart + int64(i)*int64(10*time.Millisecond)) for _, channelID := range channels { - ch <- &llocommon.LLOReportTelemetry{ + ch <- &lloprotocol.LLOReportTelemetry{ ChannelId: channelID, ObservationTimestampNanoseconds: obsTs, SeqNr: seqNr, @@ -1163,7 +1163,7 @@ func Test_Telemeter_reportTelemetry_samplingAtFlushTime(t *testing.T) { // Append 3 reports at the same seqNr for distinct channels. for _, channelID := range []uint32{10, 20, 30} { - ch <- &llocommon.LLOReportTelemetry{ + ch <- &lloprotocol.LLOReportTelemetry{ ChannelId: channelID, ObservationTimestampNanoseconds: obsTs, SeqNr: opts.SeqNr(), @@ -1183,7 +1183,7 @@ func Test_Telemeter_reportTelemetry_samplingAtFlushTime(t *testing.T) { for i := range 3 { select { case tLog := <-m.chTypedLogs: - decoded := &llocommon.LLOReportTelemetry{} + decoded := &lloprotocol.LLOReportTelemetry{} require.NoError(t, proto.Unmarshal(tLog.log, decoded)) received[decoded.ChannelId] = struct{}{} case <-time.After(testutils.WaitTimeout(t)): diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 5f3f7f1ff6a..dff2d5f3c41 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -33,7 +33,7 @@ import ( ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner" ocr2keepers21config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" - "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" evmconfig "github.com/smartcontractkit/chainlink-evm/pkg/config" functionsRelay "github.com/smartcontractkit/chainlink-evm/pkg/functions" @@ -59,8 +59,8 @@ import ( llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink-common/pkg/workflows/dontime" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - lloconfig "github.com/smartcontractkit/chainlink-data-streams/llo/config" + lloconfig "github.com/smartcontractkit/chainlink-data-streams/llo/pluginconfig" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" "github.com/smartcontractkit/chainlink-data-streams/llo/retirement" llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" "github.com/smartcontractkit/chainlink-evm/pkg/chains/legacyevm" @@ -170,7 +170,7 @@ type DelegateConfig interface { OCR2() ocr2Config JobPipeline() jobPipelineConfig Insecure() insecureConfig - Mercury() de.Mercury + Mercury() dataengine.Mercury Threshold() coreconfig.Threshold Sharding() coreconfig.Sharding RingStoreForShard0() *ring.Store @@ -200,7 +200,7 @@ func (d *delegateConfig) Threshold() coreconfig.Threshold { return d.threshold } -func (d *delegateConfig) Mercury() de.Mercury { +func (d *delegateConfig) Mercury() dataengine.Mercury { return d.mercury } @@ -244,9 +244,9 @@ type jobPipelineConfig interface { type mercuryConfig interface { Credentials(credName string) *types.MercuryCredentials - Cache() de.MercuryCache - TLS() de.MercuryTLS - Transmitter() de.MercuryTransmitter + Cache() dataengine.MercuryCache + TLS() dataengine.MercuryTLS + Transmitter() dataengine.MercuryTransmitter VerboseLogging() bool } @@ -254,7 +254,7 @@ type thresholdConfig interface { ThresholdKeyShare() string } -func NewDelegateConfig(ocr2Cfg ocr2Config, m de.Mercury, t coreconfig.Threshold, i insecureConfig, jp jobPipelineConfig, pluginProcessCfg plugins.RegistrarConfig, s coreconfig.Sharding, ringStore *ring.Store) DelegateConfig { +func NewDelegateConfig(ocr2Cfg ocr2Config, m dataengine.Mercury, t coreconfig.Threshold, i insecureConfig, jp jobPipelineConfig, pluginProcessCfg plugins.RegistrarConfig, s coreconfig.Sharding, ringStore *ring.Store) DelegateConfig { return &delegateConfig{ ocr2: ocr2Cfg, RegistrarConfig: pluginProcessCfg, @@ -1549,7 +1549,7 @@ func (d *Delegate) newServicesLLO( ChannelDefinitionCache: provider.ChannelDefinitionCache(), RetirementReportCache: d.retirementReportCache, ShouldRetireCache: provider.ShouldRetireCache(), - RetirementReportCodec: llocommon.StandardRetirementReportCodec{}, + RetirementReportCodec: lloprotocol.StandardRetirementReportCodec{}, PluginMonitoringEndpoint: d.monitoringEndpointGen.GenMultitypeMonitoringEndpoint(rid.Network, rid.ChainID, telemetryContractID), DonID: pluginCfg.DonID, ChainID: rid.ChainID, diff --git a/core/services/ocr2/plugins/llo/bench/harness_test.go b/core/services/ocr2/plugins/llo/bench/harness_test.go index 6bbc79c1fd8..fc26051f52d 100644 --- a/core/services/ocr2/plugins/llo/bench/harness_test.go +++ b/core/services/ocr2/plugins/llo/bench/harness_test.go @@ -1,6 +1,6 @@ // Package bench contains a comparative micro-benchmark between the OCR3.0 LLO // plugin (chainlink-data-streams/llo/v30) and the OCR3.1 LLO plugin -// (chainlink-data-streams/llo/v31). +// (chainlink-data-streams/llo/dev/v31). // // The two plugins implement identical LLO application logic on top of different // OCR protocols. The performance question this benchmark answers is what that @@ -43,9 +43,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" + llodatasource "github.com/smartcontractkit/chainlink-data-streams/llo/datasource" + llov31 "github.com/smartcontractkit/chainlink-data-streams/llo/dev/v31" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" llov30 "github.com/smartcontractkit/chainlink-data-streams/llo/v30" - llov31 "github.com/smartcontractkit/chainlink-data-streams/llo/v31" "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3_1types" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" @@ -134,13 +135,13 @@ func (m *mockChannelDefinitionCache) Name() string { return "b // staticDataSource fills every requested stream with a fixed decimal value. // This keeps the DataSource out of the measured critical path (no I/O, no // allocation-heavy pipeline) so the benchmark isolates plugin cost. -type staticDataSource struct{ value *llocommon.Decimal } +type staticDataSource struct{ value *lloprotocol.Decimal } func newStaticDataSource() *staticDataSource { - return &staticDataSource{value: llocommon.ToDecimal(decimal.NewFromInt(123456))} + return &staticDataSource{value: lloprotocol.ToDecimal(decimal.NewFromInt(123456))} } -func (d *staticDataSource) Observe(_ context.Context, sv llocommon.StreamValues, _ llocommon.DSOpts) error { +func (d *staticDataSource) Observe(_ context.Context, sv lloprotocol.StreamValues, _ llodatasource.DSOpts) error { for k := range sv { sv[k] = d.value } @@ -153,16 +154,16 @@ func (mockShouldRetireCache) ShouldRetire(ocrtypes.ConfigDigest) (bool, error) { type mockOnchainConfigCodec struct{} -func (mockOnchainConfigCodec) Decode([]byte) (llocommon.OnchainConfig, error) { - return llocommon.OnchainConfig{}, nil +func (mockOnchainConfigCodec) Decode([]byte) (lloprotocol.OnchainConfig, error) { + return lloprotocol.OnchainConfig{}, nil } -func (mockOnchainConfigCodec) Encode(llocommon.OnchainConfig) ([]byte, error) { return nil, nil } +func (mockOnchainConfigCodec) Encode(lloprotocol.OnchainConfig) ([]byte, error) { return nil, nil } // --------------------------------------------------------------------------- // Plugin construction // --------------------------------------------------------------------------- -func reportCodecs() map[llotypes.ReportFormat]llocommon.ReportCodec { +func reportCodecs() map[llotypes.ReportFormat]lloprotocol.ReportCodec { // The same production codec set both plugins use (delegate.go). Only the // JSON codec is exercised by this workload. return corello.NewReportCodecs(logger.Nop(), 0) @@ -175,7 +176,7 @@ func reportCodecs() map[llotypes.ReportFormat]llocommon.ReportCodec { // second and diverge from v31). The 1ns interval effectively reports every // round while keeping both plugins on identical reportability rules. func benchOffchainConfig() []byte { - b, err := llocommon.OffchainConfig{ + b, err := lloprotocol.OffchainConfig{ ProtocolVersion: 1, DefaultMinReportIntervalNanoseconds: 1, }.Encode() @@ -200,7 +201,7 @@ func buildV30(tb testing.TB, defs llotypes.ChannelDefinitions, n, f int) ocr3typ factory := llov30.NewPluginFactory(llov30.PluginFactoryParams{ Config: llov30.Config{VerboseLogging: false}, ShouldRetireCache: mockShouldRetireCache{}, - RetirementReportCodec: llocommon.StandardRetirementReportCodec{}, + RetirementReportCodec: lloprotocol.StandardRetirementReportCodec{}, ChannelDefinitionCache: &mockChannelDefinitionCache{defs: defs}, DataSource: newStaticDataSource(), Logger: logger.Nop(), @@ -217,7 +218,7 @@ func buildV31(tb testing.TB, defs llotypes.ChannelDefinitions, n, f int) (ocr3_1 factory := llov31.NewPluginFactory(llov31.PluginFactoryParams{ Config: llov31.Config{VerboseLogging: false}, ShouldRetireCache: mockShouldRetireCache{}, - RetirementReportCodec: llocommon.StandardRetirementReportCodec{}, + RetirementReportCodec: lloprotocol.StandardRetirementReportCodec{}, ChannelDefinitionCache: &mockChannelDefinitionCache{defs: defs}, DataSource: newStaticDataSource(), Logger: logger.Nop(), diff --git a/core/services/ocr2/plugins/llo/history_backfill_integration_test.go b/core/services/ocr2/plugins/llo/history_backfill_integration_test.go index 96e6828d256..dd7679396e4 100644 --- a/core/services/ocr2/plugins/llo/history_backfill_integration_test.go +++ b/core/services/ocr2/plugins/llo/history_backfill_integration_test.go @@ -21,8 +21,8 @@ import ( "github.com/smartcontractkit/chainlink-common/keystore/corekeys/csakey" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" + mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink-data-streams/mercury" reportcodecv3 "github.com/smartcontractkit/chainlink-data-streams/mercury/v3/reportcodec" mercuryverifier "github.com/smartcontractkit/chainlink-data-streams/mercury/verifier" @@ -132,7 +132,7 @@ func testIntegrationLLOHistoryBackfill(t *testing.T, ocr31 bool) { multiplier := decimal.New(1, 18) expirationWindow := uint32(3600) - offchainConfig := llocommon.OffchainConfig{ + offchainConfig := lloprotocol.OffchainConfig{ ProtocolVersion: 1, DefaultMinReportIntervalNanoseconds: uint64(1 * time.Second), EnableObservationCompression: true, diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go index 134aeb37e41..ad8aa25e9f9 100644 --- a/core/services/ocr2/plugins/llo/integration_test.go +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -40,9 +40,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" "github.com/smartcontractkit/chainlink-common/pkg/utils" - llocommon "github.com/smartcontractkit/chainlink-data-streams/llo/common" - lloevm "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodecs/evm" - mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + lloprotocol "github.com/smartcontractkit/chainlink-data-streams/llo/protocol" + lloreportcodec "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodec" + lloevm "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodec/evm" + mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink-data-streams/mercury" reportcodecv3 "github.com/smartcontractkit/chainlink-data-streams/mercury/v3/reportcodec" mercuryverifier "github.com/smartcontractkit/chainlink-data-streams/mercury/verifier" @@ -235,7 +236,7 @@ type OCRConfig struct { } func makeDefaultOCRConfig() *OCRConfig { - defaultOnchainConfig, err := (&llocommon.EVMOnchainConfigCodec{}).Encode(llocommon.OnchainConfig{ + defaultOnchainConfig, err := (&lloprotocol.EVMOnchainConfigCodec{}).Encode(lloprotocol.OnchainConfig{ Version: 1, PredecessorConfigDigest: nil, }) @@ -264,7 +265,7 @@ func makeDefaultOCRConfig() *OCRConfig { func WithPredecessorConfigDigest(predecessorConfigDigest ocr2types.ConfigDigest) OCRConfigOption { return func(cfg *OCRConfig) { - onchainConfig, err := (&llocommon.EVMOnchainConfigCodec{}).Encode(llocommon.OnchainConfig{ + onchainConfig, err := (&lloprotocol.EVMOnchainConfigCodec{}).Encode(lloprotocol.OnchainConfig{ Version: 1, PredecessorConfigDigest: &predecessorConfigDigest, }) @@ -275,7 +276,7 @@ func WithPredecessorConfigDigest(predecessorConfigDigest ocr2types.ConfigDigest) } } -func WithOffchainConfig(offchainConfig llocommon.OffchainConfig) OCRConfigOption { +func WithOffchainConfig(offchainConfig lloprotocol.OffchainConfig) OCRConfigOption { return func(cfg *OCRConfig) { offchainConfigEncoded, err := offchainConfig.Encode() if err != nil { @@ -390,7 +391,7 @@ func generateOCR31Config(cfg *OCRConfig) (signers []types.OnchainPublicKey, tran ) } -func setLegacyConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, legacyVerifier *verifier.Verifier, legacyVerifierAddr common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra, inOffchainConfig llocommon.OffchainConfig) ocr2types.ConfigDigest { +func setLegacyConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, backend evmtypes.Backend, legacyVerifier *verifier.Verifier, legacyVerifierAddr common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra, inOffchainConfig lloprotocol.OffchainConfig) ocr2types.ConfigDigest { signers, _, _, onchainConfig, offchainConfigVersion, offchainConfig := generateConfig(t, WithOracles(oracles), WithOffchainConfig(inOffchainConfig)) signerAddresses, err := evm.OnchainPublicKeyToAddress(signers) @@ -437,7 +438,7 @@ func setBlueGreenConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, ba donIDPadded := llo.DonIDToBytes32(donID) var isProduction bool { - cfg, err := (&llocommon.EVMOnchainConfigCodec{}).Decode(onchainConfig) + cfg, err := (&lloprotocol.EVMOnchainConfigCodec{}).Decode(onchainConfig) require.NoError(t, err) isProduction = cfg.PredecessorConfigDigest == nil } @@ -485,7 +486,7 @@ func promoteStagingConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, func TestIntegration_LLO_evm_premium_legacy(t *testing.T) { t.Parallel() - offchainConfigs := []llocommon.OffchainConfig{ + offchainConfigs := []lloprotocol.OffchainConfig{ { ProtocolVersion: 0, DefaultMinReportIntervalNanoseconds: 0, @@ -503,7 +504,7 @@ func TestIntegration_LLO_evm_premium_legacy(t *testing.T) { } } -func testIntegrationLLOEVMPremiumLegacy(t *testing.T, offchainConfig llocommon.OffchainConfig) { +func testIntegrationLLOEVMPremiumLegacy(t *testing.T, offchainConfig lloprotocol.OffchainConfig) { testStartTimeStamp := time.Now() multiplier := decimal.New(1, 18) expirationWindow := time.Hour / time.Second @@ -725,7 +726,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi func TestIntegration_LLO_multi_formats(t *testing.T) { t.Parallel() - offchainConfigs := []llocommon.OffchainConfig{ + offchainConfigs := []lloprotocol.OffchainConfig{ { ProtocolVersion: 0, DefaultMinReportIntervalNanoseconds: 0, @@ -752,7 +753,7 @@ func TestIntegration_LLO_multi_formats(t *testing.T) { } } -func testIntegrationLLOMultiFormats(t *testing.T, offchainConfig llocommon.OffchainConfig, ocr31 bool) { +func testIntegrationLLOMultiFormats(t *testing.T, offchainConfig lloprotocol.OffchainConfig, ocr31 bool) { testStartTimeStamp := time.Now() expirationWindow := uint32(3600) @@ -1182,9 +1183,9 @@ stonk_price_timestamped_missing_indicated_time [type=merge left="{}" right="{\\" dp -> missing_provider_indicated_time_parse -> missing_provider_indicated_time; dp -> stonk_price_parse -> stonk_price_timestamped_missing_indicated_time; `, bridgeName, marketStatusStreamID, - llocommon.LLOStreamValue_TimestampedStreamValue, timestampedStonkPriceStreamID, - llocommon.LLOStreamValue_TimestampedStreamValue, nullTimestampPriceStreamID, - llocommon.LLOStreamValue_TimestampedStreamValue, missingTimestampPriceStreamID, + lloprotocol.LLOStreamValue_TimestampedStreamValue, timestampedStonkPriceStreamID, + lloprotocol.LLOStreamValue_TimestampedStreamValue, nullTimestampPriceStreamID, + lloprotocol.LLOStreamValue_TimestampedStreamValue, missingTimestampPriceStreamID, ) benchmarkPricePipeline := fmt.Sprintf(` @@ -1520,7 +1521,7 @@ func TestIntegration_LLO_stress_test_V1(t *testing.T) { // PROTOCOL CONFIGURATION ocrConfigOpts := []OCRConfigOption{ - WithOffchainConfig(llocommon.OffchainConfig{ + WithOffchainConfig(lloprotocol.OffchainConfig{ ProtocolVersion: 1, DefaultMinReportIntervalNanoseconds: uint64(defaultMinReportInterval), EnableObservationCompression: true, @@ -1642,7 +1643,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi // mercurytransmitter addr => count of reports cnts := map[string]int{} // mercurytransmitter addr => channel ID => reports - m := map[string]map[uint32][]llocommon.Report{} + m := map[string]map[uint32][]lloprotocol.Report{} for { pckt, err := receiveWithTimeout(t, packets, reportTimeout) @@ -1653,12 +1654,12 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi req := pckt.req assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) cm, exists := m[addr.String()] if !exists { - cm = make(map[uint32][]llocommon.Report) + cm = make(map[uint32][]lloprotocol.Report) m[addr.String()] = cm } cm[r.ChannelID] = append(cm[r.ChannelID], r) @@ -1698,7 +1699,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi assert.Equal(t, blueDigest, r.ConfigDigest) assert.False(t, r.Specimen) assert.Len(t, r.Values, 1) - assert.Equal(t, "2976.39", r.Values[0].(*llocommon.Decimal).String()) + assert.Equal(t, "2976.39", r.Values[0].(*lloprotocol.Decimal).String()) if i > 0 { if rs[i-1].SeqNr+1 != r.SeqNr { @@ -1756,7 +1757,7 @@ func TestIntegration_LLO_transmit_errors(t *testing.T) { // PROTOCOL CONFIGURATION // TODO: test both - offchainConfig := llocommon.OffchainConfig{ + offchainConfig := lloprotocol.OffchainConfig{ ProtocolVersion: 1, DefaultMinReportIntervalNanoseconds: uint64(50 * time.Millisecond), } @@ -1863,13 +1864,13 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, serverPubKey req := pckt.req assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) assert.Equal(t, blueDigest, r.ConfigDigest) assert.False(t, r.Specimen) assert.Len(t, r.Values, 1) - assert.Equal(t, "2976.39", r.Values[0].(*llocommon.Decimal).String()) + assert.Equal(t, "2976.39", r.Values[0].(*lloprotocol.Decimal).String()) m[addr.String()]++ finished := 0 @@ -1912,7 +1913,7 @@ func TestIntegration_LLO_blue_green_lifecycle(t *testing.T) { // starting offchainConfig, the test will handle // blue green for ProtocolVersion and EnableObservationCompression changes - offchainConfig := llocommon.OffchainConfig{ + offchainConfig := lloprotocol.OffchainConfig{ ProtocolVersion: 0, DefaultMinReportIntervalNanoseconds: 0, EnableObservationCompression: false} @@ -1928,7 +1929,7 @@ func TestIntegration_LLO_blue_green_lifecycle(t *testing.T) { } } -func testIntegrationLLOBlueGreenLifecycle(t *testing.T, offchainConfig llocommon.OffchainConfig, ocr31 bool) { +func testIntegrationLLOBlueGreenLifecycle(t *testing.T, offchainConfig lloprotocol.OffchainConfig, ocr31 bool) { // withVersion appends WithOCR31() to config options when running the v31 variant. withVersion := func(opts ...OCRConfigOption) []OCRConfigOption { if ocr31 { @@ -2018,7 +2019,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi var blueDigest ocr2types.ConfigDigest var greenDigest ocr2types.ConfigDigest - allReports := make(map[types.ConfigDigest][]llocommon.Report) + allReports := make(map[types.ConfigDigest][]lloprotocol.Report) // start off with blue=production, green=staging (specimen reports) { // Set config on configurator @@ -2033,7 +2034,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi require.NoError(t, err) req := pckt.req assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) allReports[r.ConfigDigest] = append(allReports[r.ConfigDigest], r) @@ -2041,7 +2042,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi assert.Equal(t, blueDigest, r.ConfigDigest) assert.False(t, r.Specimen) assert.Len(t, r.Values, 1) - assert.Equal(t, "2976.39", r.Values[0].(*llocommon.Decimal).String()) + assert.Equal(t, "2976.39", r.Values[0].(*lloprotocol.Decimal).String()) break } } @@ -2059,13 +2060,13 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi require.NoError(t, err) req := pckt.req assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) allReports[r.ConfigDigest] = append(allReports[r.ConfigDigest], r) if r.Specimen { assert.Len(t, r.Values, 1) - assert.Equal(t, "2976.39", r.Values[0].(*llocommon.Decimal).String()) + assert.Equal(t, "2976.39", r.Values[0].(*lloprotocol.Decimal).String()) assert.Equal(t, greenDigest, r.ConfigDigest) break @@ -2084,7 +2085,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi require.NoError(t, err) req := pckt.req assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) allReports[r.ConfigDigest] = append(allReports[r.ConfigDigest], r) @@ -2175,7 +2176,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi break } assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) allReports[r.ConfigDigest] = append(allReports[r.ConfigDigest], r) @@ -2198,7 +2199,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi require.NoError(t, err) req := pckt.req assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) allReports[r.ConfigDigest] = append(allReports[r.ConfigDigest], r) @@ -2222,7 +2223,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi require.NoError(t, err) req := pckt.req assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) allReports[r.ConfigDigest] = append(allReports[r.ConfigDigest], r) @@ -2263,7 +2264,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi require.NoError(t, err) req := pckt.req assert.Equal(t, uint32(llotypes.ReportFormatJSON), req.ReportFormat) - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) require.NoError(t, err) allReports[r.ConfigDigest] = append(allReports[r.ConfigDigest], r) @@ -2274,11 +2275,11 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi if r.ChannelID == 2 { assert.Len(t, r.Values, 1) - assert.Equal(t, "13.25", r.Values[0].(*llocommon.Decimal).String()) + assert.Equal(t, "13.25", r.Values[0].(*lloprotocol.Decimal).String()) break } assert.Len(t, r.Values, 1) - assert.Equal(t, "2976.39", r.Values[0].(*llocommon.Decimal).String()) + assert.Equal(t, "2976.39", r.Values[0].(*lloprotocol.Decimal).String()) } } t.Run("deleting the jobs turns off oracles and cleans up resources", func(t *testing.T) { @@ -2305,7 +2306,7 @@ func TestIntegration_LLO_channel_merging_owners_adders(t *testing.T) { } func testIntegrationLLOChannelMerging(t *testing.T, ocr31 bool) { - offchainConfig := llocommon.OffchainConfig{ + offchainConfig := lloprotocol.OffchainConfig{ ProtocolVersion: 1, DefaultMinReportIntervalNanoseconds: uint64(1 * time.Second), EnableObservationCompression: true, @@ -2417,7 +2418,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi ) // Track reports by channel ID - reportsByChannel := make(map[uint32][]llocommon.Report) + reportsByChannel := make(map[uint32][]lloprotocol.Report) lastReportTimeByChannel := make(map[uint32]time.Time) // Helper function to wait for reports from specific channels @@ -2438,7 +2439,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi if req.ReportFormat != uint32(llotypes.ReportFormatJSON) { continue } - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) if err != nil { continue } @@ -2505,9 +2506,9 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi assert.Equal(t, digest, report.ConfigDigest) assert.False(t, report.Specimen) if channelID == 3 { - assert.Equal(t, "13.25", report.Values[0].(*llocommon.Decimal).String()) + assert.Equal(t, "13.25", report.Values[0].(*lloprotocol.Decimal).String()) } else { - assert.Equal(t, "2976.39", report.Values[0].(*llocommon.Decimal).String()) + assert.Equal(t, "2976.39", report.Values[0].(*lloprotocol.Decimal).String()) } } }) @@ -2678,7 +2679,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi } req := pckt.req if req.ReportFormat == uint32(llotypes.ReportFormatJSON) { - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) if err == nil && tombstonedChannels[r.ChannelID] { seenTombstonedChannels[r.ChannelID] = true } @@ -2740,11 +2741,11 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi } req := pckt.req if req.ReportFormat == uint32(llotypes.ReportFormatJSON) { - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) if err == nil && r.ChannelID == 10 { // Check if it has linkStream value (13.25) - owner's configuration // It might still have ethStream value (2976.39) initially, but should eventually switch - value := r.Values[0].(*llocommon.Decimal).String() + value := r.Values[0].(*lloprotocol.Decimal).String() if value == "13.25" { foundOwnerReport = true } @@ -2801,7 +2802,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi } req := pckt.req if req.ReportFormat == uint32(llotypes.ReportFormatJSON) { - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) if err == nil && r.ChannelID == 11 { foundChannel11Report = true } @@ -2837,7 +2838,7 @@ func testIntegrationLLOTombstone(t *testing.T, ocr31 bool) { streamIDTombstone = uint32(191) ) - offchainConfig := llocommon.OffchainConfig{ + offchainConfig := lloprotocol.OffchainConfig{ ProtocolVersion: 1, DefaultMinReportIntervalNanoseconds: uint64(1 * time.Second), EnableObservationCompression: true, @@ -2948,7 +2949,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi if req.ReportFormat != uint32(llotypes.ReportFormatJSON) { return len(seenChannels) == 2 } - _, _, r, _, errDecode := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, errDecode := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) if errDecode != nil { return len(seenChannels) == 2 } @@ -2994,7 +2995,7 @@ channelDefinitionsContractFromBlock = %d`, serverURL, serverPubKey, donID, confi if req.ReportFormat != uint32(llotypes.ReportFormatJSON) { continue } - _, _, r, _, err := (llocommon.JSONReportCodec{}).UnpackDecode(req.Payload) + _, _, r, _, err := (lloreportcodec.JSONReportCodec{}).UnpackDecode(req.Payload) if err == nil && tombstonedChannel[r.ChannelID] { sawTombstoned = true break diff --git a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go index 50ce89cfc61..240de3d1cab 100644 --- a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go +++ b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go @@ -26,7 +26,7 @@ import ( llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" "github.com/smartcontractkit/chainlink-common/pkg/utils" - llotypes2 "github.com/smartcontractkit/chainlink-data-streams/llo/types" + llotypes2 "github.com/smartcontractkit/chainlink-data-streams/llo/channelsource" "github.com/smartcontractkit/chainlink-evm/gethwrappers/llo-feeds/generated/channel_config_store" "github.com/smartcontractkit/chainlink-evm/pkg/assets" "github.com/smartcontractkit/chainlink-evm/pkg/client" diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index 713203a2a9a..1685b13422b 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -18,7 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins" "github.com/smartcontractkit/chainlink-common/pkg/types" dontimeCfg "github.com/smartcontractkit/chainlink-common/pkg/workflows/dontime/pb" - lloconfig "github.com/smartcontractkit/chainlink-data-streams/llo/config" + lloconfig "github.com/smartcontractkit/chainlink-data-streams/llo/pluginconfig" "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/relay/dummy/relayer.go b/core/services/relay/dummy/relayer.go index 83bacd2b64d..4c8b172737b 100644 --- a/core/services/relay/dummy/relayer.go +++ b/core/services/relay/dummy/relayer.go @@ -8,9 +8,9 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink-data-streams/llo/config" + lloconfig "github.com/smartcontractkit/chainlink-data-streams/llo/pluginconfig" "github.com/smartcontractkit/chainlink-data-streams/llo/retirement" - "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/bm" + dummytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dummy" "github.com/smartcontractkit/chainlink-evm/pkg/llo/channeldefinitions" ) @@ -58,8 +58,8 @@ func (r *relayer) NewLLOProvider(ctx context.Context, rargs types.RelayArgs, par if err != nil { return nil, err } - transmitter := bm.NewTransmitter(r.lggr, pargs.TransmitterID) - pluginCfg := new(config.PluginConfig) + transmitter := dummytransmitter.NewTransmitter(r.lggr, pargs.TransmitterID) + pluginCfg := new(lloconfig.PluginConfig) if err = pluginCfg.Unmarshal(pargs.PluginConfig); err != nil { return nil, err } diff --git a/plugins/loop_registry.go b/plugins/loop_registry.go index a20b064688e..170515dbd24 100644 --- a/plugins/loop_registry.go +++ b/plugins/loop_registry.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/settings/cresettings" - "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink/v2/core/config" ) @@ -35,7 +35,7 @@ type LoopRegistry struct { appID string featureLogPoller bool cfgDatabase config.Database - cfgMercury de.Mercury + cfgMercury dataengine.Mercury cfgPyroscope config.Pyroscope autoPPROF config.AutoPprof cfgTracing config.Tracing @@ -46,7 +46,7 @@ type LoopRegistry struct { } func NewLoopRegistry(lggr logger.Logger, appID string, featureLogPoller bool, dbConfig config.Database, - mercury de.Mercury, pyroscope config.Pyroscope, autoPPROF config.AutoPprof, tracing config.Tracing, telemetry config.Telemetry, + mercury dataengine.Mercury, pyroscope config.Pyroscope, autoPPROF config.AutoPprof, tracing config.Tracing, telemetry config.Telemetry, telemetryAuthHeaders map[string]string, telemetryAuthPubKeyHex string, looppCfg config.LOOPP) *LoopRegistry { return &LoopRegistry{ registry: map[string]*RegisteredLoop{}, diff --git a/plugins/loop_registry_test.go b/plugins/loop_registry_test.go index 28d41bf129a..1216bda2c1d 100644 --- a/plugins/loop_registry_test.go +++ b/plugins/loop_registry_test.go @@ -12,7 +12,7 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/types" - mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/de" + mercurytransmitter "github.com/smartcontractkit/chainlink-data-streams/llo/transmitter/dataengine" "github.com/smartcontractkit/chainlink/v2/core/config" ) From d732c15f2df4e556d420273dbaa9c13e2d3e40a6 Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Fri, 7 Aug 2026 20:25:13 +0100 Subject: [PATCH 8/8] uncommited bump evm/data-streams --- core/scripts/go.mod | 4 ++-- core/scripts/go.sum | 8 ++++---- deployment/go.mod | 4 ++-- deployment/go.sum | 8 ++++---- go.mod | 4 ++-- go.sum | 8 ++++---- integration-tests/go.mod | 4 ++-- integration-tests/go.sum | 8 ++++---- integration-tests/load/go.mod | 4 ++-- integration-tests/load/go.sum | 8 ++++---- plugins/plugins.public.yaml | 4 ++-- system-tests/lib/go.mod | 4 ++-- system-tests/lib/go.sum | 8 ++++---- system-tests/tests/go.mod | 4 ++-- system-tests/tests/go.sum | 8 ++++---- 15 files changed, 44 insertions(+), 44 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index beede0a1709..7d45a93ed4e 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -49,9 +49,9 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260624154507-ea7ff77a0ddb github.com/smartcontractkit/chainlink-common v0.11.2-0.20260805141018-c1d260f42355 github.com/smartcontractkit/chainlink-common/keystore v1.3.0 - github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af + github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260804191526-b7a850ae7648 github.com/smartcontractkit/chainlink-protos/job-distributor v0.20.1-0.20260701185448-696c075849ea diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 119e54f10e6..1339ce33375 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1586,12 +1586,12 @@ github.com/smartcontractkit/chainlink-common/keystore v1.3.0 h1:V05Rp9/dTc4Wyips github.com/smartcontractkit/chainlink-common/keystore v1.3.0/go.mod h1:vHV8BGm6TN7jBbMsWxq1Hqm3HbCtYFwzvKS0CCczxG8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 h1:uWEwl7i2ryuRVoV4DmIKm6mqYevf1lH/8cQYhw/JXko= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72/go.mod h1:UYcRMb4dZcoaIPgZJ3hckCySTqtJc9K4Q+tOKErwTq0= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af h1:JXO7FcXvnNrk1CVxpccQuGwYCfQhO0Mg3gHjnhZ3YuA= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a h1:93lMD1lP413jQMnnWXiUszXofCJhAsltfGJcXuxC8y4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 h1:mzbvXxdbE/96Pdj1zyPKzf25ZlDR48+iTTDTbaITvmk= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54/go.mod h1:sz/YCiLs8i/V57WISALB7ywNjxW24sj0hi+DE4kzv6A= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 h1:86iEJlXd3BqudMqD1mm6fbHxkfRBjSG61TILlGvXTUM= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6/go.mod h1:ElPvmcWI+O6DP3SjbSjePsLDSIdBC7yF/vC0mpPmbQ0= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 h1:RzjJefmwO+X/mYMPUrTQ8aad1bQRKlep24DCfW8TGX4= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558/go.mod h1:28ktJCpU0fj9BorLjLBCkQNjVk+gcTll1LYC66Ow2bw= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b h1:UdsGoTutNrzqQ23xA49U19lucVR98agzXRVPUPUf7eU= diff --git a/deployment/go.mod b/deployment/go.mod index da362695f0e..e6bc1e7b1be 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -50,9 +50,9 @@ require ( github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260624154507-ea7ff77a0ddb github.com/smartcontractkit/chainlink-common v0.11.2-0.20260805141018-c1d260f42355 github.com/smartcontractkit/chainlink-common/keystore v1.3.0 - github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af + github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260804191526-b7a850ae7648 diff --git a/deployment/go.sum b/deployment/go.sum index 1b67d619296..54eab9e9301 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1420,12 +1420,12 @@ github.com/smartcontractkit/chainlink-common/keystore v1.3.0 h1:V05Rp9/dTc4Wyips github.com/smartcontractkit/chainlink-common/keystore v1.3.0/go.mod h1:vHV8BGm6TN7jBbMsWxq1Hqm3HbCtYFwzvKS0CCczxG8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 h1:uWEwl7i2ryuRVoV4DmIKm6mqYevf1lH/8cQYhw/JXko= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72/go.mod h1:UYcRMb4dZcoaIPgZJ3hckCySTqtJc9K4Q+tOKErwTq0= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af h1:JXO7FcXvnNrk1CVxpccQuGwYCfQhO0Mg3gHjnhZ3YuA= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a h1:93lMD1lP413jQMnnWXiUszXofCJhAsltfGJcXuxC8y4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 h1:mzbvXxdbE/96Pdj1zyPKzf25ZlDR48+iTTDTbaITvmk= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54/go.mod h1:sz/YCiLs8i/V57WISALB7ywNjxW24sj0hi+DE4kzv6A= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 h1:86iEJlXd3BqudMqD1mm6fbHxkfRBjSG61TILlGvXTUM= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6/go.mod h1:ElPvmcWI+O6DP3SjbSjePsLDSIdBC7yF/vC0mpPmbQ0= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 h1:RzjJefmwO+X/mYMPUrTQ8aad1bQRKlep24DCfW8TGX4= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558/go.mod h1:28ktJCpU0fj9BorLjLBCkQNjVk+gcTll1LYC66Ow2bw= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b h1:UdsGoTutNrzqQ23xA49U19lucVR98agzXRVPUPUf7eU= diff --git a/go.mod b/go.mod index 7a07ae9d157..c4eff9a7f4b 100644 --- a/go.mod +++ b/go.mod @@ -87,8 +87,8 @@ require ( github.com/smartcontractkit/chainlink-common v0.11.2-0.20260805141018-c1d260f42355 github.com/smartcontractkit/chainlink-common/keystore v1.3.0 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 - github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 + github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 diff --git a/go.sum b/go.sum index 5451052cb64..0e0a0abc50f 100644 --- a/go.sum +++ b/go.sum @@ -1165,10 +1165,10 @@ github.com/smartcontractkit/chainlink-common/keystore v1.3.0 h1:V05Rp9/dTc4Wyips github.com/smartcontractkit/chainlink-common/keystore v1.3.0/go.mod h1:vHV8BGm6TN7jBbMsWxq1Hqm3HbCtYFwzvKS0CCczxG8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 h1:uWEwl7i2ryuRVoV4DmIKm6mqYevf1lH/8cQYhw/JXko= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72/go.mod h1:UYcRMb4dZcoaIPgZJ3hckCySTqtJc9K4Q+tOKErwTq0= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af h1:JXO7FcXvnNrk1CVxpccQuGwYCfQhO0Mg3gHjnhZ3YuA= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 h1:86iEJlXd3BqudMqD1mm6fbHxkfRBjSG61TILlGvXTUM= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6/go.mod h1:ElPvmcWI+O6DP3SjbSjePsLDSIdBC7yF/vC0mpPmbQ0= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a h1:93lMD1lP413jQMnnWXiUszXofCJhAsltfGJcXuxC8y4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 h1:RzjJefmwO+X/mYMPUrTQ8aad1bQRKlep24DCfW8TGX4= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558/go.mod h1:28ktJCpU0fj9BorLjLBCkQNjVk+gcTll1LYC66Ow2bw= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260512150409-b4068bf735e6 h1:JFo7C3FilwhfwGBLAyj2umbL+P4QxGmVi/b8yt9kqvI= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c5f492e7c3f..7a5aae39800 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -36,7 +36,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.11.2-0.20260805141018-c1d260f42355 github.com/smartcontractkit/chainlink-common/keystore v1.3.0 github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b github.com/smartcontractkit/chainlink-protos/job-distributor v0.20.1-0.20260701185448-696c075849ea github.com/smartcontractkit/chainlink-sui v0.0.0-20260728151254-66dc095d5ccf @@ -422,7 +422,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260624154507-ea7ff77a0ddb // indirect github.com/smartcontractkit/chainlink-ccv v0.1.1-0.20260716164331-d938b371c5d6 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 // indirect - github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af // indirect + github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a // indirect github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 // indirect github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20260423135514-5b1a7565a99c // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 0629750bb2c..9cd897e0670 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1407,12 +1407,12 @@ github.com/smartcontractkit/chainlink-common/keystore v1.3.0 h1:V05Rp9/dTc4Wyips github.com/smartcontractkit/chainlink-common/keystore v1.3.0/go.mod h1:vHV8BGm6TN7jBbMsWxq1Hqm3HbCtYFwzvKS0CCczxG8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 h1:uWEwl7i2ryuRVoV4DmIKm6mqYevf1lH/8cQYhw/JXko= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72/go.mod h1:UYcRMb4dZcoaIPgZJ3hckCySTqtJc9K4Q+tOKErwTq0= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af h1:JXO7FcXvnNrk1CVxpccQuGwYCfQhO0Mg3gHjnhZ3YuA= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a h1:93lMD1lP413jQMnnWXiUszXofCJhAsltfGJcXuxC8y4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 h1:mzbvXxdbE/96Pdj1zyPKzf25ZlDR48+iTTDTbaITvmk= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54/go.mod h1:sz/YCiLs8i/V57WISALB7ywNjxW24sj0hi+DE4kzv6A= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 h1:86iEJlXd3BqudMqD1mm6fbHxkfRBjSG61TILlGvXTUM= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6/go.mod h1:ElPvmcWI+O6DP3SjbSjePsLDSIdBC7yF/vC0mpPmbQ0= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 h1:RzjJefmwO+X/mYMPUrTQ8aad1bQRKlep24DCfW8TGX4= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558/go.mod h1:28ktJCpU0fj9BorLjLBCkQNjVk+gcTll1LYC66Ow2bw= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b h1:UdsGoTutNrzqQ23xA49U19lucVR98agzXRVPUPUf7eU= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 5645eac7498..2663dc898bc 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -26,7 +26,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260624154507-ea7ff77a0ddb github.com/smartcontractkit/chainlink-common v0.11.2-0.20260805141018-c1d260f42355 github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.6-0.20260708113039-95f97b2d25e9 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.5 github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 @@ -485,7 +485,7 @@ require ( github.com/smartcontractkit/chainlink-ccv v0.1.1-0.20260716164331-d938b371c5d6 // indirect github.com/smartcontractkit/chainlink-common/keystore v1.3.0 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 // indirect - github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af // indirect + github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a // indirect github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 // indirect github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b // indirect github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 7feb7d25404..7c83e09b5de 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1643,12 +1643,12 @@ github.com/smartcontractkit/chainlink-common/keystore v1.3.0 h1:V05Rp9/dTc4Wyips github.com/smartcontractkit/chainlink-common/keystore v1.3.0/go.mod h1:vHV8BGm6TN7jBbMsWxq1Hqm3HbCtYFwzvKS0CCczxG8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 h1:uWEwl7i2ryuRVoV4DmIKm6mqYevf1lH/8cQYhw/JXko= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72/go.mod h1:UYcRMb4dZcoaIPgZJ3hckCySTqtJc9K4Q+tOKErwTq0= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af h1:JXO7FcXvnNrk1CVxpccQuGwYCfQhO0Mg3gHjnhZ3YuA= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a h1:93lMD1lP413jQMnnWXiUszXofCJhAsltfGJcXuxC8y4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 h1:mzbvXxdbE/96Pdj1zyPKzf25ZlDR48+iTTDTbaITvmk= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54/go.mod h1:sz/YCiLs8i/V57WISALB7ywNjxW24sj0hi+DE4kzv6A= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 h1:86iEJlXd3BqudMqD1mm6fbHxkfRBjSG61TILlGvXTUM= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6/go.mod h1:ElPvmcWI+O6DP3SjbSjePsLDSIdBC7yF/vC0mpPmbQ0= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 h1:RzjJefmwO+X/mYMPUrTQ8aad1bQRKlep24DCfW8TGX4= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558/go.mod h1:28ktJCpU0fj9BorLjLBCkQNjVk+gcTll1LYC66Ow2bw= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b h1:UdsGoTutNrzqQ23xA49U19lucVR98agzXRVPUPUf7eU= diff --git a/plugins/plugins.public.yaml b/plugins/plugins.public.yaml index 1b675a2b5a2..023868a79e3 100644 --- a/plugins/plugins.public.yaml +++ b/plugins/plugins.public.yaml @@ -51,7 +51,7 @@ plugins: streams: - moduleURI: "github.com/smartcontractkit/chainlink-data-streams" - gitRef: "v1.0.1-0.20260806155614-21385fa363af" + gitRef: "v1.0.1-0.20260807190645-944ed9f4988a" installPath: "./mercury/cmd/chainlink-mercury" ton: @@ -66,7 +66,7 @@ plugins: evm: - moduleURI: "github.com/smartcontractkit/chainlink-evm" - gitRef: "v0.3.4-0.20260807133723-4fe3d0dbace6" + gitRef: "v0.3.4-0.20260807191448-25353bfb9558" installPath: "./pkg/cmd/chainlink-evm" capability-cron: diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index 2cc9a65d0e7..c33a5075ad0 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -40,7 +40,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.11.2-0.20260805141018-c1d260f42355 github.com/smartcontractkit/chainlink-common/keystore v1.3.0 github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260804191526-b7a850ae7648 github.com/smartcontractkit/chainlink-protos/job-distributor v0.20.1-0.20260701185448-696c075849ea @@ -460,7 +460,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260624154507-ea7ff77a0ddb // indirect github.com/smartcontractkit/chainlink-ccv v0.1.1-0.20260716164331-d938b371c5d6 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 // indirect - github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af // indirect + github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a // indirect github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 // indirect github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20260423135514-5b1a7565a99c // indirect diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index cfd607cb404..c943ec662b5 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1557,12 +1557,12 @@ github.com/smartcontractkit/chainlink-common/keystore v1.3.0 h1:V05Rp9/dTc4Wyips github.com/smartcontractkit/chainlink-common/keystore v1.3.0/go.mod h1:vHV8BGm6TN7jBbMsWxq1Hqm3HbCtYFwzvKS0CCczxG8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 h1:uWEwl7i2ryuRVoV4DmIKm6mqYevf1lH/8cQYhw/JXko= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72/go.mod h1:UYcRMb4dZcoaIPgZJ3hckCySTqtJc9K4Q+tOKErwTq0= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af h1:JXO7FcXvnNrk1CVxpccQuGwYCfQhO0Mg3gHjnhZ3YuA= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a h1:93lMD1lP413jQMnnWXiUszXofCJhAsltfGJcXuxC8y4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 h1:mzbvXxdbE/96Pdj1zyPKzf25ZlDR48+iTTDTbaITvmk= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54/go.mod h1:sz/YCiLs8i/V57WISALB7ywNjxW24sj0hi+DE4kzv6A= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 h1:86iEJlXd3BqudMqD1mm6fbHxkfRBjSG61TILlGvXTUM= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6/go.mod h1:ElPvmcWI+O6DP3SjbSjePsLDSIdBC7yF/vC0mpPmbQ0= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 h1:RzjJefmwO+X/mYMPUrTQ8aad1bQRKlep24DCfW8TGX4= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558/go.mod h1:28ktJCpU0fj9BorLjLBCkQNjVk+gcTll1LYC66Ow2bw= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b h1:UdsGoTutNrzqQ23xA49U19lucVR98agzXRVPUPUf7eU= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index b0564a22f38..1f48f5911d4 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -240,7 +240,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260624154507-ea7ff77a0ddb // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260624154507-ea7ff77a0ddb // indirect github.com/smartcontractkit/chainlink-ccv v0.1.1-0.20260716164331-d938b371c5d6 // indirect - github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af // indirect + github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a // indirect github.com/smartcontractkit/chainlink-feeds v0.1.2-0.20250227211209-7cd000095135 // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/committee-verifier v0.0.0-20251211142334-5c3421fe2c8d // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/heartbeat v0.0.0-20260115142640-f6b99095c12e // indirect @@ -638,7 +638,7 @@ require ( github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260730150638-e7b61c05cec1 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 - github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 + github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20260423135514-5b1a7565a99c // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260724153515-bb6a2de39bcb // indirect github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260723212800-b2f21d31b1d2 // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index b6f6a4f16ef..c5f8f4005c4 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1762,12 +1762,12 @@ github.com/smartcontractkit/chainlink-common/keystore v1.3.0 h1:V05Rp9/dTc4Wyips github.com/smartcontractkit/chainlink-common/keystore v1.3.0/go.mod h1:vHV8BGm6TN7jBbMsWxq1Hqm3HbCtYFwzvKS0CCczxG8= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72 h1:uWEwl7i2ryuRVoV4DmIKm6mqYevf1lH/8cQYhw/JXko= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260724142814-45996a1bcb72/go.mod h1:UYcRMb4dZcoaIPgZJ3hckCySTqtJc9K4Q+tOKErwTq0= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af h1:JXO7FcXvnNrk1CVxpccQuGwYCfQhO0Mg3gHjnhZ3YuA= -github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260806155614-21385fa363af/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a h1:93lMD1lP413jQMnnWXiUszXofCJhAsltfGJcXuxC8y4= +github.com/smartcontractkit/chainlink-data-streams v1.0.1-0.20260807190645-944ed9f4988a/go.mod h1:dF5JiHWueHjYguUUUrFeb03MkcDqha/tssEkqTkgzp4= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54 h1:mzbvXxdbE/96Pdj1zyPKzf25ZlDR48+iTTDTbaITvmk= github.com/smartcontractkit/chainlink-deployments-framework v0.111.1-0.20260612191326-e31c0ae4cd54/go.mod h1:sz/YCiLs8i/V57WISALB7ywNjxW24sj0hi+DE4kzv6A= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6 h1:86iEJlXd3BqudMqD1mm6fbHxkfRBjSG61TILlGvXTUM= -github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807133723-4fe3d0dbace6/go.mod h1:ElPvmcWI+O6DP3SjbSjePsLDSIdBC7yF/vC0mpPmbQ0= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558 h1:RzjJefmwO+X/mYMPUrTQ8aad1bQRKlep24DCfW8TGX4= +github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260807191448-25353bfb9558/go.mod h1:28ktJCpU0fj9BorLjLBCkQNjVk+gcTll1LYC66Ow2bw= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 h1:QJiXTG9CmaQAuMRn5JGi+Jhji7fSkehVnKpjc8oNJJY= github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501/go.mod h1:4cT1BeNF8DAn6In9zr3LayVCv1KzFeuxT7zcuNkfIb0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260713161920-de075095648b h1:UdsGoTutNrzqQ23xA49U19lucVR98agzXRVPUPUf7eU=