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  }