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