diff --git a/core/capabilities/ccip/launcher/deployment.go b/core/capabilities/ccip/launcher/deployment.go index 631c02d5cc9..64571183425 100644 --- a/core/capabilities/ccip/launcher/deployment.go +++ b/core/capabilities/ccip/launcher/deployment.go @@ -1,142 +1,81 @@ package launcher import ( + "errors" "fmt" + "sync" - cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" - + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "go.uber.org/multierr" - ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" ) -// activeCandidateDeployment represents a active-candidate deployment of OCR instances. -type activeCandidateDeployment struct { - // active is the active OCR instance. - // active must always be present. - active cctypes.CCIPOracle - - // candidate is the candidate OCR instance. - // candidate may or may not be present. - // candidate must never be present if active is not present. - candidate cctypes.CCIPOracle -} - -// ccipDeployment represents active-candidate deployments of both commit and exec -// OCR instances. -type ccipDeployment struct { - commit activeCandidateDeployment - exec activeCandidateDeployment -} - -// Close shuts down all OCR instances in the deployment. -func (c *ccipDeployment) Close() error { - // we potentially run into this situation when - // trying to close an active instance that doesn't exist - // this check protects us from nil pointer exception - - if c == nil { - return nil - } - var err error - - // shutdown active commit instance. - if c.commit.active != nil { - err = multierr.Append(err, c.commit.active.Close()) - } - - // shutdown candidate commit instance. - if c.commit.candidate != nil { - err = multierr.Append(err, c.commit.candidate.Close()) - } - - // shutdown active exec instance. - if c.exec.active != nil { - err = multierr.Append(err, c.exec.active.Close()) - } +// MaxPlugins is the maximum number of plugins possible. +// A plugin represents a possible combination of (active/candidate) x (commit/exec) +// If we ever have more than 4 plugins in a prev or desired state, something went wrong +const MaxPlugins = 4 - // shutdown candidate exec instance. - if c.exec.candidate != nil { - err = multierr.Append(err, c.exec.candidate.Close()) - } +type pluginRegistry map[ocrtypes.ConfigDigest]cctypes.CCIPOracle - return err +// StartAll will call Oracle.Start on an entire don +func (c pluginRegistry) StartAll() error { + emptyPluginRegistry := make(pluginRegistry) + return c.TransitionFrom(emptyPluginRegistry) } -// StartActive starts the active OCR instances. -func (c *ccipDeployment) StartActive() error { - var err error - - err = multierr.Append(err, c.commit.active.Start()) - err = multierr.Append(err, c.exec.active.Start()) - - return err +// CloseAll is used to shut down an entire don immediately +func (c pluginRegistry) CloseAll() error { + emptyPluginRegistry := make(pluginRegistry) + return emptyPluginRegistry.TransitionFrom(c) } -// CloseActive shuts down the active OCR instances. -func (c *ccipDeployment) CloseActive() error { - var err error +// TransitionFrom manages starting and stopping ocr instances +// If there are any new config digests, we need to start those instances +// If any of the previous config digests are no longer present, we need to shut those down +// We don't care about if they're exec/commit or active/candidate, that all happens in the plugin +func (c pluginRegistry) TransitionFrom(prevPlugins pluginRegistry) error { + var allErrs error - err = multierr.Append(err, c.commit.active.Close()) - err = multierr.Append(err, c.exec.active.Close()) - - return err -} - -// TransitionDeployment handles the active-candidate deployment transition. -// prevDeployment is the previous deployment state. -// there are two possible cases: -// -// 1. both active and candidate are present in prevDeployment, but only active is present in c. -// this is a promotion of candidate to active, so we need to shut down the active deployment -// and make candidate the new active. In this case candidate is already running, so there's no -// need to start it. However, we need to shut down the active deployment. -// -// 2. only active is present in prevDeployment, both active and candidate are present in c. -// In this case, active is already running, so there's no need to start it. We need to -// start candidate. -func (c *ccipDeployment) TransitionDeployment(prevDeployment *ccipDeployment) error { - if prevDeployment == nil { - return fmt.Errorf("previous deployment is nil") + if len(c) > MaxPlugins || len(prevPlugins) > MaxPlugins { + return fmt.Errorf("current pluginRegistry or prevPlugins have more than 4 instances: len(prevPlugins): %d, len(currPlugins): %d", len(prevPlugins), len(c)) } - var err error - if prevDeployment.commit.candidate != nil && c.commit.candidate == nil { - err = multierr.Append(err, prevDeployment.commit.active.Close()) - } else if prevDeployment.commit.candidate == nil && c.commit.candidate != nil { - err = multierr.Append(err, c.commit.candidate.Start()) - } else { - return fmt.Errorf("invalid active-candidate deployment transition") + var wg sync.WaitGroup + var mu sync.Mutex + // This shuts down instances that were present previously, but are no longer needed + for digest, oracle := range prevPlugins { + if _, ok := c[digest]; !ok { + wg.Add(1) + go func(o cctypes.CCIPOracle) { + defer wg.Done() + if err := o.Close(); err != nil { + mu.Lock() + allErrs = multierr.Append(allErrs, err) + mu.Unlock() + } + }(oracle) + } } - - if prevDeployment.exec.candidate != nil && c.exec.candidate == nil { - err = multierr.Append(err, prevDeployment.exec.active.Close()) - } else if prevDeployment.exec.candidate == nil && c.exec.candidate != nil { - err = multierr.Append(err, c.exec.candidate.Start()) - } else { - return fmt.Errorf("invalid active-candidate deployment transition") + wg.Wait() + + // This will start the instances that were not previously present, but are in the new config + for digest, oracle := range c { + if digest == [32]byte{} { + allErrs = multierr.Append(allErrs, errors.New("cannot start a plugin with an empty config digest")) + } else if _, ok := prevPlugins[digest]; !ok { + wg.Add(1) + go func(o cctypes.CCIPOracle) { + defer wg.Done() + if err := o.Start(); err != nil { + mu.Lock() + allErrs = multierr.Append(allErrs, err) + mu.Unlock() + } + }(oracle) + } } + wg.Wait() - return err -} - -// HasCandidateInstance returns true if the deployment has a candidate instance for the -// given plugin type. -func (c *ccipDeployment) HasCandidateInstance(pluginType cctypes.PluginType) bool { - switch pluginType { - case cctypes.PluginTypeCCIPCommit: - return c.commit.candidate != nil - case cctypes.PluginTypeCCIPExec: - return c.exec.candidate != nil - default: - return false - } -} - -func isNewCandidateInstance(pluginType cctypes.PluginType, ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta, prevDeployment ccipDeployment) bool { - return len(ocrConfigs) == 2 && !prevDeployment.HasCandidateInstance(pluginType) -} - -func isPromotion(pluginType cctypes.PluginType, ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta, prevDeployment ccipDeployment) bool { - return len(ocrConfigs) == 1 && prevDeployment.HasCandidateInstance(pluginType) + return allErrs } diff --git a/core/capabilities/ccip/launcher/deployment_test.go b/core/capabilities/ccip/launcher/deployment_test.go index a7fa8888314..34b5dd6d18b 100644 --- a/core/capabilities/ccip/launcher/deployment_test.go +++ b/core/capabilities/ccip/launcher/deployment_test.go @@ -1,479 +1,292 @@ package launcher import ( - "errors" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "testing" - cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" - mocktypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/stretchr/testify/require" - ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + mocktypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types/mocks" ) -func Test_ccipDeployment_Close(t *testing.T) { +func Test_ccipDeployment_Transitions(t *testing.T) { + // we use a pointer to the oracle here for mock assertions type args struct { - commitBlue *mocktypes.CCIPOracle - commitGreen *mocktypes.CCIPOracle - execBlue *mocktypes.CCIPOracle - execGreen *mocktypes.CCIPOracle + prevDeployment map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle + currDeployment map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle + } + assertions := func(t *testing.T, args args) { + for i := range args.prevDeployment { + args.prevDeployment[i].AssertExpectations(t) + } + for i := range args.currDeployment { + args.currDeployment[i].AssertExpectations(t) + } } tests := []struct { - name string - args args - expect func(t *testing.T, args args) - asserts func(t *testing.T, args args) - wantErr bool + name string + makeArgs func(t *testing.T) args + expect func(t *testing.T, args args) + wantErr bool }{ { - name: "no errors, active only", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: nil, + name: "all plugins are new", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 4 { + prevP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 4 { + currP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + return args{prevDeployment: prevP, currDeployment: currP} }, expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.execBlue.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) + for _, plugin := range args.prevDeployment { + plugin.On("Close").Return(nil).Once() + } + for _, plugin := range args.currDeployment { + plugin.On("Start").Return(nil).Once() + } }, wantErr: false, }, { - name: "no errors, active and candidate", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), + name: "no configs -> candidates", + makeArgs: func(t *testing.T) args { + prev := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + + curr := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 2 { + curr[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + return args{prevDeployment: prev, currDeployment: curr} }, expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.commitGreen.On("Close").Return(nil).Once() - args.execBlue.On("Close").Return(nil).Once() - args.execGreen.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.commitGreen.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - args.execGreen.AssertExpectations(t) + // When we are creating candidates, they should be started + for _, plugin := range args.currDeployment { + plugin.On("Start").Return(nil).Once() + } }, wantErr: false, }, { - name: "error on commit active", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: nil, - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(errors.New("failed")).Once() - args.execBlue.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &ccipDeployment{ - commit: activeCandidateDeployment{ - active: tt.args.commitBlue, - }, - exec: activeCandidateDeployment{ - active: tt.args.execBlue, - }, - } - if tt.args.commitGreen != nil { - c.commit.candidate = tt.args.commitGreen - } - - if tt.args.execGreen != nil { - c.exec.candidate = tt.args.execGreen - } - - tt.expect(t, tt.args) - defer tt.asserts(t, tt.args) - err := c.Close() - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} + name: "candidates -> active", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 2 { + prevP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } -func Test_ccipDeployment_StartBlue(t *testing.T) { - type args struct { - commitBlue *mocktypes.CCIPOracle - execBlue *mocktypes.CCIPOracle - } - tests := []struct { - name string - args args - expect func(t *testing.T, args args) - asserts func(t *testing.T, args args) - wantErr bool - }{ - { - name: "no errors", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for digest, oracle := range prevP { + currP[digest] = oracle + } + return args{prevDeployment: prevP, currDeployment: currP} }, expect: func(t *testing.T, args args) { - args.commitBlue.On("Start").Return(nil).Once() - args.execBlue.On("Start").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) + // if candidates are being promoted, there should be nothing to start or stop + for _, plugin := range args.currDeployment { + plugin.AssertNotCalled(t, "Start") + plugin.AssertNotCalled(t, "Close") + } }, wantErr: false, }, { - name: "error on commit active", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), + name: "active -> active+candidates", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 2 { + prevP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for digest, oracle := range prevP { + currP[digest] = oracle + } + for range 2 { + currP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + return args{prevDeployment: prevP, currDeployment: currP} }, expect: func(t *testing.T, args args) { - args.commitBlue.On("Start").Return(errors.New("failed")).Once() - args.execBlue.On("Start").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) + for digest, plugin := range args.currDeployment { + // if it previously existed, there should be noop + // if it's a new instance, it should be started + if _, ok := args.prevDeployment[digest]; ok { + plugin.AssertNotCalled(t, "Start") + plugin.AssertNotCalled(t, "Close") + } else { + plugin.On("Start").Return(nil).Once() + } + } }, - wantErr: true, + wantErr: false, }, { - name: "error on exec active", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), + name: "active+candidate -> active", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 4 { + prevP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + // copy two digests over + i := 2 + for digest, oracle := range prevP { + if i == 0 { + continue + } + currP[digest] = oracle + i-- + } + return args{prevDeployment: prevP, currDeployment: currP} }, expect: func(t *testing.T, args args) { - args.commitBlue.On("Start").Return(nil).Once() - args.execBlue.On("Start").Return(errors.New("failed")).Once() + for digest, plugin := range args.prevDeployment { + if _, ok := args.currDeployment[digest]; !ok { + // if the instance is no longer present, it should have been deleted + plugin.On("Close").Return(nil).Once() + } else { + // otherwise, it should have been left alone + plugin.AssertNotCalled(t, "Close") + plugin.AssertNotCalled(t, "Start") + } + } }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - }, - wantErr: true, + wantErr: false, }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &ccipDeployment{ - commit: activeCandidateDeployment{ - active: tt.args.commitBlue, - }, - exec: activeCandidateDeployment{ - active: tt.args.execBlue, - }, - } + { + name: "candidate -> different candidate", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 2 { + prevP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } - tt.expect(t, tt.args) - defer tt.asserts(t, tt.args) - err := c.StartActive() - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 2 { + currP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } -func Test_ccipDeployment_CloseBlue(t *testing.T) { - type args struct { - commitBlue *mocktypes.CCIPOracle - execBlue *mocktypes.CCIPOracle - } - tests := []struct { - name string - args args - expect func(t *testing.T, args args) - asserts func(t *testing.T, args args) - wantErr bool - }{ - { - name: "no errors", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), + return args{prevDeployment: prevP, currDeployment: currP} }, expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.execBlue.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) + for _, plugin := range args.prevDeployment { + plugin.On("Close").Return(nil).Once() + } + for _, plugin := range args.currDeployment { + plugin.On("Start").Return(nil).Once() + } }, wantErr: false, }, { - name: "error on commit active", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), + name: "close all instances", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 4 { + prevP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + + return args{prevDeployment: prevP, currDeployment: currP} }, expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(errors.New("failed")).Once() - args.execBlue.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) + for _, plugin := range args.prevDeployment { + plugin.On("Close").Return(nil).Once() + } }, - wantErr: true, + wantErr: false, }, { - name: "error on exec active", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.execBlue.On("Close").Return(errors.New("failed")).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &ccipDeployment{ - commit: activeCandidateDeployment{ - active: tt.args.commitBlue, - }, - exec: activeCandidateDeployment{ - active: tt.args.execBlue, - }, - } - - tt.expect(t, tt.args) - defer tt.asserts(t, tt.args) - err := c.CloseActive() - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} + name: "start all instances", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) -func Test_ccipDeployment_HandleBlueGreen_PrevDeploymentNil(t *testing.T) { - require.Error(t, (&ccipDeployment{}).TransitionDeployment(nil)) -} + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 4 { + currP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } -func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { - type args struct { - commitBlue *mocktypes.CCIPOracle - commitGreen *mocktypes.CCIPOracle - execBlue *mocktypes.CCIPOracle - execGreen *mocktypes.CCIPOracle - } - tests := []struct { - name string - argsPrevDeployment args - argsFutureDeployment args - expect func(t *testing.T, args args, argsPrevDeployment args) - asserts func(t *testing.T, args args, argsPrevDeployment args) - wantErr bool - }{ - { - name: "promotion active to candidate", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), + return args{prevDeployment: prevP, currDeployment: currP} }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: nil, - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) { - argsPrevDeployment.commitBlue.On("Close").Return(nil).Once() - argsPrevDeployment.execBlue.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args, argsPrevDeployment args) { - argsPrevDeployment.commitBlue.AssertExpectations(t) - argsPrevDeployment.execBlue.AssertExpectations(t) + expect: func(t *testing.T, args args) { + for _, plugin := range args.currDeployment { + plugin.On("Start").Return(nil).Once() + } }, wantErr: false, }, { - name: "new candidate deployment", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: nil, + name: "should handle nil to nil", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + return args{prevDeployment: prevP, currDeployment: currP} }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.On("Start").Return(nil).Once() - args.execGreen.On("Start").Return(nil).Once() - }, - asserts: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.AssertExpectations(t) - args.execGreen.AssertExpectations(t) + expect: func(t *testing.T, args args) { + for _, plugin := range args.currDeployment { + plugin.On("Start").Return(nil).Once() + } }, wantErr: false, }, { - name: "error on commit candidate start", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: nil, - }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.On("Start").Return(errors.New("failed")).Once() - args.execGreen.On("Start").Return(nil).Once() - }, - asserts: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.AssertExpectations(t) - args.execGreen.AssertExpectations(t) - }, + name: "should throw error if there are more than 5 instances", + makeArgs: func(t *testing.T) args { + prevP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + currP := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 5 { + currP[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + return args{prevDeployment: prevP, currDeployment: currP} + }, + expect: func(t *testing.T, args args) {}, wantErr: true, }, { - name: "error on exec candidate start", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: nil, - }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.On("Start").Return(nil).Once() - args.execGreen.On("Start").Return(errors.New("failed")).Once() - }, - asserts: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.AssertExpectations(t) - args.execGreen.AssertExpectations(t) - }, - wantErr: true, - }, - { - name: "invalid active-candidate deployment transition commit: both prev and future deployment have candidate", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) {}, - asserts: func(t *testing.T, args args, argsPrevDeployment args) {}, - wantErr: true, - }, - { - name: "invalid active-candidate deployment transition exec: both prev and future exec deployment have candidate", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.On("Start").Return(nil).Once() + name: "candidate -> init", + makeArgs: func(t *testing.T) args { + prev := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + for range 2 { + prev[utils.RandomBytes32()] = mocktypes.NewCCIPOracle(t) + } + curr := make(map[ocrtypes.ConfigDigest]*mocktypes.CCIPOracle) + + return args{prevDeployment: prev, currDeployment: curr} }, - asserts: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.AssertExpectations(t) + expect: func(t *testing.T, args args) { + // When we are creating candidates, they should be started + for _, plugin := range args.prevDeployment { + plugin.On("Close").Return(nil).Once() + } }, - wantErr: true, + wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - futDeployment := &ccipDeployment{ - commit: activeCandidateDeployment{ - active: tt.argsFutureDeployment.commitBlue, - }, - exec: activeCandidateDeployment{ - active: tt.argsFutureDeployment.execBlue, - }, + args := tt.makeArgs(t) + prev := make(pluginRegistry) + for digest, oracle := range args.prevDeployment { + prev[digest] = oracle } - if tt.argsFutureDeployment.commitGreen != nil { - futDeployment.commit.candidate = tt.argsFutureDeployment.commitGreen - } - if tt.argsFutureDeployment.execGreen != nil { - futDeployment.exec.candidate = tt.argsFutureDeployment.execGreen - } - - prevDeployment := &ccipDeployment{ - commit: activeCandidateDeployment{ - active: tt.argsPrevDeployment.commitBlue, - }, - exec: activeCandidateDeployment{ - active: tt.argsPrevDeployment.execBlue, - }, + curr := make(pluginRegistry) + for digest, oracle := range args.currDeployment { + curr[digest] = oracle } - if tt.argsPrevDeployment.commitGreen != nil { - prevDeployment.commit.candidate = tt.argsPrevDeployment.commitGreen - } - if tt.argsPrevDeployment.execGreen != nil { - prevDeployment.exec.candidate = tt.argsPrevDeployment.execGreen - } - - tt.expect(t, tt.argsFutureDeployment, tt.argsPrevDeployment) - defer tt.asserts(t, tt.argsFutureDeployment, tt.argsPrevDeployment) - err := futDeployment.TransitionDeployment(prevDeployment) + tt.expect(t, args) + defer assertions(t, args) + err := curr.TransitionFrom(prev) if tt.wantErr { require.Error(t, err) } else { @@ -482,200 +295,3 @@ func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { }) } } - -func Test_isNewGreenInstance(t *testing.T) { - type args struct { - pluginType cctypes.PluginType - ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta - prevDeployment ccipDeployment - } - tests := []struct { - name string - args args - want bool - }{ - { - "prev deployment only active", - args{ - pluginType: cctypes.PluginTypeCCIPCommit, - ocrConfigs: []ccipreaderpkg.OCR3ConfigWithMeta{ - {}, {}, - }, - prevDeployment: ccipDeployment{ - commit: activeCandidateDeployment{ - active: mocktypes.NewCCIPOracle(t), - }, - }, - }, - true, - }, - { - "candidate -> active promotion", - args{ - pluginType: cctypes.PluginTypeCCIPCommit, - ocrConfigs: []ccipreaderpkg.OCR3ConfigWithMeta{ - {}, - }, - prevDeployment: ccipDeployment{ - commit: activeCandidateDeployment{ - active: mocktypes.NewCCIPOracle(t), - candidate: mocktypes.NewCCIPOracle(t), - }, - }, - }, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := isNewCandidateInstance(tt.args.pluginType, tt.args.ocrConfigs, tt.args.prevDeployment) - require.Equal(t, tt.want, got) - }) - } -} - -func Test_isPromotion(t *testing.T) { - type args struct { - pluginType cctypes.PluginType - ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta - prevDeployment ccipDeployment - } - tests := []struct { - name string - args args - want bool - }{ - { - "prev deployment only active", - args{ - pluginType: cctypes.PluginTypeCCIPCommit, - ocrConfigs: []ccipreaderpkg.OCR3ConfigWithMeta{ - {}, {}, - }, - prevDeployment: ccipDeployment{ - commit: activeCandidateDeployment{ - active: mocktypes.NewCCIPOracle(t), - }, - }, - }, - false, - }, - { - "candidate -> active promotion", - args{ - pluginType: cctypes.PluginTypeCCIPCommit, - ocrConfigs: []ccipreaderpkg.OCR3ConfigWithMeta{ - {}, - }, - prevDeployment: ccipDeployment{ - commit: activeCandidateDeployment{ - active: mocktypes.NewCCIPOracle(t), - candidate: mocktypes.NewCCIPOracle(t), - }, - }, - }, - true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := isPromotion(tt.args.pluginType, tt.args.ocrConfigs, tt.args.prevDeployment); got != tt.want { - t.Errorf("isPromotion() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_ccipDeployment_HasGreenInstance(t *testing.T) { - type fields struct { - commit activeCandidateDeployment - exec activeCandidateDeployment - } - type args struct { - pluginType cctypes.PluginType - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - "commit candidate present", - fields{ - commit: activeCandidateDeployment{ - active: mocktypes.NewCCIPOracle(t), - candidate: mocktypes.NewCCIPOracle(t), - }, - }, - args{ - pluginType: cctypes.PluginTypeCCIPCommit, - }, - true, - }, - { - "commit candidate not present", - fields{ - commit: activeCandidateDeployment{ - active: mocktypes.NewCCIPOracle(t), - }, - }, - args{ - pluginType: cctypes.PluginTypeCCIPCommit, - }, - false, - }, - { - "exec candidate present", - fields{ - exec: activeCandidateDeployment{ - active: mocktypes.NewCCIPOracle(t), - candidate: mocktypes.NewCCIPOracle(t), - }, - }, - args{ - pluginType: cctypes.PluginTypeCCIPExec, - }, - true, - }, - { - "exec candidate not present", - fields{ - exec: activeCandidateDeployment{ - active: mocktypes.NewCCIPOracle(t), - }, - }, - args{ - pluginType: cctypes.PluginTypeCCIPExec, - }, - false, - }, - { - "invalid plugin type", - fields{}, - args{ - pluginType: cctypes.PluginType(100), - }, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &ccipDeployment{} - if tt.fields.commit.active != nil { - c.commit.active = tt.fields.commit.active - } - if tt.fields.commit.candidate != nil { - c.commit.candidate = tt.fields.commit.candidate - } - if tt.fields.exec.active != nil { - c.exec.active = tt.fields.exec.active - } - if tt.fields.exec.candidate != nil { - c.exec.candidate = tt.fields.exec.candidate - } - got := c.HasCandidateInstance(tt.args.pluginType) - require.Equal(t, tt.want, got) - }) - } -} diff --git a/core/capabilities/ccip/launcher/launcher.go b/core/capabilities/ccip/launcher/launcher.go index c52c6493181..167ac0a815e 100644 --- a/core/capabilities/ccip/launcher/launcher.go +++ b/core/capabilities/ccip/launcher/launcher.go @@ -2,12 +2,14 @@ package launcher import ( "context" - "errors" "fmt" "sync" "time" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" @@ -48,9 +50,9 @@ func New( IDsToNodes: make(map[p2ptypes.PeerID]kcr.INodeInfoProviderNodeInfo), IDsToCapabilities: make(map[string]registrysyncer.Capability), }, - dons: make(map[registrysyncer.DonID]*ccipDeployment), tickInterval: tickInterval, oracleCreator: oracleCreator, + instances: make(map[registrysyncer.DonID]pluginRegistry), } } @@ -76,10 +78,10 @@ type launcher struct { wg sync.WaitGroup tickInterval time.Duration - // dons is a map of CCIP DON IDs to the OCR instances that are running on them. - // we can have up to two OCR instances per CCIP plugin, since we are running two plugins, - // thats four OCR instances per CCIP DON maximum. - dons map[registrysyncer.DonID]*ccipDeployment + // instances is a map of CCIP DON IDs to a map of the OCR instances that are running on them. + // This map uses the config digest as the key, and the instance as the value. + // We can have up to a maximum of 4 instances per CCIP DON (active/candidate) x (commit/exec) + instances map[registrysyncer.DonID]pluginRegistry } // Launch implements registrysyncer.Launcher. @@ -101,7 +103,7 @@ func (l *launcher) runningDONIDs() []registrysyncer.DonID { l.lock.RLock() defer l.lock.RUnlock() var runningDONs []registrysyncer.DonID - for id := range l.dons { + for id := range l.instances { runningDONs = append(runningDONs, id) } return runningDONs @@ -116,8 +118,8 @@ func (l *launcher) Close() error { // shut down all running oracles. var err error - for _, ceDep := range l.dons { - err = multierr.Append(err, ceDep.Close()) + for _, ceDep := range l.instances { + err = multierr.Append(err, ceDep.CloseAll()) } return err @@ -189,195 +191,180 @@ func (l *launcher) processDiff(diff diffResult) error { return err } +// processUpdate will manage when configurations of an existing don are updated +// If new oracles are needed, they are created and started. Old ones will be shut down func (l *launcher) processUpdate(updated map[registrysyncer.DonID]registrysyncer.DON) error { l.lock.Lock() defer l.lock.Unlock() for donID, don := range updated { - prevDeployment, ok := l.dons[registrysyncer.DonID(don.ID)] + prevPlugins, ok := l.instances[donID] if !ok { return fmt.Errorf("invariant violation: expected to find CCIP DON %d in the map of running deployments", don.ID) } - // we encounter this when a node is removed from the don - if prevDeployment == nil { - return errors.New("this node was closed") + + latestConfigs, err := getConfigsForDon(l.homeChainReader, don) + if err != nil { + return err } - futDeployment, err := updateDON( + newPlugins, err := updateDON( l.lggr, l.myP2PID, - l.homeChainReader, - *prevDeployment, + prevPlugins, don, l.oracleCreator, - ) + latestConfigs) if err != nil { return err } - // When we remove a node from the don, this node does not have a future deployment - if futDeployment != nil { - if err := futDeployment.TransitionDeployment(prevDeployment); err != nil { - // TODO: how to handle a failed active-candidate deployment? - return fmt.Errorf("failed to handle active-candidate deployment for CCIP DON %d: %w", donID, err) - } - // update state. - l.dons[donID] = futDeployment - // update the state with the latest config. - // this way if one of the starts errors, we don't retry all of them. - l.regState.IDsToDONs[donID] = updated[donID] + err = newPlugins.TransitionFrom(prevPlugins) + if err != nil { + return fmt.Errorf("could not transition state %w", err) } + + l.instances[donID] = newPlugins + l.regState.IDsToDONs[donID] = updated[donID] } return nil } +// processAdded is for when a new don is created. We know that all oracles +// must be created and started func (l *launcher) processAdded(added map[registrysyncer.DonID]registrysyncer.DON) error { l.lock.Lock() defer l.lock.Unlock() for donID, don := range added { - dep, err := createDON( + configs, err := getConfigsForDon(l.homeChainReader, don) + if err != nil { + return fmt.Errorf("failed to get current configs for don %d: %w", donID, err) + } + newPlugins, err := createDON( l.lggr, l.myP2PID, - l.homeChainReader, don, l.oracleCreator, + configs, ) if err != nil { return fmt.Errorf("processAdded: call createDON %d: %w", donID, err) } - if dep == nil { + if len(newPlugins) == 0 { // not a member of this DON. continue } - // TODO: this doesn't seem to be correct; a newly added DON will not have an active - // instance but a candidate instance. - if err := dep.StartActive(); err != nil { - if shutdownErr := dep.CloseActive(); shutdownErr != nil { - l.lggr.Errorw("Failed to shutdown active instance after failed start", "donId", donID, "err", shutdownErr) + // now that oracles are created, we need to start them. If there are issues with starting + // we should shut them down + if err := newPlugins.StartAll(); err != nil { + if shutdownErr := newPlugins.CloseAll(); shutdownErr != nil { + l.lggr.Errorw("Failed to shutdown don instances after a failed start", "donId", donID, "err", shutdownErr) } return fmt.Errorf("processAdded: start oracles for CCIP DON %d: %w", donID, err) } // update state. - l.dons[donID] = dep - // update the state with the latest config. - // this way if one of the starts errors, we don't retry all of them. + l.instances[donID] = newPlugins l.regState.IDsToDONs[donID] = added[donID] } return nil } +// processRemoved handles the situation when an entire DON is removed func (l *launcher) processRemoved(removed map[registrysyncer.DonID]registrysyncer.DON) error { l.lock.Lock() defer l.lock.Unlock() for id := range removed { - ceDep, ok := l.dons[id] + p, ok := l.instances[id] if !ok { // not running this particular DON. continue } - if err := ceDep.Close(); err != nil { + if err := p.CloseAll(); err != nil { return fmt.Errorf("failed to shutdown oracles for CCIP DON %d: %w", id, err) + } // after a successful shutdown we can safely remove the DON deployment from the map. - delete(l.dons, id) + delete(l.instances, id) delete(l.regState.IDsToDONs, id) } return nil } -// updateDON is a pure function that handles the case where a DON in the capability registry -// has received a new configuration. -// It returns a new ccipDeployment that can then be used to perform the active-candidate deployment, -// based on the previous deployment. func updateDON( lggr logger.Logger, p2pID ragep2ptypes.PeerID, - homeChainReader ccipreader.HomeChain, - prevDeployment ccipDeployment, + prevPlugins pluginRegistry, don registrysyncer.DON, oracleCreator cctypes.OracleCreator, -) (futDeployment *ccipDeployment, err error) { + latestConfigs []ccipreader.OCR3ConfigWithMeta, +) (pluginRegistry, error) { if !isMemberOfDON(don, p2pID) { lggr.Infow("Not a member of this DON, skipping", "donId", don.ID, "p2pId", p2pID.String()) - return nil, nil } - // this should be a retryable error. - commitOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.ID, uint8(cctypes.PluginTypeCCIPCommit)) - if err != nil { - return nil, fmt.Errorf("failed to fetch OCR configs for CCIP commit plugin (don id: %d) from home chain config contract: %w", - don.ID, err) - } - - execOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.ID, uint8(cctypes.PluginTypeCCIPExec)) - if err != nil { - return nil, fmt.Errorf("failed to fetch OCR configs for CCIP exec plugin (don id: %d) from home chain config contract: %w", - don.ID, err) - } - - commitAcd, err := createFutureActiveCandidateDeployment(don.ID, prevDeployment, commitOCRConfigs, oracleCreator, cctypes.PluginTypeCCIPCommit) - if err != nil { - return nil, fmt.Errorf("failed to create future active-candidate deployment for CCIP commit plugin: %w, don id: %d", err, don.ID) - } + newP := make(pluginRegistry) + // If a config digest is not already in our list, we need to create an oracle + // If a config digest is already in our list, we just need to point to the old one + // newP.Transition will make sure we shut down the old oracles, and start the new ones + for _, c := range latestConfigs { + digest := c.ConfigDigest + if _, ok := prevPlugins[digest]; !ok { + oracle, err := oracleCreator.Create(don.ID, cctypes.OCR3ConfigWithMeta(c)) + if err != nil { + return nil, fmt.Errorf("failed to create CCIP oracle: %w for digest %x", err, digest) + } - execAcd, err := createFutureActiveCandidateDeployment(don.ID, prevDeployment, execOCRConfigs, oracleCreator, cctypes.PluginTypeCCIPExec) - if err != nil { - return nil, fmt.Errorf("failed to create future active-candidate deployment for CCIP exec plugin: %w, don id: %d", err, don.ID) + newP[digest] = oracle + } else { + newP[digest] = prevPlugins[digest] + } } - return &ccipDeployment{ - commit: commitAcd, - exec: execAcd, - }, nil + return newP, nil } -// TODO: this is not technically correct, CCIPHome has other transitions -// that are not covered here, e.g revokeCandidate. -func createFutureActiveCandidateDeployment( - donID uint32, - prevDeployment ccipDeployment, - ocrConfigs []ccipreader.OCR3ConfigWithMeta, +// createDON is a pure function that handles the case where a new DON is added to the capability registry. +// It returns up to 4 plugins that are later started. +func createDON( + lggr logger.Logger, + p2pID ragep2ptypes.PeerID, + don registrysyncer.DON, oracleCreator cctypes.OracleCreator, - pluginType cctypes.PluginType, -) (activeCandidateDeployment, error) { - var deployment activeCandidateDeployment - if isNewCandidateInstance(pluginType, ocrConfigs, prevDeployment) { - // this is a new candidate instance. - candidateOracle, err := oracleCreator.Create(donID, cctypes.OCR3ConfigWithMeta(ocrConfigs[1])) + configs []ccipreader.OCR3ConfigWithMeta, +) (pluginRegistry, error) { + if !isMemberOfDON(don, p2pID) && oracleCreator.Type() == cctypes.OracleTypePlugin { + lggr.Infow("Not a member of this DON and not a bootstrap node either, skipping", "donId", don.ID, "p2pId", p2pID.String()) + return nil, nil + } + p := make(pluginRegistry) + for _, config := range configs { + digest, err := ocrtypes.BytesToConfigDigest(config.ConfigDigest[:]) if err != nil { - return activeCandidateDeployment{}, fmt.Errorf("failed to create CCIP commit oracle: %w", err) + return nil, fmt.Errorf("digest does not match type %w", err) } - deployment.active = prevDeployment.commit.active - deployment.candidate = candidateOracle - } else if isPromotion(pluginType, ocrConfigs, prevDeployment) { - // this is a promotion of candidate->active. - deployment.active = prevDeployment.commit.candidate - } else { - return activeCandidateDeployment{}, fmt.Errorf("invariant violation: expected 1 or 2 OCR configs for CCIP plugin (type: %d), got %d", pluginType, len(ocrConfigs)) - } + oracle, err := oracleCreator.Create(don.ID, cctypes.OCR3ConfigWithMeta(config)) + if err != nil { + return nil, fmt.Errorf("failed to create CCIP oracle: %w for digest %x", err, digest) + } - return deployment, nil + p[digest] = oracle + } + return p, nil } -// createDON is a pure function that handles the case where a new DON is added to the capability registry. -// It returns a new ccipDeployment that can then be used to start the active instance. -func createDON( - lggr logger.Logger, - p2pID ragep2ptypes.PeerID, +func getConfigsForDon( homeChainReader ccipreader.HomeChain, - don registrysyncer.DON, - oracleCreator cctypes.OracleCreator, -) (*ccipDeployment, error) { + don registrysyncer.DON) ([]ccipreader.OCR3ConfigWithMeta, error) { // this should be a retryable error. commitOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.ID, uint8(cctypes.PluginTypeCCIPCommit)) if err != nil { @@ -391,39 +378,19 @@ func createDON( don.ID, err) } - // upon creation we should only have one OCR config per plugin type. - if len(commitOCRConfigs) != 1 { - return nil, fmt.Errorf("expected exactly one OCR config for CCIP commit plugin (don id: %d), got %d", don.ID, len(commitOCRConfigs)) - } - - if len(execOCRConfigs) != 1 { - return nil, fmt.Errorf("expected exactly one OCR config for CCIP exec plugin (don id: %d), got %d", don.ID, len(execOCRConfigs)) - } - - if !isMemberOfDON(don, p2pID) && oracleCreator.Type() == cctypes.OracleTypePlugin { - lggr.Infow("Not a member of this DON and not a bootstrap node either, skipping", "donId", don.ID, "p2pId", p2pID.String()) - return nil, nil - } - - // at this point we know we are either a member of the DON or a bootstrap node. - // the injected oracleCreator will create the appropriate oracle. - commitOracle, err := oracleCreator.Create(don.ID, cctypes.OCR3ConfigWithMeta(commitOCRConfigs[0])) - if err != nil { - return nil, fmt.Errorf("failed to create CCIP commit oracle: %w", err) + c := []ccipreader.OCR3ConfigWithMeta{ + commitOCRConfigs.CandidateConfig, + commitOCRConfigs.ActiveConfig, + execOCRConfigs.CandidateConfig, + execOCRConfigs.ActiveConfig, } - execOracle, err := oracleCreator.Create(don.ID, cctypes.OCR3ConfigWithMeta(execOCRConfigs[0])) - if err != nil { - return nil, fmt.Errorf("failed to create CCIP exec oracle: %w", err) + ret := make([]ccipreader.OCR3ConfigWithMeta, 0, 4) + for _, config := range c { + if config.ConfigDigest != [32]byte{} { + ret = append(ret, config) + } } - // TODO: incorrect, should be setting candidate? - return &ccipDeployment{ - commit: activeCandidateDeployment{ - active: commitOracle, - }, - exec: activeCandidateDeployment{ - active: execOracle, - }, - }, nil + return ret, nil } diff --git a/core/capabilities/ccip/launcher/launcher_test.go b/core/capabilities/ccip/launcher/launcher_test.go index bb3bb8a843a..188ee48c215 100644 --- a/core/capabilities/ccip/launcher/launcher_test.go +++ b/core/capabilities/ccip/launcher/launcher_test.go @@ -2,9 +2,10 @@ package launcher import ( "math/big" - "reflect" "testing" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types/mocks" @@ -48,20 +49,28 @@ func Test_createDON(t *testing.T) { func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) { homeChainReader. On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPCommit)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPCommit), - Nodes: getOCR3Nodes(3, 4), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) homeChainReader. On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPExec)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPExec), - Nodes: getOCR3Nodes(3, 4), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) oracleCreator.EXPECT().Type().Return(cctypes.OracleTypePlugin).Once() }, false, @@ -81,20 +90,28 @@ func Test_createDON(t *testing.T) { func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) { homeChainReader. On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPCommit)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPCommit), - Nodes: getOCR3Nodes(3, 4), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) homeChainReader. On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPExec)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPExec), - Nodes: getOCR3Nodes(3, 4), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) oracleCreator.EXPECT().Type().Return(cctypes.OracleTypeBootstrap).Once() oracleCreator.EXPECT().Create(mock.Anything, mock.Anything).Return(mocks.NewCCIPOracle(t), nil).Twice() }, @@ -112,20 +129,29 @@ func Test_createDON(t *testing.T) { func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) { homeChainReader. On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPCommit)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPCommit), - Nodes: getOCR3Nodes(3, 4), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) + homeChainReader. On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPExec), - Nodes: getOCR3Nodes(3, 4), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) @@ -138,6 +164,65 @@ func Test_createDON(t *testing.T) { }, false, }, + { + "if a don is created with active and candidate configs, all should be created", + args{ + logger.TestLogger(t), + p2pID1, + mocks.NewHomeChainReader(t), + mocks.NewOracleCreator(t), + defaultRegistryDon, + }, + func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) { + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPCommit)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + }, nil) + + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + }, nil) + + oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) + })). + Return(mocks.NewCCIPOracle(t), nil).Twice() + oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) + })). + Return(mocks.NewCCIPOracle(t), nil).Twice() + }, + false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -145,7 +230,9 @@ func Test_createDON(t *testing.T) { tt.expect(t, tt.args, tt.args.oracleCreator, tt.args.homeChainReader) } - _, err := createDON(tt.args.lggr, tt.args.p2pID, tt.args.homeChainReader, tt.args.don, tt.args.oracleCreator) + latestConfigs, err := getConfigsForDon(tt.args.homeChainReader, tt.args.don) + require.NoError(t, err) + _, err = createDON(tt.args.lggr, tt.args.p2pID, tt.args.don, tt.args.oracleCreator, latestConfigs) if tt.wantErr { require.Error(t, err) } else { @@ -155,74 +242,280 @@ func Test_createDON(t *testing.T) { } } -func Test_createFutureBlueGreenDeployment(t *testing.T) { - type args struct { - donID uint32 - prevDeployment ccipDeployment - ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta - oracleCreator *mocks.OracleCreator - pluginType cctypes.PluginType - } - tests := []struct { - name string - args args - want activeCandidateDeployment - wantErr bool - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := createFutureActiveCandidateDeployment(tt.args.donID, tt.args.prevDeployment, tt.args.ocrConfigs, tt.args.oracleCreator, tt.args.pluginType) - if (err != nil) != tt.wantErr { - t.Errorf("createFutureActiveCandidateDeployment() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("createFutureActiveCandidateDeployment() = %v, want %v", got, tt.want) - } - }) - } -} - func Test_updateDON(t *testing.T) { + var ( + digest1 = utils.RandomBytes32() + digest2 = utils.RandomBytes32() + ) type args struct { lggr logger.Logger p2pID ragep2ptypes.PeerID homeChainReader *mocks.HomeChainReader oracleCreator *mocks.OracleCreator - prevDeployment ccipDeployment don registrysyncer.DON + prevPlugins pluginRegistry } tests := []struct { - name string - args args - wantFutDeployment *ccipDeployment - wantErr bool + name string + args args + desiredLen int + expect func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) + wantErr bool }{ - // TODO: Add test cases. + { + name: "should start new plugins", + args: args{ + logger.TestLogger(t), + p2pID3, + mocks.NewHomeChainReader(t), + mocks.NewOracleCreator(t), + registrysyncer.DON{ + DON: getDON(2, []ragep2ptypes.PeerID{p2pID3, p2pID4}, 0), + CapabilityConfigurations: defaultCapCfgs, + }, + pluginRegistry{ + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + }, + }, + desiredLen: 2, + expect: func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) { + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPCommit)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + }, nil) + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPExec)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + }, nil) + oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) + })). + Return(mocks.NewCCIPOracle(t), nil) + oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) + })). + Return(mocks.NewCCIPOracle(t), nil) + }, + wantErr: false, + }, + { + name: "should return no plugins if config is empty", + args: args{ + logger.TestLogger(t), + p2pID3, + mocks.NewHomeChainReader(t), + mocks.NewOracleCreator(t), + registrysyncer.DON{ + DON: getDON(2, []ragep2ptypes.PeerID{p2pID3, p2pID4}, 0), + CapabilityConfigurations: defaultCapCfgs, + }, + pluginRegistry{ + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + }, + }, + desiredLen: 0, + expect: func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) { + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPCommit)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + }, nil) + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPExec)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + }, nil) + oracleCreator.AssertNotCalled(t, "Create") + }, + wantErr: false, + }, + { + name: "should maintain existing plugins", + args: args{ + logger.TestLogger(t), + p2pID3, + mocks.NewHomeChainReader(t), + mocks.NewOracleCreator(t), + registrysyncer.DON{ + DON: getDON(2, []ragep2ptypes.PeerID{p2pID3, p2pID4}, 0), + CapabilityConfigurations: defaultCapCfgs, + }, + pluginRegistry{ + digest1: mocks.NewCCIPOracle(t), + digest2: mocks.NewCCIPOracle(t), + }, + }, + desiredLen: 4, + expect: func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) { + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPCommit)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: digest1, + }, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + }, nil) + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPExec)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: digest2, + }, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + }, nil) + oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) + })). + Return(mocks.NewCCIPOracle(t), nil).Once() + oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) + })). + Return(mocks.NewCCIPOracle(t), nil).Once() + }, + wantErr: false, + }, + { + name: "should start brand new plugins if all are new", + args: args{ + logger.TestLogger(t), + p2pID3, + mocks.NewHomeChainReader(t), + mocks.NewOracleCreator(t), + registrysyncer.DON{ + DON: getDON(2, []ragep2ptypes.PeerID{p2pID3, p2pID4}, 0), + CapabilityConfigurations: defaultCapCfgs, + }, + pluginRegistry{ + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + utils.RandomBytes32(): mocks.NewCCIPOracle(t), + }, + }, + desiredLen: 4, + expect: func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) { + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPCommit)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + }, nil) + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPExec)). + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), + }, + }, nil) + oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) + })). + Return(mocks.NewCCIPOracle(t), nil).Twice() + oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) + })). + Return(mocks.NewCCIPOracle(t), nil).Twice() + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotFutDeployment, err := updateDON(tt.args.lggr, tt.args.p2pID, tt.args.homeChainReader, tt.args.prevDeployment, tt.args.don, tt.args.oracleCreator) + if tt.expect != nil { + tt.expect(t, tt.args, tt.args.oracleCreator, tt.args.homeChainReader) + } + + latestConfigs, err := getConfigsForDon(tt.args.homeChainReader, tt.args.don) + require.NoError(t, err) + newPlugins, err := updateDON(tt.args.lggr, tt.args.p2pID, tt.args.prevPlugins, tt.args.don, tt.args.oracleCreator, latestConfigs) if (err != nil) != tt.wantErr { t.Errorf("updateDON() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(gotFutDeployment, tt.wantFutDeployment) { - t.Errorf("updateDON() = %v, want %v", gotFutDeployment, tt.wantFutDeployment) + + if len(newPlugins) != tt.desiredLen { + t.Errorf("updateDON() error. Wanted new length of plugins to be %d, got %d", tt.desiredLen, len(newPlugins)) } }) } } func Test_launcher_processDiff(t *testing.T) { + var ( + digest1 = utils.RandomBytes32() + digest2 = utils.RandomBytes32() + ) type fields struct { lggr logger.Logger p2pID ragep2ptypes.PeerID homeChainReader *mocks.HomeChainReader oracleCreator *mocks.OracleCreator - dons map[registrysyncer.DonID]*ccipDeployment + instances map[registrysyncer.DonID]pluginRegistry regState registrysyncer.LocalRegistry } type args struct { @@ -238,22 +531,18 @@ func Test_launcher_processDiff(t *testing.T) { { "don removed success", fields{ - dons: map[registrysyncer.DonID]*ccipDeployment{ + instances: map[registrysyncer.DonID]pluginRegistry{ 1: { - commit: activeCandidateDeployment{ - active: newMock(t, - func(t *testing.T) *mocks.CCIPOracle { return mocks.NewCCIPOracle(t) }, - func(m *mocks.CCIPOracle) { - m.On("Close").Return(nil) - }), - }, - exec: activeCandidateDeployment{ - active: newMock(t, - func(t *testing.T) *mocks.CCIPOracle { return mocks.NewCCIPOracle(t) }, - func(m *mocks.CCIPOracle) { - m.On("Close").Return(nil) - }), - }, + utils.RandomBytes32(): newMock(t, + func(t *testing.T) *mocks.CCIPOracle { return mocks.NewCCIPOracle(t) }, + func(m *mocks.CCIPOracle) { + m.On("Close").Return(nil).Once() + }), + utils.RandomBytes32(): newMock(t, + func(t *testing.T) *mocks.CCIPOracle { return mocks.NewCCIPOracle(t) }, + func(m *mocks.CCIPOracle) { + m.On("Close").Return(nil).Once() + }), }, }, regState: registrysyncer.LocalRegistry{ @@ -270,8 +559,8 @@ func Test_launcher_processDiff(t *testing.T) { }, }, func(t *testing.T, l *launcher) { - require.Len(t, l.dons, 0) - require.Len(t, l.regState.IDsToDONs, 0) + require.Empty(t, l.instances) + require.Empty(t, l.regState.IDsToDONs) }, false, }, @@ -284,17 +573,27 @@ func Test_launcher_processDiff(t *testing.T) { return mocks.NewHomeChainReader(t) }, func(m *mocks.HomeChainReader) { m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPCommit)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPExec), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{}, + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) }), oracleCreator: newMock(t, func(t *testing.T) *mocks.OracleCreator { return mocks.NewOracleCreator(t) @@ -312,7 +611,7 @@ func Test_launcher_processDiff(t *testing.T) { })). Return(execOracle, nil) }), - dons: map[registrysyncer.DonID]*ccipDeployment{}, + instances: map[registrysyncer.DonID]pluginRegistry{}, regState: registrysyncer.LocalRegistry{ IDsToDONs: map[registrysyncer.DonID]registrysyncer.DON{}, }, @@ -325,7 +624,7 @@ func Test_launcher_processDiff(t *testing.T) { }, }, func(t *testing.T, l *launcher) { - require.Len(t, l.dons, 1) + require.Len(t, l.instances, 1) require.Len(t, l.regState.IDsToDONs, 1) }, false, @@ -339,25 +638,39 @@ func Test_launcher_processDiff(t *testing.T) { return mocks.NewHomeChainReader(t) }, func(m *mocks.HomeChainReader) { m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPCommit)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }, { - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPCommit), + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: digest1, }, - }}, nil) + }, nil) m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPExec), + Return(ccipreaderpkg.ActiveAndCandidate{ + ActiveConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: digest2, }, - }, { - Config: ccipreaderpkg.OCR3Config{ - PluginType: uint8(cctypes.PluginTypeCCIPExec), + CandidateConfig: ccipreaderpkg.OCR3ConfigWithMeta{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + Nodes: getOCR3Nodes(3, 4), + }, + ConfigDigest: utils.RandomBytes32(), }, - }}, nil) + }, nil) }), oracleCreator: newMock(t, func(t *testing.T) *mocks.OracleCreator { return mocks.NewOracleCreator(t) @@ -375,18 +688,14 @@ func Test_launcher_processDiff(t *testing.T) { })). Return(execOracle, nil) }), - dons: map[registrysyncer.DonID]*ccipDeployment{ + instances: map[registrysyncer.DonID]pluginRegistry{ 1: { - commit: activeCandidateDeployment{ - active: newMock(t, func(t *testing.T) *mocks.CCIPOracle { - return mocks.NewCCIPOracle(t) - }, func(m *mocks.CCIPOracle) {}), - }, - exec: activeCandidateDeployment{ - active: newMock(t, func(t *testing.T) *mocks.CCIPOracle { - return mocks.NewCCIPOracle(t) - }, func(m *mocks.CCIPOracle) {}), - }, + digest1: newMock(t, func(t *testing.T) *mocks.CCIPOracle { + return mocks.NewCCIPOracle(t) + }, func(m *mocks.CCIPOracle) {}), + digest2: newMock(t, func(t *testing.T) *mocks.CCIPOracle { + return mocks.NewCCIPOracle(t) + }, func(m *mocks.CCIPOracle) {}), }, }, regState: registrysyncer.LocalRegistry{ @@ -407,7 +716,7 @@ func Test_launcher_processDiff(t *testing.T) { }, }, func(t *testing.T, l *launcher) { - require.Len(t, l.dons, 1) + require.Len(t, l.instances, 1) require.Len(t, l.regState.IDsToDONs, 1) require.Len(t, l.regState.IDsToDONs[1].Members, 2) }, @@ -417,7 +726,7 @@ func Test_launcher_processDiff(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { l := &launcher{ - dons: tt.fields.dons, + instances: tt.fields.instances, regState: tt.fields.regState, myP2PID: tt.fields.p2pID, lggr: tt.fields.lggr, diff --git a/core/capabilities/ccip/types/mocks/home_chain_reader.go b/core/capabilities/ccip/types/mocks/home_chain_reader.go index 7aa857d46cb..95fc20d226e 100644 --- a/core/capabilities/ccip/types/mocks/home_chain_reader.go +++ b/core/capabilities/ccip/types/mocks/home_chain_reader.go @@ -65,23 +65,23 @@ func (_m *HomeChainReader) Name() string { } // GetOCRConfigs provides a mock function with given fields: ctx, donID, pluginType -func (_m *HomeChainReader) GetOCRConfigs(ctx context.Context, donID uint32, pluginType uint8) ([]ccipreaderpkg.OCR3ConfigWithMeta, error) { +func (_m *HomeChainReader) GetOCRConfigs(ctx context.Context, donID uint32, pluginType uint8) (ccipreaderpkg.ActiveAndCandidate, error) { ret := _m.Called(ctx, donID, pluginType) if len(ret) == 0 { panic("no return value specified for GetOCRConfigs") } - var r0 []ccipreaderpkg.OCR3ConfigWithMeta + var r0 ccipreaderpkg.ActiveAndCandidate var r1 error - if rf, ok := ret.Get(0).(func(ctx context.Context, donID uint32, pluginType uint8) ([]ccipreaderpkg.OCR3ConfigWithMeta, error)); ok { + if rf, ok := ret.Get(0).(func(ctx context.Context, donID uint32, pluginType uint8) (ccipreaderpkg.ActiveAndCandidate, error)); ok { return rf(ctx, donID, pluginType) } - if rf, ok := ret.Get(0).(func(ctx context.Context, donID uint32, pluginType uint8) []ccipreaderpkg.OCR3ConfigWithMeta); ok { + if rf, ok := ret.Get(0).(func(ctx context.Context, donID uint32, pluginType uint8) ccipreaderpkg.ActiveAndCandidate); ok { r0 = rf(ctx, donID, pluginType) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]ccipreaderpkg.OCR3ConfigWithMeta) + r0 = ret.Get(0).(ccipreaderpkg.ActiveAndCandidate) } } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index d5100c6f3d2..0392bfab5c2 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -288,7 +288,7 @@ require ( github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 // indirect github.com/smartcontractkit/chain-selectors v1.0.27 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 005d95779ae..3e5bc90f90f 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1090,8 +1090,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 h1:+VR9yKhbz+iCtWnCabaCDP18lIqGnk7YvGQIbJYgDrs= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/deployment/go.mod b/deployment/go.mod index 14630351641..4f5d3e95624 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -23,7 +23,7 @@ require ( github.com/sethvargo/go-retry v0.2.4 github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 github.com/smartcontractkit/chain-selectors v1.0.27 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 diff --git a/deployment/go.sum b/deployment/go.sum index 5c4f0df9cad..0607d6161fd 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1382,8 +1382,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 h1:+VR9yKhbz+iCtWnCabaCDP18lIqGnk7YvGQIbJYgDrs= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/go.mod b/go.mod index 03084edf658..cd089a62e6d 100644 --- a/go.mod +++ b/go.mod @@ -75,7 +75,7 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.27 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e diff --git a/go.sum b/go.sum index c9ffda30070..fceb95c5efb 100644 --- a/go.sum +++ b/go.sum @@ -1075,8 +1075,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 h1:+VR9yKhbz+iCtWnCabaCDP18lIqGnk7YvGQIbJYgDrs= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 054fbb27787..b223f729e8b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -35,7 +35,7 @@ require ( github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.27 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index d4f87ad83e7..df4ad771076 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1403,8 +1403,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 h1:+VR9yKhbz+iCtWnCabaCDP18lIqGnk7YvGQIbJYgDrs= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index de72bc705e7..2cbbda250fd 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -64,7 +64,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f // indirect github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 13e1974b17e..f6c81e4c500 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1392,8 +1392,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4 h1:+VR9yKhbz+iCtWnCabaCDP18lIqGnk7YvGQIbJYgDrs= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241101132401-00090bf7feb4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw=