diff --git a/core/config/env/env.go b/core/config/env/env.go index 6edaeefb29a..01b3b26bf5a 100644 --- a/core/config/env/env.go +++ b/core/config/env/env.go @@ -14,6 +14,10 @@ var ( DatabaseAllowSimplePasswords = Var("CL_DATABASE_ALLOW_SIMPLE_PASSWORDS") IgnorePrereleaseVersionCheck = Var("CL_IGNORE_PRE_RELEASE_VERSION_CHECK") SkipAppVersionCheck = Var("CL_SKIP_APP_VERSION_CHECK") + // MeterRecordsEnabled gates emission of metering.v1.MeterRecord events for + // durable CRE resources. Accepts strconv.ParseBool values; default false. + // Temporary deploy gate; promotion to TOML config is tracked by SHARED-2718. + MeterRecordsEnabled = Var("CL_METER_RECORDS_ENABLED") DatabaseURL = Secret("CL_DATABASE_URL") DatabaseBackupURL = Secret("CL_DATABASE_BACKUP_URL") diff --git a/core/scripts/go.mod b/core/scripts/go.mod index ae0c4919e93..53515eafd39 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -43,13 +43,13 @@ require ( github.com/smartcontractkit/chain-selectors v1.0.101 github.com/smartcontractkit/chainlink-automation v0.8.1 github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260506144252-c100eabfda74 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 github.com/smartcontractkit/chainlink-common/keystore v1.2.0 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260522094612-5f9f748bd87a github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260609161557-8ceae53b8ab1 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260521215851-3fdbb363496f - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.2 github.com/smartcontractkit/chainlink-testing-framework/framework/components/dockercompose v0.1.23 @@ -494,6 +494,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/linking-service/go v0.0.0-20260512230622-65f10f4cd305 // indirect + github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2 // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.1-0.20260528221400-84746b70eeeb // indirect github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260331131315-f08a616d8dcd // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 024e9ed1067..1567c45f3d1 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1573,8 +1573,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260511195239-0f6e1b177fc7/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd h1:IMopuENFVS63AerRELdfWo6o60UNUidcldJOxJLmk24= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd/go.mod h1:SBN8Urnh5sQvrQRbSo1Nr8coWatHg8LZoPw3R/42sho= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b h1:UMQ+MwHI341h+yARqeKmY/cagkB/dH0J34aMoJG00io= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b/go.mod h1:GlEVw7ziizXoMfzl1onNSwansrVBLHhj5gUJlGQpb4I= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 h1:Ax+ifk2SCOanY6L9taki45SMPJQ0A1cZFceCVYPPaRw= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1/go.mod h1:319Wa+HHpx8nf/GUpktQN+VFrkGk8tU41ep84ylYmSM= github.com/smartcontractkit/chainlink-common/keystore v1.2.0 h1:1BH/b14CkGjArfzznlioQpIJiynECWVT48JUP9E277U= github.com/smartcontractkit/chainlink-common/keystore v1.2.0/go.mod h1:9R/74vN+bJ5PbkOyM/pUy/AeAZaRwYb/k4XPeXcbDio= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= @@ -1609,14 +1609,16 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a h1:alnfvQgCKPFqsfijZQnr6Sbus2GT5YZ9BT/KzVrbszE= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 h1:GNl+lLK0QCakqA1J1i7FoOai2JrOGOzNzSniMijaCjA= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 h1:SG+wAsNyAcA6Kk19ljuxi3HK9Ll2lpHik8OKoY4x7A0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36/go.mod h1:vL1bDgPSJjV0EqHYs4dDlR+EEE0cJchgvGLYXhwIjXY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 h1:NJdGFhzT6zMaTod4QkBqVD2sg0I25iw1boOYtTpEwRo= 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-20260615161920-ed05faf7f0a2 h1:zKfst+Wyr95rbxLNSsc4nKybe9tLXOC2r2YbDRQwNxM= +github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 h1:bnSl5p3mFekSJ6QcbZ1TmHn2ffYiX8xk6hNzVmyhstQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 54458798f4d..c2fcb2bd24c 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -662,6 +662,16 @@ func NewApplication(ctx context.Context, opts ApplicationOpts) (Application, err atomicSettings, creServices.OCRConfigService, cfg.Capabilities().Local(), + // Host-injected deployment/node metering identity for spawned capability + // LOOPs. Sourced once here from node config: product constant, env/zone + // from [Telemetry.ResourceAttributes], node_id = the same CSA pubkey hex + // the node uses for beholder auth and the engine's node_id. + standardcapabilities.NodeIdentity{ + Product: "cre", + Environment: cfg.Telemetry().ResourceAttributes()["env"], + Zone: cfg.Telemetry().ResourceAttributes()["zone"], + NodeID: csaPubKeyHex, + }, ) delegates[job.StandardCapabilities] = stdcapDelegate if creServices.SetDelegatesDeps != nil { diff --git a/core/services/cre/confidential_relay_peerid_test.go b/core/services/cre/confidential_relay_peerid_test.go index 8b92cfeaeec..77f376a5d74 100644 --- a/core/services/cre/confidential_relay_peerid_test.go +++ b/core/services/cre/confidential_relay_peerid_test.go @@ -51,6 +51,7 @@ func (s stubConfig) Workflows() config.Workflows { return nil } func (s stubConfig) CRE() config.CRE { return nil } func (s stubConfig) P2P() config.P2P { return s.p2p } func (s stubConfig) Sharding() config.Sharding { return nil } +func (s stubConfig) Telemetry() config.Telemetry { return nil } func peerIDFromByte(b byte) p2pkey.PeerID { var id p2pkey.PeerID diff --git a/core/services/cre/cre.go b/core/services/cre/cre.go index bf3d74cc9cb..8507ef1176e 100644 --- a/core/services/cre/cre.go +++ b/core/services/cre/cre.go @@ -18,12 +18,14 @@ import ( "github.com/smartcontractkit/chainlink-common/keystore/corekeys/p2pkey" "github.com/smartcontractkit/chainlink-common/keystore/corekeys/workflowkey" + "github.com/smartcontractkit/chainlink-common/pkg/beholder" "github.com/smartcontractkit/chainlink-common/pkg/billing" "github.com/smartcontractkit/chainlink-common/pkg/custmsg" "github.com/smartcontractkit/chainlink-common/pkg/diskmonitor" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/loop" nodeauthjwt "github.com/smartcontractkit/chainlink-common/pkg/nodeauth/jwt" + "github.com/smartcontractkit/chainlink-common/pkg/resourcemanager" commonsrv "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/services/orgresolver" "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" @@ -43,6 +45,7 @@ import ( remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" capStreams "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/ocr/capregconfig" @@ -284,6 +287,10 @@ func (s *Services) newSubservices( return srvs, nil } + // Build the syncer's base metering identity once from node config + CSA key; + // the handler resolves the workflow DON id later from the don notifier. + meterIdentity := newSyncerMeterIdentity(cfg, keyStore, lggr) + wfSyncer, billingClient, wfSyncerSrvcs, err := newWorkflowRegistrySyncer( cfg, relayerChainInterops, @@ -297,6 +304,7 @@ func (s *Services) newSubservices( opts.LimitsFactory, s.OrgResolver, s.GatewayConnectorWrapper, + meterIdentity, ) if err != nil { return nil, err @@ -325,6 +333,7 @@ type Config interface { CRE() config.CRE P2P() config.P2P Sharding() config.Sharding + Telemetry() config.Telemetry } // RelayerChainInterops is the minimal interface needed for relayer chain interops @@ -705,6 +714,67 @@ func newBillingClient(lggr logger.Logger, cfg Config, opts Opts) (metering.Billi return billing.NewWorkflowClient(lggr, cfg.Billing().URL(), workflowOpts...) } +// Metering identity sourcing constants. meterProduct is the deployment product +// dimension for all CRE metering records. The environment/zone dimensions are +// read from the node's [Telemetry.ResourceAttributes] map under these keys, the +// same map the host injects into trigger LOOPs (see core/services/ +// standardcapabilities), so a node's engine and its trigger LOOPs agree on the +// coarse identity. +const ( + meterProduct = "cre" + resourceAttrEnvKey = "env" + resourceAttrZoneKey = "zone" +) + +// nodeCSAPublicKeyHex returns the node's CSA public key as hex — the canonical +// node_id used uniformly across a node's engine and its trigger LOOPs (matching +// keystore.BuildBeholderAuth, which derives the beholder auth pubkey from the +// same default CSA key). A node has at most one CSA key; an empty string is +// returned (with a warning) when none is available, so metering degrades to an +// empty node_id rather than failing node startup. +func nodeCSAPublicKeyHex(keyStore Keystore, lggr logger.Logger) string { + keys, err := keyStore.CSA().GetAll() + if err != nil || len(keys) == 0 { + lggr.Warnw("no CSA key available for metering node_id; node_id will be empty", "err", err) + return "" + } + return keys[0].PublicKeyString() +} + +// newSyncerMeterIdentity builds the syncer's base metering identity from node +// config: product is the constant, environment/zone come from +// [Telemetry.ResourceAttributes], and node_id is the CSA public key. The +// workflow DON id (don_id) is resolved later by the handler from the don +// notifier (the engine runs on the workflow DON), so it is intentionally left +// empty here. Service/Resource/ResourceType are stamped by WithIdentity. +func newSyncerMeterIdentity(cfg Config, keyStore Keystore, lggr logger.Logger) resourcemanager.ResourceIdentity { + attrs := cfg.Telemetry().ResourceAttributes() + return resourcemanager.ResourceIdentity{ + Product: meterProduct, + Environment: attrs[resourceAttrEnvKey], + Zone: attrs[resourceAttrZoneKey], + NodeID: nodeCSAPublicKeyHex(keyStore, lggr), + } +} + +// meterRecordsEnabled reads the CL_METER_RECORDS_ENABLED env var gating emission of +// metering.v1.MeterRecord events. Unset, empty, or invalid values disable emission. +// Promotion of this gate to TOML config is tracked by SHARED-2718. +func meterRecordsEnabled(lggr logger.Logger) bool { + v := env.MeterRecordsEnabled.Get() + if v == "" { + return false + } + enabled, err := strconv.ParseBool(v) + if err != nil { + // Warn, not error, matching the capability producers: a bad gate value + // disables emission but must never disturb the node. + lggr.Warnw("Invalid CL_METER_RECORDS_ENABLED value; meter record emission disabled", "value", v, "err", err) + return false + } + return enabled +} + func newShardOrchestratorClient(cfg Config, lggr logger.Logger) (*shardorchestrator.Client, error) { shardID := cfg.Sharding().ShardIndex() if shardID == 0 { @@ -920,6 +990,7 @@ func newWorkflowRegistrySyncerV2( lf limits.Factory, orgResolver orgresolver.OrgResolver, gatewayConnectorWrapper *gatewayconnector.ServiceWrapper, + meterIdentity resourcemanager.ResourceIdentity, ) (syncerV2.WorkflowRegistrySyncer, []commonsrv.Service, error) { capCfg := cfg.Capabilities() wfReg := capCfg.WorkflowRegistry() @@ -1004,6 +1075,16 @@ func newWorkflowRegistrySyncerV2( syncerV2.WithLocalSecretOverrides(lggr, cfg.CRE().LocalSecretOverrides()), syncerV2.WithShardExecutionGuard(shardOrchestratorClient, shardingEnabled, shardIndex), syncerV2.WithShardRoutingSteady(shardRoutingSteady), + // The handler owns this ResourceManager's lifecycle (starts it, registers + // itself as the snapshotted Meterable, closes it). A positive + // SnapshotInterval enables the periodic absolute-state snapshot loop; the + // RM is otherwise a no-op when metering is disabled. + syncerV2.WithResourceManager(resourcemanager.NewResourceManager(lggr, resourcemanager.ResourceManagerConfig{ + Enabled: meterRecordsEnabled(lggr), + Emitter: beholder.GetEmitter(), + SnapshotInterval: resourcemanager.DefaultSnapshotInterval, + })), + syncerV2.WithIdentity(meterIdentity), } mc := capCfg.WorkflowRegistry().ModuleCache() @@ -1147,6 +1228,7 @@ func newWorkflowRegistrySyncer( lf limits.Factory, orgResolver orgresolver.OrgResolver, gatewayConnectorWrapper *gatewayconnector.ServiceWrapper, + meterIdentity resourcemanager.ResourceIdentity, ) (syncerV2.WorkflowRegistrySyncer, metering.BillingClient, []commonsrv.Service, error) { capCfg := cfg.Capabilities() @@ -1195,6 +1277,7 @@ func newWorkflowRegistrySyncer( lf, orgResolver, gatewayConnectorWrapper, + meterIdentity, ) return syncer, billingClient, srvcs, err default: diff --git a/core/services/standardcapabilities/delegate.go b/core/services/standardcapabilities/delegate.go index 6efab18b06f..687f45a59da 100644 --- a/core/services/standardcapabilities/delegate.go +++ b/core/services/standardcapabilities/delegate.go @@ -46,6 +46,23 @@ type RelayGetter interface { GetIDToRelayerMap() map[types.RelayID]loop.Relayer } +// NodeIdentity is the host-injected deployment/node metering identity that the +// Delegate stamps onto every spawned capability's StandardCapabilitiesDependencies, +// mirroring how the engine sources the same dimensions from node config (see +// core/services/cre). It is sourced once at node startup (where the CSA key and +// telemetry config are both available) so operators configure it in one place and +// a node's engine and its trigger LOOPs agree on product/environment/zone/node_id. +type NodeIdentity struct { + // Product is the deployment product, e.g. "cre". + Product string + // Environment is the deployment environment, from [Telemetry.ResourceAttributes]["env"]. + Environment string + // Zone is the deployment zone, from [Telemetry.ResourceAttributes]["zone"]. + Zone string + // NodeID is the node's CSA public key (hex), matching the engine's node_id. + NodeID string +} + type Delegate struct { logger logger.Logger ds sqlutil.DataSource @@ -66,6 +83,7 @@ type Delegate struct { creSettings core.SettingsBroadcaster ocrConfigService capregconfig.OCRConfigService localCfg coreconfig.LocalCapabilities + nodeIdentity NodeIdentity initErr error isNewlyCreatedJob bool @@ -98,6 +116,7 @@ func NewDelegate( creSettings core.SettingsBroadcaster, ocrConfigService capregconfig.OCRConfigService, localCfg coreconfig.LocalCapabilities, + nodeIdentity NodeIdentity, opts ...func(*gateway.RoundRobinSelector), ) *Delegate { initErr := registerOptionalMockStreamsTrigger(logger, localCfg, registry) @@ -125,6 +144,7 @@ func NewDelegate( creSettings: creSettings, ocrConfigService: ocrConfigService, localCfg: localCfg, + nodeIdentity: nodeIdentity, initErr: initErr, selectorOpts: opts, } @@ -380,6 +400,13 @@ func (d *Delegate) NewServices( OrgResolver: d.orgResolver, CRESettings: d.creSettings, TriggerEventStore: triggercap.NewTriggerEventStore(d.ds), + // Host-injected deployment/node metering identity, delivered to trigger + // LOOPs through the standardized Initialise channel. CapabilityDonID is + // resolved separately by #619 (InitMyDON) and intentionally left to it. + Product: d.nodeIdentity.Product, + Environment: d.nodeIdentity.Environment, + Zone: d.nodeIdentity.Zone, + NodeID: d.nodeIdentity.NodeID, } standardCapability := NewStandardCapabilities(log, command, configJSON, d.cfg, dependencies) diff --git a/core/services/standardcapabilities/standard_capabilities.go b/core/services/standardcapabilities/standard_capabilities.go index 1fe02f5783d..3263b183288 100644 --- a/core/services/standardcapabilities/standard_capabilities.go +++ b/core/services/standardcapabilities/standard_capabilities.go @@ -40,6 +40,13 @@ type StandardCapabilities struct { orgResolver orgresolver.OrgResolver creSettings core.SettingsBroadcaster triggerEventStore capabilities.EventStore + // Host-injected deployment/node metering identity, captured from the deps + // passed at construction and re-delivered to the LOOP through the deps built + // for Initialise below. + product string + environment string + zone string + nodeID string capabilitiesLoop *loop.StandardCapabilitiesService @@ -70,17 +77,50 @@ func NewStandardCapabilities( orgResolver: dependencies.OrgResolver, creSettings: dependencies.CRESettings, triggerEventStore: dependencies.TriggerEventStore, + product: dependencies.Product, + environment: dependencies.Environment, + zone: dependencies.Zone, + nodeID: dependencies.NodeID, stopChan: make(chan struct{}), readyChan: make(chan struct{}), } } +// initialiseDependencies builds the StandardCapabilitiesDependencies delivered to +// the capability LOOP via Initialise. It re-emits the host-injected metering +// identity (product/environment/zone/node_id) captured at construction so trigger +// LOOPs receive it through the standardized Initialise channel. +func (s *StandardCapabilities) initialiseDependencies() core.StandardCapabilitiesDependencies { + return core.StandardCapabilitiesDependencies{ + Config: s.config, + Store: s.store, + CapabilityRegistry: s.CapabilitiesRegistry, + RelayerSet: s.relayerSet, + OracleFactory: s.oracleFactory, + GatewayConnector: s.gatewayConnector, + P2PKeystore: s.keystore, + OrgResolver: s.orgResolver, + CRESettings: s.creSettings, + TriggerEventStore: s.triggerEventStore, + Product: s.product, + Environment: s.environment, + Zone: s.zone, + NodeID: s.nodeID, + } +} + func (s *StandardCapabilities) Start(ctx context.Context) error { return s.StartOnce("StandardCapabilities", func() error { envVars, err := plugins.ParseEnvFile(env.CapabilitiesPlugin.Env.Get()) if err != nil { return fmt.Errorf("failed to parse capabilities env file: %w", err) } + // Pass through the node's meter-record emission gate so capability LOOPPs + // inherit it: plugins.NewCmdFactory builds the child env from CmdConfig.Env + // only and does not inherit os.Environ(). + if v := env.MeterRecordsEnabled.Get(); v != "" { + envVars = append(envVars, fmt.Sprintf("%s=%s", env.MeterRecordsEnabled, v)) + } cmdFn, opts, err := s.pluginRegistrar.RegisterLOOP(plugins.CmdConfig{ ID: s.log.Name(), Cmd: s.command, @@ -112,18 +152,7 @@ func (s *StandardCapabilities) Start(ctx context.Context) error { return } - dependencies := core.StandardCapabilitiesDependencies{ - Config: s.config, - Store: s.store, - CapabilityRegistry: s.CapabilitiesRegistry, - RelayerSet: s.relayerSet, - OracleFactory: s.oracleFactory, - GatewayConnector: s.gatewayConnector, - P2PKeystore: s.keystore, - OrgResolver: s.orgResolver, - CRESettings: s.creSettings, - TriggerEventStore: s.triggerEventStore, - } + dependencies := s.initialiseDependencies() if err = s.capabilitiesLoop.Service.Initialise(cctx, dependencies); err != nil { s.log.Errorf("error initialising standard capabilities service: %v", err) return diff --git a/core/services/standardcapabilities/standard_capabilities_test.go b/core/services/standardcapabilities/standard_capabilities_test.go index ce0159b54f7..622166a54be 100644 --- a/core/services/standardcapabilities/standard_capabilities_test.go +++ b/core/services/standardcapabilities/standard_capabilities_test.go @@ -74,6 +74,7 @@ func TestStandardCapabilities_ForwardsPluginEnvFile(t *testing.T) { t.Run("env file unset results in empty CmdConfig.Env", func(t *testing.T) { t.Setenv(string(env.CapabilitiesPlugin.Env), "") + t.Setenv(string(env.MeterRecordsEnabled), "") cfg, err := startAndCapture(t) require.Error(t, err, "expected synthetic Start failure from capturingRegistrar") @@ -83,6 +84,33 @@ func TestStandardCapabilities_ForwardsPluginEnvFile(t *testing.T) { "no operator-provided env vars should be forwarded when CL_CAPABILITIES_ENV is unset") }) + t.Run("CL_METER_RECORDS_ENABLED set on the node is passed through to the LOOPP", func(t *testing.T) { + envFile := writeEnvFile(t, "FOO=bar\n") + t.Setenv(string(env.CapabilitiesPlugin.Env), envFile) + t.Setenv(string(env.MeterRecordsEnabled), "true") + + cfg, err := startAndCapture(t) + require.Error(t, err, "expected synthetic Start failure from capturingRegistrar") + require.Contains(t, err.Error(), capturingRegistrarErr) + + require.Contains(t, cfg.Env, "CL_METER_RECORDS_ENABLED=true", + "the node's meter-record emission gate must reach capability LOOPPs, which do not inherit os.Environ()") + require.Contains(t, cfg.Env, "FOO=bar", + "the pass-through must not displace entries from the operator-supplied env file") + }) + + t.Run("CL_METER_RECORDS_ENABLED unset is not forwarded", func(t *testing.T) { + t.Setenv(string(env.CapabilitiesPlugin.Env), "") + t.Setenv(string(env.MeterRecordsEnabled), "") + + cfg, err := startAndCapture(t) + require.Error(t, err, "expected synthetic Start failure from capturingRegistrar") + require.Contains(t, err.Error(), capturingRegistrarErr) + + require.Empty(t, cfg.Env, + "no CL_METER_RECORDS_ENABLED entry should be forwarded when the gate is unset on the node") + }) + t.Run("missing env file fails Start before RegisterLOOP", func(t *testing.T) { missingPath := filepath.Join(t.TempDir(), "does-not-exist.env") t.Setenv(string(env.CapabilitiesPlugin.Env), missingPath) @@ -106,6 +134,40 @@ func TestStandardCapabilities_ForwardsPluginEnvFile(t *testing.T) { }) } +// TestStandardCapabilities_ForwardsNodeMeteringIdentity asserts that the +// host-injected deployment/node metering identity (Product/Environment/Zone/ +// NodeID) supplied on the deps at construction is re-delivered, unchanged, on the +// StandardCapabilitiesDependencies handed to the capability LOOP at Initialise. +// This is the standardized Initialise channel that gives trigger LOOPs the same +// coarse metering identity the node's engine uses. +func TestStandardCapabilities_ForwardsNodeMeteringIdentity(t *testing.T) { + want := core.StandardCapabilitiesDependencies{ + Product: "cre", + Environment: "staging", + Zone: "wf-zone-a", + NodeID: "0a1b2c3d4e5f", + } + + std := NewStandardCapabilities( + logger.TestLogger(t), + "not/found/path/to/binary", + "{}", + &capturingRegistrar{}, + want, + ) + + got := std.initialiseDependencies() + + require.Equal(t, want.Product, got.Product, + "the host-injected product must reach the capability LOOP via Initialise") + require.Equal(t, want.Environment, got.Environment, + "the host-injected environment must reach the capability LOOP via Initialise") + require.Equal(t, want.Zone, got.Zone, + "the host-injected zone must reach the capability LOOP via Initialise") + require.Equal(t, want.NodeID, got.NodeID, + "the host-injected node_id (CSA pubkey) must reach the capability LOOP via Initialise") +} + const capturingRegistrarErr = "capturingRegistrar: stop after capture" // capturingRegistrar records the CmdConfig handed to RegisterLOOP and then diff --git a/core/services/workflows/syncer/v2/engine_registry.go b/core/services/workflows/syncer/v2/engine_registry.go index afd712afe2a..6dd9adfc428 100644 --- a/core/services/workflows/syncer/v2/engine_registry.go +++ b/core/services/workflows/syncer/v2/engine_registry.go @@ -18,7 +18,7 @@ type ServiceWithMetadata struct { services.Service } -// engineEntry holds the engine and its associated source for internal storage +// engineEntry holds the engine and its associated source for internal storage. type engineEntry struct { engine services.Service source string diff --git a/core/services/workflows/syncer/v2/handler.go b/core/services/workflows/syncer/v2/handler.go index 8d38502915b..5c2a8c0fbf1 100644 --- a/core/services/workflows/syncer/v2/handler.go +++ b/core/services/workflows/syncer/v2/handler.go @@ -7,7 +7,9 @@ import ( "fmt" "io" "maps" + "strconv" "sync" + "sync/atomic" "time" "go.opentelemetry.io/otel" @@ -18,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/contexts" "github.com/smartcontractkit/chainlink-common/pkg/custmsg" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/resourcemanager" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/services/orgresolver" "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" @@ -27,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/workflows/dontime" generichost "github.com/smartcontractkit/chainlink-common/pkg/workflows/host" "github.com/smartcontractkit/chainlink-common/pkg/workflows/wasm/host" + meteringpb "github.com/smartcontractkit/chainlink-protos/metering/go" "github.com/smartcontractkit/chainlink-common/keystore/corekeys/workflowkey" "github.com/smartcontractkit/chainlink/v2/core/capabilities" @@ -63,6 +67,17 @@ type DrainableService interface { var ErrDrainInProgress = errors.New("drain in progress") +// Service-level metering identity constants for the workflow syncer. Service is +// the stable service constant; Resource/ResourceType identify the workflow_specs_v2 +// pool and its billing unit. The coarse deployment/node/DON dimensions (product, +// environment, zone, don_id, node_id) are supplied at construction via +// WithIdentity; resource_id is set per workflow via WithResourceID. +const ( + meterService = "workflow-syncer-v2" + meterResource = "workflow_specs_v2" + meterResourceType = "operations" +) + // eventHandler is a handler for WorkflowRegistryEvent events. Each event type has a corresponding method that handles the event. type eventHandler struct { services.Service @@ -86,8 +101,25 @@ type eventHandler struct { workflowEncryptionKey workflowkey.Key workflowDonSubscriber capabilities.DonSubscriber billingClient metering.BillingClient - orgResolver orgresolver.OrgResolver - secretsFetcher v2.SecretsFetcher + resourceManager *resourcemanager.ResourceManager + // meterIdentity is the base metering identity for this node's syncer: the + // six coarse dimensions (product, environment, zone, don_id, node_id, + // service) plus the workflow_specs_v2 resource/resource_type. resource_id is + // set per workflow via WithResourceID. It is populated by WithIdentity in + // cre.go from node TOML + CSA + the workflow DON; Service/Resource/ + // ResourceType are forced to the syncer constants regardless of the option. + meterIdentity resourcemanager.ResourceIdentity + // resolvedDonID holds the workflow DON id once resolved from the don notifier + // at start (the engine runs on the workflow DON). It is resolved + // asynchronously so node boot is not blocked while waiting for the DON to be + // set, and read on the hot snapshot/emit paths; an atomic keeps that read + // lock-free. Nil until resolved; baseIdentity folds it into meterIdentity. + resolvedDonID atomic.Pointer[string] + // rmUnregister removes this handler from the ResourceManager's snapshot + // registry; set in start, called in close. Nil until started. + rmUnregister func() + orgResolver orgresolver.OrgResolver + secretsFetcher v2.SecretsFetcher // localSecretOverrides is keyed by owner address; values are secret id -> secret value localSecretOverrides map[string]map[string]string @@ -151,6 +183,30 @@ func WithBillingClient(client metering.BillingClient) func(*eventHandler) { } } +// WithResourceManager overrides the default (disabled) ResourceManager used to +// emit metering.v1.MeterRecord events for the workflow_specs_v2 storage lifecycle. +func WithResourceManager(rm *resourcemanager.ResourceManager) func(*eventHandler) { + return func(e *eventHandler) { + e.resourceManager = rm + } +} + +// WithIdentity supplies the coarse metering identity dimensions sourced once at +// construction in cre.go (product, environment, zone from node TOML; node_id = +// CSA pubkey hex; don_id = the workflow DON the engine runs on). The syncer +// Service/Resource/ResourceType are stable constants and always overwrite +// whatever the caller passes, so the option carries only the deployment/node/DON +// dimensions; resource_id is left empty and set per workflow via WithResourceID. +func WithIdentity(id resourcemanager.ResourceIdentity) func(*eventHandler) { + return func(e *eventHandler) { + id.Service = meterService + id.Resource = meterResource + id.ResourceType = meterResourceType + id.ResourceID = "" + e.meterIdentity = id + } +} + func WithShardExecutionGuard(client shardorchestrator.ClientInterface, shardingEnabled bool, shardID uint32) func(*eventHandler) { return func(e *eventHandler) { e.shardOrchestratorClient = client @@ -302,7 +358,15 @@ func NewEventHandler( workflowArtifactsStore: workflowArtifacts, workflowEncryptionKey: workflowEncryptionKey, workflowDonSubscriber: workflowDonSubscriber, - tracer: noop.NewTracerProvider().Tracer(""), // default to noop, enable via WithDebugMode + resourceManager: resourcemanager.NewResourceManager(lggr, resourcemanager.ResourceManagerConfig{}), // default to disabled, enable via WithResourceManager + // Default identity carries only the service-level constants; the coarse + // deployment/node/DON dimensions are filled in by WithIdentity in cre.go. + meterIdentity: resourcemanager.ResourceIdentity{ + Service: meterService, + Resource: meterResource, + ResourceType: meterResourceType, + }, + tracer: noop.NewTracerProvider().Tracer(""), // default to noop, enable via WithDebugMode } metricsInst, metricsErr := newMetrics() if metricsErr != nil { @@ -323,20 +387,72 @@ func NewEventHandler( return eh, nil } -func (h *eventHandler) start(_ context.Context) error { +func (h *eventHandler) start(ctx context.Context) error { if h.moduleLRU != nil { h.moduleLRU.Start() } + // The handler is the single owner of its ResourceManager: it starts the RM + // (which owns the snapshot tick) and registers itself as the Meterable that + // is snapshotted. The RM is a no-op when metering is disabled, so this is + // safe regardless of the gate. + if h.resourceManager != nil { + if err := h.resourceManager.Start(ctx); err != nil { + return fmt.Errorf("failed to start resource manager: %w", err) + } + h.rmUnregister = h.resourceManager.Register(h) + h.resolveWorkflowDonID() + } return nil } +// resolveWorkflowDonID asynchronously resolves the workflow DON id (the engine +// runs on the workflow DON) and folds it into the metering identity. It +// subscribes to the don notifier and stores the first DON's id, so node boot is +// never blocked waiting for the DON to be set. Until it resolves, emitted records +// carry an empty don_id (the host-injection fallback semantics); once resolved, +// every subsequent record and snapshot carries it. Resolution happens at most +// once per start. +func (h *eventHandler) resolveWorkflowDonID() { + if h.workflowDonSubscriber == nil || h.resolvedDonID.Load() != nil { + return + } + h.eng.Go(func(ctx context.Context) { + ch, unsubscribe, err := h.workflowDonSubscriber.Subscribe(ctx) + if err != nil { + h.lggr.Warnw("failed to subscribe to workflow DON for metering identity; don_id will be empty", "err", err) + return + } + defer unsubscribe() + select { + case <-ctx.Done(): + return + case don := <-ch: + donID := strconv.FormatUint(uint64(don.ID), 10) + h.resolvedDonID.Store(&donID) + h.lggr.Debugw("resolved workflow DON id for metering identity", "donID", donID) + } + }) +} + func (h *eventHandler) close() error { if h.moduleLRU != nil { h.moduleLRU.Close() } es := h.engineRegistry.PopAll() - cs := make([]io.Closer, 0, len(es)+1) + // Emit a graceful-close RELEASE for each still-running engine before popping + // them, so a clean shutdown pairs every snapshot RESERVE. Best-effort and + // fail-open: metering must never gate shutdown. + h.emitGracefulCloseReleases(context.Background(), es) + // Stop snapshotting this handler, then close the ResourceManager service. + if h.rmUnregister != nil { + h.rmUnregister() + h.rmUnregister = nil + } + cs := make([]io.Closer, 0, len(es)+2) cs = append(cs, h.engineLimiters) + if h.resourceManager != nil { + cs = append(cs, h.resourceManager) + } for _, e := range es { cs = append(cs, e) } @@ -496,7 +612,7 @@ func (h *eventHandler) Handle(ctx context.Context, event Event) error { } }() - if herr = h.workflowDeletedEvent(ctx, payload); herr != nil { + if herr = h.workflowDeletedEvent(ctx, payload, WorkflowDeleted); herr != nil { if errors.Is(herr, ErrDrainInProgress) { logCustMsg(ctx, cma, fmt.Sprintf("workflow deletion deferred: %v", herr), h.lggr) } else { @@ -520,7 +636,7 @@ func (h *eventHandler) workflowActivatedEvent( ) error { // Convert WorkflowActivatedEvent to WorkflowRegisteredEvent since they have identical fields registeredPayload := WorkflowRegisteredEvent(payload) - return h.workflowRegisteredEvent(ctx, registeredPayload) + return h.workflowRegisteredEvent(ctx, registeredPayload, WorkflowActivated) } // workflowRegisteredEvent handles the WorkflowRegisteredEvent event type. @@ -528,9 +644,13 @@ func (h *eventHandler) workflowActivatedEvent( // workflowRegisteredEvent proceeds in two phases: // - phase 1 synchronizes the database state // - phase 2 synchronizes the state of the engine registry. +// originatingEvent names the event that triggered this call (e.g. WorkflowActivated +// when delegated from workflowActivatedEvent) and identifies the originating intent +// in emitted meter records. func (h *eventHandler) workflowRegisteredEvent( ctx context.Context, payload WorkflowRegisteredEvent, + originatingEvent WorkflowRegistryEventName, ) error { ctx, span := h.tracer.Start(ctx, "workflow_registered", trace.WithAttributes( @@ -553,6 +673,7 @@ func (h *eventHandler) workflowRegisteredEvent( if innerErr != nil { return innerErr } + h.emitMeterRecord(ctx, meteringpb.MeterAction_METER_ACTION_RESERVE, originatingEvent, payload.WorkflowID.Hex()) spec = newSpec case spec.WorkflowID != payload.WorkflowID.Hex(): @@ -560,6 +681,7 @@ func (h *eventHandler) workflowRegisteredEvent( if innerErr != nil { return innerErr } + h.emitMeterRecord(ctx, meteringpb.MeterAction_METER_ACTION_RESERVE, originatingEvent, payload.WorkflowID.Hex()) spec = newSpec case spec.Status != status: @@ -567,6 +689,7 @@ func (h *eventHandler) workflowRegisteredEvent( if _, innerErr := h.workflowArtifactsStore.UpsertWorkflowSpec(ctx, spec); innerErr != nil { return fmt.Errorf("failed to update workflow spec: %w", innerErr) } + h.emitMeterRecord(ctx, meteringpb.MeterAction_METER_ACTION_UPDATE, originatingEvent, payload.WorkflowID.Hex()) } // Next, let's synchronize the engine. @@ -813,13 +936,17 @@ func (h *eventHandler) workflowPausedEvent( ctx context.Context, payload WorkflowPausedEvent, ) error { - return h.workflowDeletedEvent(ctx, WorkflowDeletedEvent{WorkflowID: payload.WorkflowID}) + return h.workflowDeletedEvent(ctx, WorkflowDeletedEvent{WorkflowID: payload.WorkflowID}, WorkflowPaused) } // workflowDeletedEvent handles the WorkflowDeletedEvent event type. This method must remain idempotent. +// originatingEvent names the event that triggered this call (e.g. WorkflowPaused +// when delegated from workflowPausedEvent) and identifies the originating intent +// in emitted meter records. func (h *eventHandler) workflowDeletedEvent( ctx context.Context, payload WorkflowDeletedEvent, + originatingEvent WorkflowRegistryEventName, ) error { // The order in the handler is slightly different to the order in `tryEngineCleanup`. // This is because the engine requires its corresponding DB record to be present to be successfully @@ -858,6 +985,7 @@ func (h *eventHandler) workflowDeletedEvent( if err := h.workflowArtifactsStore.DeleteWorkflowArtifacts(ctx, payload.WorkflowID.Hex()); err != nil { return fmt.Errorf("failed to delete workflow artifacts: %w", err) } + h.emitMeterRecord(ctx, meteringpb.MeterAction_METER_ACTION_RELEASE, originatingEvent, workflowID) h.cleanupModuleCache(payload.WorkflowID.Hex()) @@ -878,6 +1006,100 @@ func (h *eventHandler) workflowDeletedEvent( return nil } +// emitMeterRecord emits a metering.v1.MeterRecord for a workflow_specs_v2 mutation. +// Callers must invoke it only after the database mutation has succeeded. Emission is +// fail-open: it never returns an error and must never affect event handling. The +// idempotency key is deterministic over workflowID, originatingEvent, and action, so +// a retried event emits a record the billing consumer dedups against the original. +// +// Known lost-record windows (consumer-side reconciliation tracked by SHARED-2141): +// - A node crash between the workflow spec database write and this emit loses the +// record (e.g. the RESERVE for a new spec): the syncer has no recovery path that +// re-emits records for mutations persisted before the crash. +// - A workflow paused or deleted while the node is down loses its RELEASE: +// reconciliation only synthesizes events for engine-registry entries, and the +// restarted node has no engine registered for the already-removed workflow. +func (h *eventHandler) emitMeterRecord(ctx context.Context, action meteringpb.MeterAction, originatingEvent WorkflowRegistryEventName, workflowID string) { + if h.resourceManager == nil { + return + } + // resource_id = workflow_id (the syncer has no shared physical resource); the + // originating event name is the event identity that distinguishes lifecycle + // edges in the idempotency key. + identity := h.baseIdentity().WithResourceID(workflowID) + h.resourceManager.EmitMeterRecord(ctx, identity, action, + resourcemanager.NewUtilization(identity, action, 1, string(originatingEvent))) +} + +// baseIdentity returns the handler's metering identity with the workflow DON id +// folded in once it has been resolved from the don notifier. meterIdentity itself +// is immutable after construction (set via WithIdentity); only the DON id is +// learned later, so it is read from an atomic and overlaid here. +func (h *eventHandler) baseIdentity() resourcemanager.ResourceIdentity { + id := h.meterIdentity + if donID := h.resolvedDonID.Load(); donID != nil { + id.DONID = *donID + } + return id +} + +// ResourceIdentity implements resourcemanager.Meterable: it returns the syncer's +// base identity (six coarse dimensions + workflow_specs_v2 resource/resource_type, +// resource_id empty). The ResourceManager uses it as the top-level identity of +// each emitted Snapshot. +func (h *eventHandler) ResourceIdentity() resourcemanager.ResourceIdentity { + return h.baseIdentity() +} + +// GetUtilization implements resourcemanager.Meterable: it returns one +// SnapshotEntry per running engine, with Value 1 (each running workflow holds one +// workflow_specs_v2 reservation). It is a pure in-memory read over the engine +// registry — each entry's resource_id is the workflow_id, which fully identifies +// the resource (Design 1; reconciling persisted-but-not-running specs is the +// Design 2 follow-up, see snapshotDesign2Followup below). +func (h *eventHandler) GetUtilization(_ context.Context) []resourcemanager.SnapshotEntry { + base := h.baseIdentity() + engines := h.engineRegistry.GetAll() + entries := make([]resourcemanager.SnapshotEntry, 0, len(engines)) + for _, e := range engines { + workflowID := e.WorkflowID.Hex() + entries = append(entries, resourcemanager.SnapshotEntry{ + Identity: base.WithResourceID(workflowID), + Value: 1, + }) + } + return entries +} + +// snapshotDesign2Followup documents the deferred reconciliation work. Design 1 +// (implemented here) snapshots only engines that are currently running in this +// node's engine registry. Design 2 — the follow-up — would additionally +// enumerate persisted-but-not-running specs via WorkflowSpecsDS.ListAll so that +// snapshots also account for specs the database has but for which no engine is +// live (e.g. paused specs, or specs whose engine failed to start). That requires +// a store-backed read on the tick (not a cheap in-memory snapshot) and a policy +// for the utilization value of a non-running spec, so it is intentionally left +// out of the in-memory GetUtilization above. Tracked as the syncer Design 2 +// follow-up. +const snapshotDesign2Followup = "Design 2: reconcile persisted-but-not-running specs via WorkflowSpecsDS.ListAll" + +// emitGracefulCloseReleases emits a RELEASE meter record for every engine still +// in the registry at shutdown, so a clean close pairs every snapshot RESERVE. It +// is fail-open like all metering emission. Called from close before the engines +// are popped. +func (h *eventHandler) emitGracefulCloseReleases(ctx context.Context, engines []ServiceWithMetadata) { + if h.resourceManager == nil { + return + } + base := h.baseIdentity() + for _, e := range engines { + workflowID := e.WorkflowID.Hex() + identity := base.WithResourceID(workflowID) + h.resourceManager.EmitMeterRecord(ctx, identity, meteringpb.MeterAction_METER_ACTION_RELEASE, + resourcemanager.NewUtilization(identity, meteringpb.MeterAction_METER_ACTION_RELEASE, 1, string(WorkflowDeleted))) + } +} + // tryEngineCleanup attempts to stop the workflow engine for the given workflow ID. Does nothing if the // workflow engine is not running. func (h *eventHandler) tryEngineCleanup(workflowID types.WorkflowID) error { @@ -1001,7 +1223,7 @@ func (h *eventHandler) tryEngineCreate(ctx context.Context, spec *job.WorkflowSp } } - // Engine is fully initialized, add to registry with source tracking + // Engine is fully initialized, add to registry with source tracking. if err := h.engineRegistry.Add(wid, source, engine); err != nil { if closeErr := engine.Close(); closeErr != nil { return fmt.Errorf("failed to close workflow engine: %w during invariant violation: %w", closeErr, err) diff --git a/core/services/workflows/syncer/v2/handler_metering_test.go b/core/services/workflows/syncer/v2/handler_metering_test.go new file mode 100644 index 00000000000..d48ed3d90fc --- /dev/null +++ b/core/services/workflows/syncer/v2/handler_metering_test.go @@ -0,0 +1,462 @@ +package v2 + +import ( + "context" + "encoding/hex" + "errors" + "math/big" + "sync" + "testing" + "time" + + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + "github.com/smartcontractkit/chainlink-common/keystore/corekeys/workflowkey" + "github.com/smartcontractkit/chainlink-common/pkg/custmsg" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/resourcemanager" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" + pkgworkflows "github.com/smartcontractkit/chainlink-common/pkg/workflows" + meteringpb "github.com/smartcontractkit/chainlink-protos/metering/go" + + "github.com/smartcontractkit/chainlink/v2/core/capabilities" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/workflows/ratelimiter" + workflowstore "github.com/smartcontractkit/chainlink/v2/core/services/workflows/store" + "github.com/smartcontractkit/chainlink/v2/core/services/workflows/syncerlimiter" + "github.com/smartcontractkit/chainlink/v2/core/services/workflows/types" + v2 "github.com/smartcontractkit/chainlink/v2/core/services/workflows/v2" +) + +// recordingEmitter is a fake resourcemanager.Emitter that decodes and stores +// every emitted MeterRecord. If err is set, Emit fails instead. +type recordingEmitter struct { + mu sync.Mutex + err error + records []*meteringpb.MeterRecord +} + +func (r *recordingEmitter) Emit(_ context.Context, body []byte, _ ...any) error { + r.mu.Lock() + defer r.mu.Unlock() + if r.err != nil { + return r.err + } + var record meteringpb.MeterRecord + if err := proto.Unmarshal(body, &record); err != nil { + return err + } + r.records = append(r.records, &record) + return nil +} + +func (r *recordingEmitter) Records() []*meteringpb.MeterRecord { + r.mu.Lock() + defer r.mu.Unlock() + records := make([]*meteringpb.MeterRecord, len(r.records)) + copy(records, r.records) + return records +} + +func newMeteringResourceManager(t *testing.T, enabled bool, emitter resourcemanager.Emitter) *resourcemanager.ResourceManager { + t.Helper() + return resourcemanager.NewResourceManager(commonlogger.Test(t), resourcemanager.ResourceManagerConfig{ + Enabled: enabled, + Emitter: emitter, + }) +} + +// recordingSnapshotEmitter is a fake resourcemanager.Emitter that decodes and +// stores every emitted MeterSnapshot (one per active resource). Used to drive +// the Meterable snapshot path. +type recordingSnapshotEmitter struct { + mu sync.Mutex + snapshots []*meteringpb.MeterSnapshot +} + +func (r *recordingSnapshotEmitter) Emit(_ context.Context, body []byte, _ ...any) error { + r.mu.Lock() + defer r.mu.Unlock() + var snapshot meteringpb.MeterSnapshot + if err := proto.Unmarshal(body, &snapshot); err != nil { + return err + } + r.snapshots = append(r.snapshots, &snapshot) + return nil +} + +func (r *recordingSnapshotEmitter) Snapshots() []*meteringpb.MeterSnapshot { + r.mu.Lock() + defer r.mu.Unlock() + snapshots := make([]*meteringpb.MeterSnapshot, len(r.snapshots)) + copy(snapshots, r.snapshots) + return snapshots +} + +func newMeteringTestHandler(t *testing.T, artifactsStore WorkflowArtifactsStore, rm *resourcemanager.ResourceManager) *eventHandler { + t.Helper() + lggr := logger.TestLogger(t) + lf := limits.Factory{Logger: lggr} + registry := capabilities.NewRegistry(lggr) + registry.SetLocalRegistry(&capabilities.TestMetadataRegistry{}) + limiters, err := v2.NewLimiters(lf, nil) + require.NoError(t, err) + rl, err := ratelimiter.NewRateLimiter(rlConfig) + require.NoError(t, err) + workflowLimits, err := syncerlimiter.NewWorkflowLimits(lggr, wlConfig, lf) + require.NoError(t, err) + + h, err := NewEventHandler( + lggr, + workflowstore.NewInMemoryStore(lggr, clockwork.NewFakeClock()), + nil, + true, + registry, + NewEngineRegistry(), + custmsg.NewLabeler(), + limiters, + nil, + rl, + workflowLimits, + artifactsStore, + workflowkey.MustNewXXXTestingOnly(big.NewInt(1)), + &testDonNotifier{}, + WithResourceManager(rm), + WithEngineFactoryFn(mockEngineFactory), + ) + require.NoError(t, err) + return h +} + +func requireMeterRecord(t *testing.T, record *meteringpb.MeterRecord, action meteringpb.MeterAction, originatingEvent WorkflowRegistryEventName, workflowID string) { + t.Helper() + require.NotNil(t, record.Identity) + assert.Equal(t, "workflow-syncer-v2", record.Identity.Service) + assert.Equal(t, "workflow_specs_v2", record.Identity.Resource) + assert.Equal(t, "operations", record.Identity.ResourceType) + // resource_id = workflow_id for the syncer (no shared physical resource). + assert.Equal(t, workflowID, record.Identity.ResourceId) + assert.Equal(t, action, record.Action) + assert.NotNil(t, record.Timestamp) + require.NotNil(t, record.Utilization) + assert.Equal(t, int64(1), record.Utilization.Value) + // The idempotency key is derived over the full identity (with resource_id set + // to workflow_id) and the originating event name as the event identity. + wantIdentity := record.Identity + wantID := resourcemanager.ResourceIdentity{ + Product: wantIdentity.Product, + Environment: wantIdentity.Environment, + Zone: wantIdentity.Zone, + DONID: wantIdentity.DonId, + NodeID: wantIdentity.NodeId, + Service: wantIdentity.Service, + Resource: wantIdentity.Resource, + ResourceType: wantIdentity.ResourceType, + ResourceID: wantIdentity.ResourceId, + } + assert.Equal(t, resourcemanager.IdempotencyKey(wantID, action, string(originatingEvent)), record.Utilization.IdempotencyKey) +} + +func Test_meterRecords(t *testing.T) { + t.Parallel() + + wfOwner := []byte{0xaa, 0xbb, 0xcc, 0xdd} + wfOwnerHex := hex.EncodeToString(wfOwner) + + t.Run("registered event creating a new spec emits RESERVE", func(t *testing.T) { + t.Parallel() + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{}, newMeteringResourceManager(t, true, emitter)) + + wfID := types.WorkflowID{1} + err := h.workflowRegisteredEvent(t.Context(), WorkflowRegisteredEvent{ + Status: WorkflowStatusPaused, + WorkflowID: wfID, + WorkflowOwner: wfOwner, + WorkflowName: "wf-name", + }, WorkflowRegistered) + require.NoError(t, err) + + records := emitter.Records() + require.Len(t, records, 1) + requireMeterRecord(t, records[0], meteringpb.MeterAction_METER_ACTION_RESERVE, WorkflowRegistered, wfID.Hex()) + }) + + t.Run("retried registered event emits an identical idempotency key", func(t *testing.T) { + t.Parallel() + emitter := &recordingEmitter{} + // The stub never returns a stored spec, so each call replays the + // new-spec path exactly as a reprocessed event would. + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{}, newMeteringResourceManager(t, true, emitter)) + + event := WorkflowRegisteredEvent{ + Status: WorkflowStatusPaused, + WorkflowID: types.WorkflowID{2}, + WorkflowOwner: wfOwner, + WorkflowName: "wf-name", + } + require.NoError(t, h.workflowRegisteredEvent(t.Context(), event, WorkflowRegistered)) + require.NoError(t, h.workflowRegisteredEvent(t.Context(), event, WorkflowRegistered)) + + records := emitter.Records() + require.Len(t, records, 2) + assert.Equal(t, records[0].Utilization.IdempotencyKey, records[1].Utilization.IdempotencyKey) + }) + + t.Run("activated event with existing spec emits UPDATE", func(t *testing.T) { + t.Parallel() + binary := []byte("binary-data") + config := []byte("") + giveWFID, err := pkgworkflows.GenerateWorkflowID(wfOwner, "wf-name", binary, config, "") + require.NoError(t, err) + wfID := types.WorkflowID(giveWFID) + + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{ + spec: &job.WorkflowSpec{ + Workflow: hex.EncodeToString(binary), + Config: string(config), + WorkflowID: wfID.Hex(), + Status: job.WorkflowSpecStatusPaused, + WorkflowOwner: wfOwnerHex, + WorkflowName: "wf-name", + }, + }, newMeteringResourceManager(t, true, emitter)) + + err = h.workflowActivatedEvent(t.Context(), WorkflowActivatedEvent{ + Status: WorkflowStatusActive, + WorkflowID: wfID, + WorkflowOwner: wfOwner, + WorkflowName: "wf-name", + }) + require.NoError(t, err) + + records := emitter.Records() + require.Len(t, records, 1) + requireMeterRecord(t, records[0], meteringpb.MeterAction_METER_ACTION_UPDATE, WorkflowActivated, wfID.Hex()) + }) + + t.Run("paused event emits RELEASE after artifacts are deleted", func(t *testing.T) { + t.Parallel() + wfID := types.WorkflowID{3} + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{ + spec: &job.WorkflowSpec{ + WorkflowID: wfID.Hex(), + Status: job.WorkflowSpecStatusActive, + WorkflowOwner: wfOwnerHex, + }, + }, newMeteringResourceManager(t, true, emitter)) + + require.NoError(t, h.workflowPausedEvent(t.Context(), WorkflowPausedEvent{WorkflowID: wfID})) + + records := emitter.Records() + require.Len(t, records, 1) + requireMeterRecord(t, records[0], meteringpb.MeterAction_METER_ACTION_RELEASE, WorkflowPaused, wfID.Hex()) + }) + + t.Run("deleted event emits RELEASE after artifacts are deleted", func(t *testing.T) { + t.Parallel() + wfID := types.WorkflowID{4} + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{ + spec: &job.WorkflowSpec{ + WorkflowID: wfID.Hex(), + Status: job.WorkflowSpecStatusActive, + WorkflowOwner: wfOwnerHex, + }, + }, newMeteringResourceManager(t, true, emitter)) + + require.NoError(t, h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: wfID}, WorkflowDeleted)) + + records := emitter.Records() + require.Len(t, records, 1) + requireMeterRecord(t, records[0], meteringpb.MeterAction_METER_ACTION_RELEASE, WorkflowDeleted, wfID.Hex()) + }) + + t.Run("no record when creating a new spec fails", func(t *testing.T) { + t.Parallel() + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{upsertErr: assert.AnError}, newMeteringResourceManager(t, true, emitter)) + + err := h.workflowRegisteredEvent(t.Context(), WorkflowRegisteredEvent{ + Status: WorkflowStatusPaused, + WorkflowID: types.WorkflowID{5}, + WorkflowOwner: wfOwner, + WorkflowName: "wf-name", + }, WorkflowRegistered) + require.ErrorIs(t, err, assert.AnError) + assert.Empty(t, emitter.Records()) + }) + + t.Run("no record when a status-only update fails", func(t *testing.T) { + t.Parallel() + wfID := types.WorkflowID{6} + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{ + spec: &job.WorkflowSpec{ + WorkflowID: wfID.Hex(), + Status: job.WorkflowSpecStatusActive, + WorkflowOwner: wfOwnerHex, + }, + upsertErr: assert.AnError, + }, newMeteringResourceManager(t, true, emitter)) + + err := h.workflowRegisteredEvent(t.Context(), WorkflowRegisteredEvent{ + Status: WorkflowStatusPaused, + WorkflowID: wfID, + WorkflowOwner: wfOwner, + WorkflowName: "wf-name", + }, WorkflowRegistered) + require.ErrorIs(t, err, assert.AnError) + assert.Empty(t, emitter.Records()) + }) + + t.Run("no record when deleting artifacts fails", func(t *testing.T) { + t.Parallel() + wfID := types.WorkflowID{7} + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{ + spec: &job.WorkflowSpec{ + WorkflowID: wfID.Hex(), + Status: job.WorkflowSpecStatusActive, + WorkflowOwner: wfOwnerHex, + }, + deleteErr: assert.AnError, + }, newMeteringResourceManager(t, true, emitter)) + + err := h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: wfID}, WorkflowDeleted) + require.ErrorIs(t, err, assert.AnError) + assert.Empty(t, emitter.Records()) + }) + + t.Run("no record while a delete is deferred by drain; exactly one on the successful retry", func(t *testing.T) { + t.Parallel() + wfID := types.WorkflowID{8} + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{ + spec: &job.WorkflowSpec{ + WorkflowID: wfID.Hex(), + Status: job.WorkflowSpecStatusActive, + WorkflowOwner: wfOwnerHex, + }, + }, newMeteringResourceManager(t, true, emitter)) + + drainable := &mockDrainableEngine{} + drainable.activeExecutions.Store(1) + require.NoError(t, h.engineRegistry.Add(wfID, "test-source", drainable)) + + err := h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: wfID}, WorkflowDeleted) + require.ErrorIs(t, err, ErrDrainInProgress) + assert.Empty(t, emitter.Records()) + + drainable.activeExecutions.Store(0) + require.NoError(t, h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: wfID}, WorkflowDeleted)) + + records := emitter.Records() + require.Len(t, records, 1) + requireMeterRecord(t, records[0], meteringpb.MeterAction_METER_ACTION_RELEASE, WorkflowDeleted, wfID.Hex()) + }) + + t.Run("emit failure never fails event handling", func(t *testing.T) { + t.Parallel() + wfID := types.WorkflowID{9} + emitter := &recordingEmitter{err: errors.New("beholder unavailable")} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{ + spec: &job.WorkflowSpec{ + WorkflowID: wfID.Hex(), + Status: job.WorkflowSpecStatusActive, + WorkflowOwner: wfOwnerHex, + }, + }, newMeteringResourceManager(t, true, emitter)) + + require.NoError(t, h.workflowRegisteredEvent(t.Context(), WorkflowRegisteredEvent{ + Status: WorkflowStatusPaused, + WorkflowID: wfID, + WorkflowOwner: wfOwner, + WorkflowName: "wf-name", + }, WorkflowRegistered)) + require.NoError(t, h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: wfID}, WorkflowDeleted)) + assert.Empty(t, emitter.Records()) + }) + + t.Run("disabled resource manager emits nothing", func(t *testing.T) { + t.Parallel() + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{}, newMeteringResourceManager(t, false, emitter)) + + require.NoError(t, h.workflowRegisteredEvent(t.Context(), WorkflowRegisteredEvent{ + Status: WorkflowStatusPaused, + WorkflowID: types.WorkflowID{10}, + WorkflowOwner: wfOwner, + WorkflowName: "wf-name", + }, WorkflowRegistered)) + assert.Empty(t, emitter.Records()) + }) + + t.Run("snapshot emits one MeterSnapshot per running engine", func(t *testing.T) { + t.Parallel() + emitter := &recordingSnapshotEmitter{} + clock := clockwork.NewFakeClockAt(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)) + rm := resourcemanager.NewResourceManager(commonlogger.Test(t), resourcemanager.ResourceManagerConfig{ + Enabled: true, + Emitter: emitter, + SnapshotInterval: time.Minute, + Clock: clock, + }) + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{}, rm) + unregister := rm.Register(h) + t.Cleanup(unregister) + + // Register two engines; the running engine registry is the in-memory source + // the snapshot reads (no per-tick GetWorkflowSpec). + wfID1 := types.WorkflowID{20} + wfID2 := types.WorkflowID{21} + require.NoError(t, h.engineRegistry.Add(wfID1, "test-source", &fakeService{})) + require.NoError(t, h.engineRegistry.Add(wfID2, "test-source", &fakeService{})) + + servicetest.Run(t, rm) + require.NoError(t, clock.BlockUntilContext(t.Context(), 1)) + clock.Advance(time.Minute) + + require.Eventually(t, func() bool { + return len(emitter.Snapshots()) == 2 + }, time.Second, time.Millisecond) + snapshots := emitter.Snapshots() + require.Len(t, snapshots, 2) + + byWorkflowID := map[string]*meteringpb.MeterSnapshot{} + for _, snap := range snapshots { + require.NotNil(t, snap.Identity) + assert.Equal(t, "workflow-syncer-v2", snap.Identity.Service) + assert.Equal(t, "workflow_specs_v2", snap.Identity.Resource) + require.NotNil(t, snap.Utilization) + assert.Equal(t, int64(1), snap.Utilization.Value) + // resource_id = workflow_id fully identifies the resource; no labels. + byWorkflowID[snap.Identity.ResourceId] = snap + } + require.NotNil(t, byWorkflowID[wfID1.Hex()], "snapshot must contain an entry for the first running engine") + require.NotNil(t, byWorkflowID[wfID2.Hex()], "snapshot must contain an entry for the second running engine") + }) + + t.Run("graceful close emits a RELEASE per running engine", func(t *testing.T) { + t.Parallel() + emitter := &recordingEmitter{} + h := newMeteringTestHandler(t, &stubWorkflowArtifactsStore{}, newMeteringResourceManager(t, true, emitter)) + + wfID := types.WorkflowID{30} + require.NoError(t, h.engineRegistry.Add(wfID, "test-source", &fakeService{})) + + es := h.engineRegistry.GetAll() + h.emitGracefulCloseReleases(t.Context(), es) + + records := emitter.Records() + require.Len(t, records, 1) + requireMeterRecord(t, records[0], meteringpb.MeterAction_METER_ACTION_RELEASE, WorkflowDeleted, wfID.Hex()) + }) +} diff --git a/core/services/workflows/syncer/v2/handler_test.go b/core/services/workflows/syncer/v2/handler_test.go index cade2017fe9..3301550f941 100644 --- a/core/services/workflows/syncer/v2/handler_test.go +++ b/core/services/workflows/syncer/v2/handler_test.go @@ -240,7 +240,7 @@ func Test_workflowRegisteredHandler(t *testing.T) { signedURLParameter := "?auth=abc123" defaultValidationFn := func(t *testing.T, ctx context.Context, event WorkflowRegisteredEvent, h *eventHandler, s *artifacts.Store, wfOwner []byte, wfName string, wfID types.WorkflowID, _ *mockFetcher) { - err := h.workflowRegisteredEvent(ctx, event) + err := h.workflowRegisteredEvent(ctx, event, WorkflowRegistered) require.NoError(t, err) // Verify the record is updated in the database @@ -382,7 +382,7 @@ func Test_workflowRegisteredHandler(t *testing.T) { validationFn: func(t *testing.T, ctx context.Context, event WorkflowRegisteredEvent, h *eventHandler, s *artifacts.Store, wfOwner []byte, wfName string, wfID types.WorkflowID, fetcher *mockFetcher, binaryURL string, configURL string, ) { - err := h.workflowRegisteredEvent(ctx, event) + err := h.workflowRegisteredEvent(ctx, event, WorkflowRegistered) require.Error(t, err) require.ErrorIs(t, err, assert.AnError) }, @@ -420,7 +420,7 @@ func Test_workflowRegisteredHandler(t *testing.T) { me := &mockEngine{} err := h.engineRegistry.Add(wfID, event.Source, me) require.NoError(t, err) - err = h.workflowRegisteredEvent(ctx, event) + err = h.workflowRegisteredEvent(ctx, event, WorkflowRegistered) require.NoError(t, err) }, }, @@ -459,7 +459,7 @@ func Test_workflowRegisteredHandler(t *testing.T) { oldWfIDBytes := [32]byte{0, 1, 2, 3, 5} err := h.engineRegistry.Add(oldWfIDBytes, event.Source, me) require.NoError(t, err) - err = h.workflowRegisteredEvent(ctx, event) + err = h.workflowRegisteredEvent(ctx, event, WorkflowRegistered) require.NoError(t, err) engineInRegistry, ok := h.engineRegistry.Get(wfID) assert.True(t, ok) @@ -498,7 +498,7 @@ func Test_workflowRegisteredHandler(t *testing.T) { validationFn: func(t *testing.T, ctx context.Context, event WorkflowRegisteredEvent, h *eventHandler, s *artifacts.Store, wfOwner []byte, wfName string, wfID types.WorkflowID, fetcher *mockFetcher, binaryURL string, configURL string, ) { - err := h.workflowRegisteredEvent(ctx, event) + err := h.workflowRegisteredEvent(ctx, event, WorkflowRegistered) require.NoError(t, err) // Verify the record is updated in the database @@ -561,7 +561,7 @@ func Test_workflowRegisteredHandler(t *testing.T) { _, err := s.UpsertWorkflowSpec(ctx, entry) require.NoError(t, err) - err = h.workflowRegisteredEvent(ctx, event) + err = h.workflowRegisteredEvent(ctx, event, WorkflowRegistered) require.NoError(t, err) // Verify the record is updated in the database @@ -781,7 +781,7 @@ func Test_workflowRegisteredHandler_confidentialRouting(t *testing.T) { } ctx = contexts.WithCRE(ctx, contexts.CRE{Owner: hex.EncodeToString(wfOwner), Workflow: wfIDString}) - err = h.workflowRegisteredEvent(ctx, event) + err = h.workflowRegisteredEvent(ctx, event, WorkflowRegistered) require.NoError(t, err) assert.Eventually(t, confidential.ran.Load, 10*time.Second, time.Millisecond) @@ -869,7 +869,7 @@ func Test_workflowRegisteredHandler_confidentialRouting(t *testing.T) { } ctx = contexts.WithCRE(ctx, contexts.CRE{Owner: hex.EncodeToString(wfOwner), Workflow: wfIDString}) - err = h.workflowRegisteredEvent(ctx, event) + err = h.workflowRegisteredEvent(ctx, event, WorkflowRegistered) require.NoError(t, err) assert.Eventually(t, action.ran.Load, 10*time.Second, time.Millisecond) @@ -1088,7 +1088,7 @@ func Test_workflowDeletedHandler(t *testing.T) { ) require.NoError(t, err) ctx = contexts.WithCRE(ctx, contexts.CRE{Owner: hex.EncodeToString(wfOwner), Workflow: wfIDString}) - err = h.workflowRegisteredEvent(ctx, active) + err = h.workflowRegisteredEvent(ctx, active, WorkflowRegistered) require.NoError(t, err) // Verify the record is updated in the database @@ -1107,7 +1107,7 @@ func Test_workflowDeletedHandler(t *testing.T) { deleteEvent := WorkflowDeletedEvent{ WorkflowID: giveWFID, } - err = h.workflowDeletedEvent(ctx, deleteEvent) + err = h.workflowDeletedEvent(ctx, deleteEvent, WorkflowDeleted) require.NoError(t, err) // Verify the record is deleted in the database @@ -1161,7 +1161,7 @@ func Test_workflowDeletedHandler(t *testing.T) { deleteEvent := WorkflowDeletedEvent{ WorkflowID: giveWFID, } - err = h.workflowDeletedEvent(ctx, deleteEvent) + err = h.workflowDeletedEvent(ctx, deleteEvent, WorkflowDeleted) require.NoError(t, err) // Verify the record is deleted in the database @@ -1241,7 +1241,7 @@ func Test_workflowDeletedHandler(t *testing.T) { ) require.NoError(t, err) ctx = contexts.WithCRE(ctx, contexts.CRE{Owner: hex.EncodeToString(wfOwner), Workflow: wfIDString}) - err = h.workflowRegisteredEvent(ctx, active) + err = h.workflowRegisteredEvent(ctx, active, WorkflowRegistered) require.NoError(t, err) // Verify the record is updated in the database @@ -1260,7 +1260,7 @@ func Test_workflowDeletedHandler(t *testing.T) { deleteEvent := WorkflowDeletedEvent{ WorkflowID: giveWFID, } - err = h.workflowDeletedEvent(ctx, deleteEvent) + err = h.workflowDeletedEvent(ctx, deleteEvent, WorkflowDeleted) require.Error(t, err, failWith) // Verify the record is still in the DB @@ -1275,6 +1275,7 @@ func Test_workflowDeletedHandler(t *testing.T) { type stubWorkflowArtifactsStore struct { spec *job.WorkflowSpec + upsertErr error deleteErr error deleteCalls atomic.Int32 } @@ -1291,6 +1292,9 @@ func (s *stubWorkflowArtifactsStore) GetWorkflowSpec(context.Context, string) (* } func (s *stubWorkflowArtifactsStore) UpsertWorkflowSpec(context.Context, *job.WorkflowSpec) (int64, error) { + if s.upsertErr != nil { + return 0, s.upsertErr + } return 1, nil } @@ -1319,7 +1323,7 @@ func Test_workflowDeletedEvent_DrainInProgress(t *testing.T) { workflowArtifactsStore: artifactStore, } - err := h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: workflowID}) + err := h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: workflowID}, WorkflowDeleted) require.Error(t, err) require.ErrorIs(t, err, ErrDrainInProgress) assert.Equal(t, int32(1), drainable.drainCalls.Load()) @@ -1345,7 +1349,7 @@ func Test_workflowDeletedEvent_IgnoresErrAlreadyStopped(t *testing.T) { workflowArtifactsStore: artifactStore, } - err := h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: workflowID}) + err := h.workflowDeletedEvent(t.Context(), WorkflowDeletedEvent{WorkflowID: workflowID}, WorkflowDeleted) require.NoError(t, err) assert.Equal(t, int32(1), drainable.closeCalls.Load()) assert.Equal(t, int32(1), artifactStore.deleteCalls.Load()) @@ -1383,7 +1387,7 @@ func Test_workflowRegisteredEvent_DrainingEngineNotTreatedAsHealthy(t *testing.T err := h.workflowRegisteredEvent(t.Context(), WorkflowRegisteredEvent{ Status: WorkflowStatusActive, WorkflowID: workflowID, - }) + }, WorkflowRegistered) require.Error(t, err) require.Contains(t, err.Error(), "could not clean up old engine") assert.Equal(t, int32(1), drainable.closeCalls.Load()) diff --git a/deployment/go.mod b/deployment/go.mod index 2068856fbe2..04420eb25ba 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -42,14 +42,14 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260415165642-49f23e4d76cc github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260511195239-0f6e1b177fc7 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260504204047-af9826978b72 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 github.com/smartcontractkit/chainlink-common/keystore v1.2.0 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260522094612-5f9f748bd87a github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260609161557-8ceae53b8ab1 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260521215851-3fdbb363496f - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.1-0.20260528221400-84746b70eeeb github.com/smartcontractkit/chainlink-solana v1.3.1-0.20260605202330-b5a89c32fdc1 @@ -431,6 +431,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/linking-service/go v0.0.0-20260512230622-65f10f4cd305 // indirect + github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2 // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 // indirect github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260331131315-f08a616d8dcd // indirect diff --git a/deployment/go.sum b/deployment/go.sum index f380ce33ec7..d029806cdbc 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1382,8 +1382,8 @@ github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd h github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd/go.mod h1:SBN8Urnh5sQvrQRbSo1Nr8coWatHg8LZoPw3R/42sho= github.com/smartcontractkit/chainlink-ccv/deployment v0.0.2-0.20260428205321-9ce8f4c44d23 h1:1Rt4HLpwbRN1YtBFcbsxSJYIiUP2wJ11qizevOEeCrs= github.com/smartcontractkit/chainlink-ccv/deployment v0.0.2-0.20260428205321-9ce8f4c44d23/go.mod h1:V+wrhuNve+JiFwoBr25d6y0lL1rYSCSJhTFyloL3ueo= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b h1:UMQ+MwHI341h+yARqeKmY/cagkB/dH0J34aMoJG00io= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b/go.mod h1:GlEVw7ziizXoMfzl1onNSwansrVBLHhj5gUJlGQpb4I= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 h1:Ax+ifk2SCOanY6L9taki45SMPJQ0A1cZFceCVYPPaRw= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1/go.mod h1:319Wa+HHpx8nf/GUpktQN+VFrkGk8tU41ep84ylYmSM= github.com/smartcontractkit/chainlink-common/keystore v1.2.0 h1:1BH/b14CkGjArfzznlioQpIJiynECWVT48JUP9E277U= github.com/smartcontractkit/chainlink-common/keystore v1.2.0/go.mod h1:9R/74vN+bJ5PbkOyM/pUy/AeAZaRwYb/k4XPeXcbDio= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= @@ -1418,14 +1418,16 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a h1:alnfvQgCKPFqsfijZQnr6Sbus2GT5YZ9BT/KzVrbszE= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 h1:GNl+lLK0QCakqA1J1i7FoOai2JrOGOzNzSniMijaCjA= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 h1:SG+wAsNyAcA6Kk19ljuxi3HK9Ll2lpHik8OKoY4x7A0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36/go.mod h1:vL1bDgPSJjV0EqHYs4dDlR+EEE0cJchgvGLYXhwIjXY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 h1:NJdGFhzT6zMaTod4QkBqVD2sg0I25iw1boOYtTpEwRo= 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-20260615161920-ed05faf7f0a2 h1:zKfst+Wyr95rbxLNSsc4nKybe9tLXOC2r2YbDRQwNxM= +github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 h1:bnSl5p3mFekSJ6QcbZ1TmHn2ffYiX8xk6hNzVmyhstQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= diff --git a/go.mod b/go.mod index 6d72f213c12..351b40b03a0 100644 --- a/go.mod +++ b/go.mod @@ -85,7 +85,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260415165642-49f23e4d76cc github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260415165642-49f23e4d76cc github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 github.com/smartcontractkit/chainlink-common/keystore v1.2.0 github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 github.com/smartcontractkit/chainlink-data-streams v0.1.15-0.20260522094612-5f9f748bd87a @@ -97,9 +97,10 @@ require ( github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260423135514-5b1a7565a99c github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 github.com/smartcontractkit/chainlink-protos/billing/go v0.0.0-20251024234028-0988426d98f4 - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 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-20260615161920-ed05faf7f0a2 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.1-0.20260528221400-84746b70eeeb github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260331131315-f08a616d8dcd diff --git a/go.sum b/go.sum index 070069b8768..9aa119a8784 100644 --- a/go.sum +++ b/go.sum @@ -1169,8 +1169,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260415165642-49f23e4d76cc/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd h1:IMopuENFVS63AerRELdfWo6o60UNUidcldJOxJLmk24= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd/go.mod h1:SBN8Urnh5sQvrQRbSo1Nr8coWatHg8LZoPw3R/42sho= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b h1:UMQ+MwHI341h+yARqeKmY/cagkB/dH0J34aMoJG00io= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b/go.mod h1:GlEVw7ziizXoMfzl1onNSwansrVBLHhj5gUJlGQpb4I= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 h1:Ax+ifk2SCOanY6L9taki45SMPJQ0A1cZFceCVYPPaRw= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1/go.mod h1:319Wa+HHpx8nf/GUpktQN+VFrkGk8tU41ep84ylYmSM= github.com/smartcontractkit/chainlink-common/keystore v1.2.0 h1:1BH/b14CkGjArfzznlioQpIJiynECWVT48JUP9E277U= github.com/smartcontractkit/chainlink-common/keystore v1.2.0/go.mod h1:9R/74vN+bJ5PbkOyM/pUy/AeAZaRwYb/k4XPeXcbDio= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= @@ -1203,12 +1203,14 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a h1:alnfvQgCKPFqsfijZQnr6Sbus2GT5YZ9BT/KzVrbszE= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 h1:GNl+lLK0QCakqA1J1i7FoOai2JrOGOzNzSniMijaCjA= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 h1:SG+wAsNyAcA6Kk19ljuxi3HK9Ll2lpHik8OKoY4x7A0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36/go.mod h1:vL1bDgPSJjV0EqHYs4dDlR+EEE0cJchgvGLYXhwIjXY= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 h1:NJdGFhzT6zMaTod4QkBqVD2sg0I25iw1boOYtTpEwRo= 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-20260615161920-ed05faf7f0a2 h1:zKfst+Wyr95rbxLNSsc4nKybe9tLXOC2r2YbDRQwNxM= +github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 h1:bnSl5p3mFekSJ6QcbZ1TmHn2ffYiX8xk6hNzVmyhstQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.1-0.20260528221400-84746b70eeeb h1:G8X3SR21VYAHWkDkNGZCjsrWrLJoVmXMpYBa2KKK3GU= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 31328f1d035..79a231ece63 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -29,7 +29,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260506144252-c100eabfda74 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260506144252-c100eabfda74 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260511195239-0f6e1b177fc7 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 github.com/smartcontractkit/chainlink-common/keystore v1.2.0 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260609161557-8ceae53b8ab1 @@ -408,9 +408,10 @@ require ( github.com/smartcontractkit/chainlink-protos/chainlink-ccv/heartbeat v0.0.0-20260115142640-f6b99095c12e // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 // 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/metering/go v0.0.0-20260615161920-ed05faf7f0a2 // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.1-0.20260528221400-84746b70eeeb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index ab0bf1bfc8a..fd78bedab0d 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1367,8 +1367,8 @@ github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd h github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd/go.mod h1:SBN8Urnh5sQvrQRbSo1Nr8coWatHg8LZoPw3R/42sho= github.com/smartcontractkit/chainlink-ccv/deployment v0.0.2-0.20260428205321-9ce8f4c44d23 h1:1Rt4HLpwbRN1YtBFcbsxSJYIiUP2wJ11qizevOEeCrs= github.com/smartcontractkit/chainlink-ccv/deployment v0.0.2-0.20260428205321-9ce8f4c44d23/go.mod h1:V+wrhuNve+JiFwoBr25d6y0lL1rYSCSJhTFyloL3ueo= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b h1:UMQ+MwHI341h+yARqeKmY/cagkB/dH0J34aMoJG00io= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b/go.mod h1:GlEVw7ziizXoMfzl1onNSwansrVBLHhj5gUJlGQpb4I= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 h1:Ax+ifk2SCOanY6L9taki45SMPJQ0A1cZFceCVYPPaRw= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1/go.mod h1:319Wa+HHpx8nf/GUpktQN+VFrkGk8tU41ep84ylYmSM= github.com/smartcontractkit/chainlink-common/keystore v1.2.0 h1:1BH/b14CkGjArfzznlioQpIJiynECWVT48JUP9E277U= github.com/smartcontractkit/chainlink-common/keystore v1.2.0/go.mod h1:9R/74vN+bJ5PbkOyM/pUy/AeAZaRwYb/k4XPeXcbDio= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= @@ -1403,14 +1403,16 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a h1:alnfvQgCKPFqsfijZQnr6Sbus2GT5YZ9BT/KzVrbszE= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 h1:GNl+lLK0QCakqA1J1i7FoOai2JrOGOzNzSniMijaCjA= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 h1:SG+wAsNyAcA6Kk19ljuxi3HK9Ll2lpHik8OKoY4x7A0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36/go.mod h1:vL1bDgPSJjV0EqHYs4dDlR+EEE0cJchgvGLYXhwIjXY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 h1:NJdGFhzT6zMaTod4QkBqVD2sg0I25iw1boOYtTpEwRo= 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-20260615161920-ed05faf7f0a2 h1:zKfst+Wyr95rbxLNSsc4nKybe9tLXOC2r2YbDRQwNxM= +github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 h1:bnSl5p3mFekSJ6QcbZ1TmHn2ffYiX8xk6hNzVmyhstQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index c25cacc43be..68ec6257e95 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -20,7 +20,7 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260506144252-c100eabfda74 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260506144252-c100eabfda74 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260511195239-0f6e1b177fc7 - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260609161557-8ceae53b8ab1 github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.1 @@ -490,10 +490,11 @@ require ( github.com/smartcontractkit/chainlink-protos/chainlink-ccv/heartbeat v0.0.0-20260115142640-f6b99095c12e // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 // indirect github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 // 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-20260615161920-ed05faf7f0a2 // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.1-0.20260528221400-84746b70eeeb // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index e2c2dfac698..e01fec9a2d6 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1633,8 +1633,8 @@ github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd h github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd/go.mod h1:SBN8Urnh5sQvrQRbSo1Nr8coWatHg8LZoPw3R/42sho= github.com/smartcontractkit/chainlink-ccv/deployment v0.0.2-0.20260428205321-9ce8f4c44d23 h1:1Rt4HLpwbRN1YtBFcbsxSJYIiUP2wJ11qizevOEeCrs= github.com/smartcontractkit/chainlink-ccv/deployment v0.0.2-0.20260428205321-9ce8f4c44d23/go.mod h1:V+wrhuNve+JiFwoBr25d6y0lL1rYSCSJhTFyloL3ueo= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b h1:UMQ+MwHI341h+yARqeKmY/cagkB/dH0J34aMoJG00io= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b/go.mod h1:GlEVw7ziizXoMfzl1onNSwansrVBLHhj5gUJlGQpb4I= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 h1:Ax+ifk2SCOanY6L9taki45SMPJQ0A1cZFceCVYPPaRw= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1/go.mod h1:319Wa+HHpx8nf/GUpktQN+VFrkGk8tU41ep84ylYmSM= github.com/smartcontractkit/chainlink-common/keystore v1.2.0 h1:1BH/b14CkGjArfzznlioQpIJiynECWVT48JUP9E277U= github.com/smartcontractkit/chainlink-common/keystore v1.2.0/go.mod h1:9R/74vN+bJ5PbkOyM/pUy/AeAZaRwYb/k4XPeXcbDio= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= @@ -1669,14 +1669,16 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a h1:alnfvQgCKPFqsfijZQnr6Sbus2GT5YZ9BT/KzVrbszE= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 h1:GNl+lLK0QCakqA1J1i7FoOai2JrOGOzNzSniMijaCjA= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 h1:SG+wAsNyAcA6Kk19ljuxi3HK9Ll2lpHik8OKoY4x7A0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36/go.mod h1:vL1bDgPSJjV0EqHYs4dDlR+EEE0cJchgvGLYXhwIjXY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 h1:NJdGFhzT6zMaTod4QkBqVD2sg0I25iw1boOYtTpEwRo= 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-20260615161920-ed05faf7f0a2 h1:zKfst+Wyr95rbxLNSsc4nKybe9tLXOC2r2YbDRQwNxM= +github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 h1:bnSl5p3mFekSJ6QcbZ1TmHn2ffYiX8xk6hNzVmyhstQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= diff --git a/plugins/plugins.private.yaml b/plugins/plugins.private.yaml index 29008aad52c..2c366c5ccf6 100644 --- a/plugins/plugins.private.yaml +++ b/plugins/plugins.private.yaml @@ -9,7 +9,7 @@ defaults: plugins: cron: - moduleURI: "github.com/smartcontractkit/capabilities/cron" - gitRef: "69e882e0f24cf9ae897366c4dcbbd3c7e035ae33" + gitRef: "38038ef2e97d15e74491df228460f182c8cb81a5" installPath: "." flags: "-tags timetzdata" readcontract: @@ -31,11 +31,11 @@ plugins: installPath: "." httptrigger: - moduleURI: "github.com/smartcontractkit/capabilities/http_trigger" - gitRef: "69e882e0f24cf9ae897366c4dcbbd3c7e035ae33" + gitRef: "38038ef2e97d15e74491df228460f182c8cb81a5" installPath: "." evm: - moduleURI: "github.com/smartcontractkit/capabilities/chain_capabilities/evm" - gitRef: "01e7061780b4bbf081f7aa907397301f8685fdac" + gitRef: "38038ef2e97d15e74491df228460f182c8cb81a5" installPath: "." solana: - moduleURI: "github.com/smartcontractkit/capabilities/chain_capabilities/solana" diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index bf1f91f7836..dc183c64a6e 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -33,12 +33,12 @@ require ( github.com/smartcontractkit/chain-selectors v1.0.101 github.com/smartcontractkit/chainlink-aptos v0.0.0-20260609211101-71d38bd6a0a9 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260415165642-49f23e4d76cc - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 github.com/smartcontractkit/chainlink-common/keystore v1.2.0 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260609161557-8ceae53b8ab1 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260521215851-3fdbb363496f - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260528221400-84746b70eeeb @@ -460,6 +460,7 @@ require ( github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d // indirect 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-20260615161920-ed05faf7f0a2 // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.10.1-0.20260528221400-84746b70eeeb // indirect github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260331131315-f08a616d8dcd // indirect diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index 2c779c8f6f7..4e9db2f0aa5 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1540,8 +1540,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260511195239-0f6e1b177fc7/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd h1:IMopuENFVS63AerRELdfWo6o60UNUidcldJOxJLmk24= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd/go.mod h1:SBN8Urnh5sQvrQRbSo1Nr8coWatHg8LZoPw3R/42sho= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b h1:UMQ+MwHI341h+yARqeKmY/cagkB/dH0J34aMoJG00io= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b/go.mod h1:GlEVw7ziizXoMfzl1onNSwansrVBLHhj5gUJlGQpb4I= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 h1:Ax+ifk2SCOanY6L9taki45SMPJQ0A1cZFceCVYPPaRw= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1/go.mod h1:319Wa+HHpx8nf/GUpktQN+VFrkGk8tU41ep84ylYmSM= github.com/smartcontractkit/chainlink-common/keystore v1.2.0 h1:1BH/b14CkGjArfzznlioQpIJiynECWVT48JUP9E277U= github.com/smartcontractkit/chainlink-common/keystore v1.2.0/go.mod h1:9R/74vN+bJ5PbkOyM/pUy/AeAZaRwYb/k4XPeXcbDio= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= @@ -1576,14 +1576,16 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a h1:alnfvQgCKPFqsfijZQnr6Sbus2GT5YZ9BT/KzVrbszE= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 h1:GNl+lLK0QCakqA1J1i7FoOai2JrOGOzNzSniMijaCjA= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 h1:SG+wAsNyAcA6Kk19ljuxi3HK9Ll2lpHik8OKoY4x7A0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36/go.mod h1:vL1bDgPSJjV0EqHYs4dDlR+EEE0cJchgvGLYXhwIjXY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 h1:NJdGFhzT6zMaTod4QkBqVD2sg0I25iw1boOYtTpEwRo= 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-20260615161920-ed05faf7f0a2 h1:zKfst+Wyr95rbxLNSsc4nKybe9tLXOC2r2YbDRQwNxM= +github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 h1:bnSl5p3mFekSJ6QcbZ1TmHn2ffYiX8xk6hNzVmyhstQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index 785984acc41..c010856acff 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -58,12 +58,12 @@ require ( github.com/rs/zerolog v1.34.0 github.com/smartcontractkit/chain-selectors v1.0.101 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260415165642-49f23e4d76cc - github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b + github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 github.com/smartcontractkit/chainlink-common/keystore v1.2.0 github.com/smartcontractkit/chainlink-deployments-framework v0.105.0 github.com/smartcontractkit/chainlink-evm/contracts/cre/gobindings v0.0.0-20260403151002-2c91155b5501 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260521215851-3fdbb363496f - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 github.com/smartcontractkit/chainlink-protos/ring/go v0.0.0-20260331131315-f08a616d8dcd github.com/smartcontractkit/chainlink-protos/workflows/go v0.0.0-20260528221400-84746b70eeeb github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.2 @@ -154,6 +154,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/job-distributor v0.18.0 // indirect + github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2 // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 // indirect github.com/smartcontractkit/chainlink-solana/contracts v0.0.0-20260513123719-d347eaf314e1 // indirect github.com/smartcontractkit/chainlink-testing-framework/lib v1.54.9 // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index 65afe60bd8a..93dbfd16264 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1554,8 +1554,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260511195239-0f6e1b177fc7/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd h1:IMopuENFVS63AerRELdfWo6o60UNUidcldJOxJLmk24= github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260428133800-3b1484e8b1fd/go.mod h1:SBN8Urnh5sQvrQRbSo1Nr8coWatHg8LZoPw3R/42sho= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b h1:UMQ+MwHI341h+yARqeKmY/cagkB/dH0J34aMoJG00io= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260610184803-96d1e031407b/go.mod h1:GlEVw7ziizXoMfzl1onNSwansrVBLHhj5gUJlGQpb4I= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1 h1:Ax+ifk2SCOanY6L9taki45SMPJQ0A1cZFceCVYPPaRw= +github.com/smartcontractkit/chainlink-common v0.11.2-0.20260615184430-ef0995b527f1/go.mod h1:319Wa+HHpx8nf/GUpktQN+VFrkGk8tU41ep84ylYmSM= github.com/smartcontractkit/chainlink-common/keystore v1.2.0 h1:1BH/b14CkGjArfzznlioQpIJiynECWVT48JUP9E277U= github.com/smartcontractkit/chainlink-common/keystore v1.2.0/go.mod h1:9R/74vN+bJ5PbkOyM/pUy/AeAZaRwYb/k4XPeXcbDio= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260601211238-9f526774fef0 h1:NExKM/D0HneOq/N5LGTbkV4VOa0UHCvfTNEb4GqYpto= @@ -1590,14 +1590,16 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0. github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:ATjAPIVJibHRcIfiG47rEQkUIOoYa6KDvWj3zwCAw6g= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a h1:alnfvQgCKPFqsfijZQnr6Sbus2GT5YZ9BT/KzVrbszE= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260609153034-c8423a41ef9a/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32 h1:GNl+lLK0QCakqA1J1i7FoOai2JrOGOzNzSniMijaCjA= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260611123141-db97012a6c32/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36 h1:SG+wAsNyAcA6Kk19ljuxi3HK9Ll2lpHik8OKoY4x7A0= github.com/smartcontractkit/chainlink-protos/data-feeds v0.1.1-0.20260501174546-2e8846986b36/go.mod h1:vL1bDgPSJjV0EqHYs4dDlR+EEE0cJchgvGLYXhwIjXY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20260512230622-65f10f4cd305 h1:NJdGFhzT6zMaTod4QkBqVD2sg0I25iw1boOYtTpEwRo= 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-20260615161920-ed05faf7f0a2 h1:zKfst+Wyr95rbxLNSsc4nKybe9tLXOC2r2YbDRQwNxM= +github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260615161920-ed05faf7f0a2/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305 h1:bnSl5p3mFekSJ6QcbZ1TmHn2ffYiX8xk6hNzVmyhstQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260512230622-65f10f4cd305/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo=