github.com/devfans/go-ethereum@v1.5.10-0.20170326212234-7419d0c38291/rpc/utils.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package rpc
    18  
    19  import (
    20  	"bufio"
    21  	"context"
    22  	crand "crypto/rand"
    23  	"encoding/binary"
    24  	"encoding/hex"
    25  	"math/big"
    26  	"math/rand"
    27  	"reflect"
    28  	"strings"
    29  	"sync"
    30  	"time"
    31  	"unicode"
    32  	"unicode/utf8"
    33  )
    34  
    35  var (
    36  	subscriptionIDGenMu sync.Mutex
    37  	subscriptionIDGen   = idGenerator()
    38  )
    39  
    40  // Is this an exported - upper case - name?
    41  func isExported(name string) bool {
    42  	rune, _ := utf8.DecodeRuneInString(name)
    43  	return unicode.IsUpper(rune)
    44  }
    45  
    46  // Is this type exported or a builtin?
    47  func isExportedOrBuiltinType(t reflect.Type) bool {
    48  	for t.Kind() == reflect.Ptr {
    49  		t = t.Elem()
    50  	}
    51  	// PkgPath will be non-empty even for an exported type,
    52  	// so we need to check the type name as well.
    53  	return isExported(t.Name()) || t.PkgPath() == ""
    54  }
    55  
    56  var contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
    57  
    58  // isContextType returns an indication if the given t is of context.Context or *context.Context type
    59  func isContextType(t reflect.Type) bool {
    60  	for t.Kind() == reflect.Ptr {
    61  		t = t.Elem()
    62  	}
    63  	return t == contextType
    64  }
    65  
    66  var errorType = reflect.TypeOf((*error)(nil)).Elem()
    67  
    68  // Implements this type the error interface
    69  func isErrorType(t reflect.Type) bool {
    70  	for t.Kind() == reflect.Ptr {
    71  		t = t.Elem()
    72  	}
    73  	return t.Implements(errorType)
    74  }
    75  
    76  var subscriptionType = reflect.TypeOf((*Subscription)(nil)).Elem()
    77  
    78  // isSubscriptionType returns an indication if the given t is of Subscription or *Subscription type
    79  func isSubscriptionType(t reflect.Type) bool {
    80  	for t.Kind() == reflect.Ptr {
    81  		t = t.Elem()
    82  	}
    83  	return t == subscriptionType
    84  }
    85  
    86  // isPubSub tests whether the given method has as as first argument a context.Context
    87  // and returns the pair (Subscription, error)
    88  func isPubSub(methodType reflect.Type) bool {
    89  	// numIn(0) is the receiver type
    90  	if methodType.NumIn() < 2 || methodType.NumOut() != 2 {
    91  		return false
    92  	}
    93  
    94  	return isContextType(methodType.In(1)) &&
    95  		isSubscriptionType(methodType.Out(0)) &&
    96  		isErrorType(methodType.Out(1))
    97  }
    98  
    99  // formatName will convert to first character to lower case
   100  func formatName(name string) string {
   101  	ret := []rune(name)
   102  	if len(ret) > 0 {
   103  		ret[0] = unicode.ToLower(ret[0])
   104  	}
   105  	return string(ret)
   106  }
   107  
   108  var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
   109  
   110  // Indication if this type should be serialized in hex
   111  func isHexNum(t reflect.Type) bool {
   112  	if t == nil {
   113  		return false
   114  	}
   115  	for t.Kind() == reflect.Ptr {
   116  		t = t.Elem()
   117  	}
   118  
   119  	return t == bigIntType
   120  }
   121  
   122  var blockNumberType = reflect.TypeOf((*BlockNumber)(nil)).Elem()
   123  
   124  // Indication if the given block is a BlockNumber
   125  func isBlockNumber(t reflect.Type) bool {
   126  	if t == nil {
   127  		return false
   128  	}
   129  
   130  	for t.Kind() == reflect.Ptr {
   131  		t = t.Elem()
   132  	}
   133  
   134  	return t == blockNumberType
   135  }
   136  
   137  // suitableCallbacks iterates over the methods of the given type. It will determine if a method satisfies the criteria
   138  // for a RPC callback or a subscription callback and adds it to the collection of callbacks or subscriptions. See server
   139  // documentation for a summary of these criteria.
   140  func suitableCallbacks(rcvr reflect.Value, typ reflect.Type) (callbacks, subscriptions) {
   141  	callbacks := make(callbacks)
   142  	subscriptions := make(subscriptions)
   143  
   144  METHODS:
   145  	for m := 0; m < typ.NumMethod(); m++ {
   146  		method := typ.Method(m)
   147  		mtype := method.Type
   148  		mname := formatName(method.Name)
   149  		if method.PkgPath != "" { // method must be exported
   150  			continue
   151  		}
   152  
   153  		var h callback
   154  		h.isSubscribe = isPubSub(mtype)
   155  		h.rcvr = rcvr
   156  		h.method = method
   157  		h.errPos = -1
   158  
   159  		firstArg := 1
   160  		numIn := mtype.NumIn()
   161  		if numIn >= 2 && mtype.In(1) == contextType {
   162  			h.hasCtx = true
   163  			firstArg = 2
   164  		}
   165  
   166  		if h.isSubscribe {
   167  			h.argTypes = make([]reflect.Type, numIn-firstArg) // skip rcvr type
   168  			for i := firstArg; i < numIn; i++ {
   169  				argType := mtype.In(i)
   170  				if isExportedOrBuiltinType(argType) {
   171  					h.argTypes[i-firstArg] = argType
   172  				} else {
   173  					continue METHODS
   174  				}
   175  			}
   176  
   177  			subscriptions[mname] = &h
   178  			continue METHODS
   179  		}
   180  
   181  		// determine method arguments, ignore first arg since it's the receiver type
   182  		// Arguments must be exported or builtin types
   183  		h.argTypes = make([]reflect.Type, numIn-firstArg)
   184  		for i := firstArg; i < numIn; i++ {
   185  			argType := mtype.In(i)
   186  			if !isExportedOrBuiltinType(argType) {
   187  				continue METHODS
   188  			}
   189  			h.argTypes[i-firstArg] = argType
   190  		}
   191  
   192  		// check that all returned values are exported or builtin types
   193  		for i := 0; i < mtype.NumOut(); i++ {
   194  			if !isExportedOrBuiltinType(mtype.Out(i)) {
   195  				continue METHODS
   196  			}
   197  		}
   198  
   199  		// when a method returns an error it must be the last returned value
   200  		h.errPos = -1
   201  		for i := 0; i < mtype.NumOut(); i++ {
   202  			if isErrorType(mtype.Out(i)) {
   203  				h.errPos = i
   204  				break
   205  			}
   206  		}
   207  
   208  		if h.errPos >= 0 && h.errPos != mtype.NumOut()-1 {
   209  			continue METHODS
   210  		}
   211  
   212  		switch mtype.NumOut() {
   213  		case 0, 1:
   214  			break
   215  		case 2:
   216  			if h.errPos == -1 { // method must one return value and 1 error
   217  				continue METHODS
   218  			}
   219  			break
   220  		default:
   221  			continue METHODS
   222  		}
   223  
   224  		callbacks[mname] = &h
   225  	}
   226  
   227  	return callbacks, subscriptions
   228  }
   229  
   230  // idGenerator helper utility that generates a (pseudo) random sequence of
   231  // bytes that are used to generate identifiers.
   232  func idGenerator() *rand.Rand {
   233  	if seed, err := binary.ReadVarint(bufio.NewReader(crand.Reader)); err == nil {
   234  		return rand.New(rand.NewSource(seed))
   235  	}
   236  	return rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
   237  }
   238  
   239  // NewID generates a identifier that can be used as an identifier in the RPC interface.
   240  // e.g. filter and subscription identifier.
   241  func NewID() ID {
   242  	subscriptionIDGenMu.Lock()
   243  	defer subscriptionIDGenMu.Unlock()
   244  
   245  	id := make([]byte, 16)
   246  	for i := 0; i < len(id); i += 7 {
   247  		val := subscriptionIDGen.Int63()
   248  		for j := 0; i+j < len(id) && j < 7; j++ {
   249  			id[i+j] = byte(val)
   250  			val >>= 8
   251  		}
   252  	}
   253  
   254  	rpcId := hex.EncodeToString(id)
   255  	// rpc ID's are RPC quantities, no leading zero's and 0 is 0x0
   256  	rpcId = strings.TrimLeft(rpcId, "0")
   257  	if rpcId == "" {
   258  		rpcId = "0"
   259  	}
   260  
   261  	return ID("0x" + rpcId)
   262  }