diff --git a/core/scripts/go.mod b/core/scripts/go.mod index a47b91706a1..1be0b54834a 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -538,7 +538,7 @@ require ( github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 // indirect - github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 // indirect github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260821021345-a75f67fe965c // indirect github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index f83981f1863..a5a7a8bc943 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1741,8 +1741,8 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202605122 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 h1:ecQYtdRA+NQLXf0aKYUMfcn1TRhcQ4RZCZzzadFsbSs= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 h1:/vkKPJoweLkRd56V4YHGRAtTG4+/JAlgklGEfvH6l4c= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e h1:dgw4Hi5YBqOrqkaPV5O5GSlQnUokQZjo7udfpvT3JeE= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 h1:NXKTdIESAiCkVnPS6dyZP+NXVek3GzXa6P4uFAs0o8Y= diff --git a/core/services/nodestatusreporter/jobspec/cl_job_info.go b/core/services/nodestatusreporter/jobspec/cl_job_info.go new file mode 100644 index 00000000000..9780af9c712 --- /dev/null +++ b/core/services/nodestatusreporter/jobspec/cl_job_info.go @@ -0,0 +1,122 @@ +package jobspec + +import ( + "context" + "fmt" + "time" + + "github.com/pelletier/go-toml" + "google.golang.org/protobuf/proto" + + "github.com/smartcontractkit/chainlink-common/pkg/beholder" + commonv1 "github.com/smartcontractkit/chainlink-protos/node-platform/common/v1" + "github.com/smartcontractkit/chainlink/v2/core/services/job" +) + +// CLJobInfo is the job-type-agnostic half of this reporter: common identity +// plus the whole definition as TOML, so every job type is covered with no +// per-type code. JobSpecEvent (OCR2-only) is emitted from the same service. +const ( + // Domain, Entity and DataSchema identify CLJobInfo telemetry on Beholder. + Domain = "node-platform" + Entity = "common.v1.CLJobInfo" + DataSchema = "/node-platform/common/v1" +) + +// NodeIdentity is the node-level context attached to every emitted CLJobInfo. +type NodeIdentity struct { + CSAPublicKey string + NodeVersion string + Hostname string +} + +// JobProposal is the JD provenance for a job that arrived as an approved job +// proposal. Nil for jobs created directly (CLI, UI, TOML on disk). +type JobProposal struct { + FeedsManagerID int64 + RemoteUUID string + SpecVersion int32 + ProposedAt time.Time + ApprovedAt time.Time +} + +// BuildCLJobInfo converts any job.Job into a CLJobInfo. prop may be nil. +// +// On TOML encoding failure it still returns a populated identity payload with +// an empty SpecToml alongside the error, so callers can emit and log rather +// than drop the event. +func BuildCLJobInfo(jb job.Job, trigger commonv1.CLJobInfoTrigger, id NodeIdentity, prop *JobProposal, now time.Time) (*commonv1.CLJobInfo, error) { + info := &commonv1.CLJobInfo{ + CsaPublicKey: id.CSAPublicKey, + NodeVersion: id.NodeVersion, + Hostname: id.Hostname, + ExternalJobId: jb.ExternalJobID.String(), + JobId: jb.ID, + Name: jb.Name.ValueOrZero(), + JobType: string(jb.Type), + SchemaVersion: jb.SchemaVersion, + ForwardingAllowed: jb.ForwardingAllowed, + CreatedAtMs: unixMillisOrNil(jb.CreatedAt), + Trigger: trigger, + TimestampMs: now.UnixMilli(), + } + if jb.GasLimit.Valid { + info.GasLimit = new(jb.GasLimit.Uint32) + } + if jb.StreamID != nil { + info.StreamId = new(*jb.StreamID) + } + if prop != nil { + info.FeedsManagerId = &prop.FeedsManagerID + info.RemoteUuid = &prop.RemoteUUID + info.SpecVersion = &prop.SpecVersion + info.ProposedAtMs = unixMillisOrNil(prop.ProposedAt) + info.ApprovedAtMs = unixMillisOrNil(prop.ApprovedAt) + } + + specTOML, err := jobTOML(jb) + if err != nil { + return info, fmt.Errorf("encoding job %s (%d) spec to TOML: %w", jb.ExternalJobID, jb.ID, err) + } + info.SpecToml = specTOML + + return info, nil +} + +// EmitCLJobInfo marshals a CLJobInfo and publishes it to Beholder. +func EmitCLJobInfo(ctx context.Context, emitter beholder.Emitter, info *commonv1.CLJobInfo) error { + payload, err := proto.Marshal(info) + if err != nil { + return fmt.Errorf("marshaling CLJobInfo: %w", err) + } + + err = emitter.Emit(ctx, payload, + beholder.AttrKeyDomain, Domain, + beholder.AttrKeyEntity, Entity, + beholder.AttrKeyDataSchema, DataSchema, + ) + if err != nil { + return fmt.Errorf("emitting CLJobInfo: %w", err) + } + return nil +} + +// jobTOML serializes the whole job.Job, which captures both the common fields +// and the single active type-specific spec. +func jobTOML(jb job.Job) (string, error) { + out, err := toml.Marshal(jb) + if err != nil { + return "", err + } + return string(out), nil +} + +// unixMillisOrNil maps an unset time to nil rather than the epoch. Millis not +// RFC3339Nano: Go trims trailing zeros, so those strings are variable-width and +// don't sort chronologically. +func unixMillisOrNil(t time.Time) *int64 { + if t.IsZero() { + return nil + } + return new(t.UnixMilli()) +} diff --git a/core/services/nodestatusreporter/jobspec/cl_job_info_test.go b/core/services/nodestatusreporter/jobspec/cl_job_info_test.go new file mode 100644 index 00000000000..a6d8e9f6a20 --- /dev/null +++ b/core/services/nodestatusreporter/jobspec/cl_job_info_test.go @@ -0,0 +1,232 @@ +package jobspec_test + +import ( + "testing" + "time" + + "github.com/pelletier/go-toml" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "gopkg.in/guregu/null.v4" + + "github.com/smartcontractkit/chainlink-common/pkg/beholder" + "github.com/smartcontractkit/chainlink-common/pkg/beholder/beholdertest" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + evmtypes "github.com/smartcontractkit/chainlink-evm/pkg/types" + commonv1 "github.com/smartcontractkit/chainlink-protos/node-platform/common/v1" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/nodestatusreporter/jobspec" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" +) + +func clJobInfoSampleJob() job.Job { + streamID := uint32(42) + return job.Job{ + ID: 7, + Name: null.StringFrom("my-ocr2-job"), + Type: job.OffchainReporting2, + SchemaVersion: 1, + ForwardingAllowed: true, + StreamID: &streamID, + CreatedAt: time.Date(2026, 7, 24, 10, 0, 0, 0, time.UTC), + OCR2OracleSpec: &job.OCR2OracleSpec{ + Relay: "evm", + ChainID: "1", + PluginType: commontypes.Median, + ContractID: "0xcccccccccccccccccccccccccccccccccccccccc", + TransmitterID: null.StringFrom("0x1111111111111111111111111111111111111111"), + RelayConfig: job.JSONConfig{ + "chainID": "1", + "sendingKeys": []any{"0x1111111111111111111111111111111111111111"}, + }, + }, + Pipeline: pipeline.Pipeline{Tasks: []pipeline.Task{ + &pipeline.ETHTxTask{From: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, + }}, + } +} + +// Load-bearing: an arbitrary job must round-trip to TOML with no per-type code. +func TestBuildCLJobInfo_EncodesFullSpecAsTOML(t *testing.T) { + t.Parallel() + jb := clJobInfoSampleJob() + id := jobspec.NodeIdentity{CSAPublicKey: "csa", NodeVersion: "1.2.3", Hostname: "host-1"} + + info, err := jobspec.BuildCLJobInfo(jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_CREATE, id, nil, time.Date(2026, 7, 24, 12, 0, 0, 0, time.UTC)) + require.NoError(t, err) + + require.Equal(t, "csa", info.CsaPublicKey) + require.Equal(t, "1.2.3", info.NodeVersion) + require.Equal(t, "host-1", info.Hostname) + require.Equal(t, int32(7), info.JobId) + require.Equal(t, "my-ocr2-job", info.Name) + require.Equal(t, "offchainreporting2", info.JobType) + require.Equal(t, uint32(1), info.SchemaVersion) + require.True(t, info.ForwardingAllowed) + require.NotNil(t, info.StreamId) + require.Equal(t, uint32(42), *info.StreamId) + require.Equal(t, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_CREATE, info.Trigger) + require.Equal(t, time.Date(2026, 7, 24, 12, 0, 0, 0, time.UTC).UnixMilli(), info.TimestampMs) + require.NotNil(t, info.CreatedAtMs) + require.Equal(t, time.Date(2026, 7, 24, 10, 0, 0, 0, time.UTC).UnixMilli(), *info.CreatedAtMs) + + // Must be valid TOML and contain type-specific spec data. + require.NotEmpty(t, info.SpecToml) + var decoded map[string]any + require.NoError(t, toml.Unmarshal([]byte(info.SpecToml), &decoded)) + require.Contains(t, info.SpecToml, "median") + require.Contains(t, info.SpecToml, "0xcccccccccccccccccccccccccccccccccccccccc") +} + +func TestBuildCLJobInfo_HandlesMultipleJobTypesGenerically(t *testing.T) { + t.Parallel() + jobs := []job.Job{ + {Type: job.VRF, VRFSpec: &job.VRFSpec{ + EVMChainID: sqlutil.NewI(4), + FromAddresses: []evmtypes.EIP55Address{evmtypes.MustEIP55Address("0x6666666666666666666666666666666666666666")}, + }}, + {Type: job.BlockhashStore, BlockhashStoreSpec: &job.BlockhashStoreSpec{EVMChainID: sqlutil.NewI(5)}}, + } + for _, jb := range jobs { + info, err := jobspec.BuildCLJobInfo(jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_HEARTBEAT, jobspec.NodeIdentity{}, nil, time.Now()) + require.NoErrorf(t, err, "job type %s should encode without per-type code", jb.Type) + require.NotEmpty(t, info.SpecToml) + } +} + +// remote_uuid is the join key back to api.job.v1.Job.uuid. +func TestBuildCLJobInfo_CarriesJobDistributorProvenance(t *testing.T) { + t.Parallel() + proposedAt := time.Date(2026, 7, 20, 9, 0, 0, 0, time.UTC) + approvedAt := time.Date(2026, 7, 24, 10, 0, 0, 0, time.UTC) + prop := &jobspec.JobProposal{ + FeedsManagerID: 3, + RemoteUUID: "6d7d9d1a-0d0f-4b3f-9a2f-2e4a1c0b8d55", + SpecVersion: 2, + ProposedAt: proposedAt, + ApprovedAt: approvedAt, + } + + info, err := jobspec.BuildCLJobInfo(clJobInfoSampleJob(), commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_CREATE, jobspec.NodeIdentity{}, prop, time.Now()) + require.NoError(t, err) + + require.NotNil(t, info.FeedsManagerId) + require.Equal(t, int64(3), *info.FeedsManagerId) + require.NotNil(t, info.RemoteUuid) + require.Equal(t, "6d7d9d1a-0d0f-4b3f-9a2f-2e4a1c0b8d55", *info.RemoteUuid) + require.NotNil(t, info.SpecVersion) + require.Equal(t, int32(2), *info.SpecVersion) + require.NotNil(t, info.ProposedAtMs) + require.Equal(t, proposedAt.UnixMilli(), *info.ProposedAtMs) + require.NotNil(t, info.ApprovedAtMs) + require.Equal(t, approvedAt.UnixMilli(), *info.ApprovedAtMs) +} + +// An unset feeds_manager_id marks a directly-created job. +func TestBuildCLJobInfo_UnmanagedJobHasNoProvenance(t *testing.T) { + t.Parallel() + info, err := jobspec.BuildCLJobInfo(clJobInfoSampleJob(), commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_CREATE, jobspec.NodeIdentity{}, nil, time.Now()) + require.NoError(t, err) + + require.Nil(t, info.FeedsManagerId) + require.Nil(t, info.RemoteUuid) + require.Nil(t, info.SpecVersion) + require.Nil(t, info.ProposedAtMs) + require.Nil(t, info.ApprovedAtMs) +} + +//nolint:paralleltest // installs a process-global beholder emitter +func TestEmitCLJobInfo_PublishesToBeholder(t *testing.T) { + obs := beholdertest.NewObserver(t) + + info, err := jobspec.BuildCLJobInfo(clJobInfoSampleJob(), commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_CREATE, jobspec.NodeIdentity{CSAPublicKey: "csa"}, nil, time.Now()) + require.NoError(t, err) + require.NoError(t, jobspec.EmitCLJobInfo(t.Context(), beholder.GetEmitter(), info)) + + msgs := obs.Messages(t, beholder.AttrKeyEntity, jobspec.Entity) + require.NotEmpty(t, msgs) + + msg := msgs[0] + require.Equal(t, jobspec.Domain, msg.Attrs[beholder.AttrKeyDomain]) + require.Equal(t, jobspec.DataSchema, msg.Attrs[beholder.AttrKeyDataSchema]) + + var payload commonv1.CLJobInfo + require.NoError(t, proto.Unmarshal(msg.Body, &payload)) + require.Equal(t, "csa", payload.CsaPublicKey) + require.Equal(t, "offchainreporting2", payload.JobType) + require.NotEmpty(t, payload.SpecToml) +} + +// Why these are millis and not RFC3339Nano: Go trims trailing zeros, so those +// strings are variable-width and don't sort chronologically. +func TestBuildCLJobInfo_TimestampsAreOrderedUnixMillis(t *testing.T) { + t.Parallel() + for _, tc := range []struct { + name string + at time.Time + }{ + {"whole second", time.Date(2026, 7, 24, 10, 0, 0, 0, time.UTC)}, + {"tenth of a second", time.Date(2026, 7, 24, 10, 0, 0, 100000000, time.UTC)}, + {"sub-millisecond", time.Date(2026, 7, 24, 10, 0, 0, 123400000, time.UTC)}, + {"millisecond", time.Date(2026, 7, 24, 10, 0, 0, 123000000, time.UTC)}, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + jb := clJobInfoSampleJob() + jb.CreatedAt = tc.at + + info, err := jobspec.BuildCLJobInfo(jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_HEARTBEAT, jobspec.NodeIdentity{}, nil, time.Now()) + require.NoError(t, err) + require.NotNil(t, info.CreatedAtMs) + require.Equal(t, tc.at.UnixMilli(), *info.CreatedAtMs) + }) + } +} + +// TestBuildCLJobInfo_ZeroTimeIsUnset: an absent time must be nil, not the epoch. +func TestBuildCLJobInfo_ZeroTimeIsUnset(t *testing.T) { + t.Parallel() + jb := clJobInfoSampleJob() + jb.CreatedAt = time.Time{} + + info, err := jobspec.BuildCLJobInfo(jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_HEARTBEAT, jobspec.NodeIdentity{}, nil, time.Now()) + require.NoError(t, err) + require.Nil(t, info.CreatedAtMs) +} + +// The one thing millis give up versus a nanosecond encoding. +func TestBuildCLJobInfo_SubMillisecondIsTruncated(t *testing.T) { + t.Parallel() + jb := clJobInfoSampleJob() + jb.CreatedAt = time.Date(2026, 7, 24, 10, 0, 0, 123456789, time.UTC) + + info, err := jobspec.BuildCLJobInfo(jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_HEARTBEAT, jobspec.NodeIdentity{}, nil, time.Now()) + require.NoError(t, err) + require.NotNil(t, info.CreatedAtMs) + require.Equal(t, time.Date(2026, 7, 24, 10, 0, 0, 123000000, time.UTC).UnixMilli(), *info.CreatedAtMs) +} + +// The property the old RFC3339Nano encoding violated. +func TestBuildCLJobInfo_TimestampsSortChronologically(t *testing.T) { + t.Parallel() + times := []time.Time{ + time.Date(2026, 7, 24, 10, 0, 0, 0, time.UTC), + time.Date(2026, 7, 24, 10, 0, 0, 100000000, time.UTC), + time.Date(2026, 7, 24, 10, 0, 0, 123000000, time.UTC), + time.Date(2026, 7, 24, 10, 0, 0, 900000000, time.UTC), + } + var prev int64 + for i, at := range times { + jb := clJobInfoSampleJob() + jb.CreatedAt = at + + info, err := jobspec.BuildCLJobInfo(jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_HEARTBEAT, jobspec.NodeIdentity{}, nil, time.Now()) + require.NoError(t, err) + require.NotNil(t, info.CreatedAtMs) + if i > 0 { + require.Greater(t, *info.CreatedAtMs, prev, "encoded times must increase with chronological order") + } + prev = *info.CreatedAtMs + } +} diff --git a/core/services/nodestatusreporter/jobspec/job_spec_reporter.go b/core/services/nodestatusreporter/jobspec/job_spec_reporter.go index d2dcb71dba9..43df99c280c 100644 --- a/core/services/nodestatusreporter/jobspec/job_spec_reporter.go +++ b/core/services/nodestatusreporter/jobspec/job_spec_reporter.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/beholder" "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + commonv1 "github.com/smartcontractkit/chainlink-protos/node-platform/common/v1" coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/feeds" @@ -26,8 +27,10 @@ const ServiceName = "JobSpecReporter" var _ job.Listener = (*Service)(nil) -// Service polls active jobs and pushes their specs to Beholder, and also emits -// on job create/delete via the job.Listener interface. +// Service polls active jobs and pushes their specs to Beholder, and emits on +// job create/delete via job.Listener. Two payloads per trigger: CLJobInfo for +// every job type (see cl_job_info.go), and the OCR2-only JobSpecEvent it +// supersedes, kept until its consumers migrate. type Service struct { services.Service eng *services.Engine @@ -67,13 +70,12 @@ func NewJobSpecReporter( return s } +// start always runs so CLJobInfo needs no per-node opt-in; the legacy track +// stays behind JobSpecReporter.Enabled (see ShouldEmit). Still a no-op where +// Beholder is disabled, which is the default. func (s *Service) start(ctx context.Context) error { - if !s.config.Enabled() { - s.eng.Info("Job Spec Reporter Service is disabled") - return nil - } - - s.eng.Info("Starting Job Spec Reporter Service") + s.eng.Infow("Starting Job Spec Reporter Service", + "clJobInfo", true, "legacyJobSpecEvent", s.config.Enabled()) s.spawner.RegisterListener(s) ticker := services.NewTicker(s.config.PollingInterval()) s.eng.GoTick(ticker, s.pollAllJobs) @@ -87,39 +89,90 @@ func (s *Service) HealthReport() map[string]error { // AfterJobStarted emits a create event when a job starts. func (s *Service) AfterJobStarted(ctx context.Context, jb job.Job) { - if !s.ShouldEmit(&jb) { - return - } - if err := s.EmitForJob(ctx, jb, events.EmissionTrigger_EMISSION_TRIGGER_CREATE); err != nil { - s.eng.Warnw("Failed to emit job spec telemetry on create", "jobID", jb.ID, "error", err) - } + s.emit(ctx, jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_CREATE, events.EmissionTrigger_EMISSION_TRIGGER_CREATE) } // AfterJobStopped emits a delete event when a job is removed. func (s *Service) AfterJobStopped(ctx context.Context, jb job.Job) { + s.emit(ctx, jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_DELETE, events.EmissionTrigger_EMISSION_TRIGGER_DELETE) +} + +// pollAllJobs emits heartbeat telemetry for every active job. +func (s *Service) pollAllJobs(ctx context.Context) { + for _, jb := range s.spawner.ActiveJobs() { + s.emit(ctx, jb, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_HEARTBEAT, events.EmissionTrigger_EMISSION_TRIGGER_HEARTBEAT) + } +} + +// emit reports jb on both tracks; a failure on one never suppresses the other. +func (s *Service) emit(ctx context.Context, jb job.Job, clTrigger commonv1.CLJobInfoTrigger, trigger events.EmissionTrigger) { + if err := s.EmitCLJobInfoForJob(ctx, jb, clTrigger); err != nil { + s.eng.Warnw("Failed to emit CLJobInfo", "jobID", jb.ID, "trigger", clTrigger, "error", err) + } + if !s.ShouldEmit(&jb) { return } - if err := s.EmitForJob(ctx, jb, events.EmissionTrigger_EMISSION_TRIGGER_DELETE); err != nil { - s.eng.Warnw("Failed to emit job spec telemetry on delete", "jobID", jb.ID, "error", err) + if err := s.EmitForJob(ctx, jb, trigger); err != nil { + s.eng.Warnw("Failed to emit job spec telemetry", "jobID", jb.ID, "trigger", trigger, "error", err) } } -// pollAllJobs emits heartbeat telemetry for every active job that passes the emit gate. -func (s *Service) pollAllJobs(ctx context.Context) { - for _, jb := range s.spawner.ActiveJobs() { - if !s.ShouldEmit(&jb) { - continue +// EmitCLJobInfoForJob emits the generic CLJobInfo for any job type. A job whose +// spec won't TOML-encode is still reported without spec_toml, and the encoding +// error returned for logging. +func (s *Service) EmitCLJobInfoForJob(ctx context.Context, jb job.Job, trigger commonv1.CLJobInfoTrigger) error { + prop, err := s.jobProposal(ctx, jb) + if err != nil { + // Provenance is an enrichment, not a precondition. + s.eng.Warnw("Failed to resolve job proposal provenance for CLJobInfo", + "jobID", jb.ID, "externalJobID", jb.ExternalJobID, "error", err) + } + + identity := NodeIdentity{CSAPublicKey: s.csaPublicKey, NodeVersion: s.nodeVersion, Hostname: s.hostname} + info, buildErr := BuildCLJobInfo(jb, trigger, identity, prop, time.Now()) + + if emitErr := EmitCLJobInfo(ctx, s.emitter, info); emitErr != nil { + return emitErr + } + return buildErr +} + +// jobProposal resolves JD provenance for jb, or nil if it wasn't proposed. +func (s *Service) jobProposal(ctx context.Context, jb job.Job) (*JobProposal, error) { + if s.feedsORM == nil || jb.ExternalJobID == uuid.Nil { + return nil, nil + } + + prop, err := s.feedsORM.GetJobProposalByExternalJobID(ctx, jb.ExternalJobID) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil } - if err := s.EmitForJob(ctx, jb, events.EmissionTrigger_EMISSION_TRIGGER_HEARTBEAT); err != nil { - s.eng.Warnw("Failed to emit job spec telemetry", "jobID", jb.ID, "error", err) + return nil, fmt.Errorf("fetching job proposal: %w", err) + } + + spec, err := s.feedsORM.GetApprovedSpec(ctx, prop.ID) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + // Proposal exists but has no approved spec, e.g. mid-cancellation. + return &JobProposal{FeedsManagerID: prop.FeedsManagerID, RemoteUUID: prop.RemoteUUID.String()}, nil } + return nil, fmt.Errorf("fetching approved spec: %w", err) } + + return &JobProposal{ + FeedsManagerID: prop.FeedsManagerID, + RemoteUUID: prop.RemoteUUID.String(), + SpecVersion: spec.Version, + ProposedAt: spec.CreatedAt, + ApprovedAt: spec.StatusUpdatedAt, + }, nil } -// ShouldEmit reports whether the job passes the config-driven emit gate. +// ShouldEmit gates the legacy OCR2 track only; CLJobInfo ignores it. func (s *Service) ShouldEmit(j *job.Job) bool { - if j == nil { + if j == nil || !s.config.Enabled() { return false } if j.Type != job.OffchainReporting2 || j.OCR2OracleSpec == nil { diff --git a/core/services/nodestatusreporter/jobspec/job_spec_reporter_test.go b/core/services/nodestatusreporter/jobspec/job_spec_reporter_test.go index a1d32805b35..02cc19e080c 100644 --- a/core/services/nodestatusreporter/jobspec/job_spec_reporter_test.go +++ b/core/services/nodestatusreporter/jobspec/job_spec_reporter_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/beholder" "github.com/smartcontractkit/chainlink-common/pkg/beholder/beholdertest" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + commonv1 "github.com/smartcontractkit/chainlink-protos/node-platform/common/v1" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/feeds" feedsmocks "github.com/smartcontractkit/chainlink/v2/core/services/feeds/mocks" @@ -389,3 +390,29 @@ func TestBuildEvent_ProposalLifecycle(t *testing.T) { assert.Equal(t, approvedAt.Format(time.RFC3339Nano), ev.ApprovedAt) assert.InDelta(t, approvedAt.Sub(proposedAt).Seconds(), ev.AcceptLatencySeconds, 1.0) } + +// Pins the split: CLJobInfo needs no per-node opt-in, the legacy OCR2 track +// stays behind JobSpecReporter.Enabled. +// +//nolint:paralleltest // installs a process-global beholder emitter +func TestAfterJobStarted_CLJobInfoIgnoresEnabledGate(t *testing.T) { + observer := beholdertest.NewObserver(t) + + cfg := defaultConfig() + cfg.enabled = false // legacy track off + + jb := makeMedianJob() + reporter := newTestReporter(t, cfg, newFeedsORMWithoutProposal(t, jb)) + reporter.AfterJobStarted(t.Context(), jb) + + clMsgs := observer.Messages(t, beholder.AttrKeyEntity, jobspec.Entity) + require.Len(t, clMsgs, 1, "CLJobInfo must be emitted even with JobSpecReporter disabled") + + var payload commonv1.CLJobInfo + require.NoError(t, proto.Unmarshal(clMsgs[0].Body, &payload)) + require.Equal(t, commonv1.CLJobInfoTrigger_CL_JOB_INFO_TRIGGER_CREATE, payload.Trigger) + require.NotEmpty(t, payload.SpecToml) + + legacy := observer.Messages(t, "beholder_entity", events.ProtoPkg+"."+events.JobSpecEventEntity) + require.Empty(t, legacy, "legacy JobSpecEvent must stay gated by Enabled") +} diff --git a/deployment/go.mod b/deployment/go.mod index e3aa270ff9a..3199486a15e 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -452,7 +452,7 @@ require ( github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 // indirect - github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e // indirect github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 // indirect github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260821021345-a75f67fe965c // indirect github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 // indirect diff --git a/deployment/go.sum b/deployment/go.sum index cfe02dda4b0..1310125fef3 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1458,8 +1458,8 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202605122 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 h1:ecQYtdRA+NQLXf0aKYUMfcn1TRhcQ4RZCZzzadFsbSs= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 h1:/vkKPJoweLkRd56V4YHGRAtTG4+/JAlgklGEfvH6l4c= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e h1:dgw4Hi5YBqOrqkaPV5O5GSlQnUokQZjo7udfpvT3JeE= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 h1:NXKTdIESAiCkVnPS6dyZP+NXVek3GzXa6P4uFAs0o8Y= diff --git a/go.mod b/go.mod index b31726eed15..0fa7a4ea584 100644 --- a/go.mod +++ b/go.mod @@ -98,7 +98,7 @@ require ( github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 - github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 + github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260821021345-a75f67fe965c github.com/smartcontractkit/chainlink-protos/storage-service v0.3.0 diff --git a/go.sum b/go.sum index dac6464e130..4c1e165ce8c 100644 --- a/go.sum +++ b/go.sum @@ -1162,8 +1162,8 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202605122 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 h1:ecQYtdRA+NQLXf0aKYUMfcn1TRhcQ4RZCZzzadFsbSs= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 h1:/vkKPJoweLkRd56V4YHGRAtTG4+/JAlgklGEfvH6l4c= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e h1:dgw4Hi5YBqOrqkaPV5O5GSlQnUokQZjo7udfpvT3JeE= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 h1:NXKTdIESAiCkVnPS6dyZP+NXVek3GzXa6P4uFAs0o8Y= github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260821021345-a75f67fe965c h1:Eb2ogeKJhKKdzAU4EUDIiNqdtW6jPlrBzKci3BysduA= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 6738a5fc6fc..5bfc976065d 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -437,7 +437,7 @@ require ( github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260804200254-c1accce563a8 // indirect github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 // indirect - github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e // indirect github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 // indirect github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260821021345-a75f67fe965c // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index e647f0f74a2..8422f388bfc 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1447,8 +1447,8 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202605122 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 h1:ecQYtdRA+NQLXf0aKYUMfcn1TRhcQ4RZCZzzadFsbSs= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 h1:/vkKPJoweLkRd56V4YHGRAtTG4+/JAlgklGEfvH6l4c= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e h1:dgw4Hi5YBqOrqkaPV5O5GSlQnUokQZjo7udfpvT3JeE= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 h1:NXKTdIESAiCkVnPS6dyZP+NXVek3GzXa6P4uFAs0o8Y= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 0cdd3c563a3..02b4e4cf08a 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -503,7 +503,7 @@ require ( github.com/smartcontractkit/chainlink-protos/job-distributor v0.20.1-0.20260701185448-696c075849ea // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 // indirect - github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e // indirect github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 // indirect github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260821021345-a75f67fe965c // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 0c534a76c46..5e668f2bef6 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1685,8 +1685,8 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202605122 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 h1:ecQYtdRA+NQLXf0aKYUMfcn1TRhcQ4RZCZzzadFsbSs= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 h1:/vkKPJoweLkRd56V4YHGRAtTG4+/JAlgklGEfvH6l4c= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e h1:dgw4Hi5YBqOrqkaPV5O5GSlQnUokQZjo7udfpvT3JeE= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 h1:NXKTdIESAiCkVnPS6dyZP+NXVek3GzXa6P4uFAs0o8Y= diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index 0d2fca7cc51..df40ea22a2e 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -515,7 +515,7 @@ require ( github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d // indirect github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 // indirect github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 // indirect - github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 // indirect github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260821021345-a75f67fe965c // indirect github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 // indirect diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index a4c924ccb4a..1ba219bdef7 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1712,8 +1712,8 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202605122 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 h1:ecQYtdRA+NQLXf0aKYUMfcn1TRhcQ4RZCZzzadFsbSs= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 h1:/vkKPJoweLkRd56V4YHGRAtTG4+/JAlgklGEfvH6l4c= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e h1:dgw4Hi5YBqOrqkaPV5O5GSlQnUokQZjo7udfpvT3JeE= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 h1:NXKTdIESAiCkVnPS6dyZP+NXVek3GzXa6P4uFAs0o8Y= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index 344bd60de6f..e08284e818c 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -281,7 +281,7 @@ require ( github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.20.1-0.20260701185448-696c075849ea // indirect github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 // indirect - github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e // indirect github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 // indirect github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260513123719-d347eaf314e1 // indirect github.com/smartcontractkit/chainlink-sui/codec v0.0.0-20260720132736-e99278bfdc96 // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index 418e0dffb32..3825914068d 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1893,8 +1893,8 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202605122 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536 h1:ecQYtdRA+NQLXf0aKYUMfcn1TRhcQ4RZCZzzadFsbSs= github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260729184203-90b4cdd48536/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 h1:/vkKPJoweLkRd56V4YHGRAtTG4+/JAlgklGEfvH6l4c= -github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e h1:dgw4Hi5YBqOrqkaPV5O5GSlQnUokQZjo7udfpvT3JeE= +github.com/smartcontractkit/chainlink-protos/node-platform v0.2.1-0.20260908085225-914a0fc9200e/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-protos/orchestrator v0.11.0 h1:NXKTdIESAiCkVnPS6dyZP+NXVek3GzXa6P4uFAs0o8Y=