github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/internal/cli/server/service.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"math/big"
     9  	"reflect"
    10  	"strings"
    11  	"time"
    12  
    13  	grpc_net_conn "github.com/JekaMas/go-grpc-net-conn"
    14  
    15  	"github.com/ethereum/go-ethereum/core"
    16  	"github.com/ethereum/go-ethereum/core/types"
    17  	"github.com/ethereum/go-ethereum/eth/tracers"
    18  	"github.com/ethereum/go-ethereum/eth/tracers/logger"
    19  	"github.com/ethereum/go-ethereum/internal/cli/server/pprof"
    20  	"github.com/ethereum/go-ethereum/internal/cli/server/proto"
    21  	"github.com/ethereum/go-ethereum/p2p"
    22  	"github.com/ethereum/go-ethereum/p2p/enode"
    23  )
    24  
    25  const chunkSize = 1024 * 1024 * 1024
    26  
    27  var ErrUnavailable = errors.New("bor service is currently unavailable, try again later")
    28  var ErrUnavailable2 = errors.New("bor service unavailable even after waiting for 10 seconds, make sure bor is running")
    29  
    30  func sendStreamDebugFile(stream proto.Bor_DebugPprofServer, headers map[string]string, data []byte) error {
    31  	// open the stream and send the headers
    32  	err := stream.Send(&proto.DebugFileResponse{
    33  		Event: &proto.DebugFileResponse_Open_{
    34  			Open: &proto.DebugFileResponse_Open{
    35  				Headers: headers,
    36  			},
    37  		},
    38  	})
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	// Wrap our conn around the response.
    44  	encoder := grpc_net_conn.SimpleEncoder(func(msg *proto.DebugFileResponse_Input) *[]byte {
    45  		return &msg.Data
    46  	})
    47  
    48  	conn := &grpc_net_conn.Conn[*proto.DebugFileResponse_Input, *proto.DebugFileResponse_Input]{
    49  		Stream:  stream,
    50  		Request: &proto.DebugFileResponse_Input{},
    51  		Encode:  grpc_net_conn.ChunkedEncoder(encoder, chunkSize),
    52  	}
    53  
    54  	if _, err := conn.Write(data); err != nil {
    55  		return err
    56  	}
    57  
    58  	// send the eof
    59  	err = stream.Send(&proto.DebugFileResponse{
    60  		Event: &proto.DebugFileResponse_Eof{},
    61  	})
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	return nil
    67  }
    68  
    69  func (s *Server) DebugPprof(req *proto.DebugPprofRequest, stream proto.Bor_DebugPprofServer) error {
    70  	var (
    71  		payload []byte
    72  		headers map[string]string
    73  		err     error
    74  	)
    75  
    76  	ctx := context.Background()
    77  
    78  	switch req.Type {
    79  	case proto.DebugPprofRequest_CPU:
    80  		payload, headers, err = pprof.CPUProfile(ctx, int(req.Seconds))
    81  	case proto.DebugPprofRequest_TRACE:
    82  		payload, headers, err = pprof.Trace(ctx, int(req.Seconds))
    83  	case proto.DebugPprofRequest_LOOKUP:
    84  		payload, headers, err = pprof.Profile(req.Profile, 0, 0)
    85  	}
    86  
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	// send the file on a grpc stream
    92  	if err := sendStreamDebugFile(stream, headers, payload); err != nil {
    93  		return err
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func (s *Server) PeersAdd(ctx context.Context, req *proto.PeersAddRequest) (*proto.PeersAddResponse, error) {
   100  	node, err := enode.Parse(enode.ValidSchemes, req.Enode)
   101  	if err != nil {
   102  		return nil, fmt.Errorf("invalid enode: %v", err)
   103  	}
   104  	srv := s.node.Server()
   105  	if req.Trusted {
   106  		srv.AddTrustedPeer(node)
   107  	} else {
   108  		srv.AddPeer(node)
   109  	}
   110  	return &proto.PeersAddResponse{}, nil
   111  }
   112  
   113  func (s *Server) PeersRemove(ctx context.Context, req *proto.PeersRemoveRequest) (*proto.PeersRemoveResponse, error) {
   114  	node, err := enode.Parse(enode.ValidSchemes, req.Enode)
   115  	if err != nil {
   116  		return nil, fmt.Errorf("invalid enode: %v", err)
   117  	}
   118  	srv := s.node.Server()
   119  	if req.Trusted {
   120  		srv.RemoveTrustedPeer(node)
   121  	} else {
   122  		srv.RemovePeer(node)
   123  	}
   124  	return &proto.PeersRemoveResponse{}, nil
   125  }
   126  
   127  func (s *Server) PeersList(ctx context.Context, req *proto.PeersListRequest) (*proto.PeersListResponse, error) {
   128  	resp := &proto.PeersListResponse{}
   129  
   130  	peers := s.node.Server().PeersInfo()
   131  	for _, p := range peers {
   132  		resp.Peers = append(resp.Peers, peerInfoToPeer(p))
   133  	}
   134  	return resp, nil
   135  }
   136  
   137  func (s *Server) PeersStatus(ctx context.Context, req *proto.PeersStatusRequest) (*proto.PeersStatusResponse, error) {
   138  	var peerInfo *p2p.PeerInfo
   139  	for _, p := range s.node.Server().PeersInfo() {
   140  		if strings.HasPrefix(p.ID, req.Enode) {
   141  			if peerInfo != nil {
   142  				return nil, fmt.Errorf("more than one peer with the same prefix")
   143  			}
   144  			peerInfo = p
   145  		}
   146  	}
   147  	resp := &proto.PeersStatusResponse{}
   148  	if peerInfo != nil {
   149  		resp.Peer = peerInfoToPeer(peerInfo)
   150  	}
   151  	return resp, nil
   152  }
   153  
   154  func peerInfoToPeer(info *p2p.PeerInfo) *proto.Peer {
   155  	return &proto.Peer{
   156  		Id:      info.ID,
   157  		Enode:   info.Enode,
   158  		Enr:     info.ENR,
   159  		Caps:    info.Caps,
   160  		Name:    info.Name,
   161  		Trusted: info.Network.Trusted,
   162  		Static:  info.Network.Static,
   163  	}
   164  }
   165  
   166  func (s *Server) ChainSetHead(ctx context.Context, req *proto.ChainSetHeadRequest) (*proto.ChainSetHeadResponse, error) {
   167  	s.backend.APIBackend.SetHead(req.Number)
   168  	return &proto.ChainSetHeadResponse{}, nil
   169  }
   170  
   171  func (s *Server) Status(ctx context.Context, in *proto.StatusRequest) (*proto.StatusResponse, error) {
   172  	if s.backend == nil && !in.Wait {
   173  		return nil, ErrUnavailable
   174  	}
   175  
   176  	// check for s.backend at an interval of 2 seconds
   177  	// wait for a maximum of 10 seconds (5 iterations)
   178  	if s.backend == nil && in.Wait {
   179  		i := 1
   180  
   181  		for {
   182  			time.Sleep(2 * time.Second)
   183  
   184  			if s.backend == nil {
   185  				if i == 5 {
   186  					return nil, ErrUnavailable2
   187  				}
   188  			} else {
   189  				break
   190  			}
   191  			i++
   192  		}
   193  	}
   194  
   195  	apiBackend := s.backend.APIBackend
   196  	syncProgress := apiBackend.SyncProgress()
   197  
   198  	resp := &proto.StatusResponse{
   199  		CurrentHeader: headerToProtoHeader(apiBackend.CurrentHeader()),
   200  		CurrentBlock:  headerToProtoHeader(apiBackend.CurrentBlock().Header()),
   201  		NumPeers:      int64(len(s.node.Server().PeersInfo())),
   202  		SyncMode:      s.config.SyncMode,
   203  		Syncing: &proto.StatusResponse_Syncing{
   204  			StartingBlock: int64(syncProgress.StartingBlock),
   205  			HighestBlock:  int64(syncProgress.HighestBlock),
   206  			CurrentBlock:  int64(syncProgress.CurrentBlock),
   207  		},
   208  		Forks: gatherForks(s.config.chain.Genesis.Config, s.config.chain.Genesis.Config.Bor),
   209  	}
   210  	return resp, nil
   211  }
   212  
   213  func headerToProtoHeader(h *types.Header) *proto.Header {
   214  	return &proto.Header{
   215  		Hash:   h.Hash().String(),
   216  		Number: h.Number.Uint64(),
   217  	}
   218  }
   219  
   220  func (s *Server) DebugBlock(req *proto.DebugBlockRequest, stream proto.Bor_DebugBlockServer) error {
   221  	traceReq := &tracers.TraceBlockRequest{
   222  		Number: req.Number,
   223  		Config: &tracers.TraceConfig{
   224  			Config: &logger.Config{
   225  				EnableMemory: true,
   226  			},
   227  		},
   228  	}
   229  
   230  	res, err := s.tracerAPI.TraceBorBlock(traceReq)
   231  	if err != nil {
   232  		return err
   233  	}
   234  
   235  	// this is memory heavy
   236  	data, err := json.Marshal(res)
   237  	if err != nil {
   238  		return err
   239  	}
   240  
   241  	if err := sendStreamDebugFile(stream, map[string]string{}, data); err != nil {
   242  		return err
   243  	}
   244  
   245  	return nil
   246  }
   247  
   248  var bigIntT = reflect.TypeOf(new(big.Int)).Kind()
   249  
   250  // gatherForks gathers all the fork numbers via reflection
   251  func gatherForks(configList ...interface{}) []*proto.StatusResponse_Fork {
   252  	var forks []*proto.StatusResponse_Fork
   253  
   254  	for _, config := range configList {
   255  		kind := reflect.TypeOf(config)
   256  		for kind.Kind() == reflect.Ptr {
   257  			kind = kind.Elem()
   258  		}
   259  
   260  		skip := "DAOForkBlock"
   261  
   262  		conf := reflect.ValueOf(config).Elem()
   263  		for i := 0; i < kind.NumField(); i++ {
   264  			// Fetch the next field and skip non-fork rules
   265  			field := kind.Field(i)
   266  			if strings.Contains(field.Name, skip) {
   267  				continue
   268  			}
   269  			if !strings.HasSuffix(field.Name, "Block") {
   270  				continue
   271  			}
   272  
   273  			fork := &proto.StatusResponse_Fork{
   274  				Name: strings.TrimSuffix(field.Name, "Block"),
   275  			}
   276  
   277  			val := conf.Field(i)
   278  			switch field.Type.Kind() {
   279  			case bigIntT:
   280  				rule := val.Interface().(*big.Int)
   281  				if rule != nil {
   282  					fork.Block = rule.Int64()
   283  				} else {
   284  					fork.Disabled = true
   285  				}
   286  			case reflect.Uint64:
   287  				fork.Block = int64(val.Uint())
   288  
   289  			default:
   290  				continue
   291  			}
   292  
   293  			forks = append(forks, fork)
   294  		}
   295  	}
   296  	return forks
   297  }
   298  
   299  func convertBlockToBlockStub(blocks []*types.Block) []*proto.BlockStub {
   300  
   301  	var blockStubs []*proto.BlockStub
   302  
   303  	for _, block := range blocks {
   304  		blockStub := &proto.BlockStub{
   305  			Hash:   block.Hash().String(),
   306  			Number: block.NumberU64(),
   307  		}
   308  		blockStubs = append(blockStubs, blockStub)
   309  	}
   310  
   311  	return blockStubs
   312  }
   313  
   314  func (s *Server) ChainWatch(req *proto.ChainWatchRequest, reply proto.Bor_ChainWatchServer) error {
   315  
   316  	chain2HeadChanSize := 10
   317  
   318  	chain2HeadCh := make(chan core.Chain2HeadEvent, chain2HeadChanSize)
   319  	headSub := s.backend.APIBackend.SubscribeChain2HeadEvent(chain2HeadCh)
   320  	defer headSub.Unsubscribe()
   321  
   322  	for {
   323  		msg := <-chain2HeadCh
   324  
   325  		err := reply.Send(&proto.ChainWatchResponse{Type: msg.Type,
   326  			Newchain: convertBlockToBlockStub(msg.NewChain),
   327  			Oldchain: convertBlockToBlockStub(msg.OldChain),
   328  		})
   329  		if err != nil {
   330  			return err
   331  		}
   332  	}
   333  }