diff --git a/pkg/loop/ccip_execution.go b/pkg/loop/ccip_execution.go new file mode 100644 index 000000000..697b626ea --- /dev/null +++ b/pkg/loop/ccip_execution.go @@ -0,0 +1,93 @@ +package loop + +import ( + "context" + "fmt" + "os/exec" + + "github.com/hashicorp/go-plugin" + "google.golang.org/grpc" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop/internal" + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +// CCIPExecutionLOOPName is the name for [types.CCIPExecutionFactoryGenerator]/[NewExecutionLOOP]. +const CCIPExecutionLOOPName = "ccip_execution" + +func PluginCCIPExecutionHandshakeConfig() plugin.HandshakeConfig { + return plugin.HandshakeConfig{ + MagicCookieKey: "CL_PLUGIN_CCIP_EXEC_MAGIC_COOKIE", + MagicCookieValue: "5a2d1527-6c0f-4c7e-8c96-00aa4bececd2", + } +} + +type ExecutionLoop struct { + plugin.NetRPCUnsupportedPlugin + + BrokerConfig + + PluginServer types.CCIPExecutionFactoryGenerator + + pluginClient *internal.ExecutionLOOPClient +} + +func (p *ExecutionLoop) GRPCServer(broker *plugin.GRPCBroker, server *grpc.Server) error { + return internal.RegisterExecutionLOOPServer(server, broker, p.BrokerConfig, p.PluginServer) +} + +// GRPCClient implements [plugin.GRPCPlugin] and returns the pluginClient [types.CCIPExecutionFactoryGenerator], updated with the new broker and conn. +func (p *ExecutionLoop) GRPCClient(_ context.Context, broker *plugin.GRPCBroker, conn *grpc.ClientConn) (interface{}, error) { + if p.pluginClient == nil { + p.pluginClient = internal.NewExecutionLOOPClient(broker, p.BrokerConfig, conn) + } else { + p.pluginClient.Refresh(broker, conn) + } + + return types.CCIPExecutionFactoryGenerator(p.pluginClient), nil +} + +func (p *ExecutionLoop) ClientConfig() *plugin.ClientConfig { + return &plugin.ClientConfig{ + HandshakeConfig: PluginCCIPExecutionHandshakeConfig(), + Plugins: map[string]plugin.Plugin{CCIPExecutionLOOPName: p}, + AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, + GRPCDialOptions: p.DialOpts, + Logger: HCLogLogger(p.Logger), + } +} + +var _ ocrtypes.ReportingPluginFactory = (*ExecutionFactoryService)(nil) + +// ExecutionFactoryService is a [types.Service] that maintains an internal [types.CCIPExecutionFactoryGenerator]. +type ExecutionFactoryService struct { + internal.PluginService[*ExecutionLoop, types.ReportingPluginFactory] +} + +// NewExecutionService returns a new [*ExecutionFactoryService]. +// cmd must return a new exec.Cmd each time it is called. +func NewExecutionService(lggr logger.Logger, grpcOpts GRPCOpts, cmd func() *exec.Cmd, provider types.CCIPExecProvider, config types.CCIPExecFactoryGeneratorConfig) *ExecutionFactoryService { + newService := func(ctx context.Context, instance any) (types.ReportingPluginFactory, error) { + plug, ok := instance.(types.CCIPExecutionFactoryGenerator) + if !ok { + return nil, fmt.Errorf("expected PluginMedian but got %T", instance) + } + return plug.NewExecutionFactory(ctx, provider) + } + stopCh := make(chan struct{}) + lggr = logger.Named(lggr, "CCIPExecutionService") + var efs ExecutionFactoryService + broker := BrokerConfig{StopCh: stopCh, Logger: lggr, GRPCOpts: grpcOpts} + efs.Init(CCIPExecutionLOOPName, &ExecutionLoop{BrokerConfig: broker}, newService, lggr, cmd, stopCh) + return &efs +} + +func (m *ExecutionFactoryService) NewReportingPlugin(config ocrtypes.ReportingPluginConfig) (ocrtypes.ReportingPlugin, ocrtypes.ReportingPluginInfo, error) { + if err := m.Wait(); err != nil { + return nil, ocrtypes.ReportingPluginInfo{}, err + } + return m.Service.NewReportingPlugin(config) +} diff --git a/pkg/loop/ccip_execution_test.go b/pkg/loop/ccip_execution_test.go new file mode 100644 index 000000000..153a0cbd8 --- /dev/null +++ b/pkg/loop/ccip_execution_test.go @@ -0,0 +1,124 @@ +package loop_test + +import ( + "context" + "os/exec" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/go-plugin" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop/internal" + ccip_test "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/ccip/test" + "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test" + testcore "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test/core" + testreportingplugin "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test/ocr2/reporting_plugin" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +func TestExecService(t *testing.T) { + t.Parallel() + + exec := loop.NewExecutionService(logger.Test(t), loop.GRPCOpts{}, func() *exec.Cmd { + return NewHelperProcessCommand(loop.CCIPExecutionLOOPName, false, 0) + }, ccip_test.ExecutionProvider, ccip_test.ExecutionConfig) + hook := exec.PluginService.XXXTestHook() + servicetest.Run(t, exec) + + t.Run("control", func(t *testing.T) { + testreportingplugin.RunFactory(t, exec) + }) + + t.Run("Kill", func(t *testing.T) { + hook.Kill() + + // wait for relaunch + time.Sleep(2 * internal.KeepAliveTickDuration) + + testreportingplugin.RunFactory(t, exec) + }) + + t.Run("Reset", func(t *testing.T) { + hook.Reset() + + // wait for relaunch + time.Sleep(2 * internal.KeepAliveTickDuration) + + testreportingplugin.RunFactory(t, exec) + }) +} + +func TestExecService_recovery(t *testing.T) { + t.Parallel() + var limit atomic.Int32 + exec := loop.NewExecutionService(logger.Test(t), loop.GRPCOpts{}, func() *exec.Cmd { + h := HelperProcessCommand{ + Command: loop.CCIPExecutionLOOPName, + Limit: int(limit.Add(1)), + } + return h.New() + }, ccip_test.ExecutionProvider, ccip_test.ExecutionConfig) + servicetest.Run(t, exec) + + testreportingplugin.RunFactory(t, exec) +} + +func TestExecLOOP(t *testing.T) { + // launch the exec loop via the main program + t.Parallel() + stopCh := newStopCh(t) + exec := loop.ExecutionLoop{BrokerConfig: loop.BrokerConfig{Logger: logger.Test(t), StopCh: stopCh}} + cc := exec.ClientConfig() + cc.Cmd = NewHelperProcessCommand(loop.CCIPExecutionLOOPName, false, 0) + c := plugin.NewClient(cc) + // make to kill the exec loop + t.Cleanup(c.Kill) + client, err := c.Client() + require.NoError(t, err) + defer client.Close() + require.NoError(t, client.Ping()) + // get a concrete instance of the exec loop + instance, err := client.Dispense(loop.CCIPExecutionLOOPName) + remoteExecFactory := instance.(types.CCIPExecutionFactoryGenerator) + require.NoError(t, err) + + ccip_test.RunExecutionLOOP(t, remoteExecFactory) + + t.Run("proxy: exec loop <--> relayer loop", func(t *testing.T) { + // launch the relayer as external process via the main program + pr := newPluginRelayerExec(t, false, stopCh) + remoteProvider, err := newExecutionProvider(t, pr) + require.Error(t, err, "expected error") + assert.Contains(t, err.Error(), "BCF-3061") + if err == nil { + // test to run when BCF-3061 is fixed + ccip_test.ExecutionLOOPTester{CCIPExecProvider: remoteProvider}.Run(t, remoteExecFactory) + } + }) +} + +func newExecutionProvider(t *testing.T, pr loop.PluginRelayer) (types.CCIPExecProvider, error) { + ctx := context.Background() + r, err := pr.NewRelayer(ctx, test.ConfigTOML, testcore.Keystore) + require.NoError(t, err) + servicetest.Run(t, r) + + // TODO: fix BCF-3061. we expect an error here until then. + p, err := r.NewPluginProvider(ctx, ccip_test.ExecutionRelayArgs, ccip_test.ExecutionPluginArgs) + if err != nil { + return nil, err + } + // TODO: this shouldn't run until BCF-3061 is fixed + require.NoError(t, err) + execProvider, ok := p.(types.CCIPExecProvider) + require.True(t, ok, "got %T", p) + servicetest.Run(t, execProvider) + return execProvider, nil +} diff --git a/pkg/loop/internal/broker.go b/pkg/loop/internal/broker.go index 00f09bab0..e3c4611e8 100644 --- a/pkg/loop/internal/broker.go +++ b/pkg/loop/internal/broker.go @@ -23,7 +23,7 @@ type Broker interface { var _ Broker = (*atomicBroker)(nil) -// An atomicBroker implements [Broker] and is backed by a swappable [*plugin.GRPCBroker] +// An atomicBroker implements [Broker] and is backed by a swappable [*plugin.GRPCBroker]. type atomicBroker struct { broker atomic.Pointer[Broker] } diff --git a/pkg/loop/internal/capabilities.go b/pkg/loop/internal/capabilities.go index 5922caab4..7b9344c39 100644 --- a/pkg/loop/internal/capabilities.go +++ b/pkg/loop/internal/capabilities.go @@ -7,7 +7,6 @@ import ( "sync" "google.golang.org/grpc" - "google.golang.org/protobuf/types/known/emptypb" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" diff --git a/pkg/loop/internal/capabilities_registry.go b/pkg/loop/internal/capabilities_registry.go index b18925a8a..1f3025be2 100644 --- a/pkg/loop/internal/capabilities_registry.go +++ b/pkg/loop/internal/capabilities_registry.go @@ -133,7 +133,7 @@ func (cr *capabilitiesRegistryClient) Add(ctx context.Context, c capabilities.Ba return err } - //Check the capability and the CapabilityType match here as the ServeNew method does not return an error + // Check the capability and the CapabilityType match here as the ServeNew method does not return an error err = validateCapability(c, info.CapabilityType) if err != nil { return err @@ -143,7 +143,6 @@ func (cr *capabilitiesRegistryClient) Add(ctx context.Context, c capabilities.Ba id, cRes, err := cr.ServeNew(info.ID, func(s *grpc.Server) { pbRegisterCapability(s, cr.BrokerExt, c, info.CapabilityType) }) - if err != nil { return err } @@ -182,7 +181,7 @@ func (c *capabilitiesRegistryServer) Get(ctx context.Context, request *pb.GetReq return nil, err } - //Check the capability and the CapabilityType match here as the ServeNew method does not return an error + // Check the capability and the CapabilityType match here as the ServeNew method does not return an error err = validateCapability(capability, info.CapabilityType) if err != nil { return nil, err @@ -191,7 +190,6 @@ func (c *capabilitiesRegistryServer) Get(ctx context.Context, request *pb.GetReq id, _, err := c.ServeNew("Get", func(s *grpc.Server) { pbRegisterCapability(s, c.BrokerExt, capability, info.CapabilityType) }) - if err != nil { return nil, err } @@ -208,7 +206,7 @@ func (c *capabilitiesRegistryServer) GetTrigger(ctx context.Context, request *pb return nil, err } - //Check the capability and the CapabilityType match here as the ServeNew method does not return an error + // Check the capability and the CapabilityType match here as the ServeNew method does not return an error err = validateCapability(capability, capabilities.CapabilityTypeTrigger) if err != nil { return nil, err @@ -217,7 +215,6 @@ func (c *capabilitiesRegistryServer) GetTrigger(ctx context.Context, request *pb id, _, err := c.ServeNew("GetTrigger", func(s *grpc.Server) { pbRegisterCapability(s, c.BrokerExt, capability, capabilities.CapabilityTypeTrigger) }) - if err != nil { return nil, err } @@ -233,7 +230,7 @@ func (c *capabilitiesRegistryServer) GetAction(ctx context.Context, request *pb. return nil, err } - //Check the capability and the CapabilityType match here as the ServeNew method does not return an error + // Check the capability and the CapabilityType match here as the ServeNew method does not return an error err = validateCapability(capability, capabilities.CapabilityTypeAction) if err != nil { return nil, err @@ -242,7 +239,6 @@ func (c *capabilitiesRegistryServer) GetAction(ctx context.Context, request *pb. id, _, err := c.ServeNew("GetAction", func(s *grpc.Server) { pbRegisterCapability(s, c.BrokerExt, capability, capabilities.CapabilityTypeAction) }) - if err != nil { return nil, err } @@ -258,7 +254,7 @@ func (c *capabilitiesRegistryServer) GetConsensus(ctx context.Context, request * return nil, err } - //Check the capability and the CapabilityType match here as the ServeNew method does not return an error + // Check the capability and the CapabilityType match here as the ServeNew method does not return an error err = validateCapability(capability, capabilities.CapabilityTypeConsensus) if err != nil { return nil, err @@ -267,7 +263,6 @@ func (c *capabilitiesRegistryServer) GetConsensus(ctx context.Context, request * id, _, err := c.ServeNew("GetConsensus", func(s *grpc.Server) { pbRegisterCapability(s, c.BrokerExt, capability, capabilities.CapabilityTypeConsensus) }) - if err != nil { return nil, err } @@ -283,7 +278,7 @@ func (c *capabilitiesRegistryServer) GetTarget(ctx context.Context, request *pb. return nil, err } - //Check the capability and the CapabilityType match here as the ServeNew method does not return an error + // Check the capability and the CapabilityType match here as the ServeNew method does not return an error err = validateCapability(capability, capabilities.CapabilityTypeTarget) if err != nil { return nil, err @@ -292,7 +287,6 @@ func (c *capabilitiesRegistryServer) GetTarget(ctx context.Context, request *pb. id, _, err := c.ServeNew("GetTarget", func(s *grpc.Server) { pbRegisterCapability(s, c.BrokerExt, capability, capabilities.CapabilityTypeTarget) }) - if err != nil { return nil, err } @@ -318,7 +312,7 @@ func (c *capabilitiesRegistryServer) List(ctx context.Context, _ *emptypb.Empty) return nil, err } - //Check the capability and the CapabilityType match here as the ServeNew method does not return an error + // Check the capability and the CapabilityType match here as the ServeNew method does not return an error err = validateCapability(cap, info.CapabilityType) if err != nil { c.CloseAll(resources...) @@ -396,7 +390,7 @@ func validateCapability(impl capabilities.BaseCapability, t capabilities.Capabil } // pbRegisterCapability registers the server with the correct capability based on capability type, this method assumes -// that the capability has already been validated with validateCapability +// that the capability has already been validated with validateCapability. func pbRegisterCapability(s *grpc.Server, b *BrokerExt, impl capabilities.BaseCapability, t capabilities.CapabilityType) { switch t { case capabilities.CapabilityTypeTrigger: diff --git a/pkg/loop/internal/capabilities_registry_test.go b/pkg/loop/internal/capabilities_registry_test.go index f5e8abea8..94d3f5e6a 100644 --- a/pkg/loop/internal/capabilities_registry_test.go +++ b/pkg/loop/internal/capabilities_registry_test.go @@ -148,7 +148,7 @@ func TestCapabilitiesRegistry(t *testing.T) { rc, ok := regClient.(*capabilitiesRegistryClient) require.True(t, ok) - //No capabilities in register + // No capabilities in register reg.On("Get", mock.Anything, "some-id").Return(nil, errors.New("capability not found")) _, err = rc.Get(tests.Context(t), "some-id") require.ErrorContains(t, err, "capability not found") @@ -174,7 +174,7 @@ func TestCapabilitiesRegistry(t *testing.T) { require.NoError(t, err) require.Len(t, list, 0) - //Add capability Trigger + // Add capability Trigger triggerInfo := capabilities.CapabilityInfo{ ID: "trigger-1", CapabilityType: capabilities.CapabilityTypeTrigger, @@ -195,10 +195,10 @@ func TestCapabilitiesRegistry(t *testing.T) { triggerCap, err := rc.GetTrigger(tests.Context(t), "trigger-1") require.NoError(t, err) - //Test trigger Info() + // Test trigger Info() testCapabilityInfo(t, triggerInfo, triggerCap) - //Test TriggerExecutable + // Test TriggerExecutable err = triggerCap.RegisterTrigger(tests.Context(t), callbackChan, capabilities.CapabilityRequest{ Inputs: &values.Map{}, Config: &values.Map{}, @@ -215,7 +215,7 @@ func TestCapabilitiesRegistry(t *testing.T) { require.NoError(t, err) require.Nil(t, testTrigger.callback) - //Add capability Trigger + // Add capability Trigger actionInfo := capabilities.CapabilityInfo{ ID: "action-1", CapabilityType: capabilities.CapabilityTypeAction, @@ -232,7 +232,7 @@ func TestCapabilitiesRegistry(t *testing.T) { testCapabilityInfo(t, actionInfo, actionCap) - //Test Executable + // Test Executable workflowRequest := capabilities.RegisterToWorkflowRequest{ Metadata: capabilities.RegistrationMetadata{ WorkflowID: "workflow-ID", @@ -248,7 +248,7 @@ func TestCapabilitiesRegistry(t *testing.T) { require.NoError(t, err) require.Nil(t, testAction.registeredWorkflowRequest) - //Add capability Consensus + // Add capability Consensus consensusInfo := capabilities.CapabilityInfo{ ID: "consensus-1", CapabilityType: capabilities.CapabilityTypeConsensus, @@ -265,7 +265,7 @@ func TestCapabilitiesRegistry(t *testing.T) { testCapabilityInfo(t, consensusInfo, consensusCap) - //Add capability Target + // Add capability Target targetInfo := capabilities.CapabilityInfo{ ID: "target-1", CapabilityType: capabilities.CapabilityTypeTarget, diff --git a/pkg/loop/internal/capabilities_test.go b/pkg/loop/internal/capabilities_test.go index bfa6416c0..9789630f2 100644 --- a/pkg/loop/internal/capabilities_test.go +++ b/pkg/loop/internal/capabilities_test.go @@ -49,6 +49,7 @@ func (m *mockCallback) RegisterToWorkflow(ctx context.Context, request capabilit m.regRequest = request return nil } + func (m *mockCallback) UnregisterFromWorkflow(ctx context.Context, request capabilities.UnregisterFromWorkflowRequest) error { m.unregRequest = request return nil diff --git a/pkg/loop/internal/ccip/offramp.go b/pkg/loop/internal/ccip/offramp.go index 01b300d7a..849a16fdb 100644 --- a/pkg/loop/internal/ccip/offramp.go +++ b/pkg/loop/internal/ccip/offramp.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "google.golang.org/grpc" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/emptypb" @@ -19,8 +20,8 @@ type OffRampReaderClient struct { grpc ccippb.OffRampReaderClient } -func NewOffRampReaderClient(grpc ccippb.OffRampReaderClient) *OffRampReaderClient { - return &OffRampReaderClient{grpc: grpc} +func NewOffRampReaderClient(cc grpc.ClientConnInterface) *OffRampReaderClient { + return &OffRampReaderClient{grpc: ccippb.NewOffRampReaderClient(cc)} } // Address i[github.com/smartcontractkit/chainlink-common/pkg/types/ccip.OffRampReader] diff --git a/pkg/loop/internal/ccip/onramp.go b/pkg/loop/internal/ccip/onramp.go index af0a40f85..b0553559e 100644 --- a/pkg/loop/internal/ccip/onramp.go +++ b/pkg/loop/internal/ccip/onramp.go @@ -4,8 +4,10 @@ import ( "context" "fmt" + "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" + "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb" ccippb "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb/ccip" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" ) @@ -16,8 +18,8 @@ type OnRampReaderClient struct { grpc ccippb.OnRampReaderClient } -func NewOnRampReaderClient(grpc ccippb.OnRampReaderClient) *OnRampReaderClient { - return &OnRampReaderClient{grpc: grpc} +func NewOnRampReaderClient(cc grpc.ClientConnInterface) *OnRampReaderClient { + return &OnRampReaderClient{grpc: ccippb.NewOnRampReaderClient(cc)} } // Address implements ccip.OnRampReader. @@ -53,7 +55,67 @@ func (o *OnRampReaderClient) GetSendRequestsBetweenSeqNums(ctx context.Context, // RouterAddress implements ccip.OnRampReader. func (o *OnRampReaderClient) RouterAddress() (cciptypes.Address, error) { - panic("unimplemented") + resp, err := o.grpc.RouterAddress(context.TODO(), &emptypb.Empty{}) + if err != nil { + return cciptypes.Address(""), err + } + return cciptypes.Address(resp.RouterAddress), nil +} + +// Server + +type OnRampReaderServer struct { + ccippb.UnimplementedOnRampReaderServer + + impl cciptypes.OnRampReader +} + +// mustEmbedUnimplementedOnRampReaderServer implements ccippb.OnRampReaderServer. + +var _ ccippb.OnRampReaderServer = (*OnRampReaderServer)(nil) + +func NewOnRampReaderServer(impl cciptypes.OnRampReader) *OnRampReaderServer { + return &OnRampReaderServer{impl: impl} +} + +// Address implements ccippb.OnRampReaderServer. +func (o *OnRampReaderServer) Address(context.Context, *emptypb.Empty) (*ccippb.OnrampAddressResponse, error) { + addr, err := o.impl.Address() + if err != nil { + return nil, err + } + return &ccippb.OnrampAddressResponse{Address: string(addr)}, nil +} + +// GetDynamicConfig implements ccippb.OnRampReaderServer. +func (o *OnRampReaderServer) GetDynamicConfig(context.Context, *emptypb.Empty) (*ccippb.GetDynamicConfigResponse, error) { + c, err := o.impl.GetDynamicConfig() + if err != nil { + return nil, err + } + return &ccippb.GetDynamicConfigResponse{DynamicConfig: onRampDynamicConfigPB(&c)}, nil +} + +// GetSendRequestsBetweenSeqNums implements ccippb.OnRampReaderServer. +func (o *OnRampReaderServer) GetSendRequestsBetweenSeqNums(ctx context.Context, req *ccippb.GetSendRequestsBetweenSeqNumsRequest) (*ccippb.GetSendRequestsBetweenSeqNumsResponse, error) { + sendRequests, err := o.impl.GetSendRequestsBetweenSeqNums(ctx, req.SeqNumMin, req.SeqNumMax, req.Finalized) + if err != nil { + return nil, err + } + sendRequestsPB, err := evm2EVMMessageWithTxMetaSlicePB(sendRequests) + if err != nil { + return nil, err + } + return &ccippb.GetSendRequestsBetweenSeqNumsResponse{SendRequests: sendRequestsPB}, nil +} + +// RouterAddress implements ccippb.OnRampReaderServer. +func (o *OnRampReaderServer) RouterAddress(context.Context, *emptypb.Empty) (*ccippb.RouterAddressResponse, error) { + a, err := o.impl.RouterAddress() + if err != nil { + return nil, err + } + return &ccippb.RouterAddressResponse{RouterAddress: string(a)}, nil } func onRampDynamicConfig(config *ccippb.OnRampDynamicConfig) cciptypes.OnRampDynamicConfig { @@ -71,6 +133,21 @@ func onRampDynamicConfig(config *ccippb.OnRampDynamicConfig) cciptypes.OnRampDyn } } +func onRampDynamicConfigPB(config *cciptypes.OnRampDynamicConfig) *ccippb.OnRampDynamicConfig { + return &ccippb.OnRampDynamicConfig{ + Router: string(config.Router), + MaxNumberOfTokensPerMsg: uint32(config.MaxNumberOfTokensPerMsg), + DestGasOverhead: config.DestGasOverhead, + DestGasPerByte: uint32(config.DestGasPerPayloadByte), + DestDataAvailabilityOverheadGas: config.DestDataAvailabilityOverheadGas, + DestGasPerDataAvailabilityByte: uint32(config.DestGasPerDataAvailabilityByte), + DestDataAvailabilityMultiplierBps: uint32(config.DestDataAvailabilityMultiplierBps), + PriceRegistry: string(config.PriceRegistry), + MaxDataBytes: config.MaxDataBytes, + MaxPerMsgGasLimit: config.MaxPerMsgGasLimit, + } +} + func evm2EVMMessageWithTxMetaSlice(messages []*ccippb.EVM2EVMMessageWithTxMeta) ([]cciptypes.EVM2EVMMessageWithTxMeta, error) { res := make([]cciptypes.EVM2EVMMessageWithTxMeta, len(messages)) for i, m := range messages { @@ -86,6 +163,18 @@ func evm2EVMMessageWithTxMetaSlice(messages []*ccippb.EVM2EVMMessageWithTxMeta) return res, nil } +func evm2EVMMessageWithTxMetaSlicePB(messages []cciptypes.EVM2EVMMessageWithTxMeta) ([]*ccippb.EVM2EVMMessageWithTxMeta, error) { + res := make([]*ccippb.EVM2EVMMessageWithTxMeta, len(messages)) + for i, m := range messages { + decodedMsg := evm2EVMMessagePB(m.EVM2EVMMessage) + res[i] = &ccippb.EVM2EVMMessageWithTxMeta{ + TxMeta: txMetaPB(m.TxMeta), + Message: decodedMsg, + } + } + return res, nil +} + func evm2EVMMessage(message *ccippb.EVM2EVMMessage) (cciptypes.EVM2EVMMessage, error) { msgID, err := hash(message.MessageId) if err != nil { @@ -109,6 +198,24 @@ func evm2EVMMessage(message *ccippb.EVM2EVMMessage) (cciptypes.EVM2EVMMessage, e }, nil } +func evm2EVMMessagePB(message cciptypes.EVM2EVMMessage) *ccippb.EVM2EVMMessage { + return &ccippb.EVM2EVMMessage{ + SequenceNumber: message.SequenceNumber, + GasLimit: pb.NewBigIntFromInt(message.GasLimit), + Nonce: message.Nonce, + MessageId: message.MessageID[:], + SourceChainSelector: message.SourceChainSelector, + Sender: string(message.Sender), + Receiver: string(message.Receiver), + Strict: message.Strict, + FeeToken: string(message.FeeToken), + FeeTokenAmount: pb.NewBigIntFromInt(message.FeeTokenAmount), + Data: message.Data, + TokenAmounts: tokenAmountSlicePB(message.TokenAmounts), + SourceTokenData: message.SourceTokenData, + } +} + func tokenAmountSlice(tokenAmounts []*ccippb.TokenAmount) []cciptypes.TokenAmount { res := make([]cciptypes.TokenAmount, len(tokenAmounts)) for i, t := range tokenAmounts { @@ -120,6 +227,17 @@ func tokenAmountSlice(tokenAmounts []*ccippb.TokenAmount) []cciptypes.TokenAmoun return res } +func tokenAmountSlicePB(tokenAmounts []cciptypes.TokenAmount) []*ccippb.TokenAmount { + res := make([]*ccippb.TokenAmount, len(tokenAmounts)) + for i, t := range tokenAmounts { + res[i] = &ccippb.TokenAmount{ + Token: string(t.Token), + Amount: pb.NewBigIntFromInt(t.Amount), + } + } + return res +} + func txMeta(meta *ccippb.TxMeta) cciptypes.TxMeta { return cciptypes.TxMeta{ BlockTimestampUnixMilli: meta.BlockTimestampUnixMilli, @@ -129,6 +247,15 @@ func txMeta(meta *ccippb.TxMeta) cciptypes.TxMeta { } } +func txMetaPB(meta cciptypes.TxMeta) *ccippb.TxMeta { + return &ccippb.TxMeta{ + BlockTimestampUnixMilli: meta.BlockTimestampUnixMilli, + BlockNumber: meta.BlockNumber, + TxHash: meta.TxHash, + LogIndex: meta.LogIndex, + } +} + func hash(h []byte) (cciptypes.Hash, error) { var res cciptypes.Hash if len(h) != 32 { diff --git a/pkg/loop/internal/ccip/test/exec_factory_server.go b/pkg/loop/internal/ccip/test/exec_factory_server.go new file mode 100644 index 000000000..64b3363d3 --- /dev/null +++ b/pkg/loop/internal/ccip/test/exec_factory_server.go @@ -0,0 +1,99 @@ +package test + +import ( + "context" + "testing" + + libocr "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + testreportingplugin "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test/ocr2/reporting_plugin" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" +) + +var ExecFactoryServer = execFactoryServer{ + execFactoryServerConfig: execFactoryServerConfig{ + provider: ExecutionProvider, + }, +} + +type execFactoryServerConfig struct { + provider ExecProviderEvaluator +} + +var _ types.CCIPExecutionFactoryGenerator = execFactoryServer{} + +type execFactoryServer struct { + execFactoryServerConfig +} + +// NewExecutionFactory implements types.CCIPExecFactoryGenerator. +// func (e execFactoryServer) NewExecutionFactory(ctx context.Context, provider types.CCIPExecProvider, config types.CCIPExecFactoryGeneratorConfig) (types.ReportingPluginFactory, error) { +func (e execFactoryServer) NewExecutionFactory(ctx context.Context, provider types.CCIPExecProvider) (types.ReportingPluginFactory, error) { + err := e.provider.Evaluate(ctx, provider) + if err != nil { + return nil, err + } + return testreportingplugin.Factory, nil +} + +func RunExecutionLOOP(t *testing.T, p types.CCIPExecutionFactoryGenerator) { + ExecutionLOOPTester{ExecutionProvider}.Run(t, p) +} + +type ExecutionLOOPTester struct { + types.CCIPExecProvider +} + +func (e ExecutionLOOPTester) Run(t *testing.T, p types.CCIPExecutionFactoryGenerator) { + t.Run("ExecutionLOOP", func(t *testing.T) { + ctx := tests.Context(t) + factory, err := p.NewExecutionFactory(ctx, e.CCIPExecProvider) + require.NoError(t, err) + + runReportingPluginFactory(t, factory) + }) +} + +func (e ExecutionLOOPTester) FixBCFXXX(t *testing.T, p types.CCIPExecutionFactoryGenerator) { + t.Run("Proxying a Relayer fails due to resource creation", func(t *testing.T) { + ctx := tests.Context(t) + factory, err := p.NewExecutionFactory(ctx, e.CCIPExecProvider) + require.NoError(t, err) + + runReportingPluginFactory(t, factory) + }) +} + +func runReportingPluginFactory(t *testing.T, factory types.ReportingPluginFactory) { + // TODO BCF-3068 de-dupe this with the same function in median/test/median.go + rpi := libocr.ReportingPluginInfo{ + Name: "test", + UniqueReports: true, + Limits: libocr.ReportingPluginLimits{ + MaxQueryLength: 42, + MaxObservationLength: 13, + MaxReportLength: 17, + }, + } + + t.Run("ReportingPluginFactory", func(t *testing.T) { + // we expect the static implementation to be used under the covers + // we can't compare the types directly because the returned reporting plugin may be a grpc client + // that wraps the static implementation + var expectedReportingPlugin = testreportingplugin.ReportingPlugin + + rp, gotRPI, err := factory.NewReportingPlugin(testreportingplugin.Factory.ReportingPluginConfig) + require.NoError(t, err) + assert.Equal(t, rpi, gotRPI) + t.Cleanup(func() { assert.NoError(t, rp.Close()) }) + + t.Run("ReportingPlugin", func(t *testing.T) { + ctx := tests.Context(t) + + expectedReportingPlugin.AssertEqual(ctx, t, rp) + }) + }) +} diff --git a/pkg/loop/internal/ccip/test/exec_provider.go b/pkg/loop/internal/ccip/test/exec_provider.go new file mode 100644 index 000000000..1a36188f9 --- /dev/null +++ b/pkg/loop/internal/ccip/test/exec_provider.go @@ -0,0 +1,165 @@ +package test + +import ( + "context" + "fmt" + "testing" + + libocr "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + + testpluginprovider "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test/ocr2/plugin_provider" + testtypes "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" +) + +type ExecProviderEvaluator interface { + types.CCIPExecProvider + testtypes.Evaluator[types.CCIPExecProvider] +} + +type ExecProviderTester interface { + types.CCIPExecProvider + testtypes.Evaluator[types.CCIPExecProvider] + testtypes.AssertEqualer[types.CCIPExecProvider] +} + +var ExecutionConfig = types.CCIPExecFactoryGeneratorConfig{ + OnRampAddress: ccip.Address("onramp"), + OffRampAddress: ccip.Address("offramp"), + CommitStoreAddress: ccip.Address("commitstore"), + TokenReaderAddress: ccip.Address("tokenreader"), +} + +var ExecutionProvider = &staticExecProvider{ + staticExecProviderConfig: staticExecProviderConfig{ + addr: ccip.Address("some address"), + offchainDigester: testpluginprovider.OffchainConfigDigester, + contractTracker: testpluginprovider.ContractConfigTracker, + contractTransmitter: testpluginprovider.ContractTransmitter, + onrampreader: OnRamp, + offrampreader: OffRamp, + }, +} + +var _ ExecProviderTester = (*staticExecProvider)(nil) + +type staticExecProviderConfig struct { + addr ccip.Address + offchainDigester testtypes.OffchainConfigDigesterEvaluator + contractTracker testtypes.ContractConfigTrackerEvaluator + contractTransmitter testtypes.ContractTransmitterEvaluator + onrampreader OnRampEvaluator + offrampreader OffRampEvaluator + // TODO BCF-2979 fill in the rest of exec provider components +} + +type staticExecProvider struct { + staticExecProviderConfig +} + +// ChainReader implements ExecProviderEvaluator. +func (s *staticExecProvider) ChainReader() types.ChainReader { + return nil +} + +// Close implements ExecProviderEvaluator. +func (s *staticExecProvider) Close() error { + return nil +} + +// Codec implements ExecProviderEvaluator. +func (s *staticExecProvider) Codec() types.Codec { + return nil +} + +// ContractConfigTracker implements ExecProviderEvaluator. +func (s *staticExecProvider) ContractConfigTracker() libocr.ContractConfigTracker { + return s.contractTracker +} + +// ContractTransmitter implements ExecProviderEvaluator. +func (s *staticExecProvider) ContractTransmitter() libocr.ContractTransmitter { + return s.contractTransmitter +} + +// Evaluate implements ExecProviderEvaluator. +func (s *staticExecProvider) Evaluate(ctx context.Context, other types.CCIPExecProvider) error { + otherOnRamp, err := other.NewOnRampReader(ctx, "ignored") + if err != nil { + return fmt.Errorf("failed to create other on ramp reader: %w", err) + } + err = s.onrampreader.Evaluate(ctx, otherOnRamp) + if err != nil { + return fmt.Errorf("on ramp reader evaluation failed: %w", err) + } + // TODO BCF-2979 other components of exec provider + return nil +} + +// HealthReport implements ExecProviderEvaluator. +func (s *staticExecProvider) HealthReport() map[string]error { + panic("unimplemented") +} + +// Name implements ExecProviderEvaluator. +func (s *staticExecProvider) Name() string { + panic("unimplemented") +} + +// NewCommitStoreReader implements ExecProviderEvaluator. +func (s *staticExecProvider) NewCommitStoreReader(ctx context.Context, addr ccip.Address) (ccip.CommitStoreReader, error) { + panic("unimplemented") +} + +// NewOffRampReader implements ExecProviderEvaluator. +func (s *staticExecProvider) NewOffRampReader(ctx context.Context, addr ccip.Address) (ccip.OffRampReader, error) { + return s.offrampreader, nil +} + +// NewOnRampReader implements ExecProviderEvaluator. +func (s *staticExecProvider) NewOnRampReader(ctx context.Context, addr ccip.Address) (ccip.OnRampReader, error) { + return s.onrampreader, nil +} + +// NewPriceRegistryReader implements ExecProviderEvaluator. +func (s *staticExecProvider) NewPriceRegistryReader(ctx context.Context, addr ccip.Address) (ccip.PriceRegistryReader, error) { + panic("unimplemented") +} + +// NewTokenDataReader implements ExecProviderEvaluator. +func (s *staticExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress ccip.Address) (ccip.TokenDataReader, error) { + panic("unimplemented") +} + +// NewTokenPoolBatchedReader implements ExecProviderEvaluator. +func (s *staticExecProvider) NewTokenPoolBatchedReader(ctx context.Context) (ccip.TokenPoolBatchedReader, error) { + panic("unimplemented") +} + +// OffchainConfigDigester implements ExecProviderEvaluator. +func (s *staticExecProvider) OffchainConfigDigester() libocr.OffchainConfigDigester { + return s.offchainDigester +} + +// Ready implements ExecProviderEvaluator. +func (s *staticExecProvider) Ready() error { + return nil +} + +// SourceNativeToken implements ExecProviderEvaluator. +func (s *staticExecProvider) SourceNativeToken(ctx context.Context) (ccip.Address, error) { + panic("unimplemented") +} + +// Start implements ExecProviderEvaluator. +func (s *staticExecProvider) Start(context.Context) error { + return nil +} + +// AssertEqual implements ExecProviderTester. +func (s *staticExecProvider) AssertEqual(ctx context.Context, t *testing.T, other types.CCIPExecProvider) { + // TODO BCF-2979 other components of exec provider + assert.NoError(t, s.Evaluate(ctx, other)) +} diff --git a/pkg/loop/internal/ccip/test/offramp.go b/pkg/loop/internal/ccip/test/offramp.go new file mode 100644 index 000000000..cf9daa514 --- /dev/null +++ b/pkg/loop/internal/ccip/test/offramp.go @@ -0,0 +1,318 @@ +package test + +import ( + "context" + "fmt" + "math/big" + "reflect" + + "github.com/stretchr/testify/assert" + + testtypes "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test/types" + "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" +) + +var OffRamp = staticOffRamp{ + staticOffRampConfig: staticOffRampConfig{ + addressResponse: ccip.Address("addressResponse"), + changeConfigRequest: changeConfigRequest{ + onchainConfig: []byte("onchainConfig"), + offchainConfig: []byte("offchainConfig"), + }, + changeConfigResponse: changeConfigResponse{ + onchainConfigDigest: ccip.Address("onchainConfigDigest"), + offchainConfigDigest: ccip.Address("offchainConfigDigest"), + }, + currentRateLimiterStateResponse: ccip.TokenBucketRateLimit{ + Tokens: big.NewInt(1), + }, + decodeExecutionReportResponse: ccip.ExecReport{ + Messages: []ccip.EVM2EVMMessage{ + { + SequenceNumber: 1, + GasLimit: big.NewInt(1), + }, + { + SequenceNumber: 2, + GasLimit: big.NewInt(2), + }, + }, + Proofs: [][32]byte{ + {1}, + {2}, + }, + }, + }, +} + +type OffRampEvaluator interface { + ccip.OffRampReader + testtypes.Evaluator[ccip.OffRampReader] +} + +var _ OffRampEvaluator = staticOffRamp{} + +type staticOffRampConfig struct { + addressResponse ccip.Address + changeConfigRequest + changeConfigResponse + + currentRateLimiterStateResponse ccip.TokenBucketRateLimit + + decodeExecutionReportRequest []byte + decodeExecutionReportResponse ccip.ExecReport + + encodeExecutionReportRequest ccip.ExecReport + encodeExecutionReportResponse []byte + + gasPriceEstimatorExecResponse ccip.GasPriceEstimatorExec + + getExecutionStateRequest uint64 + getExecutionStateResponse uint8 + + getExecutionStateChangesBetweenSeqNumsRequest + getExecutionStateChangesBetweenSeqNumsResponse + + getSenderNonceRequest ccip.Address + getSenderNonceResponse uint64 + + getSourceToDestTokensMappingResponse map[ccip.Address]ccip.Address + + getStaticConfigResponse ccip.OffRampStaticConfig + + getTokensResponse ccip.OffRampTokens + + offchainConfigResponse ccip.ExecOffchainConfig + + onchainConfigResponse ccip.ExecOnchainConfig +} + +type staticOffRamp struct { + staticOffRampConfig +} + +// Address implements OffRampEvaluator. +func (s staticOffRamp) Address(ctx context.Context) (ccip.Address, error) { + return s.addressResponse, nil +} + +// ChangeConfig implements OffRampEvaluator. +func (s staticOffRamp) ChangeConfig(ctx context.Context, onchainConfig []byte, offchainConfig []byte) (ccip.Address, ccip.Address, error) { + if !reflect.DeepEqual(onchainConfig, s.onchainConfig) { + return ccip.Address(""), ccip.Address(""), fmt.Errorf("expected onchainConfig %v but got %v", s.onchainConfig, onchainConfig) + } + if !reflect.DeepEqual(offchainConfig, s.offchainConfig) { + return ccip.Address(""), ccip.Address(""), fmt.Errorf("expected offchainConfig %v but got %v", s.offchainConfig, offchainConfig) + } + return s.onchainConfigDigest, s.offchainConfigDigest, nil +} + +// CurrentRateLimiterState implements OffRampEvaluator. +func (s staticOffRamp) CurrentRateLimiterState(ctx context.Context) (ccip.TokenBucketRateLimit, error) { + return s.currentRateLimiterStateResponse, nil +} + +// DecodeExecutionReport implements OffRampEvaluator. +func (s staticOffRamp) DecodeExecutionReport(ctx context.Context, report []byte) (ccip.ExecReport, error) { + if !reflect.DeepEqual(report, s.decodeExecutionReportRequest) { + return ccip.ExecReport{}, fmt.Errorf("expected report %v but got %v", s.decodeExecutionReportRequest, report) + } + return s.decodeExecutionReportResponse, nil +} + +// EncodeExecutionReport implements OffRampEvaluator. +func (s staticOffRamp) EncodeExecutionReport(ctx context.Context, report ccip.ExecReport) ([]byte, error) { + if !reflect.DeepEqual(report, s.encodeExecutionReportRequest) { + return nil, fmt.Errorf("expected report %v but got %v", s.encodeExecutionReportRequest, report) + } + return s.encodeExecutionReportResponse, nil +} + +// GasPriceEstimator implements OffRampEvaluator. +func (s staticOffRamp) GasPriceEstimator(ctx context.Context) (ccip.GasPriceEstimatorExec, error) { + return s.gasPriceEstimatorExecResponse, nil +} + +// GetExecutionState implements OffRampEvaluator. +func (s staticOffRamp) GetExecutionState(ctx context.Context, sequenceNumber uint64) (uint8, error) { + if sequenceNumber != s.getExecutionStateRequest { + return 0, fmt.Errorf("expected sequenceNumber %d but got %d", s.getExecutionStateRequest, sequenceNumber) + } + return s.getExecutionStateResponse, nil +} + +// GetExecutionStateChangesBetweenSeqNums implements OffRampEvaluator. +func (s staticOffRamp) GetExecutionStateChangesBetweenSeqNums(ctx context.Context, seqNumMin uint64, seqNumMax uint64, confirmations int) ([]ccip.ExecutionStateChangedWithTxMeta, error) { + if seqNumMin != s.seqNumMin { + return nil, fmt.Errorf("expected seqNumMin %d but got %d", s.seqNumMin, seqNumMin) + } + if seqNumMax != s.seqNumMax { + return nil, fmt.Errorf("expected seqNumMax %d but got %d", s.seqNumMax, seqNumMax) + } + if confirmations != s.confirmations { + return nil, fmt.Errorf("expected confirmations %d but got %d", s.confirmations, confirmations) + } + return s.executionStateChangedWithTxMeta, nil +} + +// GetSenderNonce implements OffRampEvaluator. +func (s staticOffRamp) GetSenderNonce(ctx context.Context, sender ccip.Address) (uint64, error) { + if sender != s.getSenderNonceRequest { + return 0, fmt.Errorf("expected sender %s but got %s", s.getSenderNonceRequest, sender) + } + return s.getSenderNonceResponse, nil +} + +// GetSourceToDestTokensMapping implements OffRampEvaluator. +func (s staticOffRamp) GetSourceToDestTokensMapping(ctx context.Context) (map[ccip.Address]ccip.Address, error) { + return s.getSourceToDestTokensMappingResponse, nil +} + +// GetStaticConfig implements OffRampEvaluator. +func (s staticOffRamp) GetStaticConfig(ctx context.Context) (ccip.OffRampStaticConfig, error) { + return s.getStaticConfigResponse, nil +} + +// GetTokens implements OffRampEvaluator. +func (s staticOffRamp) GetTokens(ctx context.Context) (ccip.OffRampTokens, error) { + return s.getTokensResponse, nil +} + +// OffchainConfig implements OffRampEvaluator. +func (s staticOffRamp) OffchainConfig(ctx context.Context) (ccip.ExecOffchainConfig, error) { + return s.offchainConfigResponse, nil +} + +// OnchainConfig implements OffRampEvaluator. +func (s staticOffRamp) OnchainConfig(ctx context.Context) (ccip.ExecOnchainConfig, error) { + return s.onchainConfigResponse, nil +} + +// Evaluate implements OffRampEvaluator. +func (s staticOffRamp) Evaluate(ctx context.Context, other ccip.OffRampReader) error { + address, err := other.Address(ctx) + if err != nil { + return fmt.Errorf("failed to get address: %w", err) + } + if address != s.addressResponse { + return fmt.Errorf("expected address %s but got %s", s.addressResponse, address) + } + + currentRateLimiterState, err := other.CurrentRateLimiterState(ctx) + if err != nil { + return fmt.Errorf("failed to get currentRateLimiterState: %w", err) + } + if currentRateLimiterState != s.currentRateLimiterStateResponse { + return fmt.Errorf("expected currentRateLimiterState %v but got %v", s.currentRateLimiterStateResponse, currentRateLimiterState) + } + + decodeExecutionReport, err := other.DecodeExecutionReport(ctx, s.decodeExecutionReportRequest) + if err != nil { + return fmt.Errorf("failed to decodeExecutionReport: %w", err) + } + if !assert.ObjectsAreEqual(decodeExecutionReport, s.decodeExecutionReportResponse) { + return fmt.Errorf("expected decodeExecutionReport %v but got %v", s.decodeExecutionReportResponse, decodeExecutionReport) + } + + encodeExecutionReport, err := other.EncodeExecutionReport(ctx, s.encodeExecutionReportRequest) + if err != nil { + return fmt.Errorf("failed to encodeExecutionReport: %w", err) + } + if !reflect.DeepEqual(encodeExecutionReport, s.encodeExecutionReportResponse) { + return fmt.Errorf("expected encodeExecutionReport %v but got %v", s.encodeExecutionReportResponse, encodeExecutionReport) + } + + gasPriceEstimator, err := other.GasPriceEstimator(ctx) + if err != nil { + return fmt.Errorf("failed to get gasPriceEstimator: %w", err) + } + if gasPriceEstimator != s.gasPriceEstimatorExecResponse { + return fmt.Errorf("expected gasPriceEstimator %v but got %v", s.gasPriceEstimatorExecResponse, gasPriceEstimator) + } + + getExecutionState, err := other.GetExecutionState(ctx, s.getExecutionStateRequest) + if err != nil { + return fmt.Errorf("failed to get getExecutionState: %w", err) + } + if getExecutionState != s.getExecutionStateResponse { + return fmt.Errorf("expected getExecutionState %d but got %d", s.getExecutionStateResponse, getExecutionState) + } + + getExecutionStateChangesBetweenSeqNums, err := other.GetExecutionStateChangesBetweenSeqNums(ctx, s.seqNumMin, s.seqNumMax, s.confirmations) + if err != nil { + return fmt.Errorf("failed to get getExecutionStateChangesBetweenSeqNums: %w", err) + } + if !reflect.DeepEqual(getExecutionStateChangesBetweenSeqNums, s.executionStateChangedWithTxMeta) { + return fmt.Errorf("expected getExecutionStateChangesBetweenSeqNums %v but got %v", s.executionStateChangedWithTxMeta, getExecutionStateChangesBetweenSeqNums) + } + + getSenderNonce, err := other.GetSenderNonce(ctx, s.getSenderNonceRequest) + if err != nil { + return fmt.Errorf("failed to get getSenderNonce: %w", err) + } + if getSenderNonce != s.getSenderNonceResponse { + return fmt.Errorf("expected getSenderNonce %d but got %d", s.getSenderNonceResponse, getSenderNonce) + } + + getSourceToDestTokensMapping, err := other.GetSourceToDestTokensMapping(ctx) + if err != nil { + return fmt.Errorf("failed to get getSourceToDestTokensMapping: %w", err) + } + if !reflect.DeepEqual(getSourceToDestTokensMapping, s.getSourceToDestTokensMappingResponse) { + return fmt.Errorf("expected getSourceToDestTokensMapping %v but got %v", s.getSourceToDestTokensMappingResponse, getSourceToDestTokensMapping) + } + + getStaticConfig, err := other.GetStaticConfig(ctx) + if err != nil { + return fmt.Errorf("failed to get getStaticConfig: %w", err) + } + if getStaticConfig != s.getStaticConfigResponse { + return fmt.Errorf("expected getStaticConfig %v but got %v", s.getStaticConfigResponse, getStaticConfig) + } + + getTokens, err := other.GetTokens(ctx) + if err != nil { + return fmt.Errorf("failed to get getTokens: %w", err) + } + if !assert.ObjectsAreEqual(getTokens, s.getTokensResponse) { + return fmt.Errorf("expected getTokens %v but got %v", s.getTokensResponse, getTokens) + } + + offchainConfig, err := other.OffchainConfig(ctx) + if err != nil { + return fmt.Errorf("failed to get offchainConfig: %w", err) + } + if offchainConfig != s.offchainConfigResponse { + return fmt.Errorf("expected offchainConfig %v but got %v", s.offchainConfigResponse, offchainConfig) + } + + onchainConfig, err := other.OnchainConfig(ctx) + if err != nil { + return fmt.Errorf("failed to get onchainConfig: %w", err) + } + if onchainConfig != s.onchainConfigResponse { + return fmt.Errorf("expected onchainConfig %v but got %v", s.onchainConfigResponse, onchainConfig) + } + + return nil +} + +type changeConfigRequest struct { + onchainConfig []byte + offchainConfig []byte +} + +type changeConfigResponse struct { + onchainConfigDigest ccip.Address + offchainConfigDigest ccip.Address +} + +type getExecutionStateChangesBetweenSeqNumsRequest struct { + seqNumMin uint64 + seqNumMax uint64 + confirmations int +} + +type getExecutionStateChangesBetweenSeqNumsResponse struct { + executionStateChangedWithTxMeta []ccip.ExecutionStateChangedWithTxMeta +} diff --git a/pkg/loop/internal/ccip/test/offramp_test.go b/pkg/loop/internal/ccip/test/offramp_test.go new file mode 100644 index 000000000..c362c382c --- /dev/null +++ b/pkg/loop/internal/ccip/test/offramp_test.go @@ -0,0 +1,15 @@ +package test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStaticOffRamp(t *testing.T) { + t.Parallel() + + ctx := context.Background() + assert.NoError(t, OffRamp.Evaluate(ctx, OffRamp)) +} diff --git a/pkg/loop/internal/ccip/test/onramp.go b/pkg/loop/internal/ccip/test/onramp.go new file mode 100644 index 000000000..80b14f86c --- /dev/null +++ b/pkg/loop/internal/ccip/test/onramp.go @@ -0,0 +1,166 @@ +package test + +import ( + "context" + "fmt" + "math/big" + + "github.com/stretchr/testify/assert" + + testtypes "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test/types" + "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" +) + +var OnRamp = staticOnRamp{ + staticOnRampConfig: staticOnRampConfig{ + addressResponse: ccip.Address("some-address"), + routerResponse: ccip.Address("some-router"), + configResponse: ccip.OnRampDynamicConfig{ + Router: "some-router", + MaxNumberOfTokensPerMsg: 11, + DestGasOverhead: 13, + DestGasPerPayloadByte: 17, + DestDataAvailabilityOverheadGas: 23, + DestGasPerDataAvailabilityByte: 29, + DestDataAvailabilityMultiplierBps: 31, + PriceRegistry: "some-price-registry", + MaxDataBytes: 37, + MaxPerMsgGasLimit: 41, + }, + getSendRequestsBetweenSeqNumsResponse: getSendRequestsBetweenSeqNumsResponse{ + EVM2EVMMessageWithTxMeta: []ccip.EVM2EVMMessageWithTxMeta{ + { + TxMeta: ccip.TxMeta{ + BlockNumber: 1, + BlockTimestampUnixMilli: 2, + TxHash: "tx-hash", + LogIndex: 3, + }, + EVM2EVMMessage: ccip.EVM2EVMMessage{ + SequenceNumber: 5, + GasLimit: big.NewInt(7), + Nonce: 11, + MessageID: ccip.Hash{0: 1, 31: 7}, + SourceChainSelector: 13, + Sender: "sender", + Receiver: "receiver", + Strict: true, + FeeToken: "fee-token", + FeeTokenAmount: big.NewInt(17), + Data: []byte{19}, + TokenAmounts: []ccip.TokenAmount{ + { + Token: "token-1", + Amount: big.NewInt(23), + }, + { + Token: "token-2", + Amount: big.NewInt(29), + }, + }, + }, + }, + }, + }, + getSendRequestsBetweenSeqNums: getSendRequestsBetweenSeqNums{ + SeqNumMin: 1, + SeqNumMax: 2, + Finalized: true, + }, + }, +} + +type OnRampEvaluator interface { + ccip.OnRampReader + testtypes.Evaluator[ccip.OnRampReader] +} + +var _ OnRampEvaluator = staticOnRamp{} + +type staticOnRampConfig struct { + addressResponse ccip.Address + routerResponse ccip.Address + configResponse ccip.OnRampDynamicConfig + getSendRequestsBetweenSeqNums + getSendRequestsBetweenSeqNumsResponse +} + +type staticOnRamp struct { + staticOnRampConfig +} + +// Address implements OnRampEvaluator. +func (s staticOnRamp) Address() (ccip.Address, error) { + return s.addressResponse, nil +} + +// Evaluate implements OnRampEvaluator. +func (s staticOnRamp) Evaluate(ctx context.Context, other ccip.OnRampReader) error { + address, err := other.Address() + if err != nil { + return fmt.Errorf("failed to get address: %w", err) + } + if address != s.addressResponse { + return fmt.Errorf("expected address %s but got %s", s.addressResponse, address) + } + + router, err := other.RouterAddress() + if err != nil { + return fmt.Errorf("failed to get router: %w", err) + } + if router != s.routerResponse { + return fmt.Errorf("expected router %s but got %s", s.routerResponse, router) + } + + config, err := other.GetDynamicConfig() + if err != nil { + return fmt.Errorf("failed to get config: %w", err) + } + if config != s.configResponse { + return fmt.Errorf("expected config %v but got %v", s.configResponse, config) + } + + sendRequests, err := other.GetSendRequestsBetweenSeqNums(ctx, s.getSendRequestsBetweenSeqNums.SeqNumMin, s.getSendRequestsBetweenSeqNums.SeqNumMax, s.getSendRequestsBetweenSeqNums.Finalized) + if err != nil { + return fmt.Errorf("failed to get send requests: %w", err) + } + if !assert.ObjectsAreEqual(s.getSendRequestsBetweenSeqNumsResponse.EVM2EVMMessageWithTxMeta, sendRequests) { + return fmt.Errorf("expected send requests %v but got %v", s.getSendRequestsBetweenSeqNumsResponse.EVM2EVMMessageWithTxMeta, sendRequests) + } + + return nil +} + +// GetDynamicConfig implements OnRampEvaluator. +func (s staticOnRamp) GetDynamicConfig() (ccip.OnRampDynamicConfig, error) { + return s.configResponse, nil +} + +// GetSendRequestsBetweenSeqNums implements OnRampEvaluator. +func (s staticOnRamp) GetSendRequestsBetweenSeqNums(ctx context.Context, seqNumMin uint64, seqNumMax uint64, finalized bool) ([]ccip.EVM2EVMMessageWithTxMeta, error) { + if seqNumMin != s.getSendRequestsBetweenSeqNums.SeqNumMin { + return nil, fmt.Errorf("expected seqNumMin %d but got %d", s.getSendRequestsBetweenSeqNums.SeqNumMin, seqNumMin) + } + if seqNumMax != s.getSendRequestsBetweenSeqNums.SeqNumMax { + return nil, fmt.Errorf("expected seqNumMax %d but got %d", s.getSendRequestsBetweenSeqNums.SeqNumMax, seqNumMax) + } + if finalized != s.getSendRequestsBetweenSeqNums.Finalized { + return nil, fmt.Errorf("expected finalized %t but got %t", s.getSendRequestsBetweenSeqNums.Finalized, finalized) + } + return s.getSendRequestsBetweenSeqNumsResponse.EVM2EVMMessageWithTxMeta, nil +} + +// RouterAddress implements OnRampEvaluator. +func (s staticOnRamp) RouterAddress() (ccip.Address, error) { + return s.routerResponse, nil +} + +type getSendRequestsBetweenSeqNums struct { + SeqNumMin uint64 + SeqNumMax uint64 + Finalized bool +} + +type getSendRequestsBetweenSeqNumsResponse struct { + EVM2EVMMessageWithTxMeta []ccip.EVM2EVMMessageWithTxMeta +} diff --git a/pkg/loop/internal/ccip/test/onramp_test.go b/pkg/loop/internal/ccip/test/onramp_test.go new file mode 100644 index 000000000..f08461c19 --- /dev/null +++ b/pkg/loop/internal/ccip/test/onramp_test.go @@ -0,0 +1,15 @@ +package test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStaticOnRamp(t *testing.T) { + t.Parallel() + + ctx := context.Background() + assert.NoError(t, OnRamp.Evaluate(ctx, OnRamp)) +} diff --git a/pkg/loop/internal/ccip/test/plugin_provider.go b/pkg/loop/internal/ccip/test/plugin_provider.go new file mode 100644 index 000000000..82c3c2aa1 --- /dev/null +++ b/pkg/loop/internal/ccip/test/plugin_provider.go @@ -0,0 +1,22 @@ +package test + +import ( + "github.com/google/uuid" + + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +var ( + ExecutionRelayArgs = types.RelayArgs{ + ExternalJobID: uuid.MustParse("12348153-1234-5678-9012-fd0985d00000"), + JobID: 42, + ContractID: "exec-testcontract", + New: true, + RelayConfig: []byte{1: 4, 36: 101}, + ProviderType: string(types.CCIPExecution), + } + ExecutionPluginArgs = types.PluginArgs{ + TransmitterID: "exec-testtransmitter", + PluginConfig: []byte{133: 79}, + } +) diff --git a/pkg/loop/internal/ccip_execution.go b/pkg/loop/internal/ccip_execution.go new file mode 100644 index 000000000..0417b211b --- /dev/null +++ b/pkg/loop/internal/ccip_execution.go @@ -0,0 +1,287 @@ +package internal + +import ( + "context" + "fmt" + + "github.com/mwitkow/grpc-proxy/proxy" + "google.golang.org/grpc" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + ccipinternal "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb" + ccippb "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/types" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" +) + +// ExecutionLOOPClient is a client is run on the core node to connect to the execution LOOP server. +type ExecutionLOOPClient struct { + // hashicorp plugin client + *PluginClient + // client to base service + *ServiceClient + + // creates new execution factory instances + generator ccippb.ExecutionFactoryGeneratorClient +} + +func NewExecutionLOOPClient(broker Broker, brokerCfg BrokerConfig, conn *grpc.ClientConn) *ExecutionLOOPClient { + brokerCfg.Logger = logger.Named(brokerCfg.Logger, "ExecutionLOOPClient") + pc := NewPluginClient(broker, brokerCfg, conn) + return &ExecutionLOOPClient{ + PluginClient: pc, + ServiceClient: NewServiceClient(pc.BrokerExt, pc), + generator: ccippb.NewExecutionFactoryGeneratorClient(pc), + } +} + +// NewExecutionFactory creates a new reporting plugin factory client. +// In practice this client is called by the core node. +// The reporting plugin factory client is a client to the LOOP server, which +// is run as an external process via hashicorp plugin. If the given provider is a GRPCClientConn, then the provider is proxied to the +// to the relayer, which is its own process via hashicorp plugin. If the provider is not a GRPCClientConn, then the provider is a local +// to the core node. The core must wrap the provider in a grpc server and serve it locally. +// func (c *ExecutionLOOPClient) NewExecutionFactory(ctx context.Context, provider types.CCIPExecProvider, config types.CCIPExecFactoryGeneratorConfig) (types.ReportingPluginFactory, error) {. +func (c *ExecutionLOOPClient) NewExecutionFactory(ctx context.Context, provider types.CCIPExecProvider) (types.ReportingPluginFactory, error) { + newExecClientFn := func(ctx context.Context) (id uint32, deps Resources, err error) { + // TODO are there any local resources that need to be passed to the executor and started as a server? + + // the proxyable resources are the Provider, which may or may not be local to the client process. (legacy vs loopp) + var ( + providerID uint32 + providerResource Resource + ) + if grpcProvider, ok := provider.(GRPCClientConn); ok { + // TODO: BCF-3061 ccip provider can create new services. the proxying needs to be augmented + // to intercept and route to the created services. also, need to prevent leaks. + providerID, providerResource, err = c.Serve("ExecProvider", proxy.NewProxy(grpcProvider.ClientConn())) + } else { + // loop client runs in the core node. if the provider is not a grpc client conn, then we are in legacy mode + // and need to serve all the required services locally. + providerID, providerResource, err = c.ServeNew("ExecProvider", func(s *grpc.Server) { + registerPluginProviderServices(s, provider) + registerCustomExecutionProviderServices(s, provider, c.BrokerExt) + }) + } + if err != nil { + return 0, nil, err + } + deps.Add(providerResource) + + resp, err := c.generator.NewExecutionFactory(ctx, &ccippb.NewExecutionFactoryRequest{ + ProviderServiceId: providerID, + }) + if err != nil { + return 0, nil, err + } + return resp.ExecutionFactoryServiceId, deps, nil + } + cc := c.NewClientConn("ExecutionFactory", newExecClientFn) + return newReportingPluginFactoryClient(c.BrokerExt, cc), nil +} + +func registerCustomExecutionProviderServices(s *grpc.Server, provider types.CCIPExecProvider, brokerExt *BrokerExt) { + // register the handler for the custom methods of the provider eg NewOffRampReader + ccippb.RegisterExecutionCustomHandlersServer(s, newExecProviderServer(provider, brokerExt)) +} + +// ExecutionLOOPServer is a server that runs the execution LOOP. +type ExecutionLOOPServer struct { + ccippb.UnimplementedExecutionFactoryGeneratorServer + + *BrokerExt + impl types.CCIPExecutionFactoryGenerator +} + +func RegisterExecutionLOOPServer(s *grpc.Server, b Broker, cfg BrokerConfig, impl types.CCIPExecutionFactoryGenerator) error { + ext := &BrokerExt{Broker: b, BrokerConfig: cfg} + ccippb.RegisterExecutionFactoryGeneratorServer(s, newExecutionLOOPServer(impl, ext)) + return nil +} + +func newExecutionLOOPServer(impl types.CCIPExecutionFactoryGenerator, b *BrokerExt) *ExecutionLOOPServer { + return &ExecutionLOOPServer{impl: impl, BrokerExt: b.WithName("ExecutionLOOPServer")} +} + +func (r *ExecutionLOOPServer) NewExecutionFactory(ctx context.Context, request *ccippb.NewExecutionFactoryRequest) (*ccippb.NewExecutionFactoryResponse, error) { + var err error + var deps Resources + defer func() { + if err != nil { + r.CloseAll(deps...) + } + }() + + // lookup the provider service + providerConn, err := r.Dial(request.ProviderServiceId) + if err != nil { + return nil, ErrConnDial{Name: "ExecProvider", ID: request.ProviderServiceId, Err: err} + } + deps.Add(Resource{providerConn, "ExecProvider"}) + provider := newExecProviderClient(r.BrokerExt, providerConn) + + // factory, err := r.impl.NewExecutionFactory(ctx, provider, execFactoryConfig(request.Config)) + factory, err := r.impl.NewExecutionFactory(ctx, provider) + if err != nil { + return nil, fmt.Errorf("failed to create new execution factory: %w", err) + } + + id, _, err := r.ServeNew("ExecutionFactory", func(s *grpc.Server) { + pb.RegisterServiceServer(s, &ServiceServer{Srv: factory}) + pb.RegisterReportingPluginFactoryServer(s, newReportingPluginFactoryServer(factory, r.BrokerExt)) + }, deps...) + if err != nil { + return nil, fmt.Errorf("failed to serve new execution factory: %w", err) + } + return &ccippb.NewExecutionFactoryResponse{ExecutionFactoryServiceId: id}, nil +} + +var ( + _ types.CCIPExecProvider = (*execProviderClient)(nil) + _ GRPCClientConn = (*execProviderClient)(nil) +) + +type execProviderClient struct { + *pluginProviderClient + + // must be shared with the server + *BrokerExt + grpcClient ccippb.ExecutionCustomHandlersClient +} + +func newExecProviderClient(b *BrokerExt, conn grpc.ClientConnInterface) *execProviderClient { + pluginProviderClient := newPluginProviderClient(b, conn) + grpc := ccippb.NewExecutionCustomHandlersClient(conn) + return &execProviderClient{ + pluginProviderClient: pluginProviderClient, + BrokerExt: b, + grpcClient: grpc, + } +} + +// NewCommitStoreReader implements types.CCIPExecProvider. +func (e *execProviderClient) NewCommitStoreReader(ctx context.Context, addr cciptypes.Address) (cciptypes.CommitStoreReader, error) { + panic("unimplemented") +} + +// NewOffRampReader implements types.CCIPExecProvider. +func (e *execProviderClient) NewOffRampReader(ctx context.Context, addr cciptypes.Address) (cciptypes.OffRampReader, error) { + req := ccippb.NewOffRampReaderRequest{Address: string(addr)} + + resp, err := e.grpcClient.NewOffRampReader(ctx, &req) + if err != nil { + return nil, err + } + // this works because the broker is shared and the id refers to a resource served by the broker + offRampConn, err := e.BrokerExt.Dial(uint32(resp.OfframpReaderServiceId)) + if err != nil { + return nil, fmt.Errorf("failed to lookup off ramp reader service at %d: %w", resp.OfframpReaderServiceId, err) + } + // need to wrap grpc offRamp into the desired interface + offRamp := ccipinternal.NewOffRampReaderClient(offRampConn) + + return offRamp, nil +} + +// NewOnRampReader implements types.CCIPExecProvider. +func (e *execProviderClient) NewOnRampReader(ctx context.Context, addr cciptypes.Address) (cciptypes.OnRampReader, error) { + req := ccippb.NewOnRampReaderRequest{Address: string(addr)} + + resp, err := e.grpcClient.NewOnRampReader(ctx, &req) + if err != nil { + return nil, err + } + // TODO BCF-3061: make this work for proxied relayer + // currently this only work for an embedded relayer + // because the broker is shared between the core node and relayer + // this effectively let us proxy connects to resources spawn by the embedded relay + // by hijacking the shared broker. id refers to a resource served by the shared broker + onRampConn, err := e.BrokerExt.Dial(uint32(resp.OnrampReaderServiceId)) + if err != nil { + return nil, fmt.Errorf("failed to lookup on ramp reader service at %d: %w", resp.OnrampReaderServiceId, err) + } + // need to wrap grpc onRamp into the desired interface + onRamp := ccipinternal.NewOnRampReaderClient(onRampConn) + + // how to convert resp to cciptypes.OnRampReader? i have an id and need to hydrate that into an instance of OnRampReader + return onRamp, err +} + +// NewPriceRegistryReader implements types.CCIPExecProvider. +func (e *execProviderClient) NewPriceRegistryReader(ctx context.Context, addr cciptypes.Address) (cciptypes.PriceRegistryReader, error) { + panic("unimplemented") +} + +// NewTokenDataReader implements types.CCIPExecProvider. +func (e *execProviderClient) NewTokenDataReader(ctx context.Context, tokenAddress cciptypes.Address) (cciptypes.TokenDataReader, error) { + panic("unimplemented") +} + +// NewTokenPoolBatchedReader implements types.CCIPExecProvider. +func (e *execProviderClient) NewTokenPoolBatchedReader(ctx context.Context) (cciptypes.TokenPoolBatchedReader, error) { + panic("unimplemented") +} + +// SourceNativeToken implements types.CCIPExecProvider. +func (e *execProviderClient) SourceNativeToken(ctx context.Context) (cciptypes.Address, error) { + panic("unimplemented") +} + +// execProviderServer is a server that wraps the custom methods of the types.CCIPExecProvider +// this is necessary because those method create new resources that need to be served by the broker +// when we are running in legacy mode. +type execProviderServer struct { + ccippb.UnimplementedExecutionCustomHandlersServer + // this has to be a shared pointer to the same impl as the client + *BrokerExt + impl types.CCIPExecProvider + + deps Resources +} + +func newExecProviderServer(impl types.CCIPExecProvider, brokerExt *BrokerExt) *execProviderServer { + return &execProviderServer{impl: impl, BrokerExt: brokerExt} +} + +func (e *execProviderServer) NewOffRampReader(ctx context.Context, req *ccippb.NewOffRampReaderRequest) (*ccippb.NewOffRampReaderResponse, error) { + reader, err := e.impl.NewOffRampReader(ctx, cciptypes.Address(req.Address)) + if err != nil { + return nil, err + } + // wrap the reader in a grpc server and serve it + srv := ccipinternal.NewOffRampReaderServer(reader) + // the id is handle to the broker, we will need it on the other sider to dial the resource + offRampID, offRampResource, err := e.ServeNew("OffRampReader", func(s *grpc.Server) { + ccippb.RegisterOffRampReaderServer(s, srv) + }) + if err != nil { + return nil, err + } + // TODO BCF-3067 LEAKS!!! + // this dependency needs to be closed when the offramp reader is closed, which + // should happen when the calling reporting plugin is closed/goes out of scope + e.deps.Add(offRampResource) + return &ccippb.NewOffRampReaderResponse{OfframpReaderServiceId: int32(offRampID)}, nil +} + +func (e *execProviderServer) NewOnRampReader(ctx context.Context, req *ccippb.NewOnRampReaderRequest) (*ccippb.NewOnRampReaderResponse, error) { + reader, err := e.impl.NewOnRampReader(ctx, cciptypes.Address(req.Address)) + if err != nil { + return nil, err + } + // wrap the reader in a grpc server and serve it + srv := ccipinternal.NewOnRampReaderServer(reader) + // the id is handle to the broker, we will need it on the other sider to dial the resource + onRampID, onRampResource, err := e.ServeNew("OnRampReader", func(s *grpc.Server) { + ccippb.RegisterOnRampReaderServer(s, srv) + }) + if err != nil { + return nil, err + } + // TODO BCF-3067 LEAKS!!! + // this dependency needs to be closed when the onramp reader is closed, which + // should happen when the calling reporting plugin is closed/goes out of scope + e.deps.Add(onRampResource) + return &ccippb.NewOnRampReaderResponse{OnrampReaderServiceId: int32(onRampID)}, nil +} diff --git a/pkg/loop/internal/chain_reader.go b/pkg/loop/internal/chain_reader.go index 9baccaa52..1c2105793 100644 --- a/pkg/loop/internal/chain_reader.go +++ b/pkg/loop/internal/chain_reader.go @@ -5,12 +5,11 @@ import ( jsonv1 "encoding/json" "fmt" + "github.com/fxamacker/cbor/v2" jsonv2 "github.com/go-json-experiment/json" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" - "github.com/fxamacker/cbor/v2" - "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb" "github.com/smartcontractkit/chainlink-common/pkg/types" ) @@ -18,7 +17,7 @@ import ( var _ types.ChainReader = (*chainReaderClient)(nil) // NewChainReaderTestClient is a test client for [types.ChainReader] -// internal users should instantiate a client directly and set all private fields +// internal users should instantiate a client directly and set all private fields. func NewChainReaderTestClient(conn *grpc.ClientConn) types.ChainReader { return &chainReaderClient{grpc: pb.NewChainReaderClient(conn)} } @@ -28,14 +27,14 @@ type chainReaderClient struct { grpc pb.ChainReaderClient } -// enum of all known encoding formats for versioned data +// enum of all known encoding formats for versioned data. const ( JSONEncodingVersion1 = iota JSONEncodingVersion2 CBOREncodingVersion ) -// Version to be used for encoding (version used for decoding is determined by data received) +// Version to be used for encoding (version used for decoding is determined by data received). const CurrentEncodingVersion = CBOREncodingVersion func EncodeVersionedBytes(data any, version uint32) (*pb.VersionedBytes, error) { diff --git a/pkg/loop/internal/client.go b/pkg/loop/internal/client.go index a34eaf13b..21b1f1ed7 100644 --- a/pkg/loop/internal/client.go +++ b/pkg/loop/internal/client.go @@ -17,7 +17,7 @@ import ( var _ grpc.ClientConnInterface = (*atomicClient)(nil) -// An atomicClient implements [grpc.ClientConnInterface] and is backed by a swappable [*grpc.ClientConn] +// An atomicClient implements [grpc.ClientConnInterface] and is backed by a swappable [*grpc.ClientConn]. type atomicClient struct { cc atomic.Pointer[grpc.ClientConn] } diff --git a/pkg/loop/internal/codec.go b/pkg/loop/internal/codec.go index fb44286d8..b4a692d26 100644 --- a/pkg/loop/internal/codec.go +++ b/pkg/loop/internal/codec.go @@ -12,7 +12,7 @@ import ( var _ types.Codec = (*codecClient)(nil) // NewCodecTestClient is a test client for [types.Codec] -// internal users should instantiate a client directly and set all private fields +// internal users should instantiate a client directly and set all private fields. func NewCodecTestClient(conn *grpc.ClientConn) types.Codec { return &codecClient{grpc: pb.NewCodecClient(conn)} } @@ -32,7 +32,6 @@ func (c *codecClient) Encode(ctx context.Context, item any, itemType string) ([] Params: versionedParams, ItemType: itemType, }) - if err != nil { return nil, wrapRPCErr(err) } diff --git a/pkg/loop/internal/config.go b/pkg/loop/internal/config.go index 213376b16..11016f3ac 100644 --- a/pkg/loop/internal/config.go +++ b/pkg/loop/internal/config.go @@ -4,16 +4,17 @@ import ( "context" "math" - "google.golang.org/grpc" - libocr "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "google.golang.org/grpc" "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb" "github.com/smartcontractkit/chainlink-common/pkg/types" ) -var _ types.ConfigProvider = (*configProviderClient)(nil) -var _ GRPCClientConn = (*configProviderClient)(nil) +var ( + _ types.ConfigProvider = (*configProviderClient)(nil) + _ GRPCClientConn = (*configProviderClient)(nil) +) type configProviderClient struct { *ServiceClient diff --git a/pkg/loop/internal/median.go b/pkg/loop/internal/median.go index 6325955e2..ea62837cf 100644 --- a/pkg/loop/internal/median.go +++ b/pkg/loop/internal/median.go @@ -8,15 +8,14 @@ import ( "time" "github.com/mwitkow/grpc-proxy/proxy" + "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + libocr "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" - libocr "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-common/pkg/logger" median_internal "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/median" "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb" diff --git a/pkg/loop/internal/median/test/median.go b/pkg/loop/internal/median/test/median.go index e865166d0..f7385f818 100644 --- a/pkg/loop/internal/median/test/median.go +++ b/pkg/loop/internal/median/test/median.go @@ -300,7 +300,7 @@ type staticReportCodec struct{} var _ testtypes.Evaluator[median.ReportCodec] = staticReportCodec{} var _ median.ReportCodec = staticReportCodec{} -// TODO remove hard coded values +// TODO BCF-3068 remove hard coded values, use the staticXXXConfig pattern elsewhere in the test framework func (s staticReportCodec) BuildReport(os []median.ParsedAttributedObservation) (libocr.Report, error) { if !assert.ObjectsAreEqual(pobs, os) { return nil, fmt.Errorf("expected observations %v but got %v", pobs, os) diff --git a/pkg/loop/internal/mercury.go b/pkg/loop/internal/mercury.go index b2b37b2a0..dcee3a55f 100644 --- a/pkg/loop/internal/mercury.go +++ b/pkg/loop/internal/mercury.go @@ -4,9 +4,8 @@ import ( "context" "fmt" - "google.golang.org/grpc" - "github.com/mwitkow/grpc-proxy/proxy" + "google.golang.org/grpc" "github.com/smartcontractkit/chainlink-common/pkg/logger" mercury_common_internal "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/mercury/common" @@ -43,7 +42,8 @@ func NewMercuryAdapterClient(broker Broker, brokerCfg BrokerConfig, conn *grpc.C } func (c *MercuryAdapterClient) NewMercuryV1Factory(ctx context.Context, - provider types.MercuryProvider, dataSource mercury_v1.DataSource) (types.MercuryPluginFactory, error) { + provider types.MercuryProvider, dataSource mercury_v1.DataSource, +) (types.MercuryPluginFactory, error) { // every time a new client is created, we have to ensure that all the external dependencies are satisfied. // at this layer of the stack, all of those dependencies are other gRPC services. // some of those services are hosted in the same process as the client itself and others may be remote. @@ -93,7 +93,8 @@ func (c *MercuryAdapterClient) NewMercuryV1Factory(ctx context.Context, } func (c *MercuryAdapterClient) NewMercuryV2Factory(ctx context.Context, - provider types.MercuryProvider, dataSource mercury_v2.DataSource) (types.MercuryPluginFactory, error) { + provider types.MercuryProvider, dataSource mercury_v2.DataSource, +) (types.MercuryPluginFactory, error) { // every time a new client is created, we have to ensure that all the external dependencies are satisfied. // at this layer of the stack, all of those dependencies are other gRPC services. // some of those services are hosted in the same process as the client itself and others may be remote. @@ -171,9 +172,11 @@ func (c *MercuryAdapterClient) NewMercuryV3Factory(ctx context.Context, providerID uint32 providerRes Resource ) + // loop mode; proxy to the relayer if grpcProvider, ok := provider.(GRPCClientConn); ok { providerID, providerRes, err = c.Serve("MercuryProvider", proxy.NewProxy(grpcProvider.ClientConn())) } else { + // legacy mode; serve the provider locally in the client process (ie the core node) providerID, providerRes, err = c.ServeNew("MercuryProvider", func(s *grpc.Server) { registerCommonServices(s, provider) @@ -345,7 +348,7 @@ func (ms *mercuryAdapterServer) NewMercuryV3Factory(ctx context.Context, req *me var ( _ types.MercuryProvider = (*mercuryProviderClient)(nil) - // in practice, inherited from pluginProviderClient + // in practice, inherited from pluginProviderClient. _ GRPCClientConn = (*mercuryProviderClient)(nil) ) diff --git a/pkg/loop/internal/mercury_reporting.go b/pkg/loop/internal/mercury_reporting.go index 589e34f53..de7f3b01f 100644 --- a/pkg/loop/internal/mercury_reporting.go +++ b/pkg/loop/internal/mercury_reporting.go @@ -4,12 +4,11 @@ import ( "context" "time" - "google.golang.org/grpc" - "google.golang.org/protobuf/types/known/emptypb" - "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" libocr "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb" mercurypb "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb/mercury" @@ -130,7 +129,7 @@ func (r *mercuryPluginClient) Observation(ctx context.Context, timestamp libocr. return response.Observation, nil } -// TODO: BCF-2887 plumb context through +// TODO: BCF-2887 plumb context through. func (r *mercuryPluginClient) Report(timestamp libocr.ReportTimestamp, previousReport libocr.Report, obs []libocr.AttributedObservation) (bool, libocr.Report, error) { response, err := r.grpc.Report(context.TODO(), &mercurypb.ReportRequest{ ReportTimestamp: pb.ReportTimestampToPb(timestamp), diff --git a/pkg/loop/internal/pb/ccip/exec_factory.pb.go b/pkg/loop/internal/pb/ccip/exec_factory.pb.go index a1265fa3a..b61f39bb9 100644 --- a/pkg/loop/internal/pb/ccip/exec_factory.pb.go +++ b/pkg/loop/internal/pb/ccip/exec_factory.pb.go @@ -9,6 +9,7 @@ package ccippb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" ) @@ -26,17 +27,8 @@ type NewExecutionFactoryRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OnrampReaderServiceId int32 `protobuf:"varint,1,opt,name=onramp_reader_service_id,json=onrampReaderServiceId,proto3" json:"onramp_reader_service_id,omitempty"` - OfframpReaderServiceId int32 `protobuf:"varint,2,opt,name=offramp_reader_service_id,json=offrampReaderServiceId,proto3" json:"offramp_reader_service_id,omitempty"` - CommitStoreServiceId int32 `protobuf:"varint,3,opt,name=commit_store_service_id,json=commitStoreServiceId,proto3" json:"commit_store_service_id,omitempty"` - SourcePriceRegistiryServiceId int32 `protobuf:"varint,4,opt,name=source_price_registiry_service_id,json=sourcePriceRegistiryServiceId,proto3" json:"source_price_registiry_service_id,omitempty"` - SourceWrappedNativeToken string `protobuf:"bytes,5,opt,name=source_wrapped_native_token,json=sourceWrappedNativeToken,proto3" json:"source_wrapped_native_token,omitempty"` // Address - // TODO BCF-2979: what is this? there is no type in the common repo for it. seems like it can be - // moved to an implementation detail of the ccipexec plugin - TokenDataWorkerServiceId int32 `protobuf:"varint,6,opt,name=token_data_worker_service_id,json=tokenDataWorkerServiceId,proto3" json:"token_data_worker_service_id,omitempty"` - DestChainSelector uint64 `protobuf:"varint,7,opt,name=dest_chain_selector,json=destChainSelector,proto3" json:"dest_chain_selector,omitempty"` - PriceRegistryServiceId int32 `protobuf:"varint,8,opt,name=price_registry_service_id,json=priceRegistryServiceId,proto3" json:"price_registry_service_id,omitempty"` - TokenPoolBatchReaderServiceId int32 `protobuf:"varint,9,opt,name=token_pool_batch_reader_service_id,json=tokenPoolBatchReaderServiceId,proto3" json:"token_pool_batch_reader_service_id,omitempty"` // TODO: there is a metrics collector defined in the corresponding config. what to do with it? + ProviderServiceId uint32 `protobuf:"varint,1,opt,name=provider_service_id,json=providerServiceId,proto3" json:"provider_service_id,omitempty"` + Config *ExecutionFactoryConfig `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` } func (x *NewExecutionFactoryRequest) Reset() { @@ -71,95 +63,261 @@ func (*NewExecutionFactoryRequest) Descriptor() ([]byte, []int) { return file_exec_factory_proto_rawDescGZIP(), []int{0} } -func (x *NewExecutionFactoryRequest) GetOnrampReaderServiceId() int32 { +func (x *NewExecutionFactoryRequest) GetProviderServiceId() uint32 { if x != nil { - return x.OnrampReaderServiceId + return x.ProviderServiceId } return 0 } -func (x *NewExecutionFactoryRequest) GetOfframpReaderServiceId() int32 { +func (x *NewExecutionFactoryRequest) GetConfig() *ExecutionFactoryConfig { if x != nil { - return x.OfframpReaderServiceId + return x.Config + } + return nil +} + +// NewExecutionFactoryResponse is a contains the id of the created execution factory +type NewExecutionFactoryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ExecutionFactoryServiceId uint32 `protobuf:"varint,1,opt,name=execution_factory_service_id,json=executionFactoryServiceId,proto3" json:"execution_factory_service_id,omitempty"` +} + +func (x *NewExecutionFactoryResponse) Reset() { + *x = NewExecutionFactoryResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_exec_factory_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *NewExecutionFactoryRequest) GetCommitStoreServiceId() int32 { +func (x *NewExecutionFactoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewExecutionFactoryResponse) ProtoMessage() {} + +func (x *NewExecutionFactoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_exec_factory_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NewExecutionFactoryResponse.ProtoReflect.Descriptor instead. +func (*NewExecutionFactoryResponse) Descriptor() ([]byte, []int) { + return file_exec_factory_proto_rawDescGZIP(), []int{1} +} + +func (x *NewExecutionFactoryResponse) GetExecutionFactoryServiceId() uint32 { if x != nil { - return x.CommitStoreServiceId + return x.ExecutionFactoryServiceId } return 0 } -func (x *NewExecutionFactoryRequest) GetSourcePriceRegistiryServiceId() int32 { +type ExecutionFactoryConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OnRampAddress string `protobuf:"bytes,1,opt,name=on_ramp_address,json=onRampAddress,proto3" json:"on_ramp_address,omitempty"` // ccip.Address + OffRampAddress string `protobuf:"bytes,2,opt,name=off_ramp_address,json=offRampAddress,proto3" json:"off_ramp_address,omitempty"` // ccip.Address + CommitStoreAddress string `protobuf:"bytes,3,opt,name=commit_store_address,json=commitStoreAddress,proto3" json:"commit_store_address,omitempty"` // ccip.Address + TokenReaderAddress string `protobuf:"bytes,4,opt,name=token_reader_address,json=tokenReaderAddress,proto3" json:"token_reader_address,omitempty"` // ccip.Address +} + +func (x *ExecutionFactoryConfig) Reset() { + *x = ExecutionFactoryConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_exec_factory_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecutionFactoryConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecutionFactoryConfig) ProtoMessage() {} + +func (x *ExecutionFactoryConfig) ProtoReflect() protoreflect.Message { + mi := &file_exec_factory_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecutionFactoryConfig.ProtoReflect.Descriptor instead. +func (*ExecutionFactoryConfig) Descriptor() ([]byte, []int) { + return file_exec_factory_proto_rawDescGZIP(), []int{2} +} + +func (x *ExecutionFactoryConfig) GetOnRampAddress() string { if x != nil { - return x.SourcePriceRegistiryServiceId + return x.OnRampAddress } - return 0 + return "" } -func (x *NewExecutionFactoryRequest) GetSourceWrappedNativeToken() string { +func (x *ExecutionFactoryConfig) GetOffRampAddress() string { if x != nil { - return x.SourceWrappedNativeToken + return x.OffRampAddress } return "" } -func (x *NewExecutionFactoryRequest) GetTokenDataWorkerServiceId() int32 { +func (x *ExecutionFactoryConfig) GetCommitStoreAddress() string { if x != nil { - return x.TokenDataWorkerServiceId + return x.CommitStoreAddress } - return 0 + return "" } -func (x *NewExecutionFactoryRequest) GetDestChainSelector() uint64 { +func (x *ExecutionFactoryConfig) GetTokenReaderAddress() string { if x != nil { - return x.DestChainSelector + return x.TokenReaderAddress } - return 0 + return "" +} + +// NewOnRampReaderRequest is a gRPC adapter for the input arguments of +type NewOnRampReaderRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *NewOnRampReaderRequest) Reset() { + *x = NewOnRampReaderRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_exec_factory_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewOnRampReaderRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (x *NewExecutionFactoryRequest) GetPriceRegistryServiceId() int32 { +func (*NewOnRampReaderRequest) ProtoMessage() {} + +func (x *NewOnRampReaderRequest) ProtoReflect() protoreflect.Message { + mi := &file_exec_factory_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NewOnRampReaderRequest.ProtoReflect.Descriptor instead. +func (*NewOnRampReaderRequest) Descriptor() ([]byte, []int) { + return file_exec_factory_proto_rawDescGZIP(), []int{3} +} + +func (x *NewOnRampReaderRequest) GetAddress() string { if x != nil { - return x.PriceRegistryServiceId + return x.Address } - return 0 + return "" +} + +// NewOnRampReaderResponse is a gRPC adapter for the output arguments of +type NewOnRampReaderResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OnrampReaderServiceId int32 `protobuf:"varint,1,opt,name=onramp_reader_service_id,json=onrampReaderServiceId,proto3" json:"onramp_reader_service_id,omitempty"` +} + +func (x *NewOnRampReaderResponse) Reset() { + *x = NewOnRampReaderResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_exec_factory_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewOnRampReaderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewOnRampReaderResponse) ProtoMessage() {} + +func (x *NewOnRampReaderResponse) ProtoReflect() protoreflect.Message { + mi := &file_exec_factory_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NewOnRampReaderResponse.ProtoReflect.Descriptor instead. +func (*NewOnRampReaderResponse) Descriptor() ([]byte, []int) { + return file_exec_factory_proto_rawDescGZIP(), []int{4} } -func (x *NewExecutionFactoryRequest) GetTokenPoolBatchReaderServiceId() int32 { +func (x *NewOnRampReaderResponse) GetOnrampReaderServiceId() int32 { if x != nil { - return x.TokenPoolBatchReaderServiceId + return x.OnrampReaderServiceId } return 0 } -// NewExecutionFactoryResponse is a contains the id of the created execution factory -type NewExecutionFactoryResponse struct { +// NewOffRampReaderRequest is a gRPC adapter for the input arguments of +type NewOffRampReaderRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ExecutionFactoryServiceId int32 `protobuf:"varint,1,opt,name=execution_factory_service_id,json=executionFactoryServiceId,proto3" json:"execution_factory_service_id,omitempty"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` } -func (x *NewExecutionFactoryResponse) Reset() { - *x = NewExecutionFactoryResponse{} +func (x *NewOffRampReaderRequest) Reset() { + *x = NewOffRampReaderRequest{} if protoimpl.UnsafeEnabled { - mi := &file_exec_factory_proto_msgTypes[1] + mi := &file_exec_factory_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *NewExecutionFactoryResponse) String() string { +func (x *NewOffRampReaderRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NewExecutionFactoryResponse) ProtoMessage() {} +func (*NewOffRampReaderRequest) ProtoMessage() {} -func (x *NewExecutionFactoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_exec_factory_proto_msgTypes[1] +func (x *NewOffRampReaderRequest) ProtoReflect() protoreflect.Message { + mi := &file_exec_factory_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -170,14 +328,62 @@ func (x *NewExecutionFactoryResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NewExecutionFactoryResponse.ProtoReflect.Descriptor instead. -func (*NewExecutionFactoryResponse) Descriptor() ([]byte, []int) { - return file_exec_factory_proto_rawDescGZIP(), []int{1} +// Deprecated: Use NewOffRampReaderRequest.ProtoReflect.Descriptor instead. +func (*NewOffRampReaderRequest) Descriptor() ([]byte, []int) { + return file_exec_factory_proto_rawDescGZIP(), []int{5} } -func (x *NewExecutionFactoryResponse) GetExecutionFactoryServiceId() int32 { +func (x *NewOffRampReaderRequest) GetAddress() string { if x != nil { - return x.ExecutionFactoryServiceId + return x.Address + } + return "" +} + +// NewOffRampReaderResponse is a gRPC adapter for the output arguments of +type NewOffRampReaderResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OfframpReaderServiceId int32 `protobuf:"varint,1,opt,name=offramp_reader_service_id,json=offrampReaderServiceId,proto3" json:"offramp_reader_service_id,omitempty"` +} + +func (x *NewOffRampReaderResponse) Reset() { + *x = NewOffRampReaderResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_exec_factory_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewOffRampReaderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewOffRampReaderResponse) ProtoMessage() {} + +func (x *NewOffRampReaderResponse) ProtoReflect() protoreflect.Message { + mi := &file_exec_factory_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NewOffRampReaderResponse.ProtoReflect.Descriptor instead. +func (*NewOffRampReaderResponse) Descriptor() ([]byte, []int) { + return file_exec_factory_proto_rawDescGZIP(), []int{6} +} + +func (x *NewOffRampReaderResponse) GetOfframpReaderServiceId() int32 { + if x != nil { + return x.OfframpReaderServiceId } return 0 } @@ -187,65 +393,90 @@ var File_exec_factory_proto protoreflect.FileDescriptor var file_exec_factory_proto_rawDesc = []byte{ 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x63, 0x63, 0x69, 0x70, 0x22, 0xc6, 0x04, 0x0a, 0x1a, - 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, - 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x18, 0x6f, 0x6e, - 0x72, 0x61, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x6f, 0x6e, + 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x63, 0x63, 0x69, 0x70, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x93, 0x01, 0x0a, 0x1a, 0x4e, 0x65, 0x77, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x45, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x63, 0x63, 0x69, 0x70, 0x2e, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x5e, + 0x0a, 0x1b, 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, + 0x1c, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x19, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x22, 0xce, + 0x01, 0x0a, 0x16, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0f, 0x6f, 0x6e, 0x5f, + 0x72, 0x61, 0x6d, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x6e, 0x52, 0x61, 0x6d, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x66, 0x66, 0x5f, 0x72, 0x61, 0x6d, 0x70, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x66, 0x66, + 0x52, 0x61, 0x6d, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, + 0x14, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, + 0x32, 0x0a, 0x16, 0x4e, 0x65, 0x77, 0x4f, 0x6e, 0x52, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x17, 0x4e, 0x65, 0x77, 0x4f, 0x6e, 0x52, 0x61, 0x6d, 0x70, + 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, + 0x0a, 0x18, 0x6f, 0x6e, 0x72, 0x61, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x15, 0x6f, 0x6e, 0x72, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x22, 0x33, 0x0a, 0x17, 0x4e, 0x65, 0x77, 0x4f, 0x66, + 0x66, 0x52, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x55, 0x0a, 0x18, + 0x4e, 0x65, 0x77, 0x4f, 0x66, 0x66, 0x52, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x19, 0x6f, 0x66, 0x66, 0x72, + 0x61, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x6f, 0x66, 0x66, 0x72, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x19, 0x6f, 0x66, 0x66, 0x72, 0x61, 0x6d, 0x70, 0x5f, 0x72, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x6f, 0x66, 0x66, 0x72, 0x61, 0x6d, 0x70, 0x52, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x35, - 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x14, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x48, 0x0a, 0x21, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x69, 0x72, 0x79, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x1d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x69, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, - 0x3d, 0x0a, 0x1b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, - 0x64, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x57, 0x72, 0x61, 0x70, - 0x70, 0x65, 0x64, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3e, - 0x0a, 0x1c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x77, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2e, - 0x0a, 0x13, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x64, 0x65, 0x73, - 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x39, - 0x0a, 0x19, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x16, 0x70, 0x72, 0x69, 0x63, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x49, 0x0a, 0x22, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x49, 0x64, 0x22, 0x5e, 0x0a, 0x1b, 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x1c, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x19, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x49, 0x64, 0x32, 0x99, 0x01, 0x0a, 0x19, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x12, 0x7c, 0x0a, 0x13, 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x31, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x63, 0x63, 0x69, - 0x70, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, - 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, - 0x63, 0x63, 0x69, 0x70, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, - 0x6d, 0x61, 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x63, 0x69, 0x70, 0x3b, 0x63, 0x63, 0x69, 0x70, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x49, 0x64, 0x32, 0x99, 0x01, 0x0a, 0x19, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x12, 0x7c, 0x0a, 0x13, 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x31, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x63, 0x63, 0x69, 0x70, + 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x63, + 0x63, 0x69, 0x70, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, + 0xbf, 0x02, 0x0a, 0x17, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x72, 0x0a, 0x0f, 0x4e, + 0x65, 0x77, 0x4f, 0x6e, 0x52, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x2d, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, + 0x62, 0x2e, 0x63, 0x63, 0x69, 0x70, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x6e, 0x52, 0x61, 0x6d, 0x70, + 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, + 0x2e, 0x63, 0x63, 0x69, 0x70, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x6e, 0x52, 0x61, 0x6d, 0x70, 0x52, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x75, 0x0a, 0x10, 0x4e, 0x65, 0x77, 0x4f, 0x66, 0x66, 0x52, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x12, 0x2e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x63, 0x63, 0x69, 0x70, 0x2e, 0x4e, 0x65, 0x77, 0x4f, + 0x66, 0x66, 0x52, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x63, 0x63, 0x69, 0x70, 0x2e, 0x4e, 0x65, 0x77, 0x4f, + 0x66, 0x66, 0x52, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x00, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x73, 0x6d, 0x61, 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, + 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x63, 0x69, 0x70, 0x3b, 0x63, 0x63, 0x69, 0x70, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -260,19 +491,32 @@ func file_exec_factory_proto_rawDescGZIP() []byte { return file_exec_factory_proto_rawDescData } -var file_exec_factory_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_exec_factory_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_exec_factory_proto_goTypes = []interface{}{ (*NewExecutionFactoryRequest)(nil), // 0: loop.internal.pb.ccip.NewExecutionFactoryRequest (*NewExecutionFactoryResponse)(nil), // 1: loop.internal.pb.ccip.NewExecutionFactoryResponse + (*ExecutionFactoryConfig)(nil), // 2: loop.internal.pb.ccip.ExecutionFactoryConfig + (*NewOnRampReaderRequest)(nil), // 3: loop.internal.pb.ccip.NewOnRampReaderRequest + (*NewOnRampReaderResponse)(nil), // 4: loop.internal.pb.ccip.NewOnRampReaderResponse + (*NewOffRampReaderRequest)(nil), // 5: loop.internal.pb.ccip.NewOffRampReaderRequest + (*NewOffRampReaderResponse)(nil), // 6: loop.internal.pb.ccip.NewOffRampReaderResponse + (*emptypb.Empty)(nil), // 7: google.protobuf.Empty } var file_exec_factory_proto_depIdxs = []int32{ - 0, // 0: loop.internal.pb.ccip.ExecutionFactoryGenerator.NewExecutionFactory:input_type -> loop.internal.pb.ccip.NewExecutionFactoryRequest - 1, // 1: loop.internal.pb.ccip.ExecutionFactoryGenerator.NewExecutionFactory:output_type -> loop.internal.pb.ccip.NewExecutionFactoryResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 2, // 0: loop.internal.pb.ccip.NewExecutionFactoryRequest.config:type_name -> loop.internal.pb.ccip.ExecutionFactoryConfig + 0, // 1: loop.internal.pb.ccip.ExecutionFactoryGenerator.NewExecutionFactory:input_type -> loop.internal.pb.ccip.NewExecutionFactoryRequest + 3, // 2: loop.internal.pb.ccip.ExecutionCustomHandlers.NewOnRampReader:input_type -> loop.internal.pb.ccip.NewOnRampReaderRequest + 5, // 3: loop.internal.pb.ccip.ExecutionCustomHandlers.NewOffRampReader:input_type -> loop.internal.pb.ccip.NewOffRampReaderRequest + 7, // 4: loop.internal.pb.ccip.ExecutionCustomHandlers.Close:input_type -> google.protobuf.Empty + 1, // 5: loop.internal.pb.ccip.ExecutionFactoryGenerator.NewExecutionFactory:output_type -> loop.internal.pb.ccip.NewExecutionFactoryResponse + 4, // 6: loop.internal.pb.ccip.ExecutionCustomHandlers.NewOnRampReader:output_type -> loop.internal.pb.ccip.NewOnRampReaderResponse + 6, // 7: loop.internal.pb.ccip.ExecutionCustomHandlers.NewOffRampReader:output_type -> loop.internal.pb.ccip.NewOffRampReaderResponse + 7, // 8: loop.internal.pb.ccip.ExecutionCustomHandlers.Close:output_type -> google.protobuf.Empty + 5, // [5:9] is the sub-list for method output_type + 1, // [1:5] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_exec_factory_proto_init() } @@ -305,6 +549,66 @@ func file_exec_factory_proto_init() { return nil } } + file_exec_factory_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecutionFactoryConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_exec_factory_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewOnRampReaderRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_exec_factory_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewOnRampReaderResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_exec_factory_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewOffRampReaderRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_exec_factory_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewOffRampReaderResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -312,9 +616,9 @@ func file_exec_factory_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_exec_factory_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 7, NumExtensions: 0, - NumServices: 1, + NumServices: 2, }, GoTypes: file_exec_factory_proto_goTypes, DependencyIndexes: file_exec_factory_proto_depIdxs, diff --git a/pkg/loop/internal/pb/ccip/exec_factory.proto b/pkg/loop/internal/pb/ccip/exec_factory.proto index 753003f41..6be0f9710 100644 --- a/pkg/loop/internal/pb/ccip/exec_factory.proto +++ b/pkg/loop/internal/pb/ccip/exec_factory.proto @@ -3,6 +3,7 @@ syntax = "proto3"; option go_package = "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb/ccip;ccippb"; package loop.internal.pb.ccip; +import "google/protobuf/empty.proto"; // ExecutionFactoryGenerator is a gRPC service that generates a execution factory // It is a gRPC service adapter for the https://github.com/smartcontractkit/ccip/core/services/ocr2/plugins/ccip/ccipexec/NewExecutionReportingPluginFactory @@ -12,21 +13,45 @@ service ExecutionFactoryGenerator { // NewExecutionFactoryRequest is a gRPC adapter to the factory configuration [https://github.com/smartcontractkit/ccip/core/services/ocr2/plugins/ccip/ccipexec/ExecutionPluginStaticConfig] message NewExecutionFactoryRequest { - int32 onramp_reader_service_id = 1; - int32 offramp_reader_service_id = 2; - int32 commit_store_service_id = 3; - int32 source_price_registiry_service_id = 4; - string source_wrapped_native_token = 5; // Address - // TODO BCF-2979: what is this? there is no type in the common repo for it. seems like it can be - // moved to an implementation detail of the ccipexec plugin - int32 token_data_worker_service_id = 6; - uint64 dest_chain_selector = 7; - int32 price_registry_service_id = 8; - int32 token_pool_batch_reader_service_id = 9; - // TODO: there is a metrics collector defined in the corresponding config. what to do with it? + uint32 provider_service_id = 1; + ExecutionFactoryConfig config = 2; } // NewExecutionFactoryResponse is a contains the id of the created execution factory message NewExecutionFactoryResponse { - int32 execution_factory_service_id = 1; + uint32 execution_factory_service_id = 1; +} + +message ExecutionFactoryConfig { + string on_ramp_address = 1; // ccip.Address + string off_ramp_address = 2; // ccip.Address + string commit_store_address = 3; // ccip.Address + string token_reader_address = 4; // ccip.Address +} + +service ExecutionCustomHandlers { + rpc NewOnRampReader(NewOnRampReaderRequest) returns (NewOnRampReaderResponse) {} + rpc NewOffRampReader(NewOffRampReaderRequest) returns (NewOffRampReaderResponse) {} + rpc Close(google.protobuf.Empty) returns (google.protobuf.Empty) {} + +} + +// NewOnRampReaderRequest is a gRPC adapter for the input arguments of +message NewOnRampReaderRequest { + string address = 1; +} + +// NewOnRampReaderResponse is a gRPC adapter for the output arguments of +message NewOnRampReaderResponse { + int32 onramp_reader_service_id = 1; +} + +// NewOffRampReaderRequest is a gRPC adapter for the input arguments of +message NewOffRampReaderRequest { + string address = 1; +} + +// NewOffRampReaderResponse is a gRPC adapter for the output arguments of +message NewOffRampReaderResponse { + int32 offramp_reader_service_id = 1; } \ No newline at end of file diff --git a/pkg/loop/internal/pb/ccip/exec_factory_grpc.pb.go b/pkg/loop/internal/pb/ccip/exec_factory_grpc.pb.go index f1c8e58b8..5ab37b14a 100644 --- a/pkg/loop/internal/pb/ccip/exec_factory_grpc.pb.go +++ b/pkg/loop/internal/pb/ccip/exec_factory_grpc.pb.go @@ -11,6 +11,7 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file @@ -108,3 +109,168 @@ var ExecutionFactoryGenerator_ServiceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "exec_factory.proto", } + +const ( + ExecutionCustomHandlers_NewOnRampReader_FullMethodName = "/loop.internal.pb.ccip.ExecutionCustomHandlers/NewOnRampReader" + ExecutionCustomHandlers_NewOffRampReader_FullMethodName = "/loop.internal.pb.ccip.ExecutionCustomHandlers/NewOffRampReader" + ExecutionCustomHandlers_Close_FullMethodName = "/loop.internal.pb.ccip.ExecutionCustomHandlers/Close" +) + +// ExecutionCustomHandlersClient is the client API for ExecutionCustomHandlers service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ExecutionCustomHandlersClient interface { + NewOnRampReader(ctx context.Context, in *NewOnRampReaderRequest, opts ...grpc.CallOption) (*NewOnRampReaderResponse, error) + NewOffRampReader(ctx context.Context, in *NewOffRampReaderRequest, opts ...grpc.CallOption) (*NewOffRampReaderResponse, error) + Close(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type executionCustomHandlersClient struct { + cc grpc.ClientConnInterface +} + +func NewExecutionCustomHandlersClient(cc grpc.ClientConnInterface) ExecutionCustomHandlersClient { + return &executionCustomHandlersClient{cc} +} + +func (c *executionCustomHandlersClient) NewOnRampReader(ctx context.Context, in *NewOnRampReaderRequest, opts ...grpc.CallOption) (*NewOnRampReaderResponse, error) { + out := new(NewOnRampReaderResponse) + err := c.cc.Invoke(ctx, ExecutionCustomHandlers_NewOnRampReader_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *executionCustomHandlersClient) NewOffRampReader(ctx context.Context, in *NewOffRampReaderRequest, opts ...grpc.CallOption) (*NewOffRampReaderResponse, error) { + out := new(NewOffRampReaderResponse) + err := c.cc.Invoke(ctx, ExecutionCustomHandlers_NewOffRampReader_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *executionCustomHandlersClient) Close(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, ExecutionCustomHandlers_Close_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ExecutionCustomHandlersServer is the server API for ExecutionCustomHandlers service. +// All implementations must embed UnimplementedExecutionCustomHandlersServer +// for forward compatibility +type ExecutionCustomHandlersServer interface { + NewOnRampReader(context.Context, *NewOnRampReaderRequest) (*NewOnRampReaderResponse, error) + NewOffRampReader(context.Context, *NewOffRampReaderRequest) (*NewOffRampReaderResponse, error) + Close(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + mustEmbedUnimplementedExecutionCustomHandlersServer() +} + +// UnimplementedExecutionCustomHandlersServer must be embedded to have forward compatible implementations. +type UnimplementedExecutionCustomHandlersServer struct { +} + +func (UnimplementedExecutionCustomHandlersServer) NewOnRampReader(context.Context, *NewOnRampReaderRequest) (*NewOnRampReaderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewOnRampReader not implemented") +} +func (UnimplementedExecutionCustomHandlersServer) NewOffRampReader(context.Context, *NewOffRampReaderRequest) (*NewOffRampReaderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewOffRampReader not implemented") +} +func (UnimplementedExecutionCustomHandlersServer) Close(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Close not implemented") +} +func (UnimplementedExecutionCustomHandlersServer) mustEmbedUnimplementedExecutionCustomHandlersServer() { +} + +// UnsafeExecutionCustomHandlersServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ExecutionCustomHandlersServer will +// result in compilation errors. +type UnsafeExecutionCustomHandlersServer interface { + mustEmbedUnimplementedExecutionCustomHandlersServer() +} + +func RegisterExecutionCustomHandlersServer(s grpc.ServiceRegistrar, srv ExecutionCustomHandlersServer) { + s.RegisterService(&ExecutionCustomHandlers_ServiceDesc, srv) +} + +func _ExecutionCustomHandlers_NewOnRampReader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewOnRampReaderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ExecutionCustomHandlersServer).NewOnRampReader(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ExecutionCustomHandlers_NewOnRampReader_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ExecutionCustomHandlersServer).NewOnRampReader(ctx, req.(*NewOnRampReaderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ExecutionCustomHandlers_NewOffRampReader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewOffRampReaderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ExecutionCustomHandlersServer).NewOffRampReader(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ExecutionCustomHandlers_NewOffRampReader_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ExecutionCustomHandlersServer).NewOffRampReader(ctx, req.(*NewOffRampReaderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ExecutionCustomHandlers_Close_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ExecutionCustomHandlersServer).Close(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ExecutionCustomHandlers_Close_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ExecutionCustomHandlersServer).Close(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +// ExecutionCustomHandlers_ServiceDesc is the grpc.ServiceDesc for ExecutionCustomHandlers service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ExecutionCustomHandlers_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "loop.internal.pb.ccip.ExecutionCustomHandlers", + HandlerType: (*ExecutionCustomHandlersServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "NewOnRampReader", + Handler: _ExecutionCustomHandlers_NewOnRampReader_Handler, + }, + { + MethodName: "NewOffRampReader", + Handler: _ExecutionCustomHandlers_NewOffRampReader_Handler, + }, + { + MethodName: "Close", + Handler: _ExecutionCustomHandlers_Close_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "exec_factory.proto", +} diff --git a/pkg/loop/internal/pb/ccip/onramp.proto b/pkg/loop/internal/pb/ccip/onramp.proto index 7a19dd4ce..35f94b5d0 100644 --- a/pkg/loop/internal/pb/ccip/onramp.proto +++ b/pkg/loop/internal/pb/ccip/onramp.proto @@ -6,6 +6,7 @@ package loop.internal.pb.ccip; import "google/protobuf/empty.proto"; import "models.proto"; + // OnRampReader is a gRPC service adapter for the interface // [github.com/smartcontractkit/chainlink-common/pkg/types/OnRampReader] service OnRampReader { diff --git a/pkg/loop/internal/pipeline_runner_test.go b/pkg/loop/internal/pipeline_runner_test.go index 546834fa0..ab145b2ff 100644 --- a/pkg/loop/internal/pipeline_runner_test.go +++ b/pkg/loop/internal/pipeline_runner_test.go @@ -6,10 +6,9 @@ import ( "testing" "time" - "google.golang.org/grpc" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/grpc" "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb" "github.com/smartcontractkit/chainlink-common/pkg/types" diff --git a/pkg/loop/internal/plugin_provider.go b/pkg/loop/internal/plugin_provider.go index d4cbe0200..5babf16dc 100644 --- a/pkg/loop/internal/plugin_provider.go +++ b/pkg/loop/internal/plugin_provider.go @@ -17,7 +17,7 @@ type pluginProviderClient struct { var _ types.PluginProvider = (*pluginProviderClient)(nil) -// in practice, inherited from configProviderClient +// in practice, inherited from configProviderClient. var _ GRPCClientConn = (*pluginProviderClient)(nil) func newPluginProviderClient(b *BrokerExt, cc grpc.ClientConnInterface) *pluginProviderClient { diff --git a/pkg/loop/internal/plugin_service.go b/pkg/loop/internal/plugin_service.go index 09de253bf..850c32aee 100644 --- a/pkg/loop/internal/plugin_service.go +++ b/pkg/loop/internal/plugin_service.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils" ) -const KeepAliveTickDuration = 5 * time.Second //TODO from config +const KeepAliveTickDuration = 5 * time.Second // TODO from config type grpcPlugin interface { plugin.Plugin diff --git a/pkg/loop/internal/relayer.go b/pkg/loop/internal/relayer.go index a14531b63..d4530290d 100644 --- a/pkg/loop/internal/relayer.go +++ b/pkg/loop/internal/relayer.go @@ -220,6 +220,17 @@ func (r *relayerClient) NewPluginProvider(ctx context.Context, rargs types.Relay return newPluginProviderClient(r.BrokerExt, cc), nil case string(types.Mercury): return newMercuryProviderClient(r.BrokerExt, cc), nil + case string(types.CCIPExecution): + // TODO BCF-3061 + // what do i do here? for the local embedded relayer, we are using the broker + // to share state so the the reporting plugin, as a client to the (embedded) relayer, + // calls the provider to get resources and then the provider calls the broker to serve them + // maybe the same mechanism can be used here, but we need very careful reference passing to + // ensure that this relayer client has the same broker as the server. that doesn't really + // even make sense to me because the relayer client will in the reporting plugin loop + // for now we return an error and test for the this error case + // return nil, fmt.Errorf("need to fix BCF-3061") + return newExecProviderClient(r.BrokerExt, cc), fmt.Errorf("need to fix BCF-3061") default: return nil, fmt.Errorf("provider type not supported: %s", rargs.ProviderType) } @@ -357,6 +368,12 @@ func (r *relayerServer) NewPluginProvider(ctx context.Context, request *pb.NewPl return nil, err } return &pb.NewPluginProviderReply{PluginProviderID: id}, nil + case string(types.CCIPExecution): + id, err := r.newExecProvider(ctx, relayArgs, pluginArgs) + if err != nil { + return nil, err + } + return &pb.NewPluginProviderReply{PluginProviderID: id}, nil } return nil, fmt.Errorf("provider type not supported: %s", relayArgs.ProviderType) } @@ -434,11 +451,9 @@ func (r *relayerServer) newMercuryProvider(ctx context.Context, relayArgs types. registerPluginProviderServices(s, provider) mercury_pb.RegisterOnchainConfigCodecServer(s, mercury_common_internal.NewOnchainConfigCodecServer(provider.OnchainConfigCodec())) - mercury_pb.RegisterReportCodecV1Server(s, mercury_common_internal.NewReportCodecV1Server(s, provider.ReportCodecV1())) mercury_pb.RegisterReportCodecV2Server(s, mercury_common_internal.NewReportCodecV2Server(s, provider.ReportCodecV2())) mercury_pb.RegisterReportCodecV3Server(s, mercury_common_internal.NewReportCodecV3Server(s, provider.ReportCodecV3())) - mercury_pb.RegisterServerFetcherServer(s, mercury_common_internal.NewServerFetcherServer(provider.MercuryServerFetcher())) mercury_pb.RegisterMercuryChainReaderServer(s, mercury_common_internal.NewChainReaderServer(provider.MercuryChainReader())) }, providerRes) @@ -449,6 +464,42 @@ func (r *relayerServer) newMercuryProvider(ctx context.Context, relayArgs types. return id, err } +func (r *relayerServer) newExecProvider(ctx context.Context, relayArgs types.RelayArgs, pluginArgs types.PluginArgs) (uint32, error) { + i, ok := r.impl.(CCIPExecProvider) + if !ok { + return 0, status.Error(codes.Unimplemented, fmt.Sprintf("ccip execution not supported by %T", r.impl)) + } + + provider, err := i.NewExecutionProvider(ctx, relayArgs, pluginArgs) + if err != nil { + return 0, err + } + err = provider.Start(ctx) + if err != nil { + return 0, err + } + const name = "CCIPExecutionProvider" + providerRes := Resource{Name: name, Closer: provider} + + id, _, err := r.ServeNew(name, func(s *grpc.Server) { + registerPluginProviderServices(s, provider) + // TODO BCF-3067 LEAKS!!! + // the execution provider can create resources. how to prevent a leak? + // here we are a stand alone server in relayer. there is no reporting plugin instance + // maybe the best thing to do is promote all the ccip interfaces to services + // and expect/force clients to close them + // maybe it's possible to pass reference via a context such that the reporting plugin could + // tell the provider to close all the resources it created on behalf of the plugin + // but this seems very intricate + registerCustomExecutionProviderServices(s, provider, r.BrokerExt) + }, providerRes) + if err != nil { + return 0, err + } + + return id, err +} + func (r *relayerServer) GetChainStatus(ctx context.Context, request *pb.GetChainStatusRequest) (*pb.GetChainStatusReply, error) { chain, err := r.impl.GetChainStatus(ctx) if err != nil { @@ -477,12 +528,13 @@ func (r *relayerServer) ListNodeStatuses(ctx context.Context, request *pb.ListNo } return &pb.ListNodeStatusesReply{Nodes: nodes, NextPageToken: nextPageToken, Total: int32(total)}, nil } + func (r *relayerServer) Transact(ctx context.Context, request *pb.TransactionRequest) (*emptypb.Empty, error) { return &emptypb.Empty{}, r.impl.Transact(ctx, request.From, request.To, request.Amount.Int(), request.BalanceCheck) } // RegisterStandAloneMedianProvider register the servers needed for a median plugin provider, -// this is a workaround to test the Node API on EVM until the EVM relayer is loopifyed +// this is a workaround to test the Node API on EVM until the EVM relayer is loopifyed. func RegisterStandAloneMedianProvider(s *grpc.Server, p types.MedianProvider) { registerPluginProviderServices(s, p) pb.RegisterReportCodecServer(s, &reportCodecServer{impl: p.ReportCodec()}) @@ -491,7 +543,7 @@ func RegisterStandAloneMedianProvider(s *grpc.Server, p types.MedianProvider) { } // RegisterStandAlonePluginProvider register the servers needed for a generic plugin provider, -// this is a workaround to test the Node API on EVM until the EVM relayer is loopifyed +// this is a workaround to test the Node API on EVM until the EVM relayer is loopifyed. func RegisterStandAlonePluginProvider(s *grpc.Server, p types.PluginProvider) { registerPluginProviderServices(s, p) } diff --git a/pkg/loop/internal/reporting.go b/pkg/loop/internal/reporting.go index 1e367c7d6..36cfee8c5 100644 --- a/pkg/loop/internal/reporting.go +++ b/pkg/loop/internal/reporting.go @@ -5,11 +5,10 @@ import ( "math" "time" - "google.golang.org/grpc" - "google.golang.org/protobuf/types/known/emptypb" - "github.com/smartcontractkit/libocr/commontypes" libocr "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/pb" ) @@ -21,7 +20,11 @@ type reportingPluginFactoryClient struct { } func newReportingPluginFactoryClient(b *BrokerExt, cc grpc.ClientConnInterface) *reportingPluginFactoryClient { - return &reportingPluginFactoryClient{b.WithName("ReportingPluginProviderClient"), NewServiceClient(b, cc), pb.NewReportingPluginFactoryClient(cc)} + return &reportingPluginFactoryClient{ + BrokerExt: b.WithName("ReportingPluginProviderClient"), + ServiceClient: NewServiceClient(b, cc), + grpc: pb.NewReportingPluginFactoryClient(cc), + } } func (r *reportingPluginFactoryClient) NewReportingPlugin(config libocr.ReportingPluginConfig) (libocr.ReportingPlugin, libocr.ReportingPluginInfo, error) { @@ -187,7 +190,6 @@ func (r *reportingPluginClient) ShouldTransmitAcceptedReport(ctx context.Context func (r *reportingPluginClient) Close() error { ctx, cancel := r.StopCtx() defer cancel() - _, err := r.grpc.Close(ctx, &emptypb.Empty{}) return err } diff --git a/pkg/loop/internal/service.go b/pkg/loop/internal/service.go index ebb440ce4..36a56b56f 100644 --- a/pkg/loop/internal/service.go +++ b/pkg/loop/internal/service.go @@ -14,8 +14,10 @@ import ( var ErrPluginUnavailable = errors.New("plugin unavailable") -var _ services.Service = (*ServiceClient)(nil) -var _ GRPCClientConn = (*ServiceClient)(nil) +var ( + _ services.Service = (*ServiceClient)(nil) + _ GRPCClientConn = (*ServiceClient)(nil) +) // ServiceClient is the base client implementation of a loop as a client to the core node or // to another loop that is proxied through the core node. @@ -68,7 +70,7 @@ func (s *ServiceClient) HealthReport() map[string]error { return hr } -// ClientConn implements GRPCClientConn interface +// ClientConn implements GRPCClientConn interface. func (s *ServiceClient) ClientConn() grpc.ClientConnInterface { return s.cc } var _ pb.ServiceServer = (*ServiceServer)(nil) diff --git a/pkg/loop/internal/telemetry.go b/pkg/loop/internal/telemetry.go index 3ef67c6c5..374f7dc6f 100644 --- a/pkg/loop/internal/telemetry.go +++ b/pkg/loop/internal/telemetry.go @@ -11,8 +11,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" ) -var _ types.TelemetryService = (*telemetryServiceClient)(nil) -var _ types.TelemetryClient = (*telemetryClient)(nil) +var ( + _ types.TelemetryService = (*telemetryServiceClient)(nil) + _ types.TelemetryClient = (*telemetryClient)(nil) +) type telemetryEndpoint struct { client types.TelemetryService @@ -34,7 +36,7 @@ type telemetryClient struct { types.TelemetryService } -// NewEndpoint generates a new monitoring endpoint, returns nil if one cannot be generated +// NewEndpoint generates a new monitoring endpoint, returns nil if one cannot be generated. func (t *telemetryClient) NewEndpoint(ctx context.Context, network string, chainID string, contractID string, telemetryType string) (types.TelemetryClientEndpoint, error) { if contractID == "" { return nil, errors.New("contractID cannot be empty") @@ -62,7 +64,7 @@ type telemetryServiceClient struct { grpc pb.TelemetryClient } -// Send sends payload to the desired endpoint based on network and chainID +// Send sends payload to the desired endpoint based on network and chainID. func (t *telemetryServiceClient) Send(ctx context.Context, network string, chainID string, contractID string, telemetryType string, payload []byte) error { if contractID == "" { return errors.New("contractID cannot be empty") diff --git a/pkg/loop/internal/test/cmd/main.go b/pkg/loop/internal/test/cmd/main.go index 3338467d1..17adb451f 100644 --- a/pkg/loop/internal/test/cmd/main.go +++ b/pkg/loop/internal/test/cmd/main.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/loop" + ccip_test "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/ccip/test" median_test "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/median/test" mercury_test "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/mercury/common/test" ocr3_test "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/ocr3/test" @@ -77,6 +78,7 @@ func main() { }, GRPCServer: grpcServer, }) + lggr.Info("Done serving relayer") os.Exit(0) case loop.PluginMedianName: @@ -150,6 +152,20 @@ func main() { lggr.Debugf("Done serving %s", loop.PluginMercuryName) os.Exit(0) + case loop.CCIPExecutionLOOPName: + lggr.Debugf("Starting %s", loop.CCIPExecutionLOOPName) + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: loop.PluginCCIPExecutionHandshakeConfig(), + Plugins: map[string]plugin.Plugin{ + loop.CCIPExecutionLOOPName: &loop.ExecutionLoop{ + PluginServer: ccip_test.ExecFactoryServer, + BrokerConfig: loop.BrokerConfig{Logger: lggr, StopCh: stopCh}}, + }, + GRPCServer: grpcServer, + }) + lggr.Debugf("Done serving %s", loop.CCIPExecutionLOOPName) + os.Exit(0) + case ocr3.PluginServiceName: plugin.Serve(&plugin.ServeConfig{ HandshakeConfig: reportingplugins.ReportingPluginHandshakeConfig(), diff --git a/pkg/loop/internal/test/relayer/relayer.go b/pkg/loop/internal/test/relayer/relayer.go index eca821418..9d821ac9e 100644 --- a/pkg/loop/internal/test/relayer/relayer.go +++ b/pkg/loop/internal/test/relayer/relayer.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/loop/internal" + ccip_test "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/ccip/test" median_test "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/median/test" mercury_common_test "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/mercury/common/test" testcore "github.com/smartcontractkit/chainlink-common/pkg/loop/internal/test/core" @@ -49,14 +50,16 @@ type nodeResponse struct { total int } type staticPluginRelayerConfig struct { - StaticChecks bool - relayArgs types.RelayArgs - pluginArgs types.PluginArgs - medianProvider testtypes.MedianProviderTester - agnosticProvider testtypes.PluginProviderTester - mercuryProvider mercury_common_test.MercuryProviderTester - configProvider testpluginprovider.ConfigProviderTester - // TODO: add other Provider testers + StaticChecks bool + relayArgs types.RelayArgs + pluginArgs types.PluginArgs + medianProvider testtypes.MedianProviderTester + agnosticProvider testtypes.PluginProviderTester + mercuryProvider mercury_common_test.MercuryProviderTester + executionProvider ccip_test.ExecProviderTester + configProvider testpluginprovider.ConfigProviderTester + // Note: add other Provider testers here when we implement them + // eg Functions, Automation, etc nodeRequest nodeRequest nodeResponse nodeResponse transactionRequest transactionRequest @@ -66,13 +69,14 @@ type staticPluginRelayerConfig struct { func NewRelayerTester(staticChecks bool) testtypes.RelayerTester { return staticPluginRelayer{ staticPluginRelayerConfig: staticPluginRelayerConfig{ - StaticChecks: staticChecks, - relayArgs: RelayArgs, - pluginArgs: PluginArgs, - medianProvider: median_test.MedianProvider, - mercuryProvider: mercury_common_test.MercuryProvider, - agnosticProvider: testpluginprovider.AgnosticProvider, - configProvider: testpluginprovider.ConfigProvider, + StaticChecks: staticChecks, + relayArgs: RelayArgs, + pluginArgs: PluginArgs, + medianProvider: median_test.MedianProvider, + mercuryProvider: mercury_common_test.MercuryProvider, + executionProvider: ccip_test.ExecutionProvider, + agnosticProvider: testpluginprovider.AgnosticProvider, + configProvider: testpluginprovider.ConfigProvider, nodeRequest: nodeRequest{ pageSize: 137, pageToken: "", @@ -168,6 +172,18 @@ func (s staticPluginRelayer) NewMercuryProvider(ctx context.Context, r types.Rel return s.mercuryProvider, nil } +func (s staticPluginRelayer) NewExecutionProvider(ctx context.Context, r types.RelayArgs, p types.PluginArgs) (types.CCIPExecProvider, error) { + if s.StaticChecks { + if !equalRelayArgs(r, ccip_test.ExecutionRelayArgs) { + return nil, fmt.Errorf("expected relay args:\n\t%v\nbut got:\n\t%v", mercury_common_test.RelayArgs, r) + } + if !reflect.DeepEqual(ccip_test.ExecutionPluginArgs, p) { + return nil, fmt.Errorf("expected plugin args %v but got %v", mercury_common_test.PluginArgs, p) + } + } + return s.executionProvider, nil +} + func (s staticPluginRelayer) NewLLOProvider(ctx context.Context, r types.RelayArgs, p types.PluginArgs) (types.LLOProvider, error) { return nil, errors.New("not implemented") } diff --git a/pkg/loop/internal/test/types/interfaces.go b/pkg/loop/internal/test/types/interfaces.go index 0de20250d..df31493db 100644 --- a/pkg/loop/internal/test/types/interfaces.go +++ b/pkg/loop/internal/test/types/interfaces.go @@ -84,8 +84,10 @@ type MedianProviderTester interface { type RelayerTester interface { internal.PluginRelayer internal.Relayer + // implements all the possible providers as a one-stop shop for testing internal.MercuryProvider internal.MedianProvider + internal.CCIPExecProvider AssertEqualer[loop.Relayer] } diff --git a/pkg/loop/internal/types.go b/pkg/loop/internal/types.go index 86bca17bc..2dfa62797 100644 --- a/pkg/loop/internal/types.go +++ b/pkg/loop/internal/types.go @@ -26,6 +26,10 @@ type AutomationProvider interface { NewAutomationProvider(context.Context, types.RelayArgs, types.PluginArgs) (types.AutomationProvider, error) } +type CCIPExecProvider interface { + NewExecutionProvider(context.Context, types.RelayArgs, types.PluginArgs) (types.CCIPExecProvider, error) +} + // Relayer extends [types.Relayer] and includes [context.Context]s. type Relayer interface { types.ChainService diff --git a/pkg/loop/plugin_median_test.go b/pkg/loop/plugin_median_test.go index 0d87c335f..fc830304f 100644 --- a/pkg/loop/plugin_median_test.go +++ b/pkg/loop/plugin_median_test.go @@ -22,12 +22,14 @@ func TestPluginMedian(t *testing.T) { t.Parallel() stopCh := newStopCh(t) - test.PluginTest(t, loop.PluginMedianName, - &loop.GRPCPluginMedian{ - PluginServer: median_test.MedianFactoryServer, - BrokerConfig: loop.BrokerConfig{Logger: logger.Test(t), StopCh: stopCh}, - }, - median_test.PluginMedian) + t.Run("no proxy", func(t *testing.T) { + test.PluginTest(t, loop.PluginMedianName, + &loop.GRPCPluginMedian{ + PluginServer: median_test.MedianFactoryServer, + BrokerConfig: loop.BrokerConfig{Logger: logger.Test(t), StopCh: stopCh}, + }, + median_test.PluginMedian) + }) t.Run("proxy", func(t *testing.T) { test.PluginTest(t, loop.PluginRelayerName, diff --git a/pkg/loop/plugin_relayer.go b/pkg/loop/plugin_relayer.go index d5d99ecd0..6f054870c 100644 --- a/pkg/loop/plugin_relayer.go +++ b/pkg/loop/plugin_relayer.go @@ -47,6 +47,7 @@ func (p *GRPCPluginRelayer) GRPCServer(broker *plugin.GRPCBroker, server *grpc.S } // GRPCClient implements [plugin.GRPCPlugin] and returns the pluginClient [types.PluginRelayer], updated with the new broker and conn. + func (p *GRPCPluginRelayer) GRPCClient(_ context.Context, broker *plugin.GRPCBroker, conn *grpc.ClientConn) (interface{}, error) { if p.pluginClient == nil { p.pluginClient = internal.NewPluginRelayerClient(broker, p.BrokerConfig, conn) diff --git a/pkg/types/ccip/priceregistry.go b/pkg/types/ccip/priceregistry.go index 7a383c526..467f34a15 100644 --- a/pkg/types/ccip/priceregistry.go +++ b/pkg/types/ccip/priceregistry.go @@ -50,3 +50,7 @@ type GasPriceUpdate struct { GasPrice TimestampUnixSec *big.Int } + +type PriceRegistryFactory interface { + NewPriceRegistryReader(ctx context.Context, addr Address) (PriceRegistryReader, error) +} diff --git a/pkg/types/provider_ccip.go b/pkg/types/provider_ccip.go index 07a937289..2f5295265 100644 --- a/pkg/types/provider_ccip.go +++ b/pkg/types/provider_ccip.go @@ -33,10 +33,18 @@ type CCIPCommitFactoryGenerator interface { NewCommitFactory(ctx context.Context, provider CCIPCommitProvider) (ReportingPluginFactory, error) } -type CCIPExecFactoryGenerator interface { - NewExecFactory(ctx context.Context, provider CCIPExecProvider) (ReportingPluginFactory, error) +type CCIPExecFactoryGeneratorConfig struct { + OnRampAddress ccip.Address + OffRampAddress ccip.Address + CommitStoreAddress ccip.Address + TokenReaderAddress ccip.Address +} + +type CCIPExecutionFactoryGenerator interface { + //NewExecutionFactory(ctx context.Context, provider CCIPExecProvider, config CCIPExecFactoryGeneratorConfig) (ReportingPluginFactory, error) + NewExecutionFactory(ctx context.Context, provider CCIPExecProvider) (ReportingPluginFactory, error) } type CCIPFactoryGenerator interface { CCIPCommitFactoryGenerator - CCIPExecFactoryGenerator + CCIPExecutionFactoryGenerator } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 9a98ea4b9..6be8e3df1 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -29,7 +29,7 @@ func WithJitter(d time.Duration) time.Duration { // receives or is closed. // When channel closes, the ctx.Err() will always be context.Canceled // NOTE: Spins up a goroutine that exits on cancellation. -// REMEMBER TO CALL CANCEL OTHERWISE IT CAN LEAD TO MEMORY LEAKS +// REMEMBER TO CALL CANCEL OTHERWISE IT CAN LEAD TO MEMORY BCF-3067 LEAKS func ContextFromChan(chStop <-chan struct{}) (context.Context, context.CancelFunc) { return services.StopRChan(chStop).NewCtx() }