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