* p2p/discover, p2p/discv5: add marshaling methods to Node * p2p/netutil: make Netlist decodable from TOML * common/math: encode nil HexOrDecimal256 as 0x0 * cmd/geth: add --config file flag * cmd/geth: add missing license header * eth: prettify Config again, fix tests * eth: use gasprice.Config instead of duplicating its fields * eth/gasprice: hide nil default from dumpconfig output * cmd/geth: hide genesis block in dumpconfig output * node: make tests compile * console: fix tests * cmd/geth: make TOML keys look exactly like Go struct fields * p2p: use discovery by default This makes the zero Config slightly more useful. It also fixes package node tests because Node detects reuse of the datadir through the NodeDatabase. * cmd/geth: make ethstats URL settable through config file * cmd/faucet: fix configuration * cmd/geth: dedup attach tests * eth: add comment for DefaultConfig * eth: pass downloader.SyncMode in Config This removes the FastSync, LightSync flags in favour of a more general SyncMode flag. * cmd/utils: remove jitvm flags * cmd/utils: make mutually exclusive flag error prettier It now reads: Fatal: flags --dev, --testnet can't be used at the same time * p2p: fix typo * node: add DefaultConfig, use it for geth * mobile: add missing NoDiscovery option * cmd/utils: drop MakeNode This exposed a couple of places that needed to be updated to use node.DefaultConfig. * node: fix typo * eth: make fast sync the default mode * cmd/utils: remove IPCApiFlag (unused) * node: remove default IPC path Set it in the frontends instead. * cmd/geth: add --syncmode * cmd/utils: make --ipcdisable and --ipcpath mutually exclusive * cmd/utils: don't enable WS, HTTP when setting addr * cmd/utils: fix --identity
		
			
				
	
	
		
			479 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			479 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package toml encodes and decodes the TOML configuration format using reflection.
 | |
| //
 | |
| // This library is compatible with TOML version v0.4.0.
 | |
| package toml
 | |
| 
 | |
| import (
 | |
| 	"encoding"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"io/ioutil"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/naoina/toml/ast"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	tableSeparator = '.'
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	escapeReplacer = strings.NewReplacer(
 | |
| 		"\b", "\\n",
 | |
| 		"\f", "\\f",
 | |
| 		"\n", "\\n",
 | |
| 		"\r", "\\r",
 | |
| 		"\t", "\\t",
 | |
| 	)
 | |
| 	underscoreReplacer = strings.NewReplacer(
 | |
| 		"_", "",
 | |
| 	)
 | |
| )
 | |
| 
 | |
| var timeType = reflect.TypeOf(time.Time{})
 | |
| 
 | |
| // Unmarshal parses the TOML data and stores the result in the value pointed to by v.
 | |
| //
 | |
| // Unmarshal will mapped to v that according to following rules:
 | |
| //
 | |
| //	TOML strings to string
 | |
| //	TOML integers to any int type
 | |
| //	TOML floats to float32 or float64
 | |
| //	TOML booleans to bool
 | |
| //	TOML datetimes to time.Time
 | |
| //	TOML arrays to any type of slice
 | |
| //	TOML tables to struct or map
 | |
| //	TOML array tables to slice of struct or map
 | |
| func (cfg *Config) Unmarshal(data []byte, v interface{}) error {
 | |
| 	table, err := Parse(data)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err := cfg.UnmarshalTable(table, v); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // A Decoder reads and decodes TOML from an input stream.
 | |
| type Decoder struct {
 | |
| 	r   io.Reader
 | |
| 	cfg *Config
 | |
| }
 | |
| 
 | |
| // NewDecoder returns a new Decoder that reads from r.
 | |
| // Note that it reads all from r before parsing it.
 | |
| func (cfg *Config) NewDecoder(r io.Reader) *Decoder {
 | |
| 	return &Decoder{r, cfg}
 | |
| }
 | |
| 
 | |
| // Decode parses the TOML data from its input and stores it in the value pointed to by v.
 | |
| // See the documentation for Unmarshal for details about the conversion of TOML into a Go value.
 | |
| func (d *Decoder) Decode(v interface{}) error {
 | |
| 	b, err := ioutil.ReadAll(d.r)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return d.cfg.Unmarshal(b, v)
 | |
| }
 | |
| 
 | |
| // UnmarshalerRec may be implemented by types to customize their behavior when being
 | |
| // unmarshaled from TOML. You can use it to implement custom validation or to set
 | |
| // unexported fields.
 | |
| //
 | |
| // UnmarshalTOML receives a function that can be called to unmarshal the original TOML
 | |
| // value into a field or variable. It is safe to call the function more than once if
 | |
| // necessary.
 | |
| type UnmarshalerRec interface {
 | |
| 	UnmarshalTOML(fn func(interface{}) error) error
 | |
| }
 | |
| 
 | |
| // Unmarshaler can be used to capture and process raw TOML source of a table or value.
 | |
| // UnmarshalTOML must copy the input if it wishes to retain it after returning.
 | |
| //
 | |
| // Note: this interface is retained for backwards compatibility. You probably want
 | |
| // to implement encoding.TextUnmarshaler or UnmarshalerRec instead.
 | |
| type Unmarshaler interface {
 | |
| 	UnmarshalTOML(input []byte) error
 | |
| }
 | |
| 
 | |
| // UnmarshalTable applies the contents of an ast.Table to the value pointed at by v.
 | |
| //
 | |
| // UnmarshalTable will mapped to v that according to following rules:
 | |
| //
 | |
| //	TOML strings to string
 | |
| //	TOML integers to any int type
 | |
| //	TOML floats to float32 or float64
 | |
| //	TOML booleans to bool
 | |
| //	TOML datetimes to time.Time
 | |
| //	TOML arrays to any type of slice
 | |
| //	TOML tables to struct or map
 | |
| //	TOML array tables to slice of struct or map
 | |
| func (cfg *Config) UnmarshalTable(t *ast.Table, v interface{}) error {
 | |
| 	rv := reflect.ValueOf(v)
 | |
| 	toplevelMap := rv.Kind() == reflect.Map
 | |
| 	if (!toplevelMap && rv.Kind() != reflect.Ptr) || rv.IsNil() {
 | |
| 		return &invalidUnmarshalError{reflect.TypeOf(v)}
 | |
| 	}
 | |
| 	return unmarshalTable(cfg, rv, t, toplevelMap)
 | |
| }
 | |
| 
 | |
| // used for UnmarshalerRec.
 | |
| func unmarshalTableOrValue(cfg *Config, rv reflect.Value, av interface{}) error {
 | |
| 	if (rv.Kind() != reflect.Ptr && rv.Kind() != reflect.Map) || rv.IsNil() {
 | |
| 		return &invalidUnmarshalError{rv.Type()}
 | |
| 	}
 | |
| 	rv = indirect(rv)
 | |
| 
 | |
| 	switch av.(type) {
 | |
| 	case *ast.KeyValue, *ast.Table, []*ast.Table:
 | |
| 		if err := unmarshalField(cfg, rv, av); err != nil {
 | |
| 			return lineError(fieldLineNumber(av), err)
 | |
| 		}
 | |
| 		return nil
 | |
| 	case ast.Value:
 | |
| 		return setValue(cfg, rv, av.(ast.Value))
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // unmarshalTable unmarshals the fields of a table into a struct or map.
 | |
| //
 | |
| // toplevelMap is true when rv is an (unadressable) map given to UnmarshalTable. In this
 | |
| // (special) case, the map is used as-is instead of creating a new map.
 | |
| func unmarshalTable(cfg *Config, rv reflect.Value, t *ast.Table, toplevelMap bool) error {
 | |
| 	rv = indirect(rv)
 | |
| 	if err, ok := setUnmarshaler(cfg, rv, t); ok {
 | |
| 		return lineError(t.Line, err)
 | |
| 	}
 | |
| 	switch {
 | |
| 	case rv.Kind() == reflect.Struct:
 | |
| 		fc := makeFieldCache(cfg, rv.Type())
 | |
| 		for key, fieldAst := range t.Fields {
 | |
| 			fv, fieldName, err := fc.findField(cfg, rv, key)
 | |
| 			if err != nil {
 | |
| 				return lineError(fieldLineNumber(fieldAst), err)
 | |
| 			}
 | |
| 			if fv.IsValid() {
 | |
| 				if err := unmarshalField(cfg, fv, fieldAst); err != nil {
 | |
| 					return lineErrorField(fieldLineNumber(fieldAst), rv.Type().String()+"."+fieldName, err)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	case rv.Kind() == reflect.Map || isEface(rv):
 | |
| 		m := rv
 | |
| 		if !toplevelMap {
 | |
| 			if rv.Kind() == reflect.Interface {
 | |
| 				m = reflect.ValueOf(make(map[string]interface{}))
 | |
| 			} else {
 | |
| 				m = reflect.MakeMap(rv.Type())
 | |
| 			}
 | |
| 		}
 | |
| 		elemtyp := m.Type().Elem()
 | |
| 		for key, fieldAst := range t.Fields {
 | |
| 			kv, err := unmarshalMapKey(m.Type().Key(), key)
 | |
| 			if err != nil {
 | |
| 				return lineError(fieldLineNumber(fieldAst), err)
 | |
| 			}
 | |
| 			fv := reflect.New(elemtyp).Elem()
 | |
| 			if err := unmarshalField(cfg, fv, fieldAst); err != nil {
 | |
| 				return lineError(fieldLineNumber(fieldAst), err)
 | |
| 			}
 | |
| 			m.SetMapIndex(kv, fv)
 | |
| 		}
 | |
| 		if !toplevelMap {
 | |
| 			rv.Set(m)
 | |
| 		}
 | |
| 	default:
 | |
| 		return lineError(t.Line, &unmarshalTypeError{"table", "struct or map", rv.Type()})
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func fieldLineNumber(fieldAst interface{}) int {
 | |
| 	switch av := fieldAst.(type) {
 | |
| 	case *ast.KeyValue:
 | |
| 		return av.Line
 | |
| 	case *ast.Table:
 | |
| 		return av.Line
 | |
| 	case []*ast.Table:
 | |
| 		return av[0].Line
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("BUG: unhandled node type %T", fieldAst))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func unmarshalField(cfg *Config, rv reflect.Value, fieldAst interface{}) error {
 | |
| 	switch av := fieldAst.(type) {
 | |
| 	case *ast.KeyValue:
 | |
| 		return setValue(cfg, rv, av.Value)
 | |
| 	case *ast.Table:
 | |
| 		return unmarshalTable(cfg, rv, av, false)
 | |
| 	case []*ast.Table:
 | |
| 		rv = indirect(rv)
 | |
| 		if err, ok := setUnmarshaler(cfg, rv, fieldAst); ok {
 | |
| 			return err
 | |
| 		}
 | |
| 		var slice reflect.Value
 | |
| 		switch {
 | |
| 		case rv.Kind() == reflect.Slice:
 | |
| 			slice = reflect.MakeSlice(rv.Type(), len(av), len(av))
 | |
| 		case isEface(rv):
 | |
| 			slice = reflect.ValueOf(make([]interface{}, len(av)))
 | |
| 		default:
 | |
| 			return &unmarshalTypeError{"array table", "slice", rv.Type()}
 | |
| 		}
 | |
| 		for i, tbl := range av {
 | |
| 			vv := reflect.New(slice.Type().Elem()).Elem()
 | |
| 			if err := unmarshalTable(cfg, vv, tbl, false); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			slice.Index(i).Set(vv)
 | |
| 		}
 | |
| 		rv.Set(slice)
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func unmarshalMapKey(typ reflect.Type, key string) (reflect.Value, error) {
 | |
| 	rv := reflect.New(typ).Elem()
 | |
| 	if u, ok := rv.Addr().Interface().(encoding.TextUnmarshaler); ok {
 | |
| 		return rv, u.UnmarshalText([]byte(key))
 | |
| 	}
 | |
| 	switch typ.Kind() {
 | |
| 	case reflect.String:
 | |
| 		rv.SetString(key)
 | |
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | |
| 		i, err := strconv.ParseInt(key, 10, int(typ.Size()*8))
 | |
| 		if err != nil {
 | |
| 			return rv, convertNumError(typ.Kind(), err)
 | |
| 		}
 | |
| 		rv.SetInt(i)
 | |
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | |
| 		i, err := strconv.ParseUint(key, 10, int(typ.Size()*8))
 | |
| 		if err != nil {
 | |
| 			return rv, convertNumError(typ.Kind(), err)
 | |
| 		}
 | |
| 		rv.SetUint(i)
 | |
| 	default:
 | |
| 		return rv, fmt.Errorf("invalid map key type %s", typ)
 | |
| 	}
 | |
| 	return rv, nil
 | |
| }
 | |
| 
 | |
| func setValue(cfg *Config, lhs reflect.Value, val ast.Value) error {
 | |
| 	lhs = indirect(lhs)
 | |
| 	if err, ok := setUnmarshaler(cfg, lhs, val); ok {
 | |
| 		return err
 | |
| 	}
 | |
| 	if err, ok := setTextUnmarshaler(lhs, val); ok {
 | |
| 		return err
 | |
| 	}
 | |
| 	switch v := val.(type) {
 | |
| 	case *ast.Integer:
 | |
| 		return setInt(lhs, v)
 | |
| 	case *ast.Float:
 | |
| 		return setFloat(lhs, v)
 | |
| 	case *ast.String:
 | |
| 		return setString(lhs, v)
 | |
| 	case *ast.Boolean:
 | |
| 		return setBoolean(lhs, v)
 | |
| 	case *ast.Datetime:
 | |
| 		return setDatetime(lhs, v)
 | |
| 	case *ast.Array:
 | |
| 		return setArray(cfg, lhs, v)
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("BUG: unhandled node type %T", v))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func indirect(rv reflect.Value) reflect.Value {
 | |
| 	for rv.Kind() == reflect.Ptr {
 | |
| 		if rv.IsNil() {
 | |
| 			rv.Set(reflect.New(rv.Type().Elem()))
 | |
| 		}
 | |
| 		rv = rv.Elem()
 | |
| 	}
 | |
| 	return rv
 | |
| }
 | |
| 
 | |
| func setUnmarshaler(cfg *Config, lhs reflect.Value, av interface{}) (error, bool) {
 | |
| 	if lhs.CanAddr() {
 | |
| 		if u, ok := lhs.Addr().Interface().(UnmarshalerRec); ok {
 | |
| 			err := u.UnmarshalTOML(func(v interface{}) error {
 | |
| 				return unmarshalTableOrValue(cfg, reflect.ValueOf(v), av)
 | |
| 			})
 | |
| 			return err, true
 | |
| 		}
 | |
| 		if u, ok := lhs.Addr().Interface().(Unmarshaler); ok {
 | |
| 			return u.UnmarshalTOML(unmarshalerSource(av)), true
 | |
| 		}
 | |
| 	}
 | |
| 	return nil, false
 | |
| }
 | |
| 
 | |
| func unmarshalerSource(av interface{}) []byte {
 | |
| 	var source []byte
 | |
| 	switch av := av.(type) {
 | |
| 	case []*ast.Table:
 | |
| 		for i, tab := range av {
 | |
| 			source = append(source, tab.Source()...)
 | |
| 			if i != len(av)-1 {
 | |
| 				source = append(source, '\n')
 | |
| 			}
 | |
| 		}
 | |
| 	case ast.Value:
 | |
| 		source = []byte(av.Source())
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("BUG: unhandled node type %T", av))
 | |
| 	}
 | |
| 	return source
 | |
| }
 | |
| 
 | |
| func setTextUnmarshaler(lhs reflect.Value, val ast.Value) (error, bool) {
 | |
| 	if !lhs.CanAddr() {
 | |
| 		return nil, false
 | |
| 	}
 | |
| 	u, ok := lhs.Addr().Interface().(encoding.TextUnmarshaler)
 | |
| 	if !ok || lhs.Type() == timeType {
 | |
| 		return nil, false
 | |
| 	}
 | |
| 	var data string
 | |
| 	switch val := val.(type) {
 | |
| 	case *ast.Array:
 | |
| 		return &unmarshalTypeError{"array", "", lhs.Type()}, true
 | |
| 	case *ast.String:
 | |
| 		data = val.Value
 | |
| 	default:
 | |
| 		data = val.Source()
 | |
| 	}
 | |
| 	return u.UnmarshalText([]byte(data)), true
 | |
| }
 | |
| 
 | |
| func setInt(fv reflect.Value, v *ast.Integer) error {
 | |
| 	k := fv.Kind()
 | |
| 	switch {
 | |
| 	case k >= reflect.Int && k <= reflect.Int64:
 | |
| 		i, err := strconv.ParseInt(v.Value, 10, int(fv.Type().Size()*8))
 | |
| 		if err != nil {
 | |
| 			return convertNumError(fv.Kind(), err)
 | |
| 		}
 | |
| 		fv.SetInt(i)
 | |
| 	case k >= reflect.Uint && k <= reflect.Uintptr:
 | |
| 		i, err := strconv.ParseUint(v.Value, 10, int(fv.Type().Size()*8))
 | |
| 		if err != nil {
 | |
| 			return convertNumError(fv.Kind(), err)
 | |
| 		}
 | |
| 		fv.SetUint(i)
 | |
| 	case isEface(fv):
 | |
| 		i, err := strconv.ParseInt(v.Value, 10, 64)
 | |
| 		if err != nil {
 | |
| 			return convertNumError(reflect.Int64, err)
 | |
| 		}
 | |
| 		fv.Set(reflect.ValueOf(i))
 | |
| 	default:
 | |
| 		return &unmarshalTypeError{"integer", "", fv.Type()}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func setFloat(fv reflect.Value, v *ast.Float) error {
 | |
| 	f, err := v.Float()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	switch {
 | |
| 	case fv.Kind() == reflect.Float32 || fv.Kind() == reflect.Float64:
 | |
| 		if fv.OverflowFloat(f) {
 | |
| 			return &overflowError{fv.Kind(), v.Value}
 | |
| 		}
 | |
| 		fv.SetFloat(f)
 | |
| 	case isEface(fv):
 | |
| 		fv.Set(reflect.ValueOf(f))
 | |
| 	default:
 | |
| 		return &unmarshalTypeError{"float", "", fv.Type()}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func setString(fv reflect.Value, v *ast.String) error {
 | |
| 	switch {
 | |
| 	case fv.Kind() == reflect.String:
 | |
| 		fv.SetString(v.Value)
 | |
| 	case isEface(fv):
 | |
| 		fv.Set(reflect.ValueOf(v.Value))
 | |
| 	default:
 | |
| 		return &unmarshalTypeError{"string", "", fv.Type()}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func setBoolean(fv reflect.Value, v *ast.Boolean) error {
 | |
| 	b, _ := v.Boolean()
 | |
| 	switch {
 | |
| 	case fv.Kind() == reflect.Bool:
 | |
| 		fv.SetBool(b)
 | |
| 	case isEface(fv):
 | |
| 		fv.Set(reflect.ValueOf(b))
 | |
| 	default:
 | |
| 		return &unmarshalTypeError{"boolean", "", fv.Type()}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func setDatetime(rv reflect.Value, v *ast.Datetime) error {
 | |
| 	t, err := v.Time()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if !timeType.AssignableTo(rv.Type()) {
 | |
| 		return &unmarshalTypeError{"datetime", "", rv.Type()}
 | |
| 	}
 | |
| 	rv.Set(reflect.ValueOf(t))
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func setArray(cfg *Config, rv reflect.Value, v *ast.Array) error {
 | |
| 	var slicetyp reflect.Type
 | |
| 	switch {
 | |
| 	case rv.Kind() == reflect.Slice:
 | |
| 		slicetyp = rv.Type()
 | |
| 	case isEface(rv):
 | |
| 		slicetyp = reflect.SliceOf(rv.Type())
 | |
| 	default:
 | |
| 		return &unmarshalTypeError{"array", "slice", rv.Type()}
 | |
| 	}
 | |
| 
 | |
| 	if len(v.Value) == 0 {
 | |
| 		// Ensure defined slices are always set to a non-nil value.
 | |
| 		rv.Set(reflect.MakeSlice(slicetyp, 0, 0))
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	tomltyp := reflect.TypeOf(v.Value[0])
 | |
| 	slice := reflect.MakeSlice(slicetyp, len(v.Value), len(v.Value))
 | |
| 	typ := slicetyp.Elem()
 | |
| 	for i, vv := range v.Value {
 | |
| 		if i > 0 && tomltyp != reflect.TypeOf(vv) {
 | |
| 			return errArrayMultiType
 | |
| 		}
 | |
| 		tmp := reflect.New(typ).Elem()
 | |
| 		if err := setValue(cfg, tmp, vv); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		slice.Index(i).Set(tmp)
 | |
| 	}
 | |
| 	rv.Set(slice)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func isEface(rv reflect.Value) bool {
 | |
| 	return rv.Kind() == reflect.Interface && rv.Type().NumMethod() == 0
 | |
| }
 |