utils.go 6.5 KB

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