go-ethereum/internal/cli/server/service.go
2022-01-19 12:10:20 +01:00

220 lines
5.6 KiB
Go

package server
import (
"context"
"fmt"
"math/big"
"reflect"
"strings"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/cli/server/pprof"
"github.com/ethereum/go-ethereum/internal/cli/server/proto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
gproto "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
grpc_net_conn "github.com/mitchellh/go-grpc-net-conn"
)
func (s *Server) Pprof(req *proto.PprofRequest, stream proto.Bor_PprofServer) error {
var payload []byte
var headers map[string]string
var err error
ctx := context.Background()
switch req.Type {
case proto.PprofRequest_CPU:
payload, headers, err = pprof.CPUProfile(ctx, int(req.Seconds))
case proto.PprofRequest_TRACE:
payload, headers, err = pprof.Trace(ctx, int(req.Seconds))
case proto.PprofRequest_LOOKUP:
payload, headers, err = pprof.Profile(req.Profile, 0, 0)
}
if err != nil {
return err
}
// open the stream and send the headers
err = stream.Send(&proto.PprofResponse{
Event: &proto.PprofResponse_Open_{
Open: &proto.PprofResponse_Open{
Headers: headers,
Size: int64(len(payload)),
},
},
})
if err != nil {
return err
}
// Wrap our conn around the response.
conn := &grpc_net_conn.Conn{
Stream: stream,
Request: &proto.PprofResponse_Input{},
Encode: grpc_net_conn.SimpleEncoder(func(msg gproto.Message) *[]byte {
return &msg.(*proto.PprofResponse_Input).Data
}),
}
if _, err := conn.Write(payload); err != nil {
return err
}
// send the eof
err = stream.Send(&proto.PprofResponse{
Event: &proto.PprofResponse_Eof{},
})
if err != nil {
return err
}
return nil
}
func (s *Server) PeersAdd(ctx context.Context, req *proto.PeersAddRequest) (*proto.PeersAddResponse, error) {
node, err := enode.Parse(enode.ValidSchemes, req.Enode)
if err != nil {
return nil, fmt.Errorf("invalid enode: %v", err)
}
srv := s.node.Server()
if req.Trusted {
srv.AddTrustedPeer(node)
} else {
srv.AddPeer(node)
}
return &proto.PeersAddResponse{}, nil
}
func (s *Server) PeersRemove(ctx context.Context, req *proto.PeersRemoveRequest) (*proto.PeersRemoveResponse, error) {
node, err := enode.Parse(enode.ValidSchemes, req.Enode)
if err != nil {
return nil, fmt.Errorf("invalid enode: %v", err)
}
srv := s.node.Server()
if req.Trusted {
srv.RemoveTrustedPeer(node)
} else {
srv.RemovePeer(node)
}
return &proto.PeersRemoveResponse{}, nil
}
func (s *Server) PeersList(ctx context.Context, req *proto.PeersListRequest) (*proto.PeersListResponse, error) {
resp := &proto.PeersListResponse{}
peers := s.node.Server().PeersInfo()
for _, p := range peers {
resp.Peers = append(resp.Peers, peerInfoToPeer(p))
}
return resp, nil
}
func (s *Server) PeersStatus(ctx context.Context, req *proto.PeersStatusRequest) (*proto.PeersStatusResponse, error) {
var peerInfo *p2p.PeerInfo
for _, p := range s.node.Server().PeersInfo() {
if strings.HasPrefix(p.ID, req.Enode) {
if peerInfo != nil {
return nil, fmt.Errorf("more than one peer with the same prefix")
}
peerInfo = p
}
}
resp := &proto.PeersStatusResponse{}
if peerInfo != nil {
resp.Peer = peerInfoToPeer(peerInfo)
}
return resp, nil
}
func peerInfoToPeer(info *p2p.PeerInfo) *proto.Peer {
return &proto.Peer{
Id: info.ID,
Enode: info.Enode,
Enr: info.ENR,
Caps: info.Caps,
Name: info.Name,
Trusted: info.Network.Trusted,
Static: info.Network.Static,
}
}
func (s *Server) ChainSetHead(ctx context.Context, req *proto.ChainSetHeadRequest) (*proto.ChainSetHeadResponse, error) {
s.backend.APIBackend.SetHead(req.Number)
return &proto.ChainSetHeadResponse{}, nil
}
func (s *Server) Status(ctx context.Context, _ *empty.Empty) (*proto.StatusResponse, error) {
apiBackend := s.backend.APIBackend
syncProgress := apiBackend.SyncProgress()
resp := &proto.StatusResponse{
CurrentHeader: headerToProtoHeader(apiBackend.CurrentHeader()),
CurrentBlock: headerToProtoHeader(apiBackend.CurrentBlock().Header()),
NumPeers: int64(len(s.node.Server().PeersInfo())),
SyncMode: s.config.SyncMode,
Syncing: &proto.StatusResponse_Syncing{
StartingBlock: int64(syncProgress.StartingBlock),
HighestBlock: int64(syncProgress.HighestBlock),
CurrentBlock: int64(syncProgress.CurrentBlock),
},
Forks: gatherForks(s.config.chain.Genesis.Config, s.config.chain.Genesis.Config.Bor),
}
return resp, nil
}
func headerToProtoHeader(h *types.Header) *proto.Header {
return &proto.Header{
Hash: h.Hash().String(),
Number: h.Number.Uint64(),
}
}
var bigIntT = reflect.TypeOf(new(big.Int)).Kind()
// gatherForks gathers all the fork numbers via reflection
func gatherForks(configList ...interface{}) []*proto.StatusResponse_Fork {
var forks []*proto.StatusResponse_Fork
for _, config := range configList {
kind := reflect.TypeOf(config)
for kind.Kind() == reflect.Ptr {
kind = kind.Elem()
}
skip := "DAOForkBlock"
conf := reflect.ValueOf(config).Elem()
for i := 0; i < kind.NumField(); i++ {
// Fetch the next field and skip non-fork rules
field := kind.Field(i)
if strings.Contains(field.Name, skip) {
continue
}
if !strings.HasSuffix(field.Name, "Block") {
continue
}
fork := &proto.StatusResponse_Fork{
Name: strings.TrimSuffix(field.Name, "Block"),
}
val := conf.Field(i)
switch field.Type.Kind() {
case bigIntT:
rule := val.Interface().(*big.Int)
if rule != nil {
fork.Block = rule.Int64()
} else {
fork.Disabled = true
}
case reflect.Uint64:
fork.Block = int64(val.Uint())
default:
continue
}
forks = append(forks, fork)
}
}
return forks
}