github.com/MetalBlockchain/subnet-evm@v0.4.9/rpc/handler.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2019 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package rpc
    28  
    29  import (
    30  	"context"
    31  	"encoding/json"
    32  	"reflect"
    33  	"strconv"
    34  	"strings"
    35  	"sync"
    36  	"time"
    37  
    38  	"github.com/MetalBlockchain/subnet-evm/metrics"
    39  	"github.com/ethereum/go-ethereum/log"
    40  	"golang.org/x/time/rate"
    41  )
    42  
    43  // handler handles JSON-RPC messages. There is one handler per connection. Note that
    44  // handler is not safe for concurrent use. Message handling never blocks indefinitely
    45  // because RPCs are processed on background goroutines launched by handler.
    46  //
    47  // The entry points for incoming messages are:
    48  //
    49  //    h.handleMsg(message)
    50  //    h.handleBatch(message)
    51  //
    52  // Outgoing calls use the requestOp struct. Register the request before sending it
    53  // on the connection:
    54  //
    55  //    op := &requestOp{ids: ...}
    56  //    h.addRequestOp(op)
    57  //
    58  // Now send the request, then wait for the reply to be delivered through handleMsg:
    59  //
    60  //    if err := op.wait(...); err != nil {
    61  //        h.removeRequestOp(op) // timeout, etc.
    62  //    }
    63  //
    64  type handler struct {
    65  	reg            *serviceRegistry
    66  	unsubscribeCb  *callback
    67  	idgen          func() ID                      // subscription ID generator
    68  	respWait       map[string]*requestOp          // active client requests
    69  	clientSubs     map[string]*ClientSubscription // active client subscriptions
    70  	callWG         sync.WaitGroup                 // pending call goroutines
    71  	rootCtx        context.Context                // canceled by close()
    72  	cancelRoot     func()                         // cancel function for rootCtx
    73  	conn           jsonWriter                     // where responses will be sent
    74  	log            log.Logger
    75  	allowSubscribe bool
    76  
    77  	subLock    sync.Mutex
    78  	serverSubs map[ID]*Subscription
    79  
    80  	deadlineContext time.Duration // limits execution after some time.Duration
    81  	limiter         *rate.Limiter
    82  }
    83  
    84  type callProc struct {
    85  	ctx       context.Context
    86  	notifiers []*Notifier
    87  	callStart time.Time
    88  	procStart time.Time
    89  }
    90  
    91  func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry) *handler {
    92  	rootCtx, cancelRoot := context.WithCancel(connCtx)
    93  	h := &handler{
    94  		reg:            reg,
    95  		idgen:          idgen,
    96  		conn:           conn,
    97  		respWait:       make(map[string]*requestOp),
    98  		clientSubs:     make(map[string]*ClientSubscription),
    99  		rootCtx:        rootCtx,
   100  		cancelRoot:     cancelRoot,
   101  		allowSubscribe: true,
   102  		serverSubs:     make(map[ID]*Subscription),
   103  		log:            log.Root(),
   104  	}
   105  	if conn.remoteAddr() != "" {
   106  		h.log = h.log.New("conn", conn.remoteAddr())
   107  	}
   108  	h.unsubscribeCb = newCallback(reflect.Value{}, reflect.ValueOf(h.unsubscribe))
   109  	return h
   110  }
   111  
   112  // addLimiter adds a rate limiter to the handler that will allow at most
   113  // [refillRate] cpu to be used per second. At most [maxStored] cpu time will be
   114  // stored for this limiter.
   115  // If any values are provided that would make the rate limiting trivial, then no
   116  // limiter is added.
   117  func (h *handler) addLimiter(refillRate, maxStored time.Duration) {
   118  	if refillRate <= 0 || maxStored < h.deadlineContext || h.deadlineContext <= 0 {
   119  		return
   120  	}
   121  	h.limiter = rate.NewLimiter(rate.Limit(refillRate), int(maxStored))
   122  }
   123  
   124  // handleBatch executes all messages in a batch and returns the responses.
   125  func (h *handler) handleBatch(msgs []*jsonrpcMessage) {
   126  	// Emit error response for empty batches:
   127  	if len(msgs) == 0 {
   128  		h.startCallProc(func(cp *callProc) {
   129  			h.conn.writeJSONSkipDeadline(cp.ctx, errorMessage(&invalidRequestError{"empty batch"}), h.deadlineContext > 0)
   130  		})
   131  		return
   132  	}
   133  
   134  	// Handle non-call messages first:
   135  	calls := make([]*jsonrpcMessage, 0, len(msgs))
   136  	for _, msg := range msgs {
   137  		if handled := h.handleImmediate(msg); !handled {
   138  			calls = append(calls, msg)
   139  		}
   140  	}
   141  	if len(calls) == 0 {
   142  		return
   143  	}
   144  	// Process calls on a goroutine because they may block indefinitely:
   145  	h.startCallProc(func(cp *callProc) {
   146  		answers := make([]*jsonrpcMessage, 0, len(msgs))
   147  		for _, msg := range calls {
   148  			if answer := h.handleCallMsg(cp, msg); answer != nil {
   149  				answers = append(answers, answer)
   150  			}
   151  		}
   152  		h.addSubscriptions(cp.notifiers)
   153  		if len(answers) > 0 {
   154  			h.conn.writeJSONSkipDeadline(cp.ctx, answers, h.deadlineContext > 0)
   155  		}
   156  		for _, n := range cp.notifiers {
   157  			n.activate()
   158  		}
   159  	})
   160  }
   161  
   162  // handleMsg handles a single message.
   163  func (h *handler) handleMsg(msg *jsonrpcMessage) {
   164  	if ok := h.handleImmediate(msg); ok {
   165  		return
   166  	}
   167  	h.startCallProc(func(cp *callProc) {
   168  		answer := h.handleCallMsg(cp, msg)
   169  		h.addSubscriptions(cp.notifiers)
   170  		if answer != nil {
   171  			h.conn.writeJSONSkipDeadline(cp.ctx, answer, h.deadlineContext > 0)
   172  		}
   173  		for _, n := range cp.notifiers {
   174  			n.activate()
   175  		}
   176  	})
   177  }
   178  
   179  // close cancels all requests except for inflightReq and waits for
   180  // call goroutines to shut down.
   181  func (h *handler) close(err error, inflightReq *requestOp) {
   182  	h.cancelAllRequests(err, inflightReq)
   183  	h.callWG.Wait()
   184  	h.cancelRoot()
   185  	h.cancelServerSubscriptions(err)
   186  }
   187  
   188  // addRequestOp registers a request operation.
   189  func (h *handler) addRequestOp(op *requestOp) {
   190  	for _, id := range op.ids {
   191  		h.respWait[string(id)] = op
   192  	}
   193  }
   194  
   195  // removeRequestOps stops waiting for the given request IDs.
   196  func (h *handler) removeRequestOp(op *requestOp) {
   197  	for _, id := range op.ids {
   198  		delete(h.respWait, string(id))
   199  	}
   200  }
   201  
   202  // cancelAllRequests unblocks and removes pending requests and active subscriptions.
   203  func (h *handler) cancelAllRequests(err error, inflightReq *requestOp) {
   204  	didClose := make(map[*requestOp]bool)
   205  	if inflightReq != nil {
   206  		didClose[inflightReq] = true
   207  	}
   208  
   209  	for id, op := range h.respWait {
   210  		// Remove the op so that later calls will not close op.resp again.
   211  		delete(h.respWait, id)
   212  
   213  		if !didClose[op] {
   214  			op.err = err
   215  			close(op.resp)
   216  			didClose[op] = true
   217  		}
   218  	}
   219  	for id, sub := range h.clientSubs {
   220  		delete(h.clientSubs, id)
   221  		sub.close(err)
   222  	}
   223  }
   224  
   225  func (h *handler) addSubscriptions(nn []*Notifier) {
   226  	h.subLock.Lock()
   227  	defer h.subLock.Unlock()
   228  
   229  	for _, n := range nn {
   230  		if sub := n.takeSubscription(); sub != nil {
   231  			h.serverSubs[sub.ID] = sub
   232  		}
   233  	}
   234  }
   235  
   236  // cancelServerSubscriptions removes all subscriptions and closes their error channels.
   237  func (h *handler) cancelServerSubscriptions(err error) {
   238  	h.subLock.Lock()
   239  	defer h.subLock.Unlock()
   240  
   241  	for id, s := range h.serverSubs {
   242  		s.err <- err
   243  		close(s.err)
   244  		delete(h.serverSubs, id)
   245  	}
   246  }
   247  
   248  // awaitLimit blocks until the context is marked as done or the rate limiter is
   249  // full.
   250  func (h *handler) awaitLimit(ctx context.Context) {
   251  	if h.limiter == nil {
   252  		return
   253  	}
   254  
   255  	now := time.Now()
   256  	reservation := h.limiter.ReserveN(now, int(h.deadlineContext))
   257  	delay := reservation.Delay()
   258  	reservation.CancelAt(now)
   259  
   260  	timer := time.NewTimer(delay)
   261  	select {
   262  	case <-ctx.Done():
   263  	case <-timer.C:
   264  	}
   265  	timer.Stop()
   266  }
   267  
   268  // consumeLimit removes the time since [procStart] from the rate limiter. It is
   269  // assumed that the rate limiter is full.
   270  func (h *handler) consumeLimit(procStart time.Time) {
   271  	if h.limiter == nil {
   272  		return
   273  	}
   274  
   275  	stopTime := time.Now()
   276  	processingTime := stopTime.Sub(procStart)
   277  	if processingTime > h.deadlineContext {
   278  		processingTime = h.deadlineContext
   279  	}
   280  
   281  	h.limiter.ReserveN(stopTime, int(processingTime))
   282  }
   283  
   284  // startCallProc runs fn in a new goroutine and starts tracking it in the h.calls wait group.
   285  func (h *handler) startCallProc(fn func(*callProc)) {
   286  	h.callWG.Add(1)
   287  	callFn := func() {
   288  		var (
   289  			ctx    context.Context
   290  			cancel context.CancelFunc
   291  		)
   292  		if h.deadlineContext > 0 {
   293  			ctx, cancel = context.WithTimeout(h.rootCtx, h.deadlineContext)
   294  		} else {
   295  			ctx, cancel = context.WithCancel(h.rootCtx)
   296  		}
   297  		defer h.callWG.Done()
   298  
   299  		// Capture the time before we await for processing
   300  		callStart := time.Now()
   301  		h.awaitLimit(ctx)
   302  
   303  		// If we are not limiting CPU, [procStart] will be identical to
   304  		// [callStart]
   305  		procStart := time.Now()
   306  		defer cancel()
   307  
   308  		fn(&callProc{ctx: ctx, callStart: callStart, procStart: procStart})
   309  		h.consumeLimit(procStart)
   310  	}
   311  	if h.limiter == nil {
   312  		go callFn()
   313  	} else {
   314  		callFn()
   315  	}
   316  }
   317  
   318  // handleImmediate executes non-call messages. It returns false if the message is a
   319  // call or requires a reply.
   320  func (h *handler) handleImmediate(msg *jsonrpcMessage) bool {
   321  	execStart := time.Now()
   322  	switch {
   323  	case msg.isNotification():
   324  		if strings.HasSuffix(msg.Method, notificationMethodSuffix) {
   325  			h.handleSubscriptionResult(msg)
   326  			return true
   327  		}
   328  		return false
   329  	case msg.isResponse():
   330  		h.handleResponse(msg)
   331  		h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "duration", time.Since(execStart))
   332  		return true
   333  	default:
   334  		return false
   335  	}
   336  }
   337  
   338  // handleSubscriptionResult processes subscription notifications.
   339  func (h *handler) handleSubscriptionResult(msg *jsonrpcMessage) {
   340  	var result subscriptionResult
   341  	if err := json.Unmarshal(msg.Params, &result); err != nil {
   342  		h.log.Debug("Dropping invalid subscription message")
   343  		return
   344  	}
   345  	if h.clientSubs[result.ID] != nil {
   346  		h.clientSubs[result.ID].deliver(result.Result)
   347  	}
   348  }
   349  
   350  // handleResponse processes method call responses.
   351  func (h *handler) handleResponse(msg *jsonrpcMessage) {
   352  	op := h.respWait[string(msg.ID)]
   353  	if op == nil {
   354  		h.log.Debug("Unsolicited RPC response", "reqid", idForLog{msg.ID})
   355  		return
   356  	}
   357  	delete(h.respWait, string(msg.ID))
   358  	// For normal responses, just forward the reply to Call/BatchCall.
   359  	if op.sub == nil {
   360  		op.resp <- msg
   361  		return
   362  	}
   363  	// For subscription responses, start the subscription if the server
   364  	// indicates success. EthSubscribe gets unblocked in either case through
   365  	// the op.resp channel.
   366  	defer close(op.resp)
   367  	if msg.Error != nil {
   368  		op.err = msg.Error
   369  		return
   370  	}
   371  	if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil {
   372  		go op.sub.run()
   373  		h.clientSubs[op.sub.subid] = op.sub
   374  	}
   375  }
   376  
   377  // handleCallMsg executes a call message and returns the answer.
   378  func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   379  	// [callStart] is the time the message was enqueued for handler processing
   380  	callStart := ctx.callStart
   381  	// [procStart] is the time the message cleared the [limiter] and began to be
   382  	// processed by the handler
   383  	procStart := ctx.procStart
   384  	// [execStart] is the time the message began to be executed by the handler
   385  	//
   386  	// Note: This can be different than the executionStart in [startCallProc] as
   387  	// the goroutine that handles execution may not be executed right away.
   388  	execStart := time.Now()
   389  
   390  	switch {
   391  	case msg.isNotification():
   392  		h.handleCall(ctx, msg)
   393  		h.log.Debug("Served "+msg.Method, "execTime", time.Since(execStart), "procTime", time.Since(procStart), "totalTime", time.Since(callStart))
   394  		return nil
   395  	case msg.isCall():
   396  		resp := h.handleCall(ctx, msg)
   397  		var ctx []interface{}
   398  		ctx = append(ctx, "reqid", idForLog{msg.ID}, "execTime", time.Since(execStart), "procTime", time.Since(procStart), "totalTime", time.Since(callStart))
   399  		if resp.Error != nil {
   400  			ctx = append(ctx, "err", resp.Error.Message)
   401  			if resp.Error.Data != nil {
   402  				ctx = append(ctx, "errdata", resp.Error.Data)
   403  			}
   404  			h.log.Warn("Served "+msg.Method, ctx...)
   405  		} else {
   406  			h.log.Debug("Served "+msg.Method, ctx...)
   407  		}
   408  		return resp
   409  	case msg.hasValidID():
   410  		return msg.errorResponse(&invalidRequestError{"invalid request"})
   411  	default:
   412  		return errorMessage(&invalidRequestError{"invalid request"})
   413  	}
   414  }
   415  
   416  // handleCall processes method calls.
   417  func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   418  	if msg.isSubscribe() {
   419  		return h.handleSubscribe(cp, msg)
   420  	}
   421  	var callb *callback
   422  	if msg.isUnsubscribe() {
   423  		callb = h.unsubscribeCb
   424  	} else {
   425  		callb = h.reg.callback(msg.Method)
   426  	}
   427  	if callb == nil {
   428  		return msg.errorResponse(&methodNotFoundError{method: msg.Method})
   429  	}
   430  	args, err := parsePositionalArguments(msg.Params, callb.argTypes)
   431  	if err != nil {
   432  		return msg.errorResponse(&invalidParamsError{err.Error()})
   433  	}
   434  	start := time.Now()
   435  	answer := h.runMethod(cp.ctx, msg, callb, args)
   436  
   437  	// Collect the statistics for RPC calls if metrics is enabled.
   438  	// We only care about pure rpc call. Filter out subscription.
   439  	if callb != h.unsubscribeCb {
   440  		rpcRequestGauge.Inc(1)
   441  		if answer.Error != nil {
   442  			failedRequestGauge.Inc(1)
   443  		} else {
   444  			successfulRequestGauge.Inc(1)
   445  		}
   446  		rpcServingTimer.UpdateSince(start)
   447  		if metrics.EnabledExpensive {
   448  			updateServeTimeHistogram(msg.Method, answer.Error == nil, time.Since(start))
   449  		}
   450  	}
   451  	return answer
   452  }
   453  
   454  // handleSubscribe processes *_subscribe method calls.
   455  func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage {
   456  	if !h.allowSubscribe {
   457  		return msg.errorResponse(ErrNotificationsUnsupported)
   458  	}
   459  
   460  	// Subscription method name is first argument.
   461  	name, err := parseSubscriptionName(msg.Params)
   462  	if err != nil {
   463  		return msg.errorResponse(&invalidParamsError{err.Error()})
   464  	}
   465  	namespace := msg.namespace()
   466  	callb := h.reg.subscription(namespace, name)
   467  	if callb == nil {
   468  		return msg.errorResponse(&subscriptionNotFoundError{namespace, name})
   469  	}
   470  
   471  	// Parse subscription name arg too, but remove it before calling the callback.
   472  	argTypes := append([]reflect.Type{stringType}, callb.argTypes...)
   473  	args, err := parsePositionalArguments(msg.Params, argTypes)
   474  	if err != nil {
   475  		return msg.errorResponse(&invalidParamsError{err.Error()})
   476  	}
   477  	args = args[1:]
   478  
   479  	// Install notifier in context so the subscription handler can find it.
   480  	n := &Notifier{h: h, namespace: namespace}
   481  	cp.notifiers = append(cp.notifiers, n)
   482  	ctx := context.WithValue(cp.ctx, notifierKey{}, n)
   483  
   484  	return h.runMethod(ctx, msg, callb, args)
   485  }
   486  
   487  // runMethod runs the Go callback for an RPC method.
   488  func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *callback, args []reflect.Value) *jsonrpcMessage {
   489  	result, err := callb.call(ctx, msg.Method, args)
   490  	if err != nil {
   491  		return msg.errorResponse(err)
   492  	}
   493  	return msg.response(result)
   494  }
   495  
   496  // unsubscribe is the callback function for all *_unsubscribe calls.
   497  func (h *handler) unsubscribe(ctx context.Context, id ID) (bool, error) {
   498  	h.subLock.Lock()
   499  	defer h.subLock.Unlock()
   500  
   501  	s := h.serverSubs[id]
   502  	if s == nil {
   503  		return false, ErrSubscriptionNotFound
   504  	}
   505  	close(s.err)
   506  	delete(h.serverSubs, id)
   507  	return true, nil
   508  }
   509  
   510  type idForLog struct{ json.RawMessage }
   511  
   512  func (id idForLog) String() string {
   513  	if s, err := strconv.Unquote(string(id.RawMessage)); err == nil {
   514  		return s
   515  	}
   516  	return string(id.RawMessage)
   517  }