Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions commit/metrics/prom.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ var (
Name: "ccip_commit_loopp_ccip_provider_supported",
Help: "Tracks whether LOOPP CCIP provider is supported for each chain family (1 = supported, 0 = not supported)",
}, []string{"chain_family"})
promCommitConfigDigestMismatch = promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "ccip_commit_config_digest_mismatch",
Help: "Reports whether the home chain config digest differs from the offramp config digest (1 = mismatch, 0 = match)",
}, []string{"chain_family", "chain_id"})
)

type PromReporter struct {
Expand All @@ -116,6 +120,9 @@ type PromReporter struct {
bhSequenceNumbers metric.Int64Gauge
bhCommitLatestRound metric.Int64Gauge
bhLooppProviderSupported metric.Int64Gauge

configDigestMismatch *prometheus.GaugeVec
bhConfigDigestMismatch metric.Int64Gauge
}

func NewPromReporter(
Expand Down Expand Up @@ -149,6 +156,10 @@ func NewPromReporter(
if err != nil {
return nil, fmt.Errorf("failed to register ccip_commit_loopp_ccip_provider_supported gauge: %w", err)
}
configDigestMismatch, err := bhClient.Meter.Int64Gauge("ccip_commit_config_digest_mismatch")
if err != nil {
return nil, fmt.Errorf("failed to register ccip_commit_config_digest_mismatch gauge: %w", err)
}

return &PromReporter{
lggr: lggr,
Expand All @@ -162,6 +173,7 @@ func NewPromReporter(
sequenceNumbers: promSequenceNumbers,
commitLatestRound: promCommitLatestRoundID,
looppProviderSupported: promLooppCCIPProviderSupported,
configDigestMismatch: promCommitConfigDigestMismatch,

processorLatencyHistogram: promProcessorLatencyHistogram,
processorOutputCounter: promProcessorOutputCounter,
Expand All @@ -173,6 +185,7 @@ func NewPromReporter(
bhSequenceNumbers: sequenceNumbers,
bhCommitLatestRound: commitLatestRoundID,
bhLooppProviderSupported: looppProviderSupported,
bhConfigDigestMismatch: configDigestMismatch,
}, nil
}

Expand Down Expand Up @@ -340,3 +353,15 @@ func (p *PromReporter) TrackLooppProviderSupported(looppCCIPProviderSupported ma
))
}
}

func (p *PromReporter) TrackConfigDigestMismatch(mismatch bool) {
var value float64
if mismatch {
value = 1
}
p.configDigestMismatch.WithLabelValues(p.chainFamily, p.chainID).Set(value)
p.bhConfigDigestMismatch.Record(context.Background(), int64(value), metric.WithAttributes(
attribute.String("chain_family", p.chainFamily),
attribute.String("chain_id", p.chainID),
))
}
5 changes: 5 additions & 0 deletions commit/metrics/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ type Reporter interface {

TrackProcessorLatency(processor string, method plugincommon.MethodType, latency time.Duration, err error)
TrackProcessorOutput(processor string, method plugincommon.MethodType, obs plugintypes.Trackable)

TrackConfigDigestMismatch(mismatch bool)
}

type CommitPluginReporter interface {
TrackObservation(obs committypes.Observation, round uint64)
TrackOutcome(outcome committypes.Outcome, round uint64)
TrackConfigDigestMismatch(mismatch bool)
}

type Noop struct{}
Expand All @@ -48,6 +51,8 @@ func (n *Noop) TrackProcessorLatency(string, plugincommon.MethodType, time.Durat

func (n *Noop) TrackProcessorOutput(string, plugincommon.MethodType, plugintypes.Trackable) {}

func (n *Noop) TrackConfigDigestMismatch(bool) {}

var _ Reporter = &PromReporter{}
var _ CommitPluginReporter = &PromReporter{}
var _ merkleroot.MetricsReporter = &PromReporter{}
18 changes: 18 additions & 0 deletions commit/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ func (p *Plugin) Observation(
return encoded, nil
}

// Check config digest every round and emit a mismatch metric.
p.trackConfigDigestMismatch(ctx, lggr)

prevOutcome, err := p.ocrTypeCodec.DecodeOutcome(outCtx.PreviousOutcome)
if err != nil {
return nil, fmt.Errorf("decode previous outcome: %w", err)
Expand Down Expand Up @@ -412,6 +415,21 @@ func (p *Plugin) ObserveFChain(lggr logger.Logger) map[cciptypes.ChainSelector]i
return fChain
}

func (p *Plugin) trackConfigDigestMismatch(ctx context.Context, lggr logger.Logger) {
configMatch, _, err := plugincommon.ConfigDigestsMatch(
ctx, p.ccipReader, consts.PluginTypeCommit, p.reportingCfg.ConfigDigest,
)
if err != nil {
lggr.Errorw("failed to check for config digest mismatch",
"err", err,
"homeChainConfigDigest", p.reportingCfg.ConfigDigest,
"pluginType", consts.PluginTypeCommit,
)
return
}
p.metricsReporter.TrackConfigDigestMismatch(!configMatch)
}

//nolint:gocyclo
func (p *Plugin) Outcome(
ctx context.Context, outCtx ocr3types.OutcomeContext, q types.Query, aos []types.AttributedObservation,
Expand Down
6 changes: 6 additions & 0 deletions commit/plugin_roledon_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ func TestPlugin_RoleDonE2E_NoPrevOutcome(t *testing.T) {
{
deps.ccipReader.EXPECT().DiscoverContracts(mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
deps.ccipReader.EXPECT().Sync(mock.Anything, mock.Anything).Return(nil)
deps.ccipReader.EXPECT().GetOffRampConfigDigest(mock.Anything, mock.AnythingOfType("uint8")).
Return([32]byte{}, nil).Maybe()
}

// Source Chain Expectations - Makes sure only oracles that support specific source chains are reading them.
Expand Down Expand Up @@ -209,6 +211,8 @@ func TestPlugin_RoleDonE2E_RangesAndPricesSelectedPreviously(t *testing.T) {
{
deps.ccipReader.EXPECT().DiscoverContracts(mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
deps.ccipReader.EXPECT().Sync(mock.Anything, mock.Anything).Return(nil)
deps.ccipReader.EXPECT().GetOffRampConfigDigest(mock.Anything, mock.AnythingOfType("uint8")).
Return([32]byte{}, nil).Maybe()
}

// Source Chain Expectations - Makes sure only oracles that support specific source chains are reading them.
Expand Down Expand Up @@ -372,6 +376,8 @@ func TestPlugin_RoleDonE2E_Discovery(t *testing.T) {
}, addresses)
return nil
})
deps.ccipReader.EXPECT().GetOffRampConfigDigest(mock.Anything, mock.AnythingOfType("uint8")).
Return([32]byte{}, nil).Maybe()
}

p := s.newRoleDonTestPlugin(oracleID, true)
Expand Down
3 changes: 3 additions & 0 deletions commit/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ func TestObservation_prices(t *testing.T) {

ccr.EXPECT().GetLatestPriceSeqNr(mock.Anything).Return(tc.onchainOcrSeqNum, tc.rpcErr).Maybe()

ccr.EXPECT().GetOffRampConfigDigest(mock.Anything, mock.AnythingOfType("uint8")).
Return([32]byte{}, nil).Maybe()

tokenPriceObs := tokenprice.Observation{}
if tc.expObservedPrices {
tokenPriceObs.FeedTokenPrices = map[ccipocr3.UnknownEncodedAddress]ccipocr3.BigInt{
Expand Down
14 changes: 7 additions & 7 deletions commit/report.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package commit

import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"maps"
Expand Down Expand Up @@ -212,16 +210,18 @@ func (p *Plugin) validateReport(
return cciptypes.CommitPluginReport{}, plugincommon.NewErrInvalidReport("dest chain not supported")
}

offRampConfigDigest, err := p.ccipReader.GetOffRampConfigDigest(ctx, consts.PluginTypeCommit)
match, offRampDigest, err := plugincommon.ConfigDigestsMatch(
ctx, p.ccipReader, consts.PluginTypeCommit, p.reportingCfg.ConfigDigest,
)
if err != nil {
err = plugincommon.NewErrValidatingReport(fmt.Errorf("get offramp config digest: %w", err))
return cciptypes.CommitPluginReport{}, plugincommon.NewErrValidatingReport(err)
return cciptypes.CommitPluginReport{},
plugincommon.NewErrValidatingReport(fmt.Errorf("check config digest: %w", err))
}

if !bytes.Equal(offRampConfigDigest[:], p.reportingCfg.ConfigDigest[:]) {
if !match {
lggr.Warnw("my config digest doesn't match offramp's config digest, not accepting report",
"myConfigDigest", p.reportingCfg.ConfigDigest,
"offRampConfigDigest", hex.EncodeToString(offRampConfigDigest[:]),
"offRampConfigDigest", plugincommon.FormatConfigDigest(offRampDigest),
)
return cciptypes.CommitPluginReport{}, plugincommon.NewErrInvalidReport("config digest mismatch")
}
Expand Down
44 changes: 44 additions & 0 deletions devenv/tests/config_digest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package tests

import (
"testing"

"github.com/Masterminds/semver/v3"
chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/chainlink-ccip/deployment/deploy"
"github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
"github.com/smartcontractkit/chainlink-deployments-framework/deployment"
"github.com/smartcontractkit/chainlink-deployments-framework/operations"
"github.com/stretchr/testify/require"
)

func RunUpdateConfigDigestTests(t *testing.T, e *deployment.Environment, selectors []uint64) {
selectorsToImpl := buildImplsMap(t, e, selectors)

// get two distinct selectors
fromImpl, toImpl := selectorsToImpl[selectors[0]], selectorsToImpl[selectors[1]]
require.NotEqual(t, fromImpl, toImpl)

destFamily, err := chainsel.GetSelectorFamily(toImpl.ChainSelector())
require.NoError(t, err)

deployAdapter, ok := deploy.GetRegistry().GetDeployer(destFamily, semver.MustParse("1.6.0"))
require.True(t, ok)
require.NotNil(t, deployAdapter)

_, err = operations.ExecuteSequence(e.OperationsBundle, deployAdapter.SetOCR3Config(), e.BlockChains, deploy.SetOCR3ConfigInput{
ChainSelector: toImpl.ChainSelector(),
Datastore: e.DataStore,
Configs: map[ccipocr3.PluginType]deploy.OCR3ConfigArgs{
ccipocr3.PluginTypeCCIPCommit: {
ConfigDigest: [32]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
PluginType: ccipocr3.PluginTypeCCIPCommit,
F: 1,
IsSignatureVerificationEnabled: true,
Signers: [][]byte{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}, {13, 14, 15}},
Transmitters: [][]byte{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}, {13, 14, 15}},
},
},
})
require.NoError(t, err)
}
29 changes: 29 additions & 0 deletions devenv/tests/e2e/update_cfg_digest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package e2e

import (
"fmt"
"testing"

ccip "github.com/smartcontractkit/chainlink-ccip/devenv"
"github.com/smartcontractkit/chainlink-ccip/devenv/tests"
"github.com/smartcontractkit/chainlink-testing-framework/framework"
"github.com/stretchr/testify/require"
)

func TestUpdateConfigDigest(t *testing.T) {
in, err := ccip.LoadOutput[ccip.Cfg]("../../env-out.toml")
require.NoError(t, err)
if in.ForkedEnvConfig != nil {
t.Skip("Skipping E2E tests on forked environments, not supported yet")
}

selectors, e, err := ccip.NewCLDFOperationsEnvironment(in.Blockchains, in.CLDF.DataStore)
require.NoError(t, err)

t.Cleanup(func() {
_, err := framework.SaveContainerLogs(fmt.Sprintf("%s-%s", framework.DefaultCTFLogsDir, t.Name()))
require.NoError(t, err)
})

tests.RunUpdateConfigDigestTests(t, e, selectors)
}
25 changes: 25 additions & 0 deletions execute/metrics/prom.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ var (
Name: "ccip_exec_loopp_ccip_provider_supported",
Help: "Tracks whether LOOPP CCIP provider is supported for each chain family (1 = supported, 0 = not supported)",
}, []string{"chain_family"})
promExecConfigDigestMismatch = promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "ccip_exec_config_digest_mismatch",
Help: "Reports whether the home chain config digest differs from the offramp config digest (1 = mismatch, 0 = match)",
}, []string{"chain_family", "chain_id"})
)

type PromReporter struct {
Expand All @@ -127,6 +131,9 @@ type PromReporter struct {
beholderProcessorErrors metric.Int64Counter
bhExecLatestRound metric.Int64Gauge
bhLooppProviderSupported metric.Int64Gauge

configDigestMismatch *prometheus.GaugeVec
bhConfigDigestMismatch metric.Int64Gauge
}

func NewPromReporter(
Expand Down Expand Up @@ -169,6 +176,10 @@ func NewPromReporter(
if err != nil {
return nil, fmt.Errorf("failed to register ccip_exec_loopp_ccip_provider_supported gauge: %w", err)
}
configDigestMismatch, err := bhClient.Meter.Int64Gauge("ccip_exec_config_digest_mismatch")
if err != nil {
return nil, fmt.Errorf("failed to register ccip_exec_config_digest_mismatch gauge: %w", err)
}

return &PromReporter{
lggr: lggr,
Expand All @@ -184,6 +195,7 @@ func NewPromReporter(
processorErrors: PromExecProcessorErrors,
latestRoundID: PromExecLatestRoundID,
looppProviderSupported: promLooppCCIPProviderSupported,
configDigestMismatch: promExecConfigDigestMismatch,

bhLatencyHistogram: latencyHistogram,
bhProcessorLatencyHistogram: processorLatencyHistogram,
Expand All @@ -193,6 +205,7 @@ func NewPromReporter(
beholderProcessorErrors: processorErrors,
bhExecLatestRound: execLatestRoundID,
bhLooppProviderSupported: looppProviderSupported,
bhConfigDigestMismatch: configDigestMismatch,
}, nil
}

Expand Down Expand Up @@ -399,3 +412,15 @@ func (p *PromReporter) TrackLooppProviderSupported(looppCCIPProviderSupported ma
))
}
}

func (p *PromReporter) TrackConfigDigestMismatch(mismatch bool) {
var value float64
if mismatch {
value = 1
}
p.configDigestMismatch.WithLabelValues(p.chainFamily, p.chainID).Set(value)
p.bhConfigDigestMismatch.Record(context.Background(), int64(value), metric.WithAttributes(
attribute.String("chain_family", p.chainFamily),
attribute.String("chain_id", p.chainID),
))
}
3 changes: 3 additions & 0 deletions execute/metrics/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Reporter interface {
TrackLatency(state exectypes.PluginState, method plugincommon.MethodType, latency time.Duration, err error)
TrackProcessorOutput(string, plugincommon.MethodType, plugintypes.Trackable)
TrackProcessorLatency(processor string, method plugincommon.MethodType, latency time.Duration, err error)
TrackConfigDigestMismatch(mismatch bool)
}

type Noop struct{}
Expand All @@ -33,5 +34,7 @@ func (n *Noop) TrackProcessorOutput(string, plugincommon.MethodType, plugintypes

func (n *Noop) TrackProcessorLatency(string, plugincommon.MethodType, time.Duration, error) {}

func (n *Noop) TrackConfigDigestMismatch(bool) {}

var _ Reporter = &Noop{}
var _ Reporter = &PromReporter{}
Loading
Loading