utils.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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 v2
  17. import (
  18. "crypto/rand"
  19. "encoding/hex"
  20. "errors"
  21. "math/big"
  22. "reflect"
  23. "unicode"
  24. "unicode/utf8"
  25. )
  26. // Is this an exported - upper case - name?
  27. func isExported(name string) bool {
  28. rune, _ := utf8.DecodeRuneInString(name)
  29. return unicode.IsUpper(rune)
  30. }
  31. // Is this type exported or a builtin?
  32. func isExportedOrBuiltinType(t reflect.Type) bool {
  33. for t.Kind() == reflect.Ptr {
  34. t = t.Elem()
  35. }
  36. // PkgPath will be non-empty even for an exported type,
  37. // so we need to check the type name as well.
  38. return isExported(t.Name()) || t.PkgPath() == ""
  39. }
  40. var errorType = reflect.TypeOf((*error)(nil)).Elem()
  41. // Implements this type the error interface
  42. func isErrorType(t reflect.Type) bool {
  43. for t.Kind() == reflect.Ptr {
  44. t = t.Elem()
  45. }
  46. return t.Implements(errorType)
  47. }
  48. var subscriptionType = reflect.TypeOf((*Subscription)(nil)).Elem()
  49. func isSubscriptionType(t reflect.Type) bool {
  50. for t.Kind() == reflect.Ptr {
  51. t = t.Elem()
  52. }
  53. return t == subscriptionType
  54. }
  55. // isPubSub tests whether the given method return the pair (v2.Subscription, error)
  56. func isPubSub(methodType reflect.Type) bool {
  57. if methodType.NumOut() != 2 {
  58. return false
  59. }
  60. return isSubscriptionType(methodType.Out(0)) && isErrorType(methodType.Out(1))
  61. }
  62. // formatName will convert to first character to lower case
  63. func formatName(name string) string {
  64. ret := []rune(name)
  65. if len(ret) > 0 {
  66. ret[0] = unicode.ToLower(ret[0])
  67. }
  68. return string(ret)
  69. }
  70. var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
  71. // Indication if this type should be serialized in hex
  72. func isHexNum(t reflect.Type) bool {
  73. if t == nil {
  74. return false
  75. }
  76. for t.Kind() == reflect.Ptr {
  77. t = t.Elem()
  78. }
  79. return t == bigIntType
  80. }
  81. var blockNumberType = reflect.TypeOf((*BlockNumber)(nil)).Elem()
  82. // Indication if the given block is a BlockNumber
  83. func isBlockNumber(t reflect.Type) bool {
  84. if t == nil {
  85. return false
  86. }
  87. for t.Kind() == reflect.Ptr {
  88. t = t.Elem()
  89. }
  90. return t == blockNumberType
  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. if h.isSubscribe {
  112. h.argTypes = make([]reflect.Type, mtype.NumIn()-1) // skip rcvr type
  113. for i := 1; i < mtype.NumIn(); i++ {
  114. argType := mtype.In(i)
  115. if isExportedOrBuiltinType(argType) {
  116. h.argTypes[i-1] = argType
  117. } else {
  118. continue METHODS
  119. }
  120. }
  121. subscriptions[mname] = &h
  122. continue METHODS
  123. }
  124. numIn := mtype.NumIn()
  125. // determine method arguments, ignore first arg since it's the receiver type
  126. // Arguments must be exported or builtin types
  127. h.argTypes = make([]reflect.Type, numIn-1)
  128. for i := 1; i < numIn; i++ {
  129. argType := mtype.In(i)
  130. if !isExportedOrBuiltinType(argType) {
  131. continue METHODS
  132. }
  133. h.argTypes[i-1] = argType
  134. }
  135. // check that all returned values are exported or builtin types
  136. for i := 0; i < mtype.NumOut(); i++ {
  137. if !isExportedOrBuiltinType(mtype.Out(i)) {
  138. continue METHODS
  139. }
  140. }
  141. // when a method returns an error it must be the last returned value
  142. h.errPos = -1
  143. for i := 0; i < mtype.NumOut(); i++ {
  144. if isErrorType(mtype.Out(i)) {
  145. h.errPos = i
  146. break
  147. }
  148. }
  149. if h.errPos >= 0 && h.errPos != mtype.NumOut()-1 {
  150. continue METHODS
  151. }
  152. switch mtype.NumOut() {
  153. case 0, 1:
  154. break
  155. case 2:
  156. if h.errPos == -1 { // method must one return value and 1 error
  157. continue METHODS
  158. }
  159. break
  160. default:
  161. continue METHODS
  162. }
  163. callbacks[mname] = &h
  164. }
  165. return callbacks, subscriptions
  166. }
  167. func newSubscriptionId() (string, error) {
  168. var subid [16]byte
  169. n, _ := rand.Read(subid[:])
  170. if n != 16 {
  171. return "", errors.New("Unable to generate subscription id")
  172. }
  173. return "0x" + hex.EncodeToString(subid[:]), nil
  174. }