| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | package rpc | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2017-03-22 18:20:33 +01:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	crand "crypto/rand" | 
					
						
							|  |  |  | 	"encoding/binary" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"encoding/hex" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2016-09-01 11:48:15 +02:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"unicode" | 
					
						
							|  |  |  | 	"unicode/utf8" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	subscriptionIDGenMu sync.Mutex | 
					
						
							|  |  |  | 	subscriptionIDGen   = idGenerator() | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // Is this an exported - upper case - name? | 
					
						
							|  |  |  | func isExported(name string) bool { | 
					
						
							|  |  |  | 	rune, _ := utf8.DecodeRuneInString(name) | 
					
						
							|  |  |  | 	return unicode.IsUpper(rune) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Is this type exported or a builtin? | 
					
						
							|  |  |  | func isExportedOrBuiltinType(t reflect.Type) bool { | 
					
						
							|  |  |  | 	for t.Kind() == reflect.Ptr { | 
					
						
							|  |  |  | 		t = t.Elem() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// PkgPath will be non-empty even for an exported type, | 
					
						
							|  |  |  | 	// so we need to check the type name as well. | 
					
						
							|  |  |  | 	return isExported(t.Name()) || t.PkgPath() == "" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isContextType returns an indication if the given t is of context.Context or *context.Context type | 
					
						
							|  |  |  | func isContextType(t reflect.Type) bool { | 
					
						
							|  |  |  | 	for t.Kind() == reflect.Ptr { | 
					
						
							|  |  |  | 		t = t.Elem() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return t == contextType | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | var errorType = reflect.TypeOf((*error)(nil)).Elem() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Implements this type the error interface | 
					
						
							|  |  |  | func isErrorType(t reflect.Type) bool { | 
					
						
							|  |  |  | 	for t.Kind() == reflect.Ptr { | 
					
						
							|  |  |  | 		t = t.Elem() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return t.Implements(errorType) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var subscriptionType = reflect.TypeOf((*Subscription)(nil)).Elem() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // isSubscriptionType returns an indication if the given t is of Subscription or *Subscription type | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | func isSubscriptionType(t reflect.Type) bool { | 
					
						
							|  |  |  | 	for t.Kind() == reflect.Ptr { | 
					
						
							|  |  |  | 		t = t.Elem() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return t == subscriptionType | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // isPubSub tests whether the given method has as as first argument a context.Context | 
					
						
							|  |  |  | // and returns the pair (Subscription, error) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | func isPubSub(methodType reflect.Type) bool { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	// numIn(0) is the receiver type | 
					
						
							|  |  |  | 	if methodType.NumIn() < 2 || methodType.NumOut() != 2 { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return isContextType(methodType.In(1)) && | 
					
						
							|  |  |  | 		isSubscriptionType(methodType.Out(0)) && | 
					
						
							|  |  |  | 		isErrorType(methodType.Out(1)) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // formatName will convert to first character to lower case | 
					
						
							|  |  |  | func formatName(name string) string { | 
					
						
							|  |  |  | 	ret := []rune(name) | 
					
						
							|  |  |  | 	if len(ret) > 0 { | 
					
						
							|  |  |  | 		ret[0] = unicode.ToLower(ret[0]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return string(ret) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Indication if this type should be serialized in hex | 
					
						
							|  |  |  | func isHexNum(t reflect.Type) bool { | 
					
						
							|  |  |  | 	if t == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for t.Kind() == reflect.Ptr { | 
					
						
							|  |  |  | 		t = t.Elem() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return t == bigIntType | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var blockNumberType = reflect.TypeOf((*BlockNumber)(nil)).Elem() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Indication if the given block is a BlockNumber | 
					
						
							|  |  |  | func isBlockNumber(t reflect.Type) bool { | 
					
						
							|  |  |  | 	if t == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for t.Kind() == reflect.Ptr { | 
					
						
							|  |  |  | 		t = t.Elem() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return t == blockNumberType | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // suitableCallbacks iterates over the methods of the given type. It will determine if a method satisfies the criteria | 
					
						
							|  |  |  | // for a RPC callback or a subscription callback and adds it to the collection of callbacks or subscriptions. See server | 
					
						
							|  |  |  | // documentation for a summary of these criteria. | 
					
						
							|  |  |  | func suitableCallbacks(rcvr reflect.Value, typ reflect.Type) (callbacks, subscriptions) { | 
					
						
							|  |  |  | 	callbacks := make(callbacks) | 
					
						
							|  |  |  | 	subscriptions := make(subscriptions) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | METHODS: | 
					
						
							|  |  |  | 	for m := 0; m < typ.NumMethod(); m++ { | 
					
						
							|  |  |  | 		method := typ.Method(m) | 
					
						
							|  |  |  | 		mtype := method.Type | 
					
						
							|  |  |  | 		mname := formatName(method.Name) | 
					
						
							|  |  |  | 		if method.PkgPath != "" { // method must be exported | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var h callback | 
					
						
							|  |  |  | 		h.isSubscribe = isPubSub(mtype) | 
					
						
							|  |  |  | 		h.rcvr = rcvr | 
					
						
							|  |  |  | 		h.method = method | 
					
						
							|  |  |  | 		h.errPos = -1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 		firstArg := 1 | 
					
						
							|  |  |  | 		numIn := mtype.NumIn() | 
					
						
							|  |  |  | 		if numIn >= 2 && mtype.In(1) == contextType { | 
					
						
							|  |  |  | 			h.hasCtx = true | 
					
						
							|  |  |  | 			firstArg = 2 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		if h.isSubscribe { | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 			h.argTypes = make([]reflect.Type, numIn-firstArg) // skip rcvr type | 
					
						
							|  |  |  | 			for i := firstArg; i < numIn; i++ { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 				argType := mtype.In(i) | 
					
						
							|  |  |  | 				if isExportedOrBuiltinType(argType) { | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 					h.argTypes[i-firstArg] = argType | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					continue METHODS | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subscriptions[mname] = &h | 
					
						
							|  |  |  | 			continue METHODS | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// determine method arguments, ignore first arg since it's the receiver type | 
					
						
							|  |  |  | 		// Arguments must be exported or builtin types | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 		h.argTypes = make([]reflect.Type, numIn-firstArg) | 
					
						
							|  |  |  | 		for i := firstArg; i < numIn; i++ { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			argType := mtype.In(i) | 
					
						
							|  |  |  | 			if !isExportedOrBuiltinType(argType) { | 
					
						
							|  |  |  | 				continue METHODS | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 			h.argTypes[i-firstArg] = argType | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// check that all returned values are exported or builtin types | 
					
						
							|  |  |  | 		for i := 0; i < mtype.NumOut(); i++ { | 
					
						
							|  |  |  | 			if !isExportedOrBuiltinType(mtype.Out(i)) { | 
					
						
							|  |  |  | 				continue METHODS | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// when a method returns an error it must be the last returned value | 
					
						
							|  |  |  | 		h.errPos = -1 | 
					
						
							|  |  |  | 		for i := 0; i < mtype.NumOut(); i++ { | 
					
						
							|  |  |  | 			if isErrorType(mtype.Out(i)) { | 
					
						
							|  |  |  | 				h.errPos = i | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if h.errPos >= 0 && h.errPos != mtype.NumOut()-1 { | 
					
						
							|  |  |  | 			continue METHODS | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch mtype.NumOut() { | 
					
						
							|  |  |  | 		case 0, 1: | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		case 2: | 
					
						
							|  |  |  | 			if h.errPos == -1 { // method must one return value and 1 error | 
					
						
							|  |  |  | 				continue METHODS | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			continue METHODS | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		callbacks[mname] = &h | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return callbacks, subscriptions | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // idGenerator helper utility that generates a (pseudo) random sequence of | 
					
						
							|  |  |  | // bytes that are used to generate identifiers. | 
					
						
							|  |  |  | func idGenerator() *rand.Rand { | 
					
						
							|  |  |  | 	if seed, err := binary.ReadVarint(bufio.NewReader(crand.Reader)); err == nil { | 
					
						
							|  |  |  | 		return rand.New(rand.NewSource(seed)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewID generates a identifier that can be used as an identifier in the RPC interface. | 
					
						
							|  |  |  | // e.g. filter and subscription identifier. | 
					
						
							|  |  |  | func NewID() ID { | 
					
						
							|  |  |  | 	subscriptionIDGenMu.Lock() | 
					
						
							|  |  |  | 	defer subscriptionIDGenMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	id := make([]byte, 16) | 
					
						
							|  |  |  | 	for i := 0; i < len(id); i += 7 { | 
					
						
							|  |  |  | 		val := subscriptionIDGen.Int63() | 
					
						
							|  |  |  | 		for j := 0; i+j < len(id) && j < 7; j++ { | 
					
						
							|  |  |  | 			id[i+j] = byte(val) | 
					
						
							|  |  |  | 			val >>= 8 | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-01 11:48:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rpcId := hex.EncodeToString(id) | 
					
						
							|  |  |  | 	// rpc ID's are RPC quantities, no leading zero's and 0 is 0x0 | 
					
						
							|  |  |  | 	rpcId = strings.TrimLeft(rpcId, "0") | 
					
						
							|  |  |  | 	if rpcId == "" { | 
					
						
							|  |  |  | 		rpcId = "0" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ID("0x" + rpcId) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } |