Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/capabilities"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/types/core"

capcommon "github.com/smartcontractkit/capabilities/chain_capabilities/common"
)

// TransmissionScheduler handles capability-layer transmission scheduling by waiting for the appropriate delay
Expand Down Expand Up @@ -118,7 +120,15 @@ func InitMyDON(ctx context.Context, registry core.CapabilitiesRegistry, capabili
return capabilities.DON{}, fmt.Errorf("capabilities registry is nil")
}

donsWithNodes, err := registry.DONsForCapability(ctx, capabilityID)
// The host node populates its capabilities metadata registry asynchronously
// (CapabilitiesLauncher.OnNewRegistry after the first on-chain registry sync).
// A plugin's Initialise can win the race and call in before that happens, in
// which case the registry returns "metadataRegistry information not available".
// Retry with backoff until the registry is ready or the host-provided ctx
// (bounded by the standard-capabilities startTimeout) expires.
donsWithNodes, err := capcommon.WithPollingRetry(ctx, lggr, func(ctx context.Context) ([]capabilities.DONWithNodes, error) {
return registry.DONsForCapability(ctx, capabilityID)
})
if err != nil {
lggr.Errorw("failed getting DONs for capability", "capabilityID", capabilityID, "error", err)
return capabilities.DON{}, fmt.Errorf("failed getting dons for capability: %w", err)
Expand All @@ -139,6 +149,9 @@ func InitMyDON(ctx context.Context, registry core.CapabilitiesRegistry, capabili
}

// Legacy path: filter by local PeerID to find which DON(s) this node belongs to.
// No retry needed here: DONsForCapability above already gates on the metadata
// registry being populated, so by this point LocalNode will not hit the
// "metadataRegistry information not available" race.
localNode, err := registry.LocalNode(ctx)
if err != nil {
lggr.Errorw("failed to get local node", "error", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package transmissionschedule_test

import (
"context"
"errors"
"testing"
"time"

Expand Down Expand Up @@ -183,6 +184,19 @@ func TestInitMyDON(t *testing.T) {
require.EqualValues(t, 20, got.ID)
})

t.Run("authoritative: returns correct DON by ID after failing once", func(t *testing.T) {
reg := mocks.NewCapabilitiesRegistry(t)
reg.EXPECT().DONsForCapability(mock.Anything, capID).Return(nil, errors.New("metadataRegistry information not available")).Once()
reg.EXPECT().DONsForCapability(mock.Anything, capID).Return(buildDONs(map[uint32][]p2ptypes.PeerID{
10: {myPeerID, otherPeerID},
20: {myPeerID, otherPeerID},
}), nil)

got, err := ts.InitMyDON(context.Background(), reg, capID, 20, logger.Test(t), false)
require.NoError(t, err)
require.EqualValues(t, 20, got.ID)
})

t.Run("authoritative: selects correct DON even when local node is not listed as a member of all DONs", func(t *testing.T) {
// Plex scenario: node serves DON 20 but the registry lists it only in DON 10's
// membership. With the authoritative ID we bypass peer-membership filtering
Expand Down Expand Up @@ -234,6 +248,19 @@ func TestInitMyDON(t *testing.T) {
require.EqualValues(t, 10, got.ID)
})

t.Run("legacy: single matched DON returns that DON after failing once", func(t *testing.T) {
reg := mocks.NewCapabilitiesRegistry(t)
withLocalNode(reg)
reg.EXPECT().DONsForCapability(mock.Anything, capID).Return(nil, errors.New("metadataRegistry information not available")).Once()
reg.EXPECT().DONsForCapability(mock.Anything, capID).Return(buildDONs(map[uint32][]p2ptypes.PeerID{
10: {myPeerID, otherPeerID},
}), nil)

got, err := ts.InitMyDON(context.Background(), reg, capID, 0, logger.Test(t), false)
require.NoError(t, err)
require.EqualValues(t, 10, got.ID)
})

t.Run("legacy: multiple matched DONs returns first and warns", func(t *testing.T) {
reg := mocks.NewCapabilitiesRegistry(t)
withLocalNode(reg)
Expand Down
Loading