github.com/MetalBlockchain/subnet-evm@v0.4.9/rpc/service.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2019 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package rpc 28 29 import ( 30 "context" 31 "errors" 32 "fmt" 33 "reflect" 34 "runtime" 35 "strings" 36 "sync" 37 "unicode" 38 39 "github.com/ethereum/go-ethereum/log" 40 ) 41 42 var ( 43 contextType = reflect.TypeOf((*context.Context)(nil)).Elem() 44 errorType = reflect.TypeOf((*error)(nil)).Elem() 45 subscriptionType = reflect.TypeOf(Subscription{}) 46 stringType = reflect.TypeOf("") 47 ) 48 49 type serviceRegistry struct { 50 mu sync.Mutex 51 services map[string]service 52 } 53 54 // service represents a registered object. 55 type service struct { 56 name string // name for service 57 callbacks map[string]*callback // registered handlers 58 subscriptions map[string]*callback // available subscriptions/notifications 59 } 60 61 // callback is a method callback which was registered in the server 62 type callback struct { 63 fn reflect.Value // the function 64 rcvr reflect.Value // receiver object of method, set if fn is method 65 argTypes []reflect.Type // input argument types 66 hasCtx bool // method's first argument is a context (not included in argTypes) 67 errPos int // err return idx, of -1 when method cannot return error 68 isSubscribe bool // true if this is a subscription callback 69 } 70 71 func (r *serviceRegistry) registerName(name string, rcvr interface{}) error { 72 rcvrVal := reflect.ValueOf(rcvr) 73 if name == "" { 74 return fmt.Errorf("no service name for type %s", rcvrVal.Type().String()) 75 } 76 callbacks := suitableCallbacks(rcvrVal) 77 if len(callbacks) == 0 { 78 return fmt.Errorf("service %T doesn't have any suitable methods/subscriptions to expose", rcvr) 79 } 80 81 r.mu.Lock() 82 defer r.mu.Unlock() 83 if r.services == nil { 84 r.services = make(map[string]service) 85 } 86 svc, ok := r.services[name] 87 if !ok { 88 svc = service{ 89 name: name, 90 callbacks: make(map[string]*callback), 91 subscriptions: make(map[string]*callback), 92 } 93 r.services[name] = svc 94 } 95 for name, cb := range callbacks { 96 if cb.isSubscribe { 97 svc.subscriptions[name] = cb 98 } else { 99 svc.callbacks[name] = cb 100 } 101 } 102 return nil 103 } 104 105 // callback returns the callback corresponding to the given RPC method name. 106 func (r *serviceRegistry) callback(method string) *callback { 107 elem := strings.SplitN(method, serviceMethodSeparator, 2) 108 if len(elem) != 2 { 109 return nil 110 } 111 r.mu.Lock() 112 defer r.mu.Unlock() 113 return r.services[elem[0]].callbacks[elem[1]] 114 } 115 116 // subscription returns a subscription callback in the given service. 117 func (r *serviceRegistry) subscription(service, name string) *callback { 118 r.mu.Lock() 119 defer r.mu.Unlock() 120 return r.services[service].subscriptions[name] 121 } 122 123 // suitableCallbacks iterates over the methods of the given type. It determines if a method 124 // satisfies the criteria for a RPC callback or a subscription callback and adds it to the 125 // collection of callbacks. See server documentation for a summary of these criteria. 126 func suitableCallbacks(receiver reflect.Value) map[string]*callback { 127 typ := receiver.Type() 128 callbacks := make(map[string]*callback) 129 for m := 0; m < typ.NumMethod(); m++ { 130 method := typ.Method(m) 131 if method.PkgPath != "" { 132 continue // method not exported 133 } 134 cb := newCallback(receiver, method.Func) 135 if cb == nil { 136 continue // function invalid 137 } 138 name := formatName(method.Name) 139 callbacks[name] = cb 140 } 141 return callbacks 142 } 143 144 // newCallback turns fn (a function) into a callback object. It returns nil if the function 145 // is unsuitable as an RPC callback. 146 func newCallback(receiver, fn reflect.Value) *callback { 147 fntype := fn.Type() 148 c := &callback{fn: fn, rcvr: receiver, errPos: -1, isSubscribe: isPubSub(fntype)} 149 // Determine parameter types. They must all be exported or builtin types. 150 c.makeArgTypes() 151 152 // Verify return types. The function must return at most one error 153 // and/or one other non-error value. 154 outs := make([]reflect.Type, fntype.NumOut()) 155 for i := 0; i < fntype.NumOut(); i++ { 156 outs[i] = fntype.Out(i) 157 } 158 if len(outs) > 2 { 159 return nil 160 } 161 // If an error is returned, it must be the last returned value. 162 switch { 163 case len(outs) == 1 && isErrorType(outs[0]): 164 c.errPos = 0 165 case len(outs) == 2: 166 if isErrorType(outs[0]) || !isErrorType(outs[1]) { 167 return nil 168 } 169 c.errPos = 1 170 } 171 return c 172 } 173 174 // makeArgTypes composes the argTypes list. 175 func (c *callback) makeArgTypes() { 176 fntype := c.fn.Type() 177 // Skip receiver and context.Context parameter (if present). 178 firstArg := 0 179 if c.rcvr.IsValid() { 180 firstArg++ 181 } 182 if fntype.NumIn() > firstArg && fntype.In(firstArg) == contextType { 183 c.hasCtx = true 184 firstArg++ 185 } 186 // Add all remaining parameters. 187 c.argTypes = make([]reflect.Type, fntype.NumIn()-firstArg) 188 for i := firstArg; i < fntype.NumIn(); i++ { 189 c.argTypes[i-firstArg] = fntype.In(i) 190 } 191 } 192 193 // call invokes the callback. 194 func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res interface{}, errRes error) { 195 // Create the argument slice. 196 fullargs := make([]reflect.Value, 0, 2+len(args)) 197 if c.rcvr.IsValid() { 198 fullargs = append(fullargs, c.rcvr) 199 } 200 if c.hasCtx { 201 fullargs = append(fullargs, reflect.ValueOf(ctx)) 202 } 203 fullargs = append(fullargs, args...) 204 205 // Catch panic while running the callback. 206 defer func() { 207 if err := recover(); err != nil { 208 const size = 64 << 10 209 buf := make([]byte, size) 210 buf = buf[:runtime.Stack(buf, false)] 211 log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf)) 212 errRes = errors.New("method handler crashed") 213 } 214 }() 215 // Run the callback. 216 results := c.fn.Call(fullargs) 217 if len(results) == 0 { 218 return nil, nil 219 } 220 if c.errPos >= 0 && !results[c.errPos].IsNil() { 221 // Method has returned non-nil error value. 222 err := results[c.errPos].Interface().(error) 223 return reflect.Value{}, err 224 } 225 return results[0].Interface(), nil 226 } 227 228 // Is t context.Context or *context.Context? 229 func isContextType(t reflect.Type) bool { 230 for t.Kind() == reflect.Ptr { 231 t = t.Elem() 232 } 233 return t == contextType 234 } 235 236 // Does t satisfy the error interface? 237 func isErrorType(t reflect.Type) bool { 238 for t.Kind() == reflect.Ptr { 239 t = t.Elem() 240 } 241 return t.Implements(errorType) 242 } 243 244 // Is t Subscription or *Subscription? 245 func isSubscriptionType(t reflect.Type) bool { 246 for t.Kind() == reflect.Ptr { 247 t = t.Elem() 248 } 249 return t == subscriptionType 250 } 251 252 // isPubSub tests whether the given method has as as first argument a context.Context and 253 // returns the pair (Subscription, error). 254 func isPubSub(methodType reflect.Type) bool { 255 // numIn(0) is the receiver type 256 if methodType.NumIn() < 2 || methodType.NumOut() != 2 { 257 return false 258 } 259 return isContextType(methodType.In(1)) && 260 isSubscriptionType(methodType.Out(0)) && 261 isErrorType(methodType.Out(1)) 262 } 263 264 // formatName converts to first character of name to lowercase. 265 func formatName(name string) string { 266 ret := []rune(name) 267 if len(ret) > 0 { 268 ret[0] = unicode.ToLower(ret[0]) 269 } 270 return string(ret) 271 }