utils.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. // suitableCallbacks iterates over the methods of the given type. It will determine if a method satisfies the criteria
  105. // for a RPC callback or a subscription callback and adds it to the collection of callbacks or subscriptions. See server
  106. // documentation for a summary of these criteria.
  107. func suitableCallbacks(rcvr reflect.Value, typ reflect.Type) (callbacks, subscriptions) {
  108. callbacks := make(callbacks)
  109. subscriptions := make(subscriptions)
  110. METHODS:
  111. for m := 0; m < typ.NumMethod(); m++ {
  112. method := typ.Method(m)
  113. mtype := method.Type
  114. mname := formatName(method.Name)
  115. if method.PkgPath != "" { // method must be exported
  116. continue
  117. }
  118. var h callback
  119. h.isSubscribe = isPubSub(mtype)
  120. h.rcvr = rcvr
  121. h.method = method
  122. h.errPos = -1
  123. firstArg := 1
  124. numIn := mtype.NumIn()
  125. if numIn >= 2 && mtype.In(1) == contextType {
  126. h.hasCtx = true
  127. firstArg = 2
  128. }
  129. if h.isSubscribe {
  130. h.argTypes = make([]reflect.Type, numIn-firstArg) // skip rcvr type
  131. for i := firstArg; i < numIn; i++ {
  132. argType := mtype.In(i)
  133. if isExportedOrBuiltinType(argType) {
  134. h.argTypes[i-firstArg] = argType
  135. } else {
  136. continue METHODS
  137. }
  138. }
  139. subscriptions[mname] = &h
  140. continue METHODS
  141. }
  142. // determine method arguments, ignore first arg since it's the receiver type
  143. // Arguments must be exported or builtin types
  144. h.argTypes = make([]reflect.Type, numIn-firstArg)
  145. for i := firstArg; i < numIn; i++ {
  146. argType := mtype.In(i)
  147. if !isExportedOrBuiltinType(argType) {
  148. continue METHODS
  149. }
  150. h.argTypes[i-firstArg] = argType
  151. }
  152. // check that all returned values are exported or builtin types
  153. for i := 0; i < mtype.NumOut(); i++ {
  154. if !isExportedOrBuiltinType(mtype.Out(i)) {
  155. continue METHODS
  156. }
  157. }
  158. // when a method returns an error it must be the last returned value
  159. h.errPos = -1
  160. for i := 0; i < mtype.NumOut(); i++ {
  161. if isErrorType(mtype.Out(i)) {
  162. h.errPos = i
  163. break
  164. }
  165. }
  166. if h.errPos >= 0 && h.errPos != mtype.NumOut()-1 {
  167. continue METHODS
  168. }
  169. switch mtype.NumOut() {
  170. case 0, 1, 2:
  171. if mtype.NumOut() == 2 && h.errPos == -1 { // method must one return value and 1 error
  172. continue METHODS
  173. }
  174. callbacks[mname] = &h
  175. }
  176. }
  177. return callbacks, subscriptions
  178. }
  179. // idGenerator helper utility that generates a (pseudo) random sequence of
  180. // bytes that are used to generate identifiers.
  181. func idGenerator() *rand.Rand {
  182. if seed, err := binary.ReadVarint(bufio.NewReader(crand.Reader)); err == nil {
  183. return rand.New(rand.NewSource(seed))
  184. }
  185. return rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
  186. }
  187. // NewID generates a identifier that can be used as an identifier in the RPC interface.
  188. // e.g. filter and subscription identifier.
  189. func NewID() ID {
  190. subscriptionIDGenMu.Lock()
  191. defer subscriptionIDGenMu.Unlock()
  192. id := make([]byte, 16)
  193. for i := 0; i < len(id); i += 7 {
  194. val := subscriptionIDGen.Int63()
  195. for j := 0; i+j < len(id) && j < 7; j++ {
  196. id[i+j] = byte(val)
  197. val >>= 8
  198. }
  199. }
  200. rpcId := hex.EncodeToString(id)
  201. // rpc ID's are RPC quantities, no leading zero's and 0 is 0x0
  202. rpcId = strings.TrimLeft(rpcId, "0")
  203. if rpcId == "" {
  204. rpcId = "0"
  205. }
  206. return ID("0x" + rpcId)
  207. }