good morning!!!!

Skip to content
Snippets Groups Projects
utils.go 6.54 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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/>.
    
    
    	crand "crypto/rand"
    	"encoding/binary"
    
    	"encoding/hex"
    	"math/big"
    
    var (
    	subscriptionIDGenMu sync.Mutex
    	subscriptionIDGen   = idGenerator()
    )
    
    
    // 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() == ""
    }
    
    
    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
    }
    
    
    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()
    
    
    // isSubscriptionType returns an indication if the given t is of Subscription or *Subscription type
    
    func isSubscriptionType(t reflect.Type) bool {
    	for t.Kind() == reflect.Ptr {
    		t = t.Elem()
    	}
    	return t == subscriptionType
    }
    
    
    // isPubSub tests whether the given method has as as first argument a context.Context
    // and returns the pair (Subscription, error)
    
    func isPubSub(methodType reflect.Type) bool {
    
    	// numIn(0) is the receiver type
    	if methodType.NumIn() < 2 || methodType.NumOut() != 2 {
    
    
    	return isContextType(methodType.In(1)) &&
    		isSubscriptionType(methodType.Out(0)) &&
    		isErrorType(methodType.Out(1))
    
    }
    
    // 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
    
    
    		firstArg := 1
    		numIn := mtype.NumIn()
    		if numIn >= 2 && mtype.In(1) == contextType {
    			h.hasCtx = true
    			firstArg = 2
    		}
    
    
    			h.argTypes = make([]reflect.Type, numIn-firstArg) // skip rcvr type
    			for i := firstArg; i < numIn; i++ {
    
    				argType := mtype.In(i)
    				if isExportedOrBuiltinType(argType) {
    
    					h.argTypes[i-firstArg] = argType
    
    				} 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
    
    		h.argTypes = make([]reflect.Type, numIn-firstArg)
    		for i := firstArg; i < numIn; i++ {
    
    			argType := mtype.In(i)
    			if !isExportedOrBuiltinType(argType) {
    				continue METHODS
    			}
    
    			h.argTypes[i-firstArg] = argType
    
    		}
    
    		// 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
    }
    
    
    // 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
    		}
    
    
    	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)