Skip to content

Commit 24e8bf5

Browse files
committed
[metrics] [dataexchange] [networkmap] DXConnectEvent Callbacks for Node Identity Check Metrics
Signed-off-by: hfuss <hayden.fuss@kaleido.io>
1 parent 490539e commit 24e8bf5

29 files changed

+585
-103
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ __debug*
1212
!deploy/charts/firefly
1313
containerlogs
1414
.vscode/*.log
15-
.idea
16-
doc-site/site
15+
.idea/
16+
doc-site/site
17+
*.iml

internal/coremsgs/en_error_messages.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,4 +318,5 @@ var (
318318
MsgDuplicateContractListenerFilterLocation = ffe("FF10477", "Duplicate filter provided for contract listener for location", 400)
319319
MsgInvalidNamespaceForOperationUpdate = ffe("FF10478", "Received different namespace for operation update '%s' than expected for manager '%s'")
320320
MsgEmptyPluginForOperationUpdate = ffe("FF10479", "Received empty plugin for operation update '%s'")
321+
MsgInvalidIdentityPatch = ffe("FF10480", "A profile must be provided when updating an identity", 400)
321322
)

internal/dataexchange/ffdx/ffdx.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,14 @@ func (h *FFDX) beforeConnect(ctx context.Context, w wsclient.WSClient) error {
295295
return fmt.Errorf("DX returned non-ready status: %s", status.Status)
296296
}
297297
}
298+
299+
for _, cb := range h.callbacks.handlers {
300+
err := cb.DXConnectEvent(h)
301+
if err != nil {
302+
log.L(ctx).Errorf("error handling DX connect event: %v", err)
303+
}
304+
}
305+
298306
h.initialized = true
299307
return nil
300308
}

internal/dataexchange/ffdx/ffdx_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,8 @@ func TestMessageEventsBackgroundStart(t *testing.T) {
488488
h.SetOperationHandler("ns1", ocb)
489489
h.AddNode(context.Background(), "ns1", "node1", fftypes.JSONObject{"id": "peer1"})
490490

491+
mcb.On("DXConnectEvent", h).Return(nil)
492+
491493
err := h.Start()
492494
assert.NoError(t, err)
493495

@@ -547,6 +549,7 @@ func TestMessageEvents(t *testing.T) {
547549
ocb := &coremocks.OperationCallbacks{}
548550
h.SetOperationHandler("ns1", ocb)
549551
h.AddNode(context.Background(), "ns1", "node1", fftypes.JSONObject{"id": "peer1"})
552+
mcb.On("DXConnectEvent", h).Return(nil)
550553

551554
err := h.Start()
552555
assert.NoError(t, err)
@@ -612,6 +615,7 @@ func TestBlobEvents(t *testing.T) {
612615
ocb := &coremocks.OperationCallbacks{}
613616
h.SetOperationHandler("ns1", ocb)
614617
h.AddNode(context.Background(), "ns1", "node1", fftypes.JSONObject{"id": "peer1"})
618+
mcb.On("DXConnectEvent", h).Return(nil)
615619

616620
err := h.Start()
617621
assert.NoError(t, err)

internal/metrics/batch_pin.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2022 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -20,16 +20,16 @@ import (
2020
"github.com/prometheus/client_golang/prometheus"
2121
)
2222

23-
var BatchPinCounter prometheus.Counter
23+
var BatchPinCounter *prometheus.CounterVec
2424

2525
// MetricsBatchPin is the prometheus metric for total number of batch pins submitted
2626
var MetricsBatchPin = "ff_batchpin_total"
2727

2828
func InitBatchPinMetrics() {
29-
BatchPinCounter = prometheus.NewCounter(prometheus.CounterOpts{
29+
BatchPinCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
3030
Name: MetricsBatchPin,
3131
Help: "Number of batch pins submitted",
32-
})
32+
}, namespaceLabels)
3333
}
3434

3535
func RegisterBatchPinMetrics() {

internal/metrics/broadcast.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2022 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -20,10 +20,10 @@ import (
2020
"github.com/prometheus/client_golang/prometheus"
2121
)
2222

23-
var BroadcastSubmittedCounter prometheus.Counter
24-
var BroadcastConfirmedCounter prometheus.Counter
25-
var BroadcastRejectedCounter prometheus.Counter
26-
var BroadcastHistogram prometheus.Histogram
23+
var BroadcastSubmittedCounter *prometheus.CounterVec
24+
var BroadcastConfirmedCounter *prometheus.CounterVec
25+
var BroadcastRejectedCounter *prometheus.CounterVec
26+
var BroadcastHistogram *prometheus.HistogramVec
2727

2828
// BroadcastSubmittedCounterName is the prometheus metric for tracking the total number of broadcasts submitted
2929
var BroadcastSubmittedCounterName = "ff_broadcast_submitted_total"
@@ -38,22 +38,22 @@ var BroadcastRejectedCounterName = "ff_broadcast_rejected_total"
3838
var BroadcastHistogramName = "ff_broadcast_histogram"
3939

4040
func InitBroadcastMetrics() {
41-
BroadcastSubmittedCounter = prometheus.NewCounter(prometheus.CounterOpts{
41+
BroadcastSubmittedCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
4242
Name: BroadcastSubmittedCounterName,
4343
Help: "Number of submitted broadcasts",
44-
})
45-
BroadcastConfirmedCounter = prometheus.NewCounter(prometheus.CounterOpts{
44+
}, namespaceLabels)
45+
BroadcastConfirmedCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
4646
Name: BroadcastConfirmedCounterName,
4747
Help: "Number of confirmed broadcasts",
48-
})
49-
BroadcastRejectedCounter = prometheus.NewCounter(prometheus.CounterOpts{
48+
}, namespaceLabels)
49+
BroadcastRejectedCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
5050
Name: BroadcastRejectedCounterName,
5151
Help: "Number of rejected broadcasts",
52-
})
53-
BroadcastHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
52+
}, namespaceLabels)
53+
BroadcastHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
5454
Name: BroadcastHistogramName,
5555
Help: "Histogram of broadcasts, bucketed by time to finished",
56-
})
56+
}, namespaceLabels)
5757
}
5858

5959
func RegisterBroadcastMetrics() {

internal/metrics/contracts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2022 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//

internal/metrics/identity.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright © 2025 Kaleido, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package metrics
18+
19+
import (
20+
"github.com/prometheus/client_golang/prometheus"
21+
"time"
22+
)
23+
24+
var NodeIdentityDXCertMismatchGauge *prometheus.GaugeVec
25+
var NodeIdentityDXCertExpiryGauge *prometheus.GaugeVec
26+
27+
const (
28+
NodeIdentityDXCertMismatch = "ff_multiparty_node_identity_dx_mismatch"
29+
NodeIdentityDXCertExpiry = "ff_multiparty_node_identity_dx_expiry_epoch"
30+
)
31+
32+
func InitIdentityMetrics() {
33+
NodeIdentityDXCertMismatchGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{
34+
Name: NodeIdentityDXCertMismatch,
35+
Help: "Status of node identity DX cert mismatch",
36+
}, namespaceLabels)
37+
38+
NodeIdentityDXCertExpiryGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{
39+
Name: NodeIdentityDXCertExpiry,
40+
Help: "Timestamp, in Unix epoch format, of node identity DX cert's expiry",
41+
}, namespaceLabels)
42+
}
43+
44+
func RegisterIdentityMetrics() {
45+
registry.MustRegister(NodeIdentityDXCertMismatchGauge)
46+
registry.MustRegister(NodeIdentityDXCertExpiryGauge)
47+
}
48+
49+
// TODO should this type live elsewhere ??
50+
type NodeIdentityDXCertMismatchStatus string
51+
52+
const (
53+
NodeIdentityDXCertMismatchStatusMismatched NodeIdentityDXCertMismatchStatus = "mismatched"
54+
NodeIdentityDXCertMismatchStatusHealthy NodeIdentityDXCertMismatchStatus = "healthy"
55+
NodeIdentityDXCertMismatchStatusUnknown NodeIdentityDXCertMismatchStatus = "unknown"
56+
)
57+
58+
func (mm *metricsManager) NodeIdentityDXCertMismatch(namespace string, state NodeIdentityDXCertMismatchStatus) {
59+
var gaugeState float64
60+
switch state {
61+
case NodeIdentityDXCertMismatchStatusMismatched:
62+
gaugeState = 1.0
63+
case NodeIdentityDXCertMismatchStatusHealthy:
64+
gaugeState = 0.0
65+
case NodeIdentityDXCertMismatchStatusUnknown:
66+
fallthrough
67+
default:
68+
gaugeState = -1.0
69+
}
70+
71+
NodeIdentityDXCertMismatchGauge.WithLabelValues(namespace).Set(gaugeState)
72+
}
73+
74+
func (mm *metricsManager) NodeIdentityDXCertExpiry(namespace string, expiry time.Time) {
75+
NodeIdentityDXCertExpiryGauge.WithLabelValues(namespace).Set(float64(expiry.UTC().Unix()))
76+
}

internal/metrics/metrics.go

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import (
2929

3030
var mutex = &sync.Mutex{}
3131

32+
var namespaceLabels = []string{"ns"}
33+
3234
type Manager interface {
33-
CountBatchPin()
35+
CountBatchPin(namespace string)
3436
MessageSubmitted(msg *core.Message)
3537
MessageConfirmed(msg *core.Message, eventType fftypes.FFEnum)
3638
TransferSubmitted(transfer *core.TokenTransfer)
@@ -39,6 +41,8 @@ type Manager interface {
3941
BlockchainTransaction(location, methodName string)
4042
BlockchainQuery(location, methodName string)
4143
BlockchainEvent(location, signature string)
44+
NodeIdentityDXCertMismatch(namespace string, mismatch NodeIdentityDXCertMismatchStatus)
45+
NodeIdentityDXCertExpiry(namespace string, expiry time.Time)
4246
AddTime(id string)
4347
GetTime(id string) time.Time
4448
DeleteTime(id string)
@@ -61,17 +65,17 @@ func NewMetricsManager(ctx context.Context) Manager {
6165
return mm
6266
}
6367

64-
func (mm *metricsManager) CountBatchPin() {
65-
BatchPinCounter.Inc()
68+
func (mm *metricsManager) CountBatchPin(namespace string) {
69+
BatchPinCounter.WithLabelValues(namespace).Inc()
6670
}
6771

6872
func (mm *metricsManager) MessageSubmitted(msg *core.Message) {
6973
if len(msg.Header.ID.String()) > 0 {
7074
switch msg.Header.Type {
7175
case core.MessageTypeBroadcast:
72-
BroadcastSubmittedCounter.Inc()
76+
BroadcastSubmittedCounter.WithLabelValues(msg.LocalNamespace).Inc()
7377
case core.MessageTypePrivate:
74-
PrivateMsgSubmittedCounter.Inc()
78+
PrivateMsgSubmittedCounter.WithLabelValues(msg.LocalNamespace).Inc()
7579
}
7680
mm.AddTime(msg.Header.ID.String())
7781
}
@@ -87,23 +91,23 @@ func (mm *metricsManager) MessageConfirmed(msg *core.Message, eventType fftypes.
8791
if !eventTime.IsZero() {
8892
// Check that we recorded the submission
8993
// as we might not be the party submitting
90-
BroadcastHistogram.Observe(timeElapsed)
94+
BroadcastHistogram.WithLabelValues(msg.LocalNamespace).Observe(timeElapsed)
9195
}
9296
if eventType == core.EventTypeMessageConfirmed { // Broadcast Confirmed
93-
BroadcastConfirmedCounter.Inc()
97+
BroadcastConfirmedCounter.WithLabelValues(msg.LocalNamespace).Inc()
9498
} else if eventType == core.EventTypeMessageRejected { // Broadcast Rejected
95-
BroadcastRejectedCounter.Inc()
99+
BroadcastRejectedCounter.WithLabelValues(msg.LocalNamespace).Inc()
96100
}
97101
case core.MessageTypePrivate:
98102
if !eventTime.IsZero() {
99103
// Check that we recorded the submission
100104
// as we might not be the party submitting
101-
PrivateMsgHistogram.Observe(timeElapsed)
105+
PrivateMsgHistogram.WithLabelValues(msg.LocalNamespace).Observe(timeElapsed)
102106
}
103107
if eventType == core.EventTypeMessageConfirmed { // Private Msg Confirmed
104-
PrivateMsgConfirmedCounter.Inc()
108+
PrivateMsgConfirmedCounter.WithLabelValues(msg.LocalNamespace).Inc()
105109
} else if eventType == core.EventTypeMessageRejected { // Private Msg Rejected
106-
PrivateMsgRejectedCounter.Inc()
110+
PrivateMsgRejectedCounter.WithLabelValues(msg.LocalNamespace).Inc()
107111
}
108112
}
109113
}
@@ -112,11 +116,11 @@ func (mm *metricsManager) TransferSubmitted(transfer *core.TokenTransfer) {
112116
if len(transfer.LocalID.String()) > 0 {
113117
switch transfer.Type {
114118
case core.TokenTransferTypeMint: // Mint submitted
115-
MintSubmittedCounter.Inc()
119+
MintSubmittedCounter.WithLabelValues(transfer.Namespace).Inc()
116120
case core.TokenTransferTypeTransfer: // Transfer submitted
117-
TransferSubmittedCounter.Inc()
121+
TransferSubmittedCounter.WithLabelValues(transfer.Namespace).Inc()
118122
case core.TokenTransferTypeBurn: // Burn submitted
119-
BurnSubmittedCounter.Inc()
123+
BurnSubmittedCounter.WithLabelValues(transfer.Namespace).Inc()
120124
}
121125
mm.AddTime(transfer.LocalID.String())
122126
}
@@ -130,19 +134,19 @@ func (mm *metricsManager) TransferConfirmed(transfer *core.TokenTransfer) {
130134
switch transfer.Type {
131135
case core.TokenTransferTypeMint: // Mint confirmed
132136
if !transferEvent.IsZero() {
133-
MintHistogram.Observe(timeElapsed)
137+
MintHistogram.WithLabelValues(transfer.Namespace).Observe(timeElapsed)
134138
}
135-
MintConfirmedCounter.Inc()
139+
MintConfirmedCounter.WithLabelValues(transfer.Namespace).Inc()
136140
case core.TokenTransferTypeTransfer: // Transfer confirmed
137141
if !transferEvent.IsZero() {
138-
TransferHistogram.Observe(timeElapsed)
142+
TransferHistogram.WithLabelValues(transfer.Namespace).Observe(timeElapsed)
139143
}
140-
TransferConfirmedCounter.Inc()
144+
TransferConfirmedCounter.WithLabelValues(transfer.Namespace).Inc()
141145
case core.TokenTransferTypeBurn: // Burn confirmed
142146
if !transferEvent.IsZero() {
143-
BurnHistogram.Observe(timeElapsed)
147+
BurnHistogram.WithLabelValues(transfer.Namespace).Observe(timeElapsed)
144148
}
145-
BurnConfirmedCounter.Inc()
149+
BurnConfirmedCounter.WithLabelValues(transfer.Namespace).Inc()
146150
}
147151
}
148152

internal/metrics/metrics_test.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2022 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -62,7 +62,7 @@ func newTestMetricsManager(t *testing.T) (*metricsManager, func()) {
6262
func TestCountBatchPin(t *testing.T) {
6363
mm, cancel := newTestMetricsManager(t)
6464
defer cancel()
65-
mm.CountBatchPin()
65+
mm.CountBatchPin("a-ns")
6666
}
6767

6868
func TestMessageSubmittedBroadcast(t *testing.T) {
@@ -228,3 +228,35 @@ func TestIsMetricsEnabledFalse(t *testing.T) {
228228
mm.metricsEnabled = false
229229
assert.Equal(t, mm.IsMetricsEnabled(), false)
230230
}
231+
232+
func TestNodeIdentityDXCertMismatchSetsMismatchedState(t *testing.T) {
233+
mm, cancel := newTestMetricsManager(t)
234+
defer cancel()
235+
mm.NodeIdentityDXCertMismatch("test-namespace", NodeIdentityDXCertMismatchStatusMismatched)
236+
gaugeValue := testutil.ToFloat64(NodeIdentityDXCertMismatchGauge.WithLabelValues("test-namespace"))
237+
assert.Equal(t, 1.0, gaugeValue)
238+
}
239+
240+
func TestNodeIdentityDXCertMismatchSetsHealthyState(t *testing.T) {
241+
mm, cancel := newTestMetricsManager(t)
242+
defer cancel()
243+
mm.NodeIdentityDXCertMismatch("test-namespace", NodeIdentityDXCertMismatchStatusHealthy)
244+
gaugeValue := testutil.ToFloat64(NodeIdentityDXCertMismatchGauge.WithLabelValues("test-namespace"))
245+
assert.Equal(t, 0.0, gaugeValue)
246+
}
247+
248+
func TestNodeIdentityDXCertMismatchSetsUnknownState(t *testing.T) {
249+
mm, cancel := newTestMetricsManager(t)
250+
defer cancel()
251+
mm.NodeIdentityDXCertMismatch("test-namespace", NodeIdentityDXCertMismatchStatusUnknown)
252+
gaugeValue := testutil.ToFloat64(NodeIdentityDXCertMismatchGauge.WithLabelValues("test-namespace"))
253+
assert.Equal(t, -1.0, gaugeValue)
254+
}
255+
256+
func TestNodeIdentityDXCertMismatchSetsDefaultState(t *testing.T) {
257+
mm, cancel := newTestMetricsManager(t)
258+
defer cancel()
259+
mm.NodeIdentityDXCertMismatch("test-namespace", "invalid-state")
260+
gaugeValue := testutil.ToFloat64(NodeIdentityDXCertMismatchGauge.WithLabelValues("test-namespace"))
261+
assert.Equal(t, -1.0, gaugeValue)
262+
}

0 commit comments

Comments
 (0)