diff --git a/cmd/connector/config/config.toml b/cmd/connector/config/config.toml index 81a30b3..bacfc1d 100644 --- a/cmd/connector/config/config.toml +++ b/cmd/connector/config/config.toml @@ -30,7 +30,7 @@ [DataPool] # Should be smaller then PruningWindow - MaxDelta = 10 + MaxDelta = 100 PruningWindow = 1000 # Defines the number of active persisters to keep open @@ -50,5 +50,4 @@ MaxOpenFiles = 10 [GRPC] - Enable = true URL = "localhost:8000" diff --git a/cmd/connector/flags.go b/cmd/connector/flags.go index 60d9016..007cc45 100644 --- a/cmd/connector/flags.go +++ b/cmd/connector/flags.go @@ -28,4 +28,9 @@ var ( Usage: "Option for specifying db mode. Available options: `full-persister`, `import-db`, `optimized-persister`", Value: "full-persister", } + + enableGrpcServer = cli.BoolFlag{ + Name: "enable-grpc-server", + Usage: "Option for enabling grpc server", + } ) diff --git a/cmd/connector/main.go b/cmd/connector/main.go index 9742bd7..cbe28b2 100644 --- a/cmd/connector/main.go +++ b/cmd/connector/main.go @@ -76,7 +76,10 @@ func startConnector(ctx *cli.Context) error { dbMode := ctx.GlobalString(dbMode.Name) log.Info("storer sync mode", "dbMode", dbMode) - connectorRunner, err := connector.NewConnectorRunner(cfg, dbMode) + enableGrpcServer := ctx.GlobalBool(enableGrpcServer.Name) + log.Info("grpc server enabled", "enableGrpcServer", enableGrpcServer) + + connectorRunner, err := connector.NewConnectorRunner(cfg, dbMode, enableGrpcServer) if err != nil { return fmt.Errorf("cannot create connector runner, error: %w", err) } diff --git a/config/config.go b/config/config.go index b895453..af9b822 100644 --- a/config/config.go +++ b/config/config.go @@ -53,6 +53,5 @@ type DBConfig struct { // GRPCConfig will map the gRPC server configuration type GRPCConfig struct { - Enable bool - URL string + URL string } diff --git a/connector/connectorRunner.go b/connector/connectorRunner.go index a3a9663..461cdb0 100644 --- a/connector/connectorRunner.go +++ b/connector/connectorRunner.go @@ -6,7 +6,6 @@ import ( "os" "os/signal" "syscall" - "time" "github.com/multiversx/mx-chain-core-go/marshal" logger "github.com/multiversx/mx-chain-logger-go" @@ -22,25 +21,28 @@ var log = logger.GetOrCreate("connectorRunner") var ErrNilConfig = errors.New("nil configs provided") type connectorRunner struct { - config *config.Config - dbMode string + config *config.Config + dbMode string + enableGrpcServer bool } // NewConnectorRunner will create a new connector runner instance -func NewConnectorRunner(cfg *config.Config, dbMode string) (*connectorRunner, error) { +func NewConnectorRunner(cfg *config.Config, dbMode string, enableGrpcServer bool) (*connectorRunner, error) { if cfg == nil { return nil, ErrNilConfig } return &connectorRunner{ - config: cfg, - dbMode: dbMode, + config: cfg, + dbMode: dbMode, + enableGrpcServer: enableGrpcServer, }, nil } // Run will trigger connector service func (cr *connectorRunner) Run() error { protoMarshaller := &marshal.GogoProtoMarshalizer{} + converter := process.NewOutportBlockConverter() blockContainer, err := factory.CreateBlockContainer() if err != nil { @@ -57,30 +59,14 @@ func (cr *connectorRunner) Run() error { return err } - dataAggregator, err := process.NewDataAggregator(outportBlocksPool) + dataAggregator, err := process.NewDataAggregator(outportBlocksPool, converter) if err != nil { return err } - var ( - server *factory.GRPCServer - writer process.Writer - ) - if cr.config.GRPC.Enable { - writer = &fakeWriter{} - handler, err := process.NewGRPCBlocksHandler(outportBlocksPool, dataAggregator) - if err != nil { - return fmt.Errorf("couldn't create grpc blocks handler, error: %w", err) - } - server = factory.NewServer(cr.config.GRPC, handler) - - go func() { - if err := server.Start(); err != nil { - log.Error("couldn't start grpc server", "error", err) - } - }() - } else { - writer = os.Stdout + s, writer, err := factory.CreateGRPCServer(cr.enableGrpcServer, cr.config.GRPC, outportBlocksPool, dataAggregator) + if err != nil { + return err } publisher, err := process.NewFirehosePublisher( @@ -121,27 +107,9 @@ func (cr *connectorRunner) Run() error { log.Error(err.Error()) } - if server != nil { - server.Stop() + if s != nil { + s.Close() } return err } - -type fakeWriter struct { - err error - duration time.Duration -} - -func (f *fakeWriter) Write(p []byte) (int, error) { - time.Sleep(f.duration) - if f.err != nil { - return 0, f.err - } - - return len(p), nil -} - -func (f *fakeWriter) Close() error { - return nil -} diff --git a/data/hyperOutportBlocks/type.go b/data/hyperOutportBlocks/type.go deleted file mode 100644 index 4944bb9..0000000 --- a/data/hyperOutportBlocks/type.go +++ /dev/null @@ -1,29 +0,0 @@ -package hyperOutportBlocks - -// FieldsGetter used to retrieve common fields from data.ShardOutportBlock and data.MetaOutportBlock -type FieldsGetter interface { - GetShardID() uint32 - GetTransactionPool() *TransactionPool - GetHeaderGasConsumption() *HeaderGasConsumption - GetAlteredAccounts() map[string]*AlteredAccount - GetNotarizedHeadersHashes() []string - GetNumberOfShards() uint32 - GetSignersIndexes() []uint64 - GetHighestFinalBlockNonce() uint64 - GetHighestFinalBlockHash() []byte -} - -// BlockDataGetter used to retrieve common fields from data.BlockData and data.MetaBlockData -type BlockDataGetter interface { - GetShardID() uint32 - GetHeaderType() string - GetHeaderHash() []byte - GetBody() *Body - GetIntraShardMiniBlocks() []*MiniBlock - GetScheduledRootHash() []byte - GetScheduledAccumulatedFees() []byte - GetScheduledDeveloperFees() []byte - GetScheduledGasProvided() uint64 - GetScheduledGasPenalized() uint64 - GetScheduledGasRefunded() uint64 -} diff --git a/factory/grpcServerFactory.go b/factory/grpcServerFactory.go index 2aefcb1..ac6e85a 100644 --- a/factory/grpcServerFactory.go +++ b/factory/grpcServerFactory.go @@ -2,48 +2,54 @@ package factory import ( "fmt" - "net" - - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" + "os" + "time" "github.com/multiversx/mx-chain-ws-connector-template-go/config" - data "github.com/multiversx/mx-chain-ws-connector-template-go/data/hyperOutportBlocks" "github.com/multiversx/mx-chain-ws-connector-template-go/process" - "github.com/multiversx/mx-chain-ws-connector-template-go/service/hyperOutportBlock" + "github.com/multiversx/mx-chain-ws-connector-template-go/server" ) -type GRPCServer struct { - server *grpc.Server - config config.GRPCConfig -} +// CreateGRPCServer will create the gRPC server along with the required handlers. +func CreateGRPCServer( + enableGrpcServer bool, + cfg config.GRPCConfig, + outportBlocksPool process.DataPool, + dataAggregator process.DataAggregator) (process.GRPCServer, process.Writer, error) { + if !enableGrpcServer { + return nil, os.Stdout, nil + } -// NewServer instantiates the underlying grpc server handling rpc requests. -func NewServer(config config.GRPCConfig, blocksHandler process.GRPCBlocksHandler) *GRPCServer { - s := grpc.NewServer() + handler, err := process.NewGRPCBlocksHandler(outportBlocksPool, dataAggregator) + if err != nil { + return nil, nil, fmt.Errorf("failed to create grpc blocks handler: %w", err) + } + s := server.New(cfg, handler) + err = s.Start() + if err != nil { + return nil, nil, fmt.Errorf("failed to start grpc server: %w", err) + } - service := hyperOutportBlock.NewService(blocksHandler) - data.RegisterHyperOutportBlockServiceServer(s, service) - reflection.Register(s) + return s, &fakeWriter{}, nil - return &GRPCServer{s, config} } -// Start will start the grpc server on the configured URL. -func (s *GRPCServer) Start() error { - lis, err := net.Listen("tcp", s.config.URL) - if err != nil { - return fmt.Errorf("failed to listen: %v", err) - } +type fakeWriter struct { + err error + duration time.Duration +} - if err = s.server.Serve(lis); err != nil { - return fmt.Errorf("failed to serve: %v", err) +// Write is a mock writer. +func (f *fakeWriter) Write(p []byte) (int, error) { + time.Sleep(f.duration) + if f.err != nil { + return 0, f.err } - return nil + return len(p), nil } -// Stop will gracefully stop the grpc server. -func (s *GRPCServer) Stop() { - s.server.GracefulStop() +// Close is mock closer. +func (f *fakeWriter) Close() error { + return nil } diff --git a/process/dataAggregator.go b/process/dataAggregator.go index 4c7a3b9..8610950 100644 --- a/process/dataAggregator.go +++ b/process/dataAggregator.go @@ -19,14 +19,18 @@ type dataAggregator struct { // NewDataAggregator will create a new data aggregator instance func NewDataAggregator( blocksPool DataPool, + converter OutportBlockConverter, ) (*dataAggregator, error) { if check.IfNil(blocksPool) { return nil, ErrNilBlocksPool } + if check.IfNil(converter) { + return nil, ErrNilOutportBlocksConverter + } return &dataAggregator{ blocksPool: blocksPool, - converter: NewOutportBlockConverter(), + converter: converter, }, nil } diff --git a/process/dataAggregator_test.go b/process/dataAggregator_test.go index 9d6e9ed..1e5acf2 100644 --- a/process/dataAggregator_test.go +++ b/process/dataAggregator_test.go @@ -17,7 +17,7 @@ func TestNewDataAggregator(t *testing.T) { t.Run("nil blocks pool", func(t *testing.T) { t.Parallel() - da, err := process.NewDataAggregator(nil) + da, err := process.NewDataAggregator(nil, nil) require.Nil(t, da) require.Equal(t, process.ErrNilBlocksPool, err) }) @@ -25,7 +25,7 @@ func TestNewDataAggregator(t *testing.T) { t.Run("should work", func(t *testing.T) { t.Parallel() - da, err := process.NewDataAggregator(&testscommon.BlocksPoolStub{}) + da, err := process.NewDataAggregator(&testscommon.BlocksPoolStub{}, process.NewOutportBlockConverter()) require.Nil(t, err) require.False(t, da.IsInterfaceNil()) }) @@ -39,7 +39,7 @@ func TestDataAggregator_ProcessHyperBlock(t *testing.T) { blocksPoolStub := &testscommon.BlocksPoolStub{} - da, err := process.NewDataAggregator(blocksPoolStub) + da, err := process.NewDataAggregator(blocksPoolStub, process.NewOutportBlockConverter()) require.Nil(t, err) shardOutportBlock := createOutportBlock() @@ -65,7 +65,7 @@ func TestDataAggregator_ProcessHyperBlock(t *testing.T) { expectedResult, err := converter.HandleShardOutportBlock(shardOutportBlock) require.NoError(t, err) - da, err := process.NewDataAggregator(blocksPoolStub) + da, err := process.NewDataAggregator(blocksPoolStub, process.NewOutportBlockConverter()) require.Nil(t, err) outportBlock := createMetaOutportBlock() diff --git a/process/errors.go b/process/errors.go index a8c1a07..6ce3354 100644 --- a/process/errors.go +++ b/process/errors.go @@ -20,6 +20,9 @@ var ErrNilPublisher = errors.New("nil publisher provided") // ErrNilBlocksPool signals that a nil blocks pool was provided var ErrNilBlocksPool = errors.New("nil blocks pool provided") +// ErrNilOutportBlocksConverter signals that a nil blocks pool was provided +var ErrNilOutportBlocksConverter = errors.New("nil outport blocks converter provided") + // ErrNilDataAggregator signals that a nil data aggregator was provided var ErrNilDataAggregator = errors.New("nil data aggregator provided") diff --git a/process/fakeWriter.go b/process/fakeWriter.go new file mode 100644 index 0000000..f2ef9d2 --- /dev/null +++ b/process/fakeWriter.go @@ -0,0 +1 @@ +package process diff --git a/process/interface.go b/process/interface.go index e469196..196d548 100644 --- a/process/interface.go +++ b/process/interface.go @@ -81,6 +81,7 @@ type PruningStorer interface { type OutportBlockConverter interface { HandleShardOutportBlock(outportBlock *outport.OutportBlock) (*data.ShardOutportBlock, error) HandleMetaOutportBlock(outportBlock *outport.OutportBlock) (*data.MetaOutportBlock, error) + IsInterfaceNil() bool } // GRPCBlocksHandler defines the behaviour of handling block via gRPC @@ -88,3 +89,8 @@ type GRPCBlocksHandler interface { FetchHyperBlockByHash(hash []byte) (*data.HyperOutportBlock, error) FetchHyperBlockByNonce(nonce uint64) (*data.HyperOutportBlock, error) } + +type GRPCServer interface { + Start() error + Close() +} diff --git a/process/outportBlockConverter.go b/process/outportBlockConverter.go index bd6f585..ca14093 100644 --- a/process/outportBlockConverter.go +++ b/process/outportBlockConverter.go @@ -172,3 +172,8 @@ func (o *outportBlockConverter) castBigInt(i *big.Int) ([]byte, error) { return buf, err } + +// IsInterfaceNil returns nil if there is no value under the interface +func (o *outportBlockConverter) IsInterfaceNil() bool { + return o == nil +} diff --git a/process/outportBlockConverter_test.go b/process/outportBlockConverter_test.go index 6f28616..e23510b 100644 --- a/process/outportBlockConverter_test.go +++ b/process/outportBlockConverter_test.go @@ -11,9 +11,35 @@ import ( "github.com/multiversx/mx-chain-core-go/marshal" "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-ws-connector-template-go/data/hyperOutportBlocks" + data "github.com/multiversx/mx-chain-ws-connector-template-go/data/hyperOutportBlocks" ) +type fieldsGetter interface { + GetShardID() uint32 + GetTransactionPool() *data.TransactionPool + GetHeaderGasConsumption() *data.HeaderGasConsumption + GetAlteredAccounts() map[string]*data.AlteredAccount + GetNotarizedHeadersHashes() []string + GetNumberOfShards() uint32 + GetSignersIndexes() []uint64 + GetHighestFinalBlockNonce() uint64 + GetHighestFinalBlockHash() []byte +} + +type blockDataGetter interface { + GetShardID() uint32 + GetHeaderType() string + GetHeaderHash() []byte + GetBody() *data.Body + GetIntraShardMiniBlocks() []*data.MiniBlock + GetScheduledRootHash() []byte + GetScheduledAccumulatedFees() []byte + GetScheduledDeveloperFees() []byte + GetScheduledGasProvided() uint64 + GetScheduledGasPenalized() uint64 + GetScheduledGasRefunded() uint64 +} + const ( outportBlockHeaderV1JSONPath = "../testscommon/testdata/outportBlockHeaderV1.json" outportBlockHeaderV2JSONPath = "../testscommon/testdata/outportBlockHeaderV2.json" @@ -101,7 +127,7 @@ func TestMetaBlockConverter(t *testing.T) { checkBlockData(t, ob.BlockData, metaOutportBlock.BlockData) } -func checkHeaderV1(t *testing.T, header *block.Header, fireOutportBlock *hyperOutportBlocks.ShardOutportBlock) { +func checkHeaderV1(t *testing.T, header *block.Header, fireOutportBlock *data.ShardOutportBlock) { // Block data - Header. require.Equal(t, header.Nonce, fireOutportBlock.BlockData.Header.Nonce) require.Equal(t, header.PrevHash, fireOutportBlock.BlockData.Header.PrevHash) @@ -143,7 +169,7 @@ func checkHeaderV1(t *testing.T, header *block.Header, fireOutportBlock *hyperOu } } -func checkHeaderV2(t *testing.T, headerV2 *block.HeaderV2, fireOutportBlock *hyperOutportBlocks.ShardOutportBlock) { +func checkHeaderV2(t *testing.T, headerV2 *block.HeaderV2, fireOutportBlock *data.ShardOutportBlock) { // Block data - Header. header := headerV2.Header require.Equal(t, header.Nonce, fireOutportBlock.BlockData.Header.Nonce) @@ -193,7 +219,7 @@ func checkHeaderV2(t *testing.T, headerV2 *block.HeaderV2, fireOutportBlock *hyp require.Equal(t, headerV2.ScheduledGasRefunded, fireOutportBlock.BlockData.ScheduledGasRefunded) } -func checkHeaderMeta(t *testing.T, header *block.MetaBlock, fireOutportBlock *hyperOutportBlocks.MetaOutportBlock) { +func checkHeaderMeta(t *testing.T, header *block.MetaBlock, fireOutportBlock *data.MetaOutportBlock) { require.Equal(t, header.Nonce, fireOutportBlock.BlockData.Header.Nonce) require.Equal(t, header.Epoch, fireOutportBlock.BlockData.Header.Epoch) require.Equal(t, header.Round, fireOutportBlock.BlockData.Header.Round) @@ -295,7 +321,7 @@ func checkHeaderMeta(t *testing.T, header *block.MetaBlock, fireOutportBlock *hy require.Equal(t, header.Reserved, fireOutportBlock.BlockData.Header.Reserved) } -func checkFields(t *testing.T, outportBlock *outport.OutportBlock, fireOutportBlock hyperOutportBlocks.FieldsGetter) { +func checkFields(t *testing.T, outportBlock *outport.OutportBlock, fireOutportBlock fieldsGetter) { // Asserting values. require.Equal(t, outportBlock.ShardID, fireOutportBlock.GetShardID()) require.Equal(t, outportBlock.NotarizedHeadersHashes, fireOutportBlock.GetNotarizedHeadersHashes()) @@ -485,7 +511,7 @@ func checkFields(t *testing.T, outportBlock *outport.OutportBlock, fireOutportBl } } -func checkBlockData(t *testing.T, blockData *outport.BlockData, getter hyperOutportBlocks.BlockDataGetter) { +func checkBlockData(t *testing.T, blockData *outport.BlockData, getter blockDataGetter) { require.Equal(t, blockData.ShardID, getter.GetShardID()) require.Equal(t, blockData.HeaderType, getter.GetHeaderType()) require.Equal(t, blockData.HeaderHash, getter.GetHeaderHash()) @@ -494,7 +520,7 @@ func checkBlockData(t *testing.T, blockData *outport.BlockData, getter hyperOutp require.Equal(t, miniBlock.TxHashes, getter.GetBody().MiniBlocks[i].TxHashes) require.Equal(t, miniBlock.ReceiverShardID, getter.GetBody().MiniBlocks[i].ReceiverShardID) require.Equal(t, miniBlock.SenderShardID, getter.GetBody().MiniBlocks[i].SenderShardID) - require.Equal(t, hyperOutportBlocks.Type(miniBlock.Type), getter.GetBody().MiniBlocks[i].Type) + require.Equal(t, data.Type(miniBlock.Type), getter.GetBody().MiniBlocks[i].Type) require.Equal(t, miniBlock.Reserved, getter.GetBody().MiniBlocks[i].Reserved) } diff --git a/server/grpcServer.go b/server/grpcServer.go new file mode 100644 index 0000000..2cbaa41 --- /dev/null +++ b/server/grpcServer.go @@ -0,0 +1,104 @@ +package server + +import ( + "context" + "errors" + "fmt" + "net" + "sync" + + logger "github.com/multiversx/mx-chain-logger-go" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" + + "github.com/multiversx/mx-chain-ws-connector-template-go/config" + data "github.com/multiversx/mx-chain-ws-connector-template-go/data/hyperOutportBlocks" + "github.com/multiversx/mx-chain-ws-connector-template-go/process" + "github.com/multiversx/mx-chain-ws-connector-template-go/service/hyperOutportBlock" +) + +var ( + log = logger.GetOrCreate("server") + errServerStarted = errors.New("server is already started") +) + +type grpcServer struct { + server *grpc.Server + config config.GRPCConfig + + cancelFunc func() + closeChan chan struct{} + mutState sync.RWMutex +} + +// New instantiates the underlying grpc server handling rpc requests. +func New(config config.GRPCConfig, blocksHandler process.GRPCBlocksHandler) *grpcServer { + s := grpc.NewServer() + + service := hyperOutportBlock.NewService(blocksHandler) + data.RegisterHyperOutportBlockServiceServer(s, service) + reflection.Register(s) + + return &grpcServer{ + server: s, + config: config, + closeChan: make(chan struct{}), + } +} + +// Start will start the grpc server on the configured URL. +func (s *grpcServer) Start() error { + s.mutState.Lock() + defer s.mutState.Unlock() + + if s.cancelFunc != nil { + return errServerStarted + } + + var ( + ctx context.Context + err error + ) + ctx, s.cancelFunc = context.WithCancel(context.Background()) + + go func() { + err = s.run(ctx) + if err != nil { + log.Error("failed to serve server", "err", err) + return + } + }() + + return err +} + +func (s *grpcServer) run(ctx context.Context) error { + for { + select { + case <-ctx.Done(): + s.server.GracefulStop() + + default: + lis, err := net.Listen("tcp", s.config.URL) + if err != nil { + return fmt.Errorf("failed to listen: %v", err) + } + + if err = s.server.Serve(lis); err != nil { + return fmt.Errorf("failed to serve: %v", err) + } + } + } +} + +// Close will gracefully stop the grpc server. +func (s *grpcServer) Close() { + s.mutState.RLock() + defer s.mutState.RUnlock() + + if s.cancelFunc != nil { + s.cancelFunc() + } + + close(s.closeChan) +} diff --git a/service/hyperOutportBlock/blockService_test.go b/service/hyperOutportBlock/blockService_test.go new file mode 100644 index 0000000..3fe8f44 --- /dev/null +++ b/service/hyperOutportBlock/blockService_test.go @@ -0,0 +1,51 @@ +package hyperOutportBlock + +import ( + "context" + "encoding/hex" + "testing" + + "github.com/stretchr/testify/require" + + data "github.com/multiversx/mx-chain-ws-connector-template-go/data/hyperOutportBlocks" + "github.com/multiversx/mx-chain-ws-connector-template-go/testscommon" +) + +func TestService_GetHyperOutportBlockByHash(t *testing.T) { + handler := testscommon.GRPCBlocksHandlerStub{ + FetchHyperBlockByHashCalled: func(hash []byte) (*data.HyperOutportBlock, error) { + return &data.HyperOutportBlock{ + MetaOutportBlock: &data.MetaOutportBlock{ + BlockData: &data.MetaBlockData{ + HeaderHash: hash, + }, + }, + }, nil + }, + } + bs := NewService(&handler) + hash := "437a88d24178dea0060afd74f1282c23b34947cf96adcf71cdfa0f3f7bdcdc73" + expectedHash, _ := hex.DecodeString(hash) + outportBlock, err := bs.GetHyperOutportBlockByHash(context.Background(), &data.BlockHashRequest{Hash: hash}) + require.NoError(t, err, "couldn't get block by hash") + require.Equal(t, expectedHash, outportBlock.MetaOutportBlock.BlockData.HeaderHash) +} + +func TestService_GetHyperOutportBlockByNonce(t *testing.T) { + handler := testscommon.GRPCBlocksHandlerStub{ + FetchHyperBlockByNonceCalled: func(nonce uint64) (*data.HyperOutportBlock, error) { + return &data.HyperOutportBlock{ + MetaOutportBlock: &data.MetaOutportBlock{ + BlockData: &data.MetaBlockData{ + Header: &data.MetaHeader{Nonce: nonce}, + }, + }, + }, nil + }, + } + bs := NewService(&handler) + nonce := uint64(1) + outportBlock, err := bs.GetHyperOutportBlockByNonce(context.Background(), &data.BlockNonceRequest{Nonce: nonce}) + require.NoError(t, err, "couldn't get block by nonce") + require.Equal(t, nonce, outportBlock.MetaOutportBlock.BlockData.Header.Nonce) +} diff --git a/testscommon/grpcBlocksHandlerStub.go b/testscommon/grpcBlocksHandlerStub.go new file mode 100644 index 0000000..e53ef1e --- /dev/null +++ b/testscommon/grpcBlocksHandlerStub.go @@ -0,0 +1,24 @@ +package testscommon + +import ( + data "github.com/multiversx/mx-chain-ws-connector-template-go/data/hyperOutportBlocks" +) + +type GRPCBlocksHandlerStub struct { + FetchHyperBlockByHashCalled func(hash []byte) (*data.HyperOutportBlock, error) + FetchHyperBlockByNonceCalled func(nonce uint64) (*data.HyperOutportBlock, error) +} + +func (g *GRPCBlocksHandlerStub) FetchHyperBlockByHash(hash []byte) (*data.HyperOutportBlock, error) { + if g.FetchHyperBlockByHashCalled != nil { + return g.FetchHyperBlockByHashCalled(hash) + } + return &data.HyperOutportBlock{}, nil +} + +func (g *GRPCBlocksHandlerStub) FetchHyperBlockByNonce(nonce uint64) (*data.HyperOutportBlock, error) { + if g.FetchHyperBlockByNonceCalled != nil { + return g.FetchHyperBlockByNonceCalled(nonce) + } + return &data.HyperOutportBlock{}, nil +}