Skip to content

Commit de81e64

Browse files
feat(relay): add OCR2 on-chain view gauges for median (DF-21989)
Expose Prometheus gauges from LatestTransmissionDetails reads: latest answer, timestamp, epoch, round with chain_id, contract_address, transmitter_id labels.
1 parent 0bf2319 commit de81e64

3 files changed

Lines changed: 78 additions & 6 deletions

File tree

core/services/relay/evm/evm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ func (r *Relayer) NewMedianProvider(ctx context.Context, rargs commontypes.Relay
894894
return nil, err
895895
}
896896

897-
medianContract, err := newMedianContract(configWatcher.ContractConfigTracker(), configWatcher.contractAddress, configWatcher.chain, rargs.JobID, rargs.OracleSpecID, r.ds, lggr)
897+
medianContract, err := newMedianContract(configWatcher.ContractConfigTracker(), configWatcher.contractAddress, configWatcher.chain, rargs.JobID, rargs.OracleSpecID, r.ds, lggr, pargs.TransmitterID)
898898
if err != nil {
899899
return nil, err
900900
}

core/services/relay/evm/median.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ type medianContract struct {
3030
configTracker types.ContractConfigTracker
3131
contractCaller *ocr2aggregator.OCR2AggregatorCaller
3232
requestRoundTracker *round.RequestRoundTracker
33+
onchainViewMetrics *medianOnchainViewMetrics
3334
}
3435

35-
func newMedianContract(configTracker types.ContractConfigTracker, contractAddress common.Address, chain legacyevm.Chain, jobID int32, oracleSpecID int32, ds sqlutil.DataSource, lggr logger.Logger) (*medianContract, error) {
36+
func newMedianContract(configTracker types.ContractConfigTracker, contractAddress common.Address, chain legacyevm.Chain, jobID int32, oracleSpecID int32, ds sqlutil.DataSource, lggr logger.Logger, transmitterID string) (*medianContract, error) {
3637
lggr = logger.Named(lggr, "MedianContract")
3738
contract, err := offchain_aggregator_wrapper.NewOffchainAggregator(contractAddress, chain.Client())
3839
if err != nil {
@@ -50,9 +51,10 @@ func newMedianContract(configTracker types.ContractConfigTracker, contractAddres
5051
}
5152

5253
return &medianContract{
53-
lggr: lggr,
54-
configTracker: configTracker,
55-
contractCaller: contractCaller,
54+
lggr: lggr,
55+
configTracker: configTracker,
56+
contractCaller: contractCaller,
57+
onchainViewMetrics: newMedianOnchainViewMetrics(chain.ID().String(), contractAddress.Hex(), transmitterID),
5658
requestRoundTracker: round.NewRequestRoundTracker(
5759
contract,
5860
contractFilterer,
@@ -87,7 +89,14 @@ func (oc *medianContract) HealthReport() map[string]error {
8789
func (oc *medianContract) LatestTransmissionDetails(ctx context.Context) (ocrtypes.ConfigDigest, uint32, uint8, *big.Int, time.Time, error) {
8890
opts := bind.CallOpts{Context: ctx, Pending: false}
8991
result, err := oc.contractCaller.LatestTransmissionDetails(&opts)
90-
return result.ConfigDigest, result.Epoch, result.Round, result.LatestAnswer, time.Unix(int64(result.LatestTimestamp), 0), errors.Wrap(err, "error getting LatestTransmissionDetails")
92+
if err != nil {
93+
return ocrtypes.ConfigDigest{}, 0, 0, nil, time.Time{}, errors.Wrap(err, "error getting LatestTransmissionDetails")
94+
}
95+
updatedAt := time.Unix(int64(result.LatestTimestamp), 0)
96+
if oc.onchainViewMetrics != nil {
97+
oc.onchainViewMetrics.record(result.LatestAnswer, result.Epoch, result.Round, updatedAt)
98+
}
99+
return result.ConfigDigest, result.Epoch, result.Round, result.LatestAnswer, updatedAt, nil
91100
}
92101

93102
// LatestRoundRequested returns the configDigest, epoch, and round from the latest
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package evm
2+
3+
import (
4+
"math/big"
5+
"time"
6+
7+
"github.com/prometheus/client_golang/prometheus"
8+
"github.com/prometheus/client_golang/prometheus/promauto"
9+
)
10+
11+
// Prometheus gauges for the node's view of on-chain OCR2 median aggregator state (DF-22676).
12+
// latest_answer is exported as float64; values above ~2^53 may lose integer precision (documented in HELP).
13+
var (
14+
promOCR2OnchainLatestAnswer = promauto.NewGaugeVec(prometheus.GaugeOpts{
15+
Name: "ocr2_onchain_transmission_latest_answer",
16+
Help: "Latest median answer from the node's on-chain read of LatestTransmissionDetails (OCR2 aggregator). Float64 may not represent full int256 precision.",
17+
}, []string{"chain_id", "contract_address", "transmitter_id"})
18+
promOCR2OnchainLatestTimestampUnix = promauto.NewGaugeVec(prometheus.GaugeOpts{
19+
Name: "ocr2_onchain_transmission_latest_timestamp_unix",
20+
Help: "Unix seconds of latest on-chain transmission timestamp from the node's LatestTransmissionDetails read.",
21+
}, []string{"chain_id", "contract_address", "transmitter_id"})
22+
promOCR2OnchainEpoch = promauto.NewGaugeVec(prometheus.GaugeOpts{
23+
Name: "ocr2_onchain_transmission_epoch",
24+
Help: "Epoch from the node's on-chain LatestTransmissionDetails read.",
25+
}, []string{"chain_id", "contract_address", "transmitter_id"})
26+
promOCR2OnchainRound = promauto.NewGaugeVec(prometheus.GaugeOpts{
27+
Name: "ocr2_onchain_transmission_round",
28+
Help: "Round from the node's on-chain LatestTransmissionDetails read.",
29+
}, []string{"chain_id", "contract_address", "transmitter_id"})
30+
)
31+
32+
type medianOnchainViewMetrics struct {
33+
chainID string
34+
contract string
35+
transmitterID string
36+
}
37+
38+
func newMedianOnchainViewMetrics(chainID, contractAddress, transmitterID string) *medianOnchainViewMetrics {
39+
if transmitterID == "" {
40+
transmitterID = "unknown"
41+
}
42+
return &medianOnchainViewMetrics{
43+
chainID: chainID,
44+
contract: contractAddress,
45+
transmitterID: transmitterID,
46+
}
47+
}
48+
49+
func (m *medianOnchainViewMetrics) record(latestAnswer *big.Int, epoch uint32, round uint8, updatedAt time.Time) {
50+
lv := bigIntToPromGaugeValue(latestAnswer)
51+
promOCR2OnchainLatestAnswer.WithLabelValues(m.chainID, m.contract, m.transmitterID).Set(lv)
52+
promOCR2OnchainLatestTimestampUnix.WithLabelValues(m.chainID, m.contract, m.transmitterID).Set(float64(updatedAt.Unix()))
53+
promOCR2OnchainEpoch.WithLabelValues(m.chainID, m.contract, m.transmitterID).Set(float64(epoch))
54+
promOCR2OnchainRound.WithLabelValues(m.chainID, m.contract, m.transmitterID).Set(float64(round))
55+
}
56+
57+
func bigIntToPromGaugeValue(i *big.Int) float64 {
58+
if i == nil {
59+
return 0
60+
}
61+
f, _ := new(big.Float).SetInt(i).Float64()
62+
return f
63+
}

0 commit comments

Comments
 (0)