diff --git a/core/capabilities/ccip/delegate.go b/core/capabilities/ccip/delegate.go index ef3822c4de..2a39334549 100644 --- a/core/capabilities/ccip/delegate.go +++ b/core/capabilities/ccip/delegate.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" + cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" @@ -134,21 +135,36 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services ccipConfigBinding, ) - oracleCreator := oraclecreator.New( - ocrKeys, - transmitterKeys, - d.chains, - d.peerWrapper, - spec.ExternalJobID, - spec.ID, - d.isNewlyCreatedJob, - spec.CCIPSpec.PluginConfig, - ocrDB, - d.lggr, - d.monitoringEndpointGen, - bootstrapperLocators, - hcr, - ) + // if bootstrappers are provided we assume that the node is a plugin oracle. + // the reason for this is that bootstrap oracles do not need to be aware + // of other bootstrap oracles. however, plugin oracles, at least initially, + // must be aware of available bootstrappers. + var oracleCreator cctypes.OracleCreator + if len(spec.CCIPSpec.P2PV2Bootstrappers) > 0 { + oracleCreator = oraclecreator.NewPluginOracleCreator( + ocrKeys, + transmitterKeys, + d.chains, + d.peerWrapper, + spec.ExternalJobID, + spec.ID, + d.isNewlyCreatedJob, + spec.CCIPSpec.PluginConfig, + ocrDB, + d.lggr, + d.monitoringEndpointGen, + bootstrapperLocators, + hcr, + ) + } else { + oracleCreator = oraclecreator.NewBootstrapOracleCreator( + d.peerWrapper, + bootstrapperLocators, + ocrDB, + d.monitoringEndpointGen, + d.lggr, + ) + } capLauncher := launcher.New( spec.CCIPSpec.CapabilityVersion, @@ -156,8 +172,8 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services ragep2ptypes.PeerID(p2pID.PeerID()), d.lggr, hcr, - oracleCreator, 12*time.Second, + oracleCreator, ) // register the capability launcher with the registry syncer diff --git a/core/capabilities/ccip/launcher/bluegreen.go b/core/capabilities/ccip/launcher/bluegreen.go index 6245846629..c15f8c038f 100644 --- a/core/capabilities/ccip/launcher/bluegreen.go +++ b/core/capabilities/ccip/launcher/bluegreen.go @@ -16,21 +16,11 @@ type blueGreenDeployment struct { // blue must always be present. blue cctypes.CCIPOracle - // bootstrapBlue is the bootstrap node of the blue OCR instance. - // Only a subset of the DON will be running bootstrap instances, - // so this may be nil. - bootstrapBlue cctypes.CCIPOracle - // green is the green OCR instance. // green may or may not be present. // green must never be present if blue is not present. // TODO: should we enforce this invariant somehow? green cctypes.CCIPOracle - - // bootstrapGreen is the bootstrap node of the green OCR instance. - // Only a subset of the DON will be running bootstrap instances, - // so this may be nil, even when green is not nil. - bootstrapGreen cctypes.CCIPOracle } // ccipDeployment represents blue-green deployments of both commit and exec @@ -44,33 +34,21 @@ type ccipDeployment struct { func (c *ccipDeployment) Close() error { var err error - // shutdown blue commit instances. + // shutdown blue commit instance. err = multierr.Append(err, c.commit.blue.Close()) - if c.commit.bootstrapBlue != nil { - err = multierr.Append(err, c.commit.bootstrapBlue.Close()) - } - // shutdown green commit instances. + // shutdown green commit instance. if c.commit.green != nil { err = multierr.Append(err, c.commit.green.Close()) } - if c.commit.bootstrapGreen != nil { - err = multierr.Append(err, c.commit.bootstrapGreen.Close()) - } - // shutdown blue exec instances. + // shutdown blue exec instance. err = multierr.Append(err, c.exec.blue.Close()) - if c.exec.bootstrapBlue != nil { - err = multierr.Append(err, c.exec.bootstrapBlue.Close()) - } - // shutdown green exec instances. + // shutdown green exec instance. if c.exec.green != nil { err = multierr.Append(err, c.exec.green.Close()) } - if c.exec.bootstrapGreen != nil { - err = multierr.Append(err, c.exec.bootstrapGreen.Close()) - } return err } @@ -80,13 +58,7 @@ func (c *ccipDeployment) StartBlue() error { var err error err = multierr.Append(err, c.commit.blue.Start()) - if c.commit.bootstrapBlue != nil { - err = multierr.Append(err, c.commit.bootstrapBlue.Start()) - } err = multierr.Append(err, c.exec.blue.Start()) - if c.exec.bootstrapBlue != nil { - err = multierr.Append(err, c.exec.bootstrapBlue.Start()) - } return err } @@ -96,13 +68,7 @@ func (c *ccipDeployment) CloseBlue() error { var err error err = multierr.Append(err, c.commit.blue.Close()) - if c.commit.bootstrapBlue != nil { - err = multierr.Append(err, c.commit.bootstrapBlue.Close()) - } err = multierr.Append(err, c.exec.blue.Close()) - if c.exec.bootstrapBlue != nil { - err = multierr.Append(err, c.exec.bootstrapBlue.Close()) - } return err } @@ -127,28 +93,16 @@ func (c *ccipDeployment) HandleBlueGreen(prevDeployment *ccipDeployment) error { var err error if prevDeployment.commit.green != nil && c.commit.green == nil { err = multierr.Append(err, prevDeployment.commit.blue.Close()) - if prevDeployment.commit.bootstrapBlue != nil { - err = multierr.Append(err, prevDeployment.commit.bootstrapBlue.Close()) - } } else if prevDeployment.commit.green == nil && c.commit.green != nil { err = multierr.Append(err, c.commit.green.Start()) - if c.commit.bootstrapGreen != nil { - err = multierr.Append(err, c.commit.bootstrapGreen.Start()) - } } else { return fmt.Errorf("invalid blue-green deployment transition") } if prevDeployment.exec.green != nil && c.exec.green == nil { err = multierr.Append(err, prevDeployment.exec.blue.Close()) - if prevDeployment.exec.bootstrapBlue != nil { - err = multierr.Append(err, prevDeployment.exec.bootstrapBlue.Close()) - } } else if prevDeployment.exec.green == nil && c.exec.green != nil { err = multierr.Append(err, c.exec.green.Start()) - if c.exec.bootstrapGreen != nil { - err = multierr.Append(err, c.exec.bootstrapGreen.Start()) - } } else { return fmt.Errorf("invalid blue-green deployment transition") } diff --git a/core/capabilities/ccip/launcher/bluegreen_test.go b/core/capabilities/ccip/launcher/bluegreen_test.go index 9fd71a0cb4..965491180e 100644 --- a/core/capabilities/ccip/launcher/bluegreen_test.go +++ b/core/capabilities/ccip/launcher/bluegreen_test.go @@ -14,14 +14,10 @@ import ( func Test_ccipDeployment_Close(t *testing.T) { type args struct { - commitBlue *mocktypes.CCIPOracle - commitBlueBootstrap *mocktypes.CCIPOracle - commitGreen *mocktypes.CCIPOracle - commitGreenBootstrap *mocktypes.CCIPOracle - execBlue *mocktypes.CCIPOracle - execBlueBootstrap *mocktypes.CCIPOracle - execGreen *mocktypes.CCIPOracle - execGreenBootstrap *mocktypes.CCIPOracle + commitBlue *mocktypes.CCIPOracle + commitGreen *mocktypes.CCIPOracle + execBlue *mocktypes.CCIPOracle + execGreen *mocktypes.CCIPOracle } tests := []struct { name string @@ -33,12 +29,10 @@ func Test_ccipDeployment_Close(t *testing.T) { { name: "no errors, blue only", args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - commitGreenBootstrap: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execGreen: nil, - execGreenBootstrap: nil, + commitBlue: mocktypes.NewCCIPOracle(t), + commitGreen: nil, + execBlue: mocktypes.NewCCIPOracle(t), + execGreen: nil, }, expect: func(t *testing.T, args args) { args.commitBlue.On("Close").Return(nil).Once() @@ -90,62 +84,6 @@ func Test_ccipDeployment_Close(t *testing.T) { }, wantErr: true, }, - { - name: "bootstrap blue also closed", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.commitBlueBootstrap.On("Close").Return(nil).Once() - args.execBlue.On("Close").Return(nil).Once() - args.execBlueBootstrap.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.commitBlueBootstrap.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - args.execBlueBootstrap.AssertExpectations(t) - }, - wantErr: false, - }, - { - name: "bootstrap green also closed", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - commitGreenBootstrap: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - execGreenBootstrap: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.commitBlueBootstrap.On("Close").Return(nil).Once() - args.commitGreen.On("Close").Return(nil).Once() - args.commitGreenBootstrap.On("Close").Return(nil).Once() - args.execBlue.On("Close").Return(nil).Once() - args.execBlueBootstrap.On("Close").Return(nil).Once() - args.execGreen.On("Close").Return(nil).Once() - args.execGreenBootstrap.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.commitBlueBootstrap.AssertExpectations(t) - args.commitGreen.AssertExpectations(t) - args.commitGreenBootstrap.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - args.execBlueBootstrap.AssertExpectations(t) - args.execGreen.AssertExpectations(t) - args.execGreenBootstrap.AssertExpectations(t) - }, - wantErr: false, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -160,22 +98,10 @@ func Test_ccipDeployment_Close(t *testing.T) { if tt.args.commitGreen != nil { c.commit.green = tt.args.commitGreen } - if tt.args.commitBlueBootstrap != nil { - c.commit.bootstrapBlue = tt.args.commitBlueBootstrap - } - if tt.args.commitGreenBootstrap != nil { - c.commit.bootstrapGreen = tt.args.commitGreenBootstrap - } if tt.args.execGreen != nil { c.exec.green = tt.args.execGreen } - if tt.args.execBlueBootstrap != nil { - c.exec.bootstrapBlue = tt.args.execBlueBootstrap - } - if tt.args.execGreenBootstrap != nil { - c.exec.bootstrapGreen = tt.args.execGreenBootstrap - } tt.expect(t, tt.args) defer tt.asserts(t, tt.args) @@ -191,10 +117,8 @@ func Test_ccipDeployment_Close(t *testing.T) { func Test_ccipDeployment_StartBlue(t *testing.T) { type args struct { - commitBlue *mocktypes.CCIPOracle - commitBlueBootstrap *mocktypes.CCIPOracle - execBlue *mocktypes.CCIPOracle - execBlueBootstrap *mocktypes.CCIPOracle + commitBlue *mocktypes.CCIPOracle + execBlue *mocktypes.CCIPOracle } tests := []struct { name string @@ -204,12 +128,10 @@ func Test_ccipDeployment_StartBlue(t *testing.T) { wantErr bool }{ { - name: "no errors, no bootstrap", + name: "no errors", args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: nil, - execBlueBootstrap: nil, + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), }, expect: func(t *testing.T, args args) { args.commitBlue.On("Start").Return(nil).Once() @@ -221,35 +143,11 @@ func Test_ccipDeployment_StartBlue(t *testing.T) { }, wantErr: false, }, - { - name: "no errors, with bootstrap", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Start").Return(nil).Once() - args.commitBlueBootstrap.On("Start").Return(nil).Once() - args.execBlue.On("Start").Return(nil).Once() - args.execBlueBootstrap.On("Start").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.commitBlueBootstrap.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - args.execBlueBootstrap.AssertExpectations(t) - }, - wantErr: false, - }, { name: "error on commit blue", args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: nil, - execBlueBootstrap: nil, + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), }, expect: func(t *testing.T, args args) { args.commitBlue.On("Start").Return(errors.New("failed")).Once() @@ -264,10 +162,8 @@ func Test_ccipDeployment_StartBlue(t *testing.T) { { name: "error on exec blue", args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: nil, - execBlueBootstrap: nil, + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), }, expect: func(t *testing.T, args args) { args.commitBlue.On("Start").Return(nil).Once() @@ -279,46 +175,6 @@ func Test_ccipDeployment_StartBlue(t *testing.T) { }, wantErr: true, }, - { - name: "error on commit blue bootstrap", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: nil, - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Start").Return(nil).Once() - args.commitBlueBootstrap.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.commitBlueBootstrap.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - }, - wantErr: true, - }, - { - name: "error on exec blue bootstrap", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: nil, - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Start").Return(nil).Once() - args.execBlue.On("Start").Return(nil).Once() - args.execBlueBootstrap.On("Start").Return(errors.New("failed")).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - args.execBlueBootstrap.AssertExpectations(t) - }, - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -330,12 +186,6 @@ func Test_ccipDeployment_StartBlue(t *testing.T) { blue: tt.args.execBlue, }, } - if tt.args.commitBlueBootstrap != nil { - c.commit.bootstrapBlue = tt.args.commitBlueBootstrap - } - if tt.args.execBlueBootstrap != nil { - c.exec.bootstrapBlue = tt.args.execBlueBootstrap - } tt.expect(t, tt.args) defer tt.asserts(t, tt.args) @@ -351,10 +201,8 @@ func Test_ccipDeployment_StartBlue(t *testing.T) { func Test_ccipDeployment_CloseBlue(t *testing.T) { type args struct { - commitBlue *mocktypes.CCIPOracle - commitBlueBootstrap *mocktypes.CCIPOracle - execBlue *mocktypes.CCIPOracle - execBlueBootstrap *mocktypes.CCIPOracle + commitBlue *mocktypes.CCIPOracle + execBlue *mocktypes.CCIPOracle } tests := []struct { name string @@ -364,12 +212,10 @@ func Test_ccipDeployment_CloseBlue(t *testing.T) { wantErr bool }{ { - name: "no errors, no bootstrap", + name: "no errors", args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: nil, - execBlueBootstrap: nil, + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), }, expect: func(t *testing.T, args args) { args.commitBlue.On("Close").Return(nil).Once() @@ -381,35 +227,11 @@ func Test_ccipDeployment_CloseBlue(t *testing.T) { }, wantErr: false, }, - { - name: "no errors, with bootstrap", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.commitBlueBootstrap.On("Close").Return(nil).Once() - args.execBlue.On("Close").Return(nil).Once() - args.execBlueBootstrap.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.commitBlueBootstrap.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - args.execBlueBootstrap.AssertExpectations(t) - }, - wantErr: false, - }, { name: "error on commit blue", args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: nil, - execBlueBootstrap: nil, + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), }, expect: func(t *testing.T, args args) { args.commitBlue.On("Close").Return(errors.New("failed")).Once() @@ -424,10 +246,8 @@ func Test_ccipDeployment_CloseBlue(t *testing.T) { { name: "error on exec blue", args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: nil, - execBlueBootstrap: nil, + commitBlue: mocktypes.NewCCIPOracle(t), + execBlue: mocktypes.NewCCIPOracle(t), }, expect: func(t *testing.T, args args) { args.commitBlue.On("Close").Return(nil).Once() @@ -439,46 +259,6 @@ func Test_ccipDeployment_CloseBlue(t *testing.T) { }, wantErr: true, }, - { - name: "error on commit blue bootstrap", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: nil, - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.commitBlueBootstrap.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.commitBlueBootstrap.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - }, - wantErr: true, - }, - { - name: "error on exec blue bootstrap", - args: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: nil, - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args) { - args.commitBlue.On("Close").Return(nil).Once() - args.execBlue.On("Close").Return(nil).Once() - args.execBlueBootstrap.On("Close").Return(errors.New("failed")).Once() - }, - asserts: func(t *testing.T, args args) { - args.commitBlue.AssertExpectations(t) - args.execBlue.AssertExpectations(t) - args.execBlueBootstrap.AssertExpectations(t) - }, - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -490,12 +270,6 @@ func Test_ccipDeployment_CloseBlue(t *testing.T) { blue: tt.args.execBlue, }, } - if tt.args.commitBlueBootstrap != nil { - c.commit.bootstrapBlue = tt.args.commitBlueBootstrap - } - if tt.args.execBlueBootstrap != nil { - c.exec.bootstrapBlue = tt.args.execBlueBootstrap - } tt.expect(t, tt.args) defer tt.asserts(t, tt.args) @@ -515,14 +289,10 @@ func Test_ccipDeployment_HandleBlueGreen_PrevDeploymentNil(t *testing.T) { func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { type args struct { - commitBlue *mocktypes.CCIPOracle - commitBlueBootstrap *mocktypes.CCIPOracle - commitGreen *mocktypes.CCIPOracle - commitGreenBootstrap *mocktypes.CCIPOracle - execBlue *mocktypes.CCIPOracle - execBlueBootstrap *mocktypes.CCIPOracle - execGreen *mocktypes.CCIPOracle - execGreenBootstrap *mocktypes.CCIPOracle + commitBlue *mocktypes.CCIPOracle + commitGreen *mocktypes.CCIPOracle + execBlue *mocktypes.CCIPOracle + execGreen *mocktypes.CCIPOracle } tests := []struct { name string @@ -533,7 +303,7 @@ func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { wantErr bool }{ { - name: "promotion blue to green, no bootstrap", + name: "promotion blue to green", argsPrevDeployment: args{ commitBlue: mocktypes.NewCCIPOracle(t), commitGreen: mocktypes.NewCCIPOracle(t), @@ -557,43 +327,7 @@ func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { wantErr: false, }, { - name: "promotion blue to green, with bootstrap", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - commitGreenBootstrap: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - execGreenBootstrap: mocktypes.NewCCIPOracle(t), - }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - commitGreenBootstrap: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - execGreen: nil, - execGreenBootstrap: nil, - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) { - argsPrevDeployment.commitBlue.On("Close").Return(nil).Once() - argsPrevDeployment.commitBlueBootstrap.On("Close").Return(nil).Once() - argsPrevDeployment.execBlue.On("Close").Return(nil).Once() - argsPrevDeployment.execBlueBootstrap.On("Close").Return(nil).Once() - }, - asserts: func(t *testing.T, args args, argsPrevDeployment args) { - argsPrevDeployment.commitBlue.AssertExpectations(t) - argsPrevDeployment.commitBlueBootstrap.AssertExpectations(t) - argsPrevDeployment.execBlue.AssertExpectations(t) - argsPrevDeployment.execBlueBootstrap.AssertExpectations(t) - }, - wantErr: false, - }, - { - name: "new green deployment, no bootstrap", + name: "new green deployment", argsPrevDeployment: args{ commitBlue: mocktypes.NewCCIPOracle(t), commitGreen: nil, @@ -616,42 +350,6 @@ func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { }, wantErr: false, }, - { - name: "new green deployment, with bootstrap", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - commitGreenBootstrap: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - execGreen: nil, - execGreenBootstrap: nil, - }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - commitGreenBootstrap: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - execGreenBootstrap: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.On("Start").Return(nil).Once() - args.commitGreenBootstrap.On("Start").Return(nil).Once() - args.execGreen.On("Start").Return(nil).Once() - args.execGreenBootstrap.On("Start").Return(nil).Once() - }, - asserts: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.AssertExpectations(t) - args.commitGreenBootstrap.AssertExpectations(t) - args.execGreen.AssertExpectations(t) - args.execGreenBootstrap.AssertExpectations(t) - }, - wantErr: false, - }, { name: "error on commit green start", argsPrevDeployment: args{ @@ -700,42 +398,6 @@ func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { }, wantErr: true, }, - { - name: "error on commit green bootstrap start", - argsPrevDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - commitGreen: nil, - commitGreenBootstrap: nil, - execBlue: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - execGreen: nil, - execGreenBootstrap: nil, - }, - argsFutureDeployment: args{ - commitBlue: mocktypes.NewCCIPOracle(t), - commitBlueBootstrap: mocktypes.NewCCIPOracle(t), - commitGreen: mocktypes.NewCCIPOracle(t), - commitGreenBootstrap: mocktypes.NewCCIPOracle(t), - execBlue: mocktypes.NewCCIPOracle(t), - execBlueBootstrap: mocktypes.NewCCIPOracle(t), - execGreen: mocktypes.NewCCIPOracle(t), - execGreenBootstrap: mocktypes.NewCCIPOracle(t), - }, - expect: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.On("Start").Return(nil).Once() - args.commitGreenBootstrap.On("Start").Return(errors.New("failed")).Once() - args.execGreen.On("Start").Return(nil).Once() - args.execGreenBootstrap.On("Start").Return(nil).Once() - }, - asserts: func(t *testing.T, args args, argsPrevDeployment args) { - args.commitGreen.AssertExpectations(t) - args.commitGreenBootstrap.AssertExpectations(t) - args.execGreen.AssertExpectations(t) - args.execGreenBootstrap.AssertExpectations(t) - }, - wantErr: true, - }, { name: "invalid blue-green deployment transition commit: both prev and future deployment have green", argsPrevDeployment: args{ @@ -755,7 +417,7 @@ func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { wantErr: true, }, { - name: "invalid blue-green deployment transition exec: both prev and future deployment have green", + name: "invalid blue-green deployment transition exec: both prev and future exec deployment have green", argsPrevDeployment: args{ commitBlue: mocktypes.NewCCIPOracle(t), commitGreen: nil, @@ -790,21 +452,9 @@ func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { if tt.argsFutureDeployment.commitGreen != nil { futDeployment.commit.green = tt.argsFutureDeployment.commitGreen } - if tt.argsFutureDeployment.commitBlueBootstrap != nil { - futDeployment.commit.bootstrapBlue = tt.argsFutureDeployment.commitBlueBootstrap - } - if tt.argsFutureDeployment.commitGreenBootstrap != nil { - futDeployment.commit.bootstrapGreen = tt.argsFutureDeployment.commitGreenBootstrap - } if tt.argsFutureDeployment.execGreen != nil { futDeployment.exec.green = tt.argsFutureDeployment.execGreen } - if tt.argsFutureDeployment.execBlueBootstrap != nil { - futDeployment.exec.bootstrapBlue = tt.argsFutureDeployment.execBlueBootstrap - } - if tt.argsFutureDeployment.execGreenBootstrap != nil { - futDeployment.exec.bootstrapGreen = tt.argsFutureDeployment.execGreenBootstrap - } prevDeployment := &ccipDeployment{ commit: blueGreenDeployment{ @@ -817,21 +467,9 @@ func Test_ccipDeployment_HandleBlueGreen(t *testing.T) { if tt.argsPrevDeployment.commitGreen != nil { prevDeployment.commit.green = tt.argsPrevDeployment.commitGreen } - if tt.argsPrevDeployment.commitBlueBootstrap != nil { - prevDeployment.commit.bootstrapBlue = tt.argsPrevDeployment.commitBlueBootstrap - } - if tt.argsPrevDeployment.commitGreenBootstrap != nil { - prevDeployment.commit.bootstrapGreen = tt.argsPrevDeployment.commitGreenBootstrap - } if tt.argsPrevDeployment.execGreen != nil { prevDeployment.exec.green = tt.argsPrevDeployment.execGreen } - if tt.argsPrevDeployment.execBlueBootstrap != nil { - prevDeployment.exec.bootstrapBlue = tt.argsPrevDeployment.execBlueBootstrap - } - if tt.argsPrevDeployment.execGreenBootstrap != nil { - prevDeployment.exec.bootstrapGreen = tt.argsPrevDeployment.execGreenBootstrap - } tt.expect(t, tt.argsFutureDeployment, tt.argsPrevDeployment) defer tt.asserts(t, tt.argsFutureDeployment, tt.argsPrevDeployment) diff --git a/core/capabilities/ccip/launcher/diff.go b/core/capabilities/ccip/launcher/diff.go index e44b5478ed..787e33dadc 100644 --- a/core/capabilities/ccip/launcher/diff.go +++ b/core/capabilities/ccip/launcher/diff.go @@ -141,16 +141,3 @@ func isMemberOfDON(don kcr.CapabilitiesRegistryDONInfo, p2pID ragep2ptypes.PeerI } return false } - -// isMemberOfBootstrapSubcommittee returns true if and only if the given p2pID is a member of the given bootstrap subcommittee. -func isMemberOfBootstrapSubcommittee( - bootstrapP2PIDs [][32]byte, - p2pID ragep2ptypes.PeerID, -) bool { - for _, bootstrapID := range bootstrapP2PIDs { - if bootstrapID == p2pID { - return true - } - } - return false -} diff --git a/core/capabilities/ccip/launcher/diff_test.go b/core/capabilities/ccip/launcher/diff_test.go index 37371945ab..b1914e1770 100644 --- a/core/capabilities/ccip/launcher/diff_test.go +++ b/core/capabilities/ccip/launcher/diff_test.go @@ -467,15 +467,6 @@ func Test_isMemberOfDON(t *testing.T) { require.False(t, isMemberOfDON(don, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(5)).PeerID()))) } -func Test_isMemberOfBootstrapSubcommittee(t *testing.T) { - var bootstrapKeys [][32]byte - for i := range [4]struct{}{} { - bootstrapKeys = append(bootstrapKeys, p2pkey.MustNewV2XXXTestingOnly(big.NewInt(int64(i+1))).PeerID()) - } - require.True(t, isMemberOfBootstrapSubcommittee(bootstrapKeys, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()))) - require.False(t, isMemberOfBootstrapSubcommittee(bootstrapKeys, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(5)).PeerID()))) -} - func mustHashedCapabilityID(capabilityLabelledName, capabilityVersion string) [32]byte { r, err := capcommon.HashedCapabilityID(capabilityLabelledName, capabilityVersion) if err != nil { diff --git a/core/capabilities/ccip/launcher/integration_test.go b/core/capabilities/ccip/launcher/integration_test.go index 58b6a5d6a2..da1ac36465 100644 --- a/core/capabilities/ccip/launcher/integration_test.go +++ b/core/capabilities/ccip/launcher/integration_test.go @@ -32,17 +32,17 @@ func TestIntegration_Launcher(t *testing.T) { regSyncer, err := registrysyncer.New(lggr, uni, uni.CapReg.Address().String()) require.NoError(t, err) - hcr := uni.HomeChainReader + oracleCreator := &oracleCreatorPrints{ + t: t, + } launcher := New( it.CcipCapabilityVersion, it.CcipCapabilityLabelledName, p2pIDs[0], logger.TestLogger(t), - hcr, - &oracleCreatorPrints{ - t: t, - }, + uni.HomeChainReader, 1*time.Second, + oracleCreator, ) regSyncer.AddLauncher(launcher) @@ -99,14 +99,14 @@ type oracleCreatorPrints struct { t *testing.T } -func (o *oracleCreatorPrints) CreatePluginOracle(pluginType cctypes.PluginType, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { +func (o *oracleCreatorPrints) Create(config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { + pluginType := cctypes.PluginType(config.Config.PluginType) o.t.Logf("Creating plugin oracle (pluginType: %s) with config %+v\n", pluginType, config) return &oraclePrints{pluginType: pluginType, config: config, t: o.t}, nil } -func (o *oracleCreatorPrints) CreateBootstrapOracle(config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { - o.t.Logf("Creating bootstrap oracle with config %+v\n", config) - return &oraclePrints{pluginType: cctypes.PluginTypeCCIPCommit, config: config, isBootstrap: true, t: o.t}, nil +func (o *oracleCreatorPrints) Type() cctypes.OracleType { + return cctypes.OracleTypePlugin } var _ cctypes.OracleCreator = &oracleCreatorPrints{} diff --git a/core/capabilities/ccip/launcher/launcher.go b/core/capabilities/ccip/launcher/launcher.go index 9acfebfa9a..4d60bfcdc9 100644 --- a/core/capabilities/ccip/launcher/launcher.go +++ b/core/capabilities/ccip/launcher/launcher.go @@ -6,16 +6,14 @@ import ( "sync" "time" - cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" - "go.uber.org/multierr" ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" - ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" "github.com/smartcontractkit/chainlink-common/pkg/services" ccipreader "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -34,8 +32,8 @@ func New( p2pID ragep2ptypes.PeerID, lggr logger.Logger, homeChainReader ccipreader.HomeChain, - oracleCreator cctypes.OracleCreator, tickInterval time.Duration, + oracleCreator cctypes.OracleCreator, ) *launcher { return &launcher{ capabilityVersion: capabilityVersion, @@ -48,9 +46,9 @@ func New( IDsToNodes: make(map[p2ptypes.PeerID]kcr.CapabilitiesRegistryNodeInfo), IDsToCapabilities: make(map[registrysyncer.HashedCapabilityID]kcr.CapabilitiesRegistryCapabilityInfo), }, - oracleCreator: oracleCreator, dons: make(map[registrysyncer.DonID]*ccipDeployment), tickInterval: tickInterval, + oracleCreator: oracleCreator, } } @@ -68,10 +66,10 @@ type launcher struct { latestState registrysyncer.State // regState is the latest capability registry state that we have successfully processed. regState registrysyncer.State - oracleCreator cctypes.OracleCreator lock sync.RWMutex wg sync.WaitGroup tickInterval time.Duration + oracleCreator cctypes.OracleCreator // 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, @@ -197,9 +195,9 @@ func (l *launcher) processUpdate(updated map[registrysyncer.DonID]kcr.Capabiliti l.lggr, l.p2pID, l.homeChainReader, - l.oracleCreator, *prevDeployment, don, + l.oracleCreator, ) if err != nil { return err @@ -228,8 +226,8 @@ func (l *launcher) processAdded(added map[registrysyncer.DonID]kcr.CapabilitiesR l.lggr, l.p2pID, l.homeChainReader, - l.oracleCreator, don, + l.oracleCreator, ) if err != nil { return err @@ -287,9 +285,9 @@ func updateDON( lggr logger.Logger, p2pID ragep2ptypes.PeerID, homeChainReader ccipreader.HomeChain, - oracleCreator cctypes.OracleCreator, prevDeployment ccipDeployment, don kcr.CapabilitiesRegistryDONInfo, + oracleCreator cctypes.OracleCreator, ) (futDeployment *ccipDeployment, err error) { if !isMemberOfDON(don, p2pID) { lggr.Infow("Not a member of this DON, skipping", "donId", don.Id, "p2pId", p2pID.String()) @@ -331,14 +329,14 @@ func updateDON( // All other cases are invalid. This is enforced in the ccip config contract. func createFutureBlueGreenDeployment( prevDeployment ccipDeployment, - ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta, + ocrConfigs []ccipreader.OCR3ConfigWithMeta, oracleCreator cctypes.OracleCreator, pluginType cctypes.PluginType, ) (blueGreenDeployment, error) { var deployment blueGreenDeployment if isNewGreenInstance(pluginType, ocrConfigs, prevDeployment) { // this is a new green instance. - greenOracle, err := oracleCreator.CreatePluginOracle(pluginType, cctypes.OCR3ConfigWithMeta(ocrConfigs[1])) + greenOracle, err := oracleCreator.Create(cctypes.OCR3ConfigWithMeta(ocrConfigs[1])) if err != nil { return blueGreenDeployment{}, fmt.Errorf("failed to create CCIP commit oracle: %w", err) } @@ -361,14 +359,9 @@ func createDON( lggr logger.Logger, p2pID ragep2ptypes.PeerID, homeChainReader ccipreader.HomeChain, - oracleCreator cctypes.OracleCreator, don kcr.CapabilitiesRegistryDONInfo, + oracleCreator cctypes.OracleCreator, ) (*ccipDeployment, 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 { @@ -391,45 +384,29 @@ func createDON( return nil, fmt.Errorf("expected exactly one OCR config for CCIP exec plugin (don id: %d), got %d", don.Id, len(execOCRConfigs)) } - commitOracle, commitBootstrap, err := createOracle(p2pID, oracleCreator, cctypes.PluginTypeCCIPCommit, commitOCRConfigs) + 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(cctypes.OCR3ConfigWithMeta(commitOCRConfigs[0])) if err != nil { return nil, fmt.Errorf("failed to create CCIP commit oracle: %w", err) } - execOracle, execBootstrap, err := createOracle(p2pID, oracleCreator, cctypes.PluginTypeCCIPExec, execOCRConfigs) + execOracle, err := oracleCreator.Create(cctypes.OCR3ConfigWithMeta(execOCRConfigs[0])) if err != nil { return nil, fmt.Errorf("failed to create CCIP exec oracle: %w", err) } return &ccipDeployment{ commit: blueGreenDeployment{ - blue: commitOracle, - bootstrapBlue: commitBootstrap, + blue: commitOracle, }, exec: blueGreenDeployment{ - blue: execOracle, - bootstrapBlue: execBootstrap, + blue: execOracle, }, }, nil } - -func createOracle( - p2pID ragep2ptypes.PeerID, - oracleCreator cctypes.OracleCreator, - pluginType cctypes.PluginType, - ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta, -) (pluginOracle, bootstrapOracle cctypes.CCIPOracle, err error) { - pluginOracle, err = oracleCreator.CreatePluginOracle(pluginType, cctypes.OCR3ConfigWithMeta(ocrConfigs[0])) - if err != nil { - return nil, nil, fmt.Errorf("failed to create CCIP plugin oracle (plugintype: %d): %w", pluginType, err) - } - - if isMemberOfBootstrapSubcommittee(ocrConfigs[0].Config.BootstrapP2PIds, p2pID) { - bootstrapOracle, err = oracleCreator.CreateBootstrapOracle(cctypes.OCR3ConfigWithMeta(ocrConfigs[0])) - if err != nil { - return nil, nil, fmt.Errorf("failed to create CCIP bootstrap oracle (plugintype: %d): %w", pluginType, err) - } - } - - return pluginOracle, bootstrapOracle, nil -} diff --git a/core/capabilities/ccip/launcher/launcher_test.go b/core/capabilities/ccip/launcher/launcher_test.go index 493f962c2c..bb24464c62 100644 --- a/core/capabilities/ccip/launcher/launcher_test.go +++ b/core/capabilities/ccip/launcher/launcher_test.go @@ -1,7 +1,6 @@ package launcher import ( - "errors" "math/big" "reflect" "testing" @@ -16,154 +15,69 @@ import ( ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" ) -func Test_createOracle(t *testing.T) { - var p2pKeys []ragep2ptypes.PeerID - for i := 0; i < 3; i++ { - p2pKeys = append(p2pKeys, ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(int64(i+1))).PeerID())) - } - myP2PKey := p2pKeys[0] +func Test_createDON(t *testing.T) { type args struct { - p2pID ragep2ptypes.PeerID - oracleCreator *mocks.OracleCreator - pluginType cctypes.PluginType - ocrConfigs []ccipreaderpkg.OCR3ConfigWithMeta + lggr logger.Logger + p2pID ragep2ptypes.PeerID + homeChainReader *mocks.HomeChainReader + oracleCreator *mocks.OracleCreator + don kcr.CapabilitiesRegistryDONInfo } tests := []struct { name string args args - expect func(t *testing.T, args args, oracleCreator *mocks.OracleCreator) + expect func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) wantErr bool }{ { - "success, no bootstrap", + "not a member of the DON and not a bootstrap node", args{ - myP2PKey, + logger.TestLogger(t), + ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()), + mocks.NewHomeChainReader(t), mocks.NewOracleCreator(t), - cctypes.PluginTypeCCIPCommit, - []ccipreaderpkg.OCR3ConfigWithMeta{ - { - Config: ccipreaderpkg.OCR3Config{}, - ConfigCount: 1, - ConfigDigest: testutils.Random32Byte(), + kcr.CapabilitiesRegistryDONInfo{ + NodeP2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(3)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(4)).PeerID(), }, + Id: 2, }, }, - func(t *testing.T, args args, oracleCreator *mocks.OracleCreator) { - oracleCreator. - On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). - Return(mocks.NewCCIPOracle(t), nil) - }, - false, - }, - { - "success, with bootstrap", - args{ - myP2PKey, - mocks.NewOracleCreator(t), - cctypes.PluginTypeCCIPCommit, - []ccipreaderpkg.OCR3ConfigWithMeta{ - { + 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{ - BootstrapP2PIds: [][32]byte{myP2PKey}, + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + P2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(3)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(4)).PeerID(), + }, }, - ConfigCount: 1, - ConfigDigest: testutils.Random32Byte(), - }, - }, - }, - func(t *testing.T, args args, oracleCreator *mocks.OracleCreator) { - oracleCreator. - On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). - Return(mocks.NewCCIPOracle(t), nil) - oracleCreator. - On("CreateBootstrapOracle", cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). - Return(mocks.NewCCIPOracle(t), nil) - }, - false, - }, - { - "error creating plugin oracle", - args{ - myP2PKey, - mocks.NewOracleCreator(t), - cctypes.PluginTypeCCIPCommit, - []ccipreaderpkg.OCR3ConfigWithMeta{ - { - Config: ccipreaderpkg.OCR3Config{}, - ConfigCount: 1, - ConfigDigest: testutils.Random32Byte(), - }, - }, - }, - func(t *testing.T, args args, oracleCreator *mocks.OracleCreator) { - oracleCreator. - On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). - Return(nil, errors.New("error creating oracle")) - }, - true, - }, - { - "error creating bootstrap oracle", - args{ - myP2PKey, - mocks.NewOracleCreator(t), - cctypes.PluginTypeCCIPCommit, - []ccipreaderpkg.OCR3ConfigWithMeta{ - { + }}, nil) + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPExec)). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ Config: ccipreaderpkg.OCR3Config{ - BootstrapP2PIds: [][32]byte{myP2PKey}, + PluginType: uint8(cctypes.PluginTypeCCIPExec), + P2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(3)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(4)).PeerID(), + }, }, - ConfigCount: 1, - ConfigDigest: testutils.Random32Byte(), - }, - }, + }}, nil) + oracleCreator.EXPECT().Type().Return(cctypes.OracleTypePlugin).Once() }, - func(t *testing.T, args args, oracleCreator *mocks.OracleCreator) { - oracleCreator. - On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). - Return(mocks.NewCCIPOracle(t), nil) - oracleCreator. - On("CreateBootstrapOracle", cctypes.OCR3ConfigWithMeta(args.ocrConfigs[0])). - Return(nil, errors.New("error creating oracle")) - }, - true, + false, }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.expect(t, tt.args, tt.args.oracleCreator) - _, _, err := createOracle(tt.args.p2pID, tt.args.oracleCreator, tt.args.pluginType, tt.args.ocrConfigs) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} - -func Test_createDON(t *testing.T) { - type args struct { - lggr logger.Logger - p2pID ragep2ptypes.PeerID - homeChainReader *mocks.HomeChainReader - oracleCreator *mocks.OracleCreator - don kcr.CapabilitiesRegistryDONInfo - } - tests := []struct { - name string - args args - expect func(t *testing.T, args args, oracleCreator *mocks.OracleCreator, homeChainReader *mocks.HomeChainReader) - wantErr bool - }{ { - "not a member of the DON", + "not a member of the DON but a running a bootstrap oracle creator", args{ logger.TestLogger(t), ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()), @@ -171,17 +85,44 @@ func Test_createDON(t *testing.T) { mocks.NewOracleCreator(t), kcr.CapabilitiesRegistryDONInfo{ NodeP2PIds: [][32]byte{ - p2pkey.MustNewV2XXXTestingOnly(big.NewInt(2)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(3)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(4)).PeerID(), }, Id: 2, }, }, 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), + P2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(3)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(4)).PeerID(), + }, + }, + }}, nil) + homeChainReader. + On("GetOCRConfigs", mock.Anything, uint32(2), uint8(cctypes.PluginTypeCCIPExec)). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + P2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(3)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(4)).PeerID(), + }, + }, + }}, nil) + oracleCreator.EXPECT().Type().Return(cctypes.OracleTypeBootstrap).Once() + oracleCreator. + On("Create", mock.Anything). + Return(mocks.NewCCIPOracle(t), nil).Twice() }, false, }, { - "success, no bootstrap", + "success", args{ logger.TestLogger(t), ragep2ptypes.PeerID(p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID()), @@ -190,6 +131,7 @@ func Test_createDON(t *testing.T) { kcr.CapabilitiesRegistryDONInfo{ NodeP2PIds: [][32]byte{ p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(2)).PeerID(), }, Id: 1, }, @@ -197,15 +139,34 @@ 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{{}}, nil) + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + P2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(2)).PeerID(), + }, + }, + }}, nil) homeChainReader. On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}}, nil) - oracleCreator. - On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, mock.Anything). + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + P2PIds: [][32]byte{ + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID(), + p2pkey.MustNewV2XXXTestingOnly(big.NewInt(2)).PeerID(), + }, + }, + }}, nil) + + oracleCreator.EXPECT().Create(mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) + })). Return(mocks.NewCCIPOracle(t), nil) - oracleCreator. - On("CreatePluginOracle", cctypes.PluginTypeCCIPExec, mock.Anything). + oracleCreator.EXPECT().Create(mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) + })). Return(mocks.NewCCIPOracle(t), nil) }, false, @@ -216,7 +177,8 @@ func Test_createDON(t *testing.T) { if tt.expect != nil { tt.expect(t, tt.args, tt.args.oracleCreator, tt.args.homeChainReader) } - _, err := createDON(tt.args.lggr, tt.args.p2pID, tt.args.homeChainReader, tt.args.oracleCreator, tt.args.don) + + _, err := createDON(tt.args.lggr, tt.args.p2pID, tt.args.homeChainReader, tt.args.don, tt.args.oracleCreator) if tt.wantErr { require.Error(t, err) } else { @@ -274,7 +236,7 @@ func Test_updateDON(t *testing.T) { } 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.oracleCreator, tt.args.prevDeployment, tt.args.don) + gotFutDeployment, err := updateDON(tt.args.lggr, tt.args.p2pID, tt.args.homeChainReader, tt.args.prevDeployment, tt.args.don, tt.args.oracleCreator) if (err != nil) != tt.wantErr { t.Errorf("updateDON() error = %v, wantErr %v", err, tt.wantErr) return @@ -358,9 +320,17 @@ 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{{}}, nil) + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + }, + }}, nil) m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}}, nil) + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + }, + }}, nil) }), oracleCreator: newMock(t, func(t *testing.T) *mocks.OracleCreator { return mocks.NewOracleCreator(t) @@ -369,9 +339,13 @@ func Test_launcher_processDiff(t *testing.T) { commitOracle.On("Start").Return(nil) execOracle := mocks.NewCCIPOracle(t) execOracle.On("Start").Return(nil) - m.On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, mock.Anything). + m.EXPECT().Create(mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) + })). Return(commitOracle, nil) - m.On("CreatePluginOracle", cctypes.PluginTypeCCIPExec, mock.Anything). + m.EXPECT().Create(mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) + })). Return(execOracle, nil) }), dons: map[registrysyncer.DonID]*ccipDeployment{}, @@ -406,9 +380,25 @@ 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{{}, {}}, nil) + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + }, + }, { + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPCommit), + }, + }}, nil) m.On("GetOCRConfigs", mock.Anything, uint32(1), uint8(cctypes.PluginTypeCCIPExec)). - Return([]ccipreaderpkg.OCR3ConfigWithMeta{{}, {}}, nil) + Return([]ccipreaderpkg.OCR3ConfigWithMeta{{ + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + }, + }, { + Config: ccipreaderpkg.OCR3Config{ + PluginType: uint8(cctypes.PluginTypeCCIPExec), + }, + }}, nil) }), oracleCreator: newMock(t, func(t *testing.T) *mocks.OracleCreator { return mocks.NewOracleCreator(t) @@ -417,9 +407,13 @@ func Test_launcher_processDiff(t *testing.T) { commitOracle.On("Start").Return(nil) execOracle := mocks.NewCCIPOracle(t) execOracle.On("Start").Return(nil) - m.On("CreatePluginOracle", cctypes.PluginTypeCCIPCommit, mock.Anything). + m.EXPECT().Create(mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) + })). Return(commitOracle, nil) - m.On("CreatePluginOracle", cctypes.PluginTypeCCIPExec, mock.Anything). + m.EXPECT().Create(mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) + })). Return(execOracle, nil) }), dons: map[registrysyncer.DonID]*ccipDeployment{ diff --git a/core/capabilities/ccip/oraclecreator/bootstrap.go b/core/capabilities/ccip/oraclecreator/bootstrap.go new file mode 100644 index 0000000000..a4bf03a4de --- /dev/null +++ b/core/capabilities/ccip/oraclecreator/bootstrap.go @@ -0,0 +1,97 @@ +package oraclecreator + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum/common/hexutil" + + chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/libocr/commontypes" + libocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + + "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ocrimpls" + cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" + "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" +) + +var _ cctypes.OracleCreator = &bootstrapOracleCreator{} + +type bootstrapOracleCreator struct { + peerWrapper *ocrcommon.SingletonPeerWrapper + bootstrapperLocators []commontypes.BootstrapperLocator + db ocr3types.Database + monitoringEndpointGen telemetry.MonitoringEndpointGenerator + lggr logger.Logger +} + +func NewBootstrapOracleCreator( + peerWrapper *ocrcommon.SingletonPeerWrapper, + bootstrapperLocators []commontypes.BootstrapperLocator, + db ocr3types.Database, + monitoringEndpointGen telemetry.MonitoringEndpointGenerator, + lggr logger.Logger, +) cctypes.OracleCreator { + return &bootstrapOracleCreator{ + peerWrapper: peerWrapper, + bootstrapperLocators: bootstrapperLocators, + db: db, + monitoringEndpointGen: monitoringEndpointGen, + lggr: lggr, + } +} + +// Type implements types.OracleCreator. +func (i *bootstrapOracleCreator) Type() cctypes.OracleType { + return cctypes.OracleTypeBootstrap +} + +// Create implements types.OracleCreator. +func (i *bootstrapOracleCreator) Create(config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { + // Assuming that the chain selector is referring to an evm chain for now. + // TODO: add an api that returns chain family. + // NOTE: this doesn't really matter for the bootstrap node, it doesn't do anything on-chain. + // Its for the monitoring endpoint generation below. + chainID, err := chainsel.ChainIdFromSelector(uint64(config.Config.ChainSelector)) + if err != nil { + return nil, fmt.Errorf("failed to get chain ID from selector: %w", err) + } + + destChainFamily := chaintype.EVM + destRelayID := types.NewRelayID(string(destChainFamily), fmt.Sprintf("%d", chainID)) + + bootstrapperArgs := libocr3.BootstrapperArgs{ + BootstrapperFactory: i.peerWrapper.Peer2, + V2Bootstrappers: i.bootstrapperLocators, + ContractConfigTracker: ocrimpls.NewConfigTracker(config), + Database: i.db, + LocalConfig: defaultLocalConfig(), + Logger: ocrcommon.NewOCRWrapper( + i.lggr. + Named("CCIPBootstrap"). + Named(destRelayID.String()). + Named(config.Config.ChainSelector.String()). + Named(hexutil.Encode(config.Config.OfframpAddress)), + false, /* traceLogging */ + func(ctx context.Context, msg string) {}), + MonitoringEndpoint: i.monitoringEndpointGen.GenMonitoringEndpoint( + string(destChainFamily), + destRelayID.ChainID, + hexutil.Encode(config.Config.OfframpAddress), + synchronization.OCR3CCIPBootstrap, + ), + OffchainConfigDigester: ocrimpls.NewConfigDigester(config.ConfigDigest), + } + bootstrapper, err := libocr3.NewBootstrapper(bootstrapperArgs) + if err != nil { + return nil, err + } + return bootstrapper, nil +} diff --git a/core/capabilities/ccip/oraclecreator/inprocess_test.go b/core/capabilities/ccip/oraclecreator/inprocess_test.go deleted file mode 100644 index 639f01e62e..0000000000 --- a/core/capabilities/ccip/oraclecreator/inprocess_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package oraclecreator_test - -import ( - "fmt" - "testing" - "time" - - "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/oraclecreator" - cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/google/uuid" - "github.com/hashicorp/consul/sdk/freeport" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v4" - - chainsel "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/libocr/offchainreporting2/types" - confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" - - "github.com/smartcontractkit/chainlink-ccip/pkg/reader" - "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - - "github.com/smartcontractkit/libocr/commontypes" - - "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2" - ocr2validate "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" - "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" - "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" - "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" - "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -func TestOracleCreator_CreateBootstrap(t *testing.T) { - db := pgtest.NewSqlxDB(t) - - keyStore := keystore.New(db, utils.DefaultScryptParams, logger.NullLogger) - require.NoError(t, keyStore.Unlock(testutils.Context(t), cltest.Password), "unable to unlock keystore") - p2pKey, err := keyStore.P2P().Create(testutils.Context(t)) - require.NoError(t, err) - peerID := p2pKey.PeerID() - listenPort := freeport.GetOne(t) - generalConfig := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.P2P.PeerID = ptr(peerID) - c.P2P.TraceLogging = ptr(false) - c.P2P.V2.Enabled = ptr(true) - c.P2P.V2.ListenAddresses = ptr([]string{fmt.Sprintf("127.0.0.1:%d", listenPort)}) - - c.OCR2.Enabled = ptr(true) - }) - peerWrapper := ocrcommon.NewSingletonPeerWrapper(keyStore, generalConfig.P2P(), generalConfig.OCR(), db, logger.NullLogger) - require.NoError(t, peerWrapper.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, peerWrapper.Close()) }) - - // NOTE: this is a bit of a hack to get the OCR2 job created in order to use the ocr db - // the ocr2_contract_configs table has a foreign key constraint on ocr2_oracle_spec_id - // which is passed into ocr2.NewDB. - pipelineORM := pipeline.NewORM(db, - logger.NullLogger, generalConfig.JobPipeline().MaxSuccessfulRuns()) - bridgesORM := bridges.NewORM(db) - - jobORM := job.NewORM(db, pipelineORM, bridgesORM, keyStore, logger.TestLogger(t)) - t.Cleanup(func() { assert.NoError(t, jobORM.Close()) }) - - jb, err := ocr2validate.ValidatedOracleSpecToml(testutils.Context(t), generalConfig.OCR2(), generalConfig.Insecure(), testspecs.GetOCR2EVMSpecMinimal(), nil) - require.NoError(t, err) - const juelsPerFeeCoinSource = ` - ds [type=http method=GET url="https://chain.link/ETH-USD"]; - ds_parse [type=jsonparse path="data.price" separator="."]; - ds_multiply [type=multiply times=100]; - ds -> ds_parse -> ds_multiply;` - - _, address := cltest.MustInsertRandomKey(t, keyStore.Eth()) - jb.Name = null.StringFrom("Job 1") - jb.OCR2OracleSpec.TransmitterID = null.StringFrom(address.String()) - jb.OCR2OracleSpec.PluginConfig["juelsPerFeeCoinSource"] = juelsPerFeeCoinSource - - err = jobORM.CreateJob(testutils.Context(t), &jb) - require.NoError(t, err) - - cltest.AssertCount(t, db, "ocr2_oracle_specs", 1) - cltest.AssertCount(t, db, "jobs", 1) - - var oracleSpecID int32 - err = db.Get(&oracleSpecID, "SELECT id FROM ocr2_oracle_specs LIMIT 1") - require.NoError(t, err) - - ocrdb := ocr2.NewDB(db, oracleSpecID, 0, logger.NullLogger) - - oc := oraclecreator.New( - nil, - nil, - nil, - peerWrapper, - uuid.Max, - 0, - false, - nil, - ocrdb, - logger.TestLogger(t), - &mockEndpointGen{}, - []commontypes.BootstrapperLocator{}, - nil, - ) - - chainSelector := chainsel.GETH_TESTNET.Selector - oracles, offchainConfig := ocrOffchainConfig(t, keyStore) - bootstrapP2PID, err := p2pkey.MakePeerID(oracles[0].PeerID) - require.NoError(t, err) - transmitters := func() [][]byte { - var transmitters [][]byte - for _, o := range oracles { - transmitters = append(transmitters, hexutil.MustDecode(string(o.TransmitAccount))) - } - return transmitters - }() - configDigest := ccipConfigDigest() - bootstrap, err := oc.CreateBootstrapOracle(cctypes.OCR3ConfigWithMeta{ - ConfigDigest: configDigest, - ConfigCount: 1, - Config: reader.OCR3Config{ - ChainSelector: ccipocr3.ChainSelector(chainSelector), - OfframpAddress: testutils.NewAddress().Bytes(), - PluginType: uint8(cctypes.PluginTypeCCIPCommit), - F: 1, - OffchainConfigVersion: 30, - BootstrapP2PIds: [][32]byte{bootstrapP2PID}, - P2PIds: func() [][32]byte { - var ids [][32]byte - for _, o := range oracles { - id, err2 := p2pkey.MakePeerID(o.PeerID) - require.NoError(t, err2) - ids = append(ids, id) - } - return ids - }(), - Signers: func() [][]byte { - var signers [][]byte - for _, o := range oracles { - signers = append(signers, o.OnchainPublicKey) - } - return signers - }(), - Transmitters: transmitters, - OffchainConfig: offchainConfig, - }, - }) - require.NoError(t, err) - require.NoError(t, bootstrap.Start()) - t.Cleanup(func() { assert.NoError(t, bootstrap.Close()) }) - - tests.AssertEventually(t, func() bool { - c, err := ocrdb.ReadConfig(testutils.Context(t)) - require.NoError(t, err) - return c.ConfigDigest == configDigest - }) -} - -func ccipConfigDigest() [32]byte { - rand32Bytes := testutils.Random32Byte() - // overwrite first four bytes to be 0x000a, to match the prefix in libocr. - rand32Bytes[0] = 0x00 - rand32Bytes[1] = 0x0a - return rand32Bytes -} - -type mockEndpointGen struct{} - -func (m *mockEndpointGen) GenMonitoringEndpoint(network string, chainID string, contractID string, telemType synchronization.TelemetryType) commontypes.MonitoringEndpoint { - return &telemetry.NoopAgent{} -} - -func ptr[T any](b T) *T { - return &b -} - -func ocrOffchainConfig(t *testing.T, ks keystore.Master) (oracles []confighelper2.OracleIdentityExtra, offchainConfig []byte) { - for i := 0; i < 4; i++ { - kb, err := ks.OCR2().Create(testutils.Context(t), chaintype.EVM) - require.NoError(t, err) - p2pKey, err := ks.P2P().Create(testutils.Context(t)) - require.NoError(t, err) - ethKey, err := ks.Eth().Create(testutils.Context(t)) - require.NoError(t, err) - oracles = append(oracles, confighelper2.OracleIdentityExtra{ - OracleIdentity: confighelper2.OracleIdentity{ - OffchainPublicKey: kb.OffchainPublicKey(), - OnchainPublicKey: types.OnchainPublicKey(kb.OnChainPublicKey()), - PeerID: p2pKey.ID(), - TransmitAccount: types.Account(ethKey.Address.Hex()), - }, - ConfigEncryptionPublicKey: kb.ConfigEncryptionPublicKey(), - }) - } - var schedule []int - for range oracles { - schedule = append(schedule, 1) - } - offchainConfig, onchainConfig := []byte{}, []byte{} - f := uint8(1) - - _, _, _, _, _, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsForTests( - 30*time.Second, // deltaProgress - 10*time.Second, // deltaResend - 20*time.Second, // deltaInitial - 2*time.Second, // deltaRound - 20*time.Second, // deltaGrace - 10*time.Second, // deltaCertifiedCommitRequest - 10*time.Second, // deltaStage - 3, // rmax - schedule, - oracles, - offchainConfig, - 50*time.Millisecond, // maxDurationQuery - 5*time.Second, // maxDurationObservation - 10*time.Second, // maxDurationShouldAcceptAttestedReport - 10*time.Second, // maxDurationShouldTransmitAcceptedReport - int(f), - onchainConfig) - require.NoError(t, err, "failed to create contract config") - - return oracles, offchainConfig -} diff --git a/core/capabilities/ccip/oraclecreator/inprocess.go b/core/capabilities/ccip/oraclecreator/plugin.go similarity index 79% rename from core/capabilities/ccip/oraclecreator/inprocess.go rename to core/capabilities/ccip/oraclecreator/plugin.go index 21e9bcec6d..c87c4e97c1 100644 --- a/core/capabilities/ccip/oraclecreator/inprocess.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -36,7 +36,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/relay" @@ -46,15 +45,15 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" ) -var _ cctypes.OracleCreator = &inprocessOracleCreator{} +var _ cctypes.OracleCreator = &pluginOracleCreator{} const ( defaultCommitGasLimit = 500_000 ) -// inprocessOracleCreator creates oracles that reference plugins running +// pluginOracleCreator creates oracles that reference plugins running // in the same process as the chainlink node, i.e not LOOPPs. -type inprocessOracleCreator struct { +type pluginOracleCreator struct { ocrKeyBundles map[string]ocr2key.KeyBundle transmitters map[types.RelayID][]string chains legacyevm.LegacyChainContainer @@ -70,7 +69,7 @@ type inprocessOracleCreator struct { homeChainReader ccipreaderpkg.HomeChain } -func New( +func NewPluginOracleCreator( ocrKeyBundles map[string]ocr2key.KeyBundle, transmitters map[types.RelayID][]string, chains legacyevm.LegacyChainContainer, @@ -85,7 +84,7 @@ func New( bootstrapperLocators []commontypes.BootstrapperLocator, homeChainReader ccipreaderpkg.HomeChain, ) cctypes.OracleCreator { - return &inprocessOracleCreator{ + return &pluginOracleCreator{ ocrKeyBundles: ocrKeyBundles, transmitters: transmitters, chains: chains, @@ -102,49 +101,15 @@ func New( } } -// CreateBootstrapOracle implements types.OracleCreator. -func (i *inprocessOracleCreator) CreateBootstrapOracle(config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { - // Assuming that the chain selector is referring to an evm chain for now. - // TODO: add an api that returns chain family. - chainID, err := chainsel.ChainIdFromSelector(uint64(config.Config.ChainSelector)) - if err != nil { - return nil, fmt.Errorf("failed to get chain ID from selector: %w", err) - } - - destChainFamily := chaintype.EVM - destRelayID := types.NewRelayID(string(destChainFamily), fmt.Sprintf("%d", chainID)) - - bootstrapperArgs := libocr3.BootstrapperArgs{ - BootstrapperFactory: i.peerWrapper.Peer2, - V2Bootstrappers: i.bootstrapperLocators, - ContractConfigTracker: ocrimpls.NewConfigTracker(config), - Database: i.db, - LocalConfig: defaultLocalConfig(), - Logger: ocrcommon.NewOCRWrapper( - i.lggr. - Named("CCIPBootstrap"). - Named(destRelayID.String()). - Named(config.Config.ChainSelector.String()). - Named(hexutil.Encode(config.Config.OfframpAddress)), - false, /* traceLogging */ - func(ctx context.Context, msg string) {}), - MonitoringEndpoint: i.monitoringEndpointGen.GenMonitoringEndpoint( - string(destChainFamily), - destRelayID.ChainID, - hexutil.Encode(config.Config.OfframpAddress), - synchronization.OCR3CCIPBootstrap, - ), - OffchainConfigDigester: ocrimpls.NewConfigDigester(config.ConfigDigest), - } - bootstrapper, err := libocr3.NewBootstrapper(bootstrapperArgs) - if err != nil { - return nil, err - } - return bootstrapper, nil +// Type implements types.OracleCreator. +func (i *pluginOracleCreator) Type() cctypes.OracleType { + return cctypes.OracleTypePlugin } -// CreatePluginOracle implements types.OracleCreator. -func (i *inprocessOracleCreator) CreatePluginOracle(pluginType cctypes.PluginType, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { +// Create implements types.OracleCreator. +func (i *pluginOracleCreator) Create(config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { + pluginType := cctypes.PluginType(config.Config.PluginType) + // Assuming that the chain selector is referring to an evm chain for now. // TODO: add an api that returns chain family. destChainID, err := chainsel.ChainIdFromSelector(uint64(config.Config.ChainSelector)) @@ -172,7 +137,141 @@ func (i *inprocessOracleCreator) CreatePluginOracle(pluginType cctypes.PluginTyp execBatchGasLimit = execOffchainConfig.BatchGasLimit } - // this is so that we can use the msg hasher and report encoder from that dest chain relayer's provider. + contractReaders, chainWriters, err := i.createReadersAndWriters( + destChainID, + pluginType, + config, + execBatchGasLimit, + ) + if err != nil { + return nil, fmt.Errorf("failed to create readers and writers: %w", err) + } + + // build the onchain keyring. it will be the signing key for the destination chain family. + keybundle, ok := i.ocrKeyBundles[destChainFamily] + if !ok { + return nil, fmt.Errorf("no OCR key bundle found for chain family %s, forgot to create one?", destChainFamily) + } + onchainKeyring := ocrimpls.NewOnchainKeyring[[]byte](keybundle, i.lggr) + + // build the contract transmitter + // assume that we are using the first account in the keybundle as the from account + // and that we are able to transmit to the dest chain. + // TODO: revisit this in the future, since not all oracles will be able to transmit to the dest chain. + destChainWriter, ok := chainWriters[config.Config.ChainSelector] + if !ok { + return nil, fmt.Errorf("no chain writer found for dest chain selector %d, can't create contract transmitter", + config.Config.ChainSelector) + } + destFromAccounts, ok := i.transmitters[destRelayID] + if !ok { + return nil, fmt.Errorf("no transmitter found for dest relay ID %s, can't create contract transmitter", destRelayID) + } + + // TODO: Extract the correct transmitter address from the destsFromAccount + factory, transmitter, err := i.createFactoryAndTransmitter(config, destRelayID, contractReaders, chainWriters, destChainWriter, destFromAccounts) + if err != nil { + return nil, fmt.Errorf("failed to create factory and transmitter: %w", err) + } + + oracleArgs := libocr3.OCR3OracleArgs[[]byte]{ + BinaryNetworkEndpointFactory: i.peerWrapper.Peer2, + Database: i.db, + // NOTE: when specifying V2Bootstrappers here we actually do NOT need to run a full bootstrap node! + // Thus it is vital that the bootstrapper locators are correctly set in the job spec. + V2Bootstrappers: i.bootstrapperLocators, + ContractConfigTracker: configTracker, + ContractTransmitter: transmitter, + LocalConfig: defaultLocalConfig(), + Logger: ocrcommon.NewOCRWrapper( + i.lggr. + Named(fmt.Sprintf("CCIP%sOCR3", pluginType.String())). + Named(destRelayID.String()). + Named(hexutil.Encode(config.Config.OfframpAddress)), + false, + func(ctx context.Context, msg string) {}), + MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"name": fmt.Sprintf("commit-%d", config.Config.ChainSelector)}, prometheus.DefaultRegisterer), + MonitoringEndpoint: i.monitoringEndpointGen.GenMonitoringEndpoint( + destChainFamily, + destRelayID.ChainID, + string(config.Config.OfframpAddress), + synchronization.OCR3CCIPCommit, + ), + OffchainConfigDigester: ocrimpls.NewConfigDigester(config.ConfigDigest), + OffchainKeyring: keybundle, + OnchainKeyring: onchainKeyring, + ReportingPluginFactory: factory, + } + oracle, err := libocr3.NewOracle(oracleArgs) + if err != nil { + return nil, err + } + return oracle, nil +} + +func (i *pluginOracleCreator) createFactoryAndTransmitter( + config cctypes.OCR3ConfigWithMeta, + destRelayID types.RelayID, + contractReaders map[cciptypes.ChainSelector]types.ContractReader, + chainWriters map[cciptypes.ChainSelector]types.ChainWriter, + destChainWriter types.ChainWriter, + destFromAccounts []string, +) (ocr3types.ReportingPluginFactory[[]byte], ocr3types.ContractTransmitter[[]byte], error) { + var factory ocr3types.ReportingPluginFactory[[]byte] + var transmitter ocr3types.ContractTransmitter[[]byte] + if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) { + factory = commitocr3.NewPluginFactory( + i.lggr. + Named("CCIPCommitPlugin"). + Named(destRelayID.String()). + Named(fmt.Sprintf("%d", config.Config.ChainSelector)). + Named(hexutil.Encode(config.Config.OfframpAddress)), + ccipreaderpkg.OCR3ConfigWithMeta(config), + ccipevm.NewCommitPluginCodecV1(), + ccipevm.NewMessageHasherV1(), + i.homeChainReader, + contractReaders, + chainWriters, + ) + transmitter = ocrimpls.NewCommitContractTransmitter[[]byte](destChainWriter, + ocrtypes.Account(destFromAccounts[0]), + hexutil.Encode(config.Config.OfframpAddress), // TODO: this works for evm only, how about non-evm? + ) + } else if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) { + factory = execocr3.NewPluginFactory( + i.lggr. + Named("CCIPExecPlugin"). + Named(destRelayID.String()). + Named(hexutil.Encode(config.Config.OfframpAddress)), + ccipreaderpkg.OCR3ConfigWithMeta(config), + ccipevm.NewExecutePluginCodecV1(), + ccipevm.NewMessageHasherV1(), + i.homeChainReader, + superfakes.NewNilTokenDataReader(), + ccipevm.NewGasEstimateProvider(), + contractReaders, + chainWriters, + ) + transmitter = ocrimpls.NewExecContractTransmitter[[]byte](destChainWriter, + ocrtypes.Account(destFromAccounts[0]), + hexutil.Encode(config.Config.OfframpAddress), // TODO: this works for evm only, how about non-evm? + ) + } else { + return nil, nil, fmt.Errorf("unsupported plugin type %d", config.Config.PluginType) + } + return factory, transmitter, nil +} + +func (i *pluginOracleCreator) createReadersAndWriters( + destChainID uint64, + pluginType cctypes.PluginType, + config cctypes.OCR3ConfigWithMeta, + execBatchGasLimit uint64, +) ( + map[cciptypes.ChainSelector]types.ContractReader, + map[cciptypes.ChainSelector]types.ChainWriter, + error, +) { contractReaders := make(map[cciptypes.ChainSelector]types.ContractReader) chainWriters := make(map[cciptypes.ChainSelector]types.ChainWriter) for _, chain := range i.chains.Slice() { @@ -194,7 +293,7 @@ func (i *inprocessOracleCreator) CreatePluginOracle(pluginType cctypes.PluginTyp chainReaderConfig, ) if err2 != nil { - return nil, fmt.Errorf("failed to create contract reader for chain %s: %w", chain.ID(), err2) + return nil, nil, fmt.Errorf("failed to create contract reader for chain %s: %w", chain.ID(), err2) } if chain.ID().Uint64() == destChainID { @@ -207,7 +306,7 @@ func (i *inprocessOracleCreator) CreatePluginOracle(pluginType cctypes.PluginTyp }, }) if err3 != nil { - return nil, fmt.Errorf("failed to bind chain reader for dest chain %s's offramp at %s: %w", chain.ID(), offrampAddressHex, err3) + return nil, nil, fmt.Errorf("failed to bind chain reader for dest chain %s's offramp at %s: %w", chain.ID(), offrampAddressHex, err3) } } @@ -215,7 +314,7 @@ func (i *inprocessOracleCreator) CreatePluginOracle(pluginType cctypes.PluginTyp // maybe from the plugin directly? err2 = cr.Start(context.Background()) if err2 != nil { - return nil, fmt.Errorf("failed to start contract reader for chain %s: %w", chain.ID(), err2) + return nil, nil, fmt.Errorf("failed to start contract reader for chain %s: %w", chain.ID(), err2) } // Even though we only write to the dest chain, we need to create chain writers for all chains @@ -240,121 +339,25 @@ func (i *inprocessOracleCreator) CreatePluginOracle(pluginType cctypes.PluginTyp ), ) if err2 != nil { - return nil, fmt.Errorf("failed to create chain writer for chain %s: %w", chain.ID(), err2) + return nil, nil, fmt.Errorf("failed to create chain writer for chain %s: %w", chain.ID(), err2) } // TODO: figure out shutdown. // maybe from the plugin directly? err2 = cw.Start(context.Background()) if err2 != nil { - return nil, fmt.Errorf("failed to start chain writer for chain %s: %w", chain.ID(), err2) + return nil, nil, fmt.Errorf("failed to start chain writer for chain %s: %w", chain.ID(), err2) } chainSelector, ok := chainsel.EvmChainIdToChainSelector()[chain.ID().Uint64()] if !ok { - return nil, fmt.Errorf("failed to get chain selector from chain ID %s", chain.ID()) + return nil, nil, fmt.Errorf("failed to get chain selector from chain ID %s", chain.ID()) } contractReaders[cciptypes.ChainSelector(chainSelector)] = cr chainWriters[cciptypes.ChainSelector(chainSelector)] = cw } - - // build the onchain keyring. it will be the signing key for the destination chain family. - keybundle, ok := i.ocrKeyBundles[destChainFamily] - if !ok { - return nil, fmt.Errorf("no OCR key bundle found for chain family %s, forgot to create one?", destChainFamily) - } - onchainKeyring := ocrimpls.NewOnchainKeyring[[]byte](keybundle, i.lggr) - - // build the contract transmitter - // assume that we are using the first account in the keybundle as the from account - // and that we are able to transmit to the dest chain. - // TODO: revisit this in the future, since not all oracles will be able to transmit to the dest chain. - destChainWriter, ok := chainWriters[config.Config.ChainSelector] - if !ok { - return nil, fmt.Errorf("no chain writer found for dest chain selector %d, can't create contract transmitter", - config.Config.ChainSelector) - } - destFromAccounts, ok := i.transmitters[destRelayID] - if !ok { - return nil, fmt.Errorf("no transmitter found for dest relay ID %s, can't create contract transmitter", destRelayID) - } - - // TODO: Extract the correct transmitter address from the destsFromAccount - var factory ocr3types.ReportingPluginFactory[[]byte] - var transmitter ocr3types.ContractTransmitter[[]byte] - if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) { - factory = commitocr3.NewPluginFactory( - i.lggr. - Named("CCIPCommitPlugin"). - Named(destRelayID.String()). - Named(fmt.Sprintf("%d", config.Config.ChainSelector)). - Named(hexutil.Encode(config.Config.OfframpAddress)), - ccipreaderpkg.OCR3ConfigWithMeta(config), - ccipevm.NewCommitPluginCodecV1(), - ccipevm.NewMessageHasherV1(), - i.homeChainReader, - contractReaders, - chainWriters, - ) - transmitter = ocrimpls.NewCommitContractTransmitter[[]byte](destChainWriter, - ocrtypes.Account(destFromAccounts[0]), - hexutil.Encode(config.Config.OfframpAddress), // TODO: this works for evm only, how about non-evm? - ) - } else if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) { - factory = execocr3.NewPluginFactory( - i.lggr. - Named("CCIPExecPlugin"). - Named(destRelayID.String()). - Named(hexutil.Encode(config.Config.OfframpAddress)), - ccipreaderpkg.OCR3ConfigWithMeta(config), - ccipevm.NewExecutePluginCodecV1(), - ccipevm.NewMessageHasherV1(), - i.homeChainReader, - superfakes.NewNilTokenDataReader(), - ccipevm.NewGasEstimateProvider(), // TODO: this works for evm only, how about non-evm? - contractReaders, - chainWriters, - ) - transmitter = ocrimpls.NewExecContractTransmitter[[]byte](destChainWriter, - ocrtypes.Account(destFromAccounts[0]), - hexutil.Encode(config.Config.OfframpAddress), // TODO: this works for evm only, how about non-evm? - ) - } else { - return nil, fmt.Errorf("unsupported plugin type %d", config.Config.PluginType) - } - - oracleArgs := libocr3.OCR3OracleArgs[[]byte]{ - BinaryNetworkEndpointFactory: i.peerWrapper.Peer2, - Database: i.db, - V2Bootstrappers: i.bootstrapperLocators, - ContractConfigTracker: configTracker, - ContractTransmitter: transmitter, - LocalConfig: defaultLocalConfig(), - Logger: ocrcommon.NewOCRWrapper( - i.lggr. - Named(fmt.Sprintf("CCIP%sOCR3", pluginType.String())). - Named(destRelayID.String()). - Named(hexutil.Encode(config.Config.OfframpAddress)), - false, - func(ctx context.Context, msg string) {}), - MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"name": fmt.Sprintf("commit-%d", config.Config.ChainSelector)}, prometheus.DefaultRegisterer), - MonitoringEndpoint: i.monitoringEndpointGen.GenMonitoringEndpoint( - destChainFamily, - destRelayID.ChainID, - string(config.Config.OfframpAddress), - synchronization.OCR3CCIPCommit, - ), - OffchainConfigDigester: ocrimpls.NewConfigDigester(config.ConfigDigest), - OffchainKeyring: keybundle, - OnchainKeyring: onchainKeyring, - ReportingPluginFactory: factory, - } - oracle, err := libocr3.NewOracle(oracleArgs) - if err != nil { - return nil, err - } - return oracle, nil + return contractReaders, chainWriters, nil } func defaultLocalConfig() ocrtypes.LocalConfig { diff --git a/core/capabilities/ccip/types/mocks/oracle_creator.go b/core/capabilities/ccip/types/mocks/oracle_creator.go index d83ad042bf..1d327e9652 100644 --- a/core/capabilities/ccip/types/mocks/oracle_creator.go +++ b/core/capabilities/ccip/types/mocks/oracle_creator.go @@ -20,12 +20,12 @@ func (_m *OracleCreator) EXPECT() *OracleCreator_Expecter { return &OracleCreator_Expecter{mock: &_m.Mock} } -// CreateBootstrapOracle provides a mock function with given fields: config -func (_m *OracleCreator) CreateBootstrapOracle(config types.OCR3ConfigWithMeta) (types.CCIPOracle, error) { +// Create provides a mock function with given fields: config +func (_m *OracleCreator) Create(config types.OCR3ConfigWithMeta) (types.CCIPOracle, error) { ret := _m.Called(config) if len(ret) == 0 { - panic("no return value specified for CreateBootstrapOracle") + panic("no return value specified for Create") } var r0 types.CCIPOracle @@ -50,89 +50,75 @@ func (_m *OracleCreator) CreateBootstrapOracle(config types.OCR3ConfigWithMeta) return r0, r1 } -// OracleCreator_CreateBootstrapOracle_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateBootstrapOracle' -type OracleCreator_CreateBootstrapOracle_Call struct { +// OracleCreator_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' +type OracleCreator_Create_Call struct { *mock.Call } -// CreateBootstrapOracle is a helper method to define mock.On call +// Create is a helper method to define mock.On call // - config types.OCR3ConfigWithMeta -func (_e *OracleCreator_Expecter) CreateBootstrapOracle(config interface{}) *OracleCreator_CreateBootstrapOracle_Call { - return &OracleCreator_CreateBootstrapOracle_Call{Call: _e.mock.On("CreateBootstrapOracle", config)} +func (_e *OracleCreator_Expecter) Create(config interface{}) *OracleCreator_Create_Call { + return &OracleCreator_Create_Call{Call: _e.mock.On("Create", config)} } -func (_c *OracleCreator_CreateBootstrapOracle_Call) Run(run func(config types.OCR3ConfigWithMeta)) *OracleCreator_CreateBootstrapOracle_Call { +func (_c *OracleCreator_Create_Call) Run(run func(config types.OCR3ConfigWithMeta)) *OracleCreator_Create_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(types.OCR3ConfigWithMeta)) }) return _c } -func (_c *OracleCreator_CreateBootstrapOracle_Call) Return(_a0 types.CCIPOracle, _a1 error) *OracleCreator_CreateBootstrapOracle_Call { +func (_c *OracleCreator_Create_Call) Return(_a0 types.CCIPOracle, _a1 error) *OracleCreator_Create_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *OracleCreator_CreateBootstrapOracle_Call) RunAndReturn(run func(types.OCR3ConfigWithMeta) (types.CCIPOracle, error)) *OracleCreator_CreateBootstrapOracle_Call { +func (_c *OracleCreator_Create_Call) RunAndReturn(run func(types.OCR3ConfigWithMeta) (types.CCIPOracle, error)) *OracleCreator_Create_Call { _c.Call.Return(run) return _c } -// CreatePluginOracle provides a mock function with given fields: pluginType, config -func (_m *OracleCreator) CreatePluginOracle(pluginType types.PluginType, config types.OCR3ConfigWithMeta) (types.CCIPOracle, error) { - ret := _m.Called(pluginType, config) +// Type provides a mock function with given fields: +func (_m *OracleCreator) Type() types.OracleType { + ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for CreatePluginOracle") + panic("no return value specified for Type") } - var r0 types.CCIPOracle - var r1 error - if rf, ok := ret.Get(0).(func(types.PluginType, types.OCR3ConfigWithMeta) (types.CCIPOracle, error)); ok { - return rf(pluginType, config) - } - if rf, ok := ret.Get(0).(func(types.PluginType, types.OCR3ConfigWithMeta) types.CCIPOracle); ok { - r0 = rf(pluginType, config) + var r0 types.OracleType + if rf, ok := ret.Get(0).(func() types.OracleType); ok { + r0 = rf() } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.CCIPOracle) - } + r0 = ret.Get(0).(types.OracleType) } - if rf, ok := ret.Get(1).(func(types.PluginType, types.OCR3ConfigWithMeta) error); ok { - r1 = rf(pluginType, config) - } else { - r1 = ret.Error(1) - } - - return r0, r1 + return r0 } -// OracleCreator_CreatePluginOracle_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePluginOracle' -type OracleCreator_CreatePluginOracle_Call struct { +// OracleCreator_Type_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Type' +type OracleCreator_Type_Call struct { *mock.Call } -// CreatePluginOracle is a helper method to define mock.On call -// - pluginType types.PluginType -// - config types.OCR3ConfigWithMeta -func (_e *OracleCreator_Expecter) CreatePluginOracle(pluginType interface{}, config interface{}) *OracleCreator_CreatePluginOracle_Call { - return &OracleCreator_CreatePluginOracle_Call{Call: _e.mock.On("CreatePluginOracle", pluginType, config)} +// Type is a helper method to define mock.On call +func (_e *OracleCreator_Expecter) Type() *OracleCreator_Type_Call { + return &OracleCreator_Type_Call{Call: _e.mock.On("Type")} } -func (_c *OracleCreator_CreatePluginOracle_Call) Run(run func(pluginType types.PluginType, config types.OCR3ConfigWithMeta)) *OracleCreator_CreatePluginOracle_Call { +func (_c *OracleCreator_Type_Call) Run(run func()) *OracleCreator_Type_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.PluginType), args[1].(types.OCR3ConfigWithMeta)) + run() }) return _c } -func (_c *OracleCreator_CreatePluginOracle_Call) Return(_a0 types.CCIPOracle, _a1 error) *OracleCreator_CreatePluginOracle_Call { - _c.Call.Return(_a0, _a1) +func (_c *OracleCreator_Type_Call) Return(_a0 types.OracleType) *OracleCreator_Type_Call { + _c.Call.Return(_a0) return _c } -func (_c *OracleCreator_CreatePluginOracle_Call) RunAndReturn(run func(types.PluginType, types.OCR3ConfigWithMeta) (types.CCIPOracle, error)) *OracleCreator_CreatePluginOracle_Call { +func (_c *OracleCreator_Type_Call) RunAndReturn(run func() types.OracleType) *OracleCreator_Type_Call { _c.Call.Return(run) return _c } diff --git a/core/capabilities/ccip/types/types.go b/core/capabilities/ccip/types/types.go index 952b8fe446..e42990f4c4 100644 --- a/core/capabilities/ccip/types/types.go +++ b/core/capabilities/ccip/types/types.go @@ -27,6 +27,13 @@ func (pt PluginType) String() string { } } +type OracleType uint8 + +const ( + OracleTypePlugin OracleType = 0 + OracleTypeBootstrap OracleType = 1 +) + // CCIPOracle represents either a CCIP commit or exec oracle or a bootstrap node. type CCIPOracle interface { Close() error @@ -36,11 +43,12 @@ type CCIPOracle interface { // OracleCreator is an interface for creating CCIP oracles. // Whether the oracle uses a LOOPP or not is an implementation detail. type OracleCreator interface { - // CreatePlugin creates a new oracle that will run either the commit or exec ccip plugin. + // Create creates a new oracle that will run either the commit or exec ccip plugin, + // if its a plugin oracle, or a bootstrap oracle if its a bootstrap oracle. // The oracle must be returned unstarted. - CreatePluginOracle(pluginType PluginType, config OCR3ConfigWithMeta) (CCIPOracle, error) + Create(config OCR3ConfigWithMeta) (CCIPOracle, error) - // CreateBootstrapOracle creates a new bootstrap node with the given OCR config. - // The oracle must be returned unstarted. - CreateBootstrapOracle(config OCR3ConfigWithMeta) (CCIPOracle, error) + // Type returns the type of oracle that this creator creates. + // The only valid values are OracleTypePlugin and OracleTypeBootstrap. + Type() OracleType } diff --git a/core/capabilities/ccip/validate/validate.go b/core/capabilities/ccip/validate/validate.go index 04f4f4a495..02e1cb5c8e 100644 --- a/core/capabilities/ccip/validate/validate.go +++ b/core/capabilities/ccip/validate/validate.go @@ -40,9 +40,6 @@ func ValidatedCCIPSpec(tomlString string) (jb job.Job, err error) { if jb.CCIPSpec.P2PKeyID == "" { return job.Job{}, fmt.Errorf("p2pKeyID must be set") } - if len(jb.CCIPSpec.P2PV2Bootstrappers) == 0 { - return job.Job{}, fmt.Errorf("p2pV2Bootstrappers must be set") - } // ensure that the P2PV2Bootstrappers is in the right format. for _, bootstrapperLocator := range jb.CCIPSpec.P2PV2Bootstrappers { diff --git a/integration-tests/deployment/ccip/jobs.go b/integration-tests/deployment/ccip/jobs.go index f45fe4c553..923bda45f6 100644 --- a/integration-tests/deployment/ccip/jobs.go +++ b/integration-tests/deployment/ccip/jobs.go @@ -48,8 +48,17 @@ func NewCCIPJobSpecs(nodeIds []string, oc deployment.OffchainClient) (map[string if err != nil { return nil, err } + + // only set P2PV2Bootstrappers in the job spec if the node is a plugin node. + var p2pV2Bootstrappers []string + for _, chainConfig := range nodeChainConfigs.ChainConfigs { + if !chainConfig.Ocr2Config.IsBootstrap { + p2pV2Bootstrappers = bootstraps + break + } + } spec, err := validate.NewCCIPSpecToml(validate.SpecArgs{ - P2PV2Bootstrappers: bootstraps, + P2PV2Bootstrappers: p2pV2Bootstrappers, CapabilityVersion: CapabilityVersion, CapabilityLabelledName: CapabilityLabelledName, OCRKeyBundleIDs: map[string]string{ diff --git a/integration-tests/deployment/memory/environment.go b/integration-tests/deployment/memory/environment.go index d496d173c0..a6fa690135 100644 --- a/integration-tests/deployment/memory/environment.go +++ b/integration-tests/deployment/memory/environment.go @@ -71,15 +71,18 @@ func NewNodes(t *testing.T, logLevel zapcore.Level, chains map[uint64]deployment } } nodesByPeerID := make(map[string]Node) - ports := freeport.GetN(t, numNodes) - var existingNumBootstraps int + ports := freeport.GetN(t, numBootstraps+numNodes) + // bootstrap nodes must be separate nodes from plugin nodes, + // since we won't run a bootstrapper and a plugin oracle on the same + // chainlink node in production. + for i := 0; i < numBootstraps; i++ { + node := NewNode(t, ports[i], mchains, logLevel, true /* bootstrap */, registryConfig) + nodesByPeerID[node.Keys.PeerID.String()] = *node + // Note in real env, this ID is allocated by JD. + } for i := 0; i < numNodes; i++ { - bootstrap := false - if existingNumBootstraps < numBootstraps { - bootstrap = true - existingNumBootstraps++ - } - node := NewNode(t, ports[i], mchains, logLevel, bootstrap, registryConfig) + // grab port offset by numBootstraps, since above loop also takes some ports. + node := NewNode(t, ports[numBootstraps+i], mchains, logLevel, false /* bootstrap */, registryConfig) nodesByPeerID[node.Keys.PeerID.String()] = *node // Note in real env, this ID is allocated by JD. }