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 }