func convertErrorLevel(level zapcore.Level) pb.LogLevel {
	switch level {
	case zapcore.DebugLevel:
		return pb.LogLevel_LOG_LEVEL_DEBUG
	case zapcore.InfoLevel:
		return pb.LogLevel_LOG_LEVEL_INFO
	case zapcore.WarnLevel:
		return pb.LogLevel_LOG_LEVEL_WARN
	case zapcore.ErrorLevel:
		return pb.LogLevel_LOG_LEVEL_ERROR
	case zapcore.DPanicLevel:
		return pb.LogLevel_LOG_LEVEL_DPANIC
	case zapcore.PanicLevel:
		return pb.LogLevel_LOG_LEVEL_PANIC
	case zapcore.FatalLevel:
		return pb.LogLevel_LOG_LEVEL_FATAL
	default:
		return pb.LogLevel_LOG_LEVEL_UNSPECIFIED
	}
}
Convert internal error level into level understood by the API.
package grpcserver
import (
	"context"
	"errors"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty"
	pb "github.com/spacemeshos/api/release/go/spacemesh/v1"
	"go.uber.org/zap/zapcore"
	"google.golang.org/genproto/googleapis/rpc/code"
	rpcstatus "google.golang.org/genproto/googleapis/rpc/status"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"github.com/spacemeshos/go-spacemesh/activation"
	"github.com/spacemeshos/go-spacemesh/api"
	"github.com/spacemeshos/go-spacemesh/cmd"
	"github.com/spacemeshos/go-spacemesh/events"
	"github.com/spacemeshos/go-spacemesh/log"
)
type NodeService struct {
	Mesh        api.TxAPI
	GenTime     api.GenesisTimeAPI
	PeerCounter api.PeerCounter
	Syncer      api.Syncer
	AtxAPI      api.ActivationAPI
}
func (s NodeService) RegisterService(server *Server) {
	pb.RegisterNodeServiceServer(server.GrpcServer, s)
}
func NewNodeService(
	peers api.PeerCounter, tx api.TxAPI, genTime api.GenesisTimeAPI, syncer api.Syncer, atxapi api.ActivationAPI) *NodeService {
	return &NodeService{
		Mesh:        tx,
		GenTime:     genTime,
		PeerCounter: peers,
		Syncer:      syncer,
		AtxAPI:      atxapi,
	}
}
func (s NodeService) Echo(_ context.Context, in *pb.EchoRequest) (*pb.EchoResponse, error) {
	log.Info("GRPC NodeService.Echo")
	if in.Msg != nil {
		return &pb.EchoResponse{Msg: &pb.SimpleString{Value: in.Msg.Value}}, nil
	}
	return nil, status.Errorf(codes.InvalidArgument, "Must include `Msg`")
}
func (s NodeService) Version(context.Context, *empty.Empty) (*pb.VersionResponse, error) {
	log.Info("GRPC NodeService.Version")
	return &pb.VersionResponse{
		VersionString: &pb.SimpleString{Value: cmd.Version},
	}, nil
}
func (s NodeService) Build(context.Context, *empty.Empty) (*pb.BuildResponse, error) {
	log.Info("GRPC NodeService.Build")
	return &pb.BuildResponse{
		BuildString: &pb.SimpleString{Value: cmd.Commit},
	}, nil
}
func (s NodeService) Status(ctx context.Context, _ *pb.StatusRequest) (*pb.StatusResponse, error) {
	log.Info("GRPC NodeService.Status")
	curLayer, latestLayer, verifiedLayer := s.getLayers()
	return &pb.StatusResponse{
		Status: &pb.NodeStatus{
			ConnectedPeers: s.PeerCounter.PeerCount(),               
			IsSynced:       s.Syncer.IsSynced(ctx),                  
			SyncedLayer:    &pb.LayerNumber{Number: latestLayer},    
			TopLayer:       &pb.LayerNumber{Number: curLayer},       
			VerifiedLayer:  &pb.LayerNumber{Number: verifiedLayer},  
		},
	}, nil
}
func (s NodeService) getLayers() (curLayer, latestLayer, verifiedLayer uint32) {
	curLayerObj := s.GenTime.GetCurrentLayer()
	curLayer = curLayerObj.Uint32()
	if curLayerObj.GetEpoch().IsGenesis() {
		latestLayer = s.Mesh.LatestLayer().Uint32()
		verifiedLayer = latestLayer
	} else {
		latestLayer = s.Mesh.LatestLayer().Uint32()
		verifiedLayer = s.Mesh.LatestLayerInState().Uint32()
	}
	return
}
func (s NodeService) SyncStart(ctx context.Context, _ *pb.SyncStartRequest) (*pb.SyncStartResponse, error) {
	log.Info("GRPC NodeService.SyncStart")
	s.Syncer.Start(ctx)
	return &pb.SyncStartResponse{
		Status: &rpcstatus.Status{Code: int32(code.Code_OK)},
	}, nil
}
func (s NodeService) Shutdown(context.Context, *pb.ShutdownRequest) (*pb.ShutdownResponse, error) {
	log.Info("GRPC NodeService.Shutdown")
	cmd.Cancel()
	return &pb.ShutdownResponse{
		Status: &rpcstatus.Status{Code: int32(code.Code_OK)},
	}, nil
}
func (s NodeService) UpdatePoetServer(ctx context.Context, req *pb.UpdatePoetServerRequest) (*pb.UpdatePoetServerResponse, error) {
	err := s.AtxAPI.UpdatePoETServer(ctx, req.Url)
	if err == nil {
		return &pb.UpdatePoetServerResponse{
			Status: &rpcstatus.Status{Code: int32(code.Code_OK)},
		}, nil
	}
	switch {
	case errors.Is(err, activation.ErrPoetServiceUnstable):
		return nil, status.Errorf(codes.Unavailable, "can't reach server at %s. retry later", req.Url)
	}
	return nil, status.Errorf(codes.Internal, "failed to update poet server")
}
func (s NodeService) StatusStream(_ *pb.StatusStreamRequest, stream pb.NodeService_StatusStreamServer) error {
	log.Info("GRPC NodeService.StatusStream")
	statusStream := events.GetStatusChannel()
	for {
		select {
		case _, ok := <-statusStream:
			if !ok {
				log.Info("StatusStream closed, shutting down")
				return nil
			}
			curLayer, latestLayer, verifiedLayer := s.getLayers()
			resp := &pb.StatusStreamResponse{
				Status: &pb.NodeStatus{
					ConnectedPeers: s.PeerCounter.PeerCount(),               
					IsSynced:       s.Syncer.IsSynced(stream.Context()),     
					SyncedLayer:    &pb.LayerNumber{Number: latestLayer},    
					TopLayer:       &pb.LayerNumber{Number: curLayer},       
					VerifiedLayer:  &pb.LayerNumber{Number: verifiedLayer},  
				},
			}
			if err := stream.Send(resp); err != nil {
				return fmt.Errorf("send to stream: %w", err)
			}
		case <-stream.Context().Done():
			log.Info("StatusStream closing stream, client disconnected")
			return nil
		}
	}
}
func (s NodeService) ErrorStream(_ *pb.ErrorStreamRequest, stream pb.NodeService_ErrorStreamServer) error {
	log.Info("GRPC NodeService.ErrorStream")
	errorStream := events.GetErrorChannel()
	for {
		select {
		case nodeError, ok := <-errorStream:
			if !ok {
				log.Info("ErrorStream closed, shutting down")
				return nil
			}
			resp := &pb.ErrorStreamResponse{Error: &pb.NodeError{
				Level:      convertErrorLevel(nodeError.Level),
				Msg:        nodeError.Msg,
				StackTrace: nodeError.Trace,
			}}
			if err := stream.Send(resp); err != nil {
				return fmt.Errorf("send to stream: %w", err)
			}
		case <-stream.Context().Done():
			log.Info("ErrorStream closing stream, client disconnected")
			return nil
		}
	}
}
func (s NodeService) Close() {}