diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 19296c72f00..9905ed6ae6c 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -590,9 +590,26 @@ func (d *Delegate) newServicesGenericPlugin( } errorLog := &errorLog{jobID: jb.ID, recordError: d.jobORM.RecordError} + var providerClientConn grpc.ClientConnInterface providerConn, ok := provider.(connProvider) - if !ok { - return nil, errors.New("provider not supported: the provider is not a LOOPP provider") + if ok { + providerClientConn = providerConn.ClientConn() + } else { + //We chose to deal with the difference between a LOOP provider and an embedded provider here rather than + //in NewServerAdapter because this has a smaller blast radius, as the scope of this workaround is to + //enable the medianpoc for EVM and not touch the other providers. + //TODO: remove this workaround when the EVM relayer is running inside of an LOOPP + d.lggr.Info("provider is not a LOOPP provider, switching to provider server") + + ps, err2 := relay.NewProviderServer(provider, types.OCR2PluginType(cconf.ProviderType), d.lggr) + if err2 != nil { + return nil, fmt.Errorf("cannot start EVM provider server: %s", err) + } + providerClientConn, err2 = ps.GetConn() + if err2 != nil { + return nil, fmt.Errorf("cannot connect to EVM provider server: %s", err) + } + srvs = append(srvs, ps) } pluginConfig := types.ReportingPluginServiceConfig{ @@ -606,7 +623,7 @@ func (d *Delegate) newServicesGenericPlugin( pr := generic.NewPipelineRunnerAdapter(pluginLggr, jb, d.pipelineRunner) ta := generic.NewTelemetryAdapter(d.monitoringEndpointGen) - plugin := reportingplugins.NewLOOPPService(pluginLggr, grpcOpts, cmdFn, pluginConfig, providerConn.ClientConn(), pr, ta, errorLog) + plugin := reportingplugins.NewLOOPPService(pluginLggr, grpcOpts, cmdFn, pluginConfig, providerClientConn, pr, ta, errorLog) oracleArgs.ReportingPluginFactory = plugin srvs = append(srvs, plugin) diff --git a/core/services/relay/grpc_provider_server.go b/core/services/relay/grpc_provider_server.go new file mode 100644 index 00000000000..943af0e6362 --- /dev/null +++ b/core/services/relay/grpc_provider_server.go @@ -0,0 +1,68 @@ +package relay + +import ( + "context" + "net" + + "go.uber.org/multierr" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +type ProviderServer struct { + s *grpc.Server + lis net.Listener + lggr logger.Logger + conns []*grpc.ClientConn +} + +func (p *ProviderServer) Start(ctx context.Context) error { + p.serve() + return nil +} + +func (p *ProviderServer) Close() error { + var err error + for _, c := range p.conns { + err = multierr.Combine(err, c.Close()) + } + p.s.Stop() + return err +} + +func (p *ProviderServer) GetConn() (*grpc.ClientConn, error) { + cc, err := grpc.Dial(p.lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) + p.conns = append(p.conns, cc) + return cc, err +} + +// NewProviderServer creates a GRPC server that will wrap a provider, this is a workaround to test the Node API PoC until the EVM relayer is loopifyed +func NewProviderServer(p types.PluginProvider, pType types.OCR2PluginType, lggr logger.Logger) (*ProviderServer, error) { + lis, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } + ps := ProviderServer{ + s: grpc.NewServer(), + lis: lis, + lggr: lggr.Named("EVM.ProviderServer"), + } + err = loop.RegisterStandAloneProvider(ps.s, p, pType) + if err != nil { + return nil, err + } + + return &ps, nil +} + +func (p *ProviderServer) serve() { + go func() { + if err := p.s.Serve(p.lis); err != nil { + p.lggr.Errorf("Failed to serve EVM provider server: %v", err) + } + }() +} diff --git a/core/services/relay/grpc_provider_server_test.go b/core/services/relay/grpc_provider_server_test.go new file mode 100644 index 00000000000..e7ee8d7f150 --- /dev/null +++ b/core/services/relay/grpc_provider_server_test.go @@ -0,0 +1,27 @@ +package relay + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestProviderServer(t *testing.T) { + r := &mockRelayer{} + sa := NewServerAdapter(r, mockRelayerExt{}) + mp, _ := sa.NewPluginProvider(context.Background(), types.RelayArgs{ProviderType: string(types.Median)}, types.PluginArgs{}) + + lggr := logger.TestLogger(t) + _, err := NewProviderServer(mp, "unsupported-type", lggr) + require.Error(t, err) + + ps, err := NewProviderServer(staticMedianProvider{}, types.Median, lggr) + require.NoError(t, err) + + _, err = ps.GetConn() + require.NoError(t, err) +} diff --git a/core/services/relay/relay_test.go b/core/services/relay/relay_test.go index d3a94773498..5bcd14c64a0 100644 --- a/core/services/relay/relay_test.go +++ b/core/services/relay/relay_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/smartcontractkit/chainlink-relay/pkg/loop" @@ -61,6 +63,30 @@ type staticMedianProvider struct { types.MedianProvider } +func (s staticMedianProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { + return nil +} + +func (s staticMedianProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { + return nil +} + +func (s staticMedianProvider) ContractTransmitter() ocrtypes.ContractTransmitter { + return nil +} + +func (s staticMedianProvider) ReportCodec() median.ReportCodec { + return nil +} + +func (s staticMedianProvider) MedianContract() median.MedianContract { + return nil +} + +func (s staticMedianProvider) OnchainConfigCodec() median.OnchainConfigCodec { + return nil +} + type staticFunctionsProvider struct { types.FunctionsProvider }