utils.go 6.0 KB

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