github.com/aergoio/aergo@v1.3.1/rpc/rpc.go (about) 1 /** 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package rpc 7 8 import ( 9 "bytes" 10 "crypto/tls" 11 "crypto/x509" 12 "encoding/json" 13 "fmt" 14 "io/ioutil" 15 "net" 16 "net/http" 17 "reflect" 18 "strconv" 19 "strings" 20 "time" 21 22 "github.com/aergoio/aergo/contract/enterprise" 23 24 "github.com/aergoio/aergo-actor/actor" 25 "github.com/aergoio/aergo/config" 26 "github.com/aergoio/aergo/consensus" 27 "github.com/aergoio/aergo/message" 28 "github.com/aergoio/aergo/p2p/p2pcommon" 29 "github.com/aergoio/aergo/p2p/p2pkey" 30 "github.com/aergoio/aergo/pkg/component" 31 "github.com/aergoio/aergo/types" 32 aergorpc "github.com/aergoio/aergo/types" 33 "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" 34 "github.com/improbable-eng/grpc-web/go/grpcweb" 35 "github.com/opentracing/opentracing-go" 36 "github.com/soheilhy/cmux" 37 "google.golang.org/grpc" 38 "google.golang.org/grpc/credentials" 39 ) 40 41 // RPC is actor for providing rpc service 42 type RPC struct { 43 conf *config.Config 44 45 *component.BaseComponent 46 47 grpcServer *grpc.Server 48 grpcWebServer *grpcweb.WrappedGrpcServer 49 actualServer *AergoRPCService 50 httpServer *http.Server 51 52 ca types.ChainAccessor 53 version string 54 entConf *types.EnterpriseConfig 55 } 56 57 //var _ component.IComponent = (*RPCComponent)(nil) 58 59 // NewRPC create an rpc service 60 func NewRPC(cfg *config.Config, chainAccessor types.ChainAccessor, version string) *RPC { 61 actualServer := &AergoRPCService{ 62 msgHelper: message.GetHelper(), 63 blockStream: map[uint32]types.AergoRPCService_ListBlockStreamServer{}, 64 blockMetadataStream: map[uint32]types.AergoRPCService_ListBlockMetadataStreamServer{}, 65 eventStream: make(map[*EventStream]*EventStream), 66 } 67 68 tracer := opentracing.GlobalTracer() 69 70 opts := []grpc.ServerOption{ 71 grpc.MaxRecvMsgSize(1024 * 1024 * 256), 72 } 73 74 if cfg.RPC.NetServiceTrace { 75 opts = append(opts, grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer))) 76 opts = append(opts, grpc.StreamInterceptor(otgrpc.OpenTracingStreamServerInterceptor(tracer))) 77 } 78 79 var entConf *types.EnterpriseConfig 80 genesis := chainAccessor.GetGenesisInfo() 81 if !genesis.ID.PublicNet { 82 conf, err := chainAccessor.GetEnterpriseConfig("rpcpermissions") 83 if err != nil { 84 logger.Error().Err(err).Msg("could not get allowed client information") 85 } else { 86 entConf = conf 87 } 88 } 89 90 if cfg.RPC.NSEnableTLS { 91 certificate, err := tls.LoadX509KeyPair(cfg.RPC.NSCert, cfg.RPC.NSKey) 92 if err != nil { 93 logger.Error().Err(err).Msg("could not load server key pair") 94 } 95 certPool := x509.NewCertPool() 96 ca, err := ioutil.ReadFile(cfg.RPC.NSCACert) 97 if err != nil { 98 logger.Error().Err(err).Msg("could not read CA cert") 99 } 100 if ok := certPool.AppendCertsFromPEM(ca); !ok { 101 logger.Error().Bool("AppendCertsFromPEM", ok).Msg("failed to append server cert") 102 err = fmt.Errorf("failed to append server cert") 103 } 104 if err == nil { 105 creds := credentials.NewTLS(&tls.Config{ 106 ClientAuth: tls.RequireAndVerifyClientCert, 107 Certificates: []tls.Certificate{certificate}, 108 ClientCAs: certPool, 109 }) 110 opts = append(opts, grpc.Creds(creds)) 111 logger.Info().Str("cert", cfg.RPC.NSCert).Str("key", cfg.RPC.NSKey).Msg("grpc with TLS") 112 } 113 } 114 115 grpcServer := grpc.NewServer(opts...) 116 117 grpcWebServer := grpcweb.WrapServer( 118 grpcServer, 119 grpcweb.WithWebsockets(true), 120 grpcweb.WithWebsocketOriginFunc(func(req *http.Request) bool { 121 return true 122 })) 123 124 rpcsvc := &RPC{ 125 conf: cfg, 126 127 grpcServer: grpcServer, 128 grpcWebServer: grpcWebServer, 129 actualServer: actualServer, 130 ca: chainAccessor, 131 version: version, 132 } 133 rpcsvc.BaseComponent = component.NewBaseComponent(message.RPCSvc, rpcsvc, logger) 134 135 actualServer.actorHelper = rpcsvc 136 actualServer.setClientAuth(entConf) 137 138 rpcsvc.httpServer = &http.Server{ 139 Handler: rpcsvc.grpcWebHandlerFunc(grpcWebServer, http.DefaultServeMux), 140 ReadTimeout: 4 * time.Second, 141 WriteTimeout: 4 * time.Second, 142 MaxHeaderBytes: 1 << 20, 143 } 144 145 return rpcsvc 146 } 147 148 func (ns *RPC) SetHub(hub *component.ComponentHub) { 149 ns.actualServer.hub = hub 150 ns.BaseComponent.SetHub(hub) 151 } 152 153 func (ns *RPC) SetConsensusAccessor(ca consensus.ConsensusAccessor) { 154 ns.actualServer.SetConsensusAccessor(ca) 155 } 156 157 // Start start rpc service. 158 func (ns *RPC) BeforeStart() { 159 aergorpc.RegisterAergoRPCServiceServer(ns.grpcServer, ns.actualServer) 160 } 161 162 func (ns *RPC) AfterStart() { 163 go ns.serve() 164 } 165 166 // Stop stops rpc service. 167 func (ns *RPC) BeforeStop() { 168 ns.httpServer.Close() 169 ns.grpcServer.Stop() 170 } 171 172 func (ns *RPC) Statistics() *map[string]interface{} { 173 return &map[string]interface{}{ 174 "config": ns.conf.RPC, 175 "version": ns.version, 176 } 177 } 178 179 func (ns *RPC) Receive(context actor.Context) { 180 switch msg := context.Message().(type) { 181 case *types.Block: 182 server := ns.actualServer 183 server.BroadcastToListBlockStream(msg) 184 meta := msg.GetMetadata() 185 server.BroadcastToListBlockMetadataStream(meta) 186 case []*types.Event: 187 server := ns.actualServer 188 for _, e := range msg { 189 if bytes.Equal(e.GetContractAddress(), types.AddressPadding([]byte(types.AergoEnterprise))) { 190 eventName := strings.Split(e.GetEventName(), " ") 191 conf := strings.ToUpper(eventName[1]) 192 switch eventName[0] { 193 case "Enable": 194 if conf == enterprise.RPCPermissions { 195 value := false 196 if e.JsonArgs == "true" { 197 value = true 198 } 199 server.setClientAuthOn(value) 200 } else if conf == enterprise.AccountWhite { 201 value := false 202 if e.JsonArgs == "true" { 203 value = true 204 } 205 msg := &message.MemPoolEnableWhitelist{On: value} 206 ns.TellTo(message.MemPoolSvc, msg) 207 } else if conf == enterprise.P2PBlack || conf == enterprise.P2PWhite { 208 value := false 209 if e.JsonArgs == "true" { 210 value = true 211 } 212 msg := message.P2PWhiteListConfEnableEvent{Name: conf, On: value} 213 ns.TellTo(message.P2PSvc, msg) 214 } 215 case "Set": 216 if conf == enterprise.RPCPermissions { 217 values := make([]string, 1024) 218 if err := json.Unmarshal([]byte(e.JsonArgs), &values); err != nil { 219 return 220 } 221 server.setClientAuthMap(values) 222 } else if conf == enterprise.AccountWhite { 223 values := make([]string, 1024) 224 if err := json.Unmarshal([]byte(e.JsonArgs), &values); err != nil { 225 return 226 } 227 msg := &message.MemPoolSetWhitelist{ 228 Accounts: values, 229 } 230 ns.TellTo(message.MemPoolSvc, msg) 231 } else if conf == enterprise.P2PBlack || conf == enterprise.P2PWhite { 232 values := make([]string, 1024) 233 if err := json.Unmarshal([]byte(e.JsonArgs), &values); err != nil { 234 return 235 } 236 msg := message.P2PWhiteListConfSetEvent{ 237 Values: values, 238 } 239 ns.TellTo(message.P2PSvc, msg) 240 } 241 default: 242 logger.Warn().Str("Enterprise event", eventName[0]).Msg("unknown message in RPCPERMISSION") 243 } 244 } 245 } 246 server.BroadcastToEventStream(msg) 247 case *message.GetServerInfo: 248 context.Respond(ns.CollectServerInfo(msg.Categories)) 249 case *actor.Started, *actor.Stopping, *actor.Stopped, *component.CompStatReq: // donothing 250 // Ignore actor lfiecycle messages 251 default: 252 ns.Warn().Msgf("unknown msg received in rpc %s", reflect.TypeOf(msg).String()) 253 } 254 } 255 256 // Create HTTP handler that redirects matching grpc-web requests to the grpc-web wrapper. 257 func (ns *RPC) grpcWebHandlerFunc(grpcWebServer *grpcweb.WrappedGrpcServer, otherHandler http.Handler) http.Handler { 258 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 259 if grpcWebServer.IsAcceptableGrpcCorsRequest(r) || grpcWebServer.IsGrpcWebRequest(r) || grpcWebServer.IsGrpcWebSocketRequest(r) { 260 grpcWebServer.ServeHTTP(w, r) 261 } else { 262 ns.Info().Msg("Request handled by other hanlder. is this correct?") 263 otherHandler.ServeHTTP(w, r) 264 } 265 }) 266 } 267 268 // Serve GRPC server over TCP 269 func (ns *RPC) serveGRPC(l net.Listener, server *grpc.Server) { 270 if err := server.Serve(l); err != nil { 271 switch err { 272 case cmux.ErrListenerClosed: 273 // Server killed, usually by ctrl-c signal 274 default: 275 panic(err) 276 } 277 } 278 } 279 280 // Serve HTTP server over TCP 281 func (ns *RPC) serveHTTP(l net.Listener, server *http.Server) { 282 if err := server.Serve(l); err != nil && err != http.ErrServerClosed { 283 panic(err) 284 } 285 } 286 287 // Serve TCP multiplexer 288 func (ns *RPC) serve() { 289 ipAddr := net.ParseIP(ns.conf.RPC.NetServiceAddr) 290 if ipAddr == nil { 291 panic("Wrong IP address format in RPC.NetServiceAddr") 292 } 293 294 addr := fmt.Sprintf("%s:%d", ipAddr, ns.conf.RPC.NetServicePort) 295 296 l, err := net.Listen("tcp", addr) 297 if err != nil { 298 panic(err) 299 } 300 301 // Setup TCP multiplexer 302 tcpm := cmux.New(l) 303 304 //grpcL := tcpm.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) 305 matchers := []cmux.Matcher{cmux.HTTP2()} 306 if ns.conf.RPC.NSEnableTLS { 307 matchers = append(matchers, cmux.TLS()) 308 } else { 309 //http1 only work without TLS 310 httpL := tcpm.Match(cmux.HTTP1Fast()) 311 go ns.serveHTTP(httpL, ns.httpServer) 312 } 313 grpcL := tcpm.Match(matchers...) 314 go ns.serveGRPC(grpcL, ns.grpcServer) 315 316 ns.Info().Msg(fmt.Sprintf("Starting RPC server listening on %s, with TLS: %v", addr, ns.conf.RPC.NSEnableTLS)) 317 318 // Serve TCP multiplexer 319 if err := tcpm.Serve(); !strings.Contains(err.Error(), "use of closed network connection") { 320 ns.Fatal().Msg(fmt.Sprintf("%v", err)) 321 } 322 323 return 324 } 325 326 func (ns *RPC) CollectServerInfo(categories []string) *types.ServerInfo { 327 // 3 items are needed 328 statusInfo := make(map[string]string) 329 rsp, err := ns.CallRequestDefaultTimeout(message.P2PSvc, &message.GetSelf{}) 330 statusInfo["version"] = ns.version 331 if err != nil { 332 ns.Logger.Error().Err(err).Msg("p2p actor error") 333 statusInfo["id"] = p2pkey.NodeSID() 334 } else { 335 meta := rsp.(p2pcommon.PeerMeta) 336 statusInfo["id"] = meta.ID.Pretty() 337 statusInfo["addr"] = meta.IPAddress 338 statusInfo["port"] = strconv.Itoa(int(meta.Port)) 339 } 340 configInfo := make(map[string]*types.ConfigItem) 341 types.AddCategory(configInfo, "base").AddBool("personal", ns.conf.BaseConfig.Personal) 342 types.AddCategory(configInfo, "account").AddInt("unlocktimeout", int(ns.conf.Account.UnlockTimeout)) 343 return &types.ServerInfo{Status: statusInfo, Config: configInfo} 344 } 345 346 const defaultTTL = time.Second * 4 347 348 // TellRequest implement interface method of ActorService 349 func (ns *RPC) TellRequest(actor string, msg interface{}) { 350 ns.TellTo(actor, msg) 351 } 352 353 // SendRequest implement interface method of ActorService 354 func (ns *RPC) SendRequest(actor string, msg interface{}) { 355 ns.RequestTo(actor, msg) 356 } 357 358 // FutureRequest implement interface method of ActorService 359 func (ns *RPC) FutureRequest(actor string, msg interface{}, timeout time.Duration) *actor.Future { 360 return ns.RequestToFuture(actor, msg, timeout) 361 } 362 363 // FutureRequestDefaultTimeout implement interface method of ActorService 364 func (ns *RPC) FutureRequestDefaultTimeout(actor string, msg interface{}) *actor.Future { 365 return ns.RequestToFuture(actor, msg, defaultTTL) 366 } 367 368 // CallRequest implement interface method of ActorService 369 func (ns *RPC) CallRequest(actor string, msg interface{}, timeout time.Duration) (interface{}, error) { 370 future := ns.RequestToFuture(actor, msg, timeout) 371 return future.Result() 372 } 373 374 // CallRequest implement interface method of ActorService 375 func (ns *RPC) CallRequestDefaultTimeout(actor string, msg interface{}) (interface{}, error) { 376 future := ns.RequestToFuture(actor, msg, defaultTTL) 377 return future.Result() 378 } 379 380 // GetChainAccessor implment interface method of ActorService 381 func (ns *RPC) GetChainAccessor() types.ChainAccessor { 382 return ns.ca 383 } 384 385 func convertError(err error) types.CommitStatus { 386 switch err { 387 case nil: 388 return types.CommitStatus_TX_OK 389 case types.ErrTxNonceTooLow: 390 return types.CommitStatus_TX_NONCE_TOO_LOW 391 case types.ErrTxAlreadyInMempool: 392 return types.CommitStatus_TX_ALREADY_EXISTS 393 case types.ErrTxHasInvalidHash: 394 return types.CommitStatus_TX_INVALID_HASH 395 case types.ErrTxFormatInvalid: 396 return types.CommitStatus_TX_INVALID_FORMAT 397 case types.ErrInsufficientBalance: 398 return types.CommitStatus_TX_INSUFFICIENT_BALANCE 399 case types.ErrSameNonceAlreadyInMempool: 400 return types.CommitStatus_TX_HAS_SAME_NONCE 401 default: 402 //logger.Info().Str("hash", err.Error()).Msg("RPC encountered unconvertable error") 403 return types.CommitStatus_TX_INTERNAL_ERROR 404 } 405 }