github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/cmd/swarm/config.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package main
    13  
    14  import (
    15  	"errors"
    16  	"fmt"
    17  	"io"
    18  	"os"
    19  	"reflect"
    20  	"strconv"
    21  	"strings"
    22  	"unicode"
    23  
    24  	cli "gopkg.in/urfave/cli.v1"
    25  
    26  	"github.com/Sberex/go-sberex/cmd/utils"
    27  	"github.com/Sberex/go-sberex/common"
    28  	"github.com/Sberex/go-sberex/log"
    29  	"github.com/Sberex/go-sberex/node"
    30  	"github.com/naoina/toml"
    31  
    32  	bzzapi "github.com/Sberex/go-sberex/swarm/api"
    33  )
    34  
    35  var (
    36  	//flag definition for the dumpconfig command
    37  	DumpConfigCommand = cli.Command{
    38  		Action:      utils.MigrateFlags(dumpConfig),
    39  		Name:        "dumpconfig",
    40  		Usage:       "Show configuration values",
    41  		ArgsUsage:   "",
    42  		Flags:       app.Flags,
    43  		Category:    "MISCELLANEOUS COMMANDS",
    44  		Description: `The dumpconfig command shows configuration values.`,
    45  	}
    46  
    47  	//flag definition for the config file command
    48  	SwarmTomlConfigPathFlag = cli.StringFlag{
    49  		Name:  "config",
    50  		Usage: "TOML configuration file",
    51  	}
    52  )
    53  
    54  //constants for environment variables
    55  const (
    56  	SWARM_ENV_CHEQUEBOOK_ADDR = "SWARM_CHEQUEBOOK_ADDR"
    57  	SWARM_ENV_ACCOUNT         = "SWARM_ACCOUNT"
    58  	SWARM_ENV_LISTEN_ADDR     = "SWARM_LISTEN_ADDR"
    59  	SWARM_ENV_PORT            = "SWARM_PORT"
    60  	SWARM_ENV_NETWORK_ID      = "SWARM_NETWORK_ID"
    61  	SWARM_ENV_SWAP_ENABLE     = "SWARM_SWAP_ENABLE"
    62  	SWARM_ENV_SWAP_API        = "SWARM_SWAP_API"
    63  	SWARM_ENV_SYNC_ENABLE     = "SWARM_SYNC_ENABLE"
    64  	SWARM_ENV_ENS_API         = "SWARM_ENS_API"
    65  	SWARM_ENV_ENS_ADDR        = "SWARM_ENS_ADDR"
    66  	SWARM_ENV_CORS            = "SWARM_CORS"
    67  	SWARM_ENV_BOOTNODES       = "SWARM_BOOTNODES"
    68  	GETH_ENV_DATADIR          = "GETH_DATADIR"
    69  )
    70  
    71  // These settings ensure that TOML keys use the same names as Go struct fields.
    72  var tomlSettings = toml.Config{
    73  	NormFieldName: func(rt reflect.Type, key string) string {
    74  		return key
    75  	},
    76  	FieldToKey: func(rt reflect.Type, field string) string {
    77  		return field
    78  	},
    79  	MissingField: func(rt reflect.Type, field string) error {
    80  		link := ""
    81  		if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
    82  			link = fmt.Sprintf(", check github.com/Sberex/go-sberex/swarm/api/config.go for available fields")
    83  		}
    84  		return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link)
    85  	},
    86  }
    87  
    88  //before booting the swarm node, build the configuration
    89  func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) {
    90  	//check for deprecated flags
    91  	checkDeprecated(ctx)
    92  	//start by creating a default config
    93  	config = bzzapi.NewDefaultConfig()
    94  	//first load settings from config file (if provided)
    95  	config, err = configFileOverride(config, ctx)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	//override settings provided by environment variables
   100  	config = envVarsOverride(config)
   101  	//override settings provided by command line
   102  	config = cmdLineOverride(config, ctx)
   103  	//validate configuration parameters
   104  	err = validateConfig(config)
   105  
   106  	return
   107  }
   108  
   109  //finally, after the configuration build phase is finished, initialize
   110  func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context) {
   111  	//at this point, all vars should be set in the Config
   112  	//get the account for the provided swarm account
   113  	prvkey := getAccount(config.BzzAccount, ctx, stack)
   114  	//set the resolved config path (geth --datadir)
   115  	config.Path = stack.InstanceDir()
   116  	//finally, initialize the configuration
   117  	config.Init(prvkey)
   118  	//configuration phase completed here
   119  	log.Debug("Starting Swarm with the following parameters:")
   120  	//after having created the config, print it to screen
   121  	log.Debug(printConfig(config))
   122  }
   123  
   124  //override the current config with whatever is in the config file, if a config file has been provided
   125  func configFileOverride(config *bzzapi.Config, ctx *cli.Context) (*bzzapi.Config, error) {
   126  	var err error
   127  
   128  	//only do something if the -config flag has been set
   129  	if ctx.GlobalIsSet(SwarmTomlConfigPathFlag.Name) {
   130  		var filepath string
   131  		if filepath = ctx.GlobalString(SwarmTomlConfigPathFlag.Name); filepath == "" {
   132  			utils.Fatalf("Config file flag provided with invalid file path")
   133  		}
   134  		f, err := os.Open(filepath)
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  		defer f.Close()
   139  
   140  		//decode the TOML file into a Config struct
   141  		//note that we are decoding into the existing defaultConfig;
   142  		//if an entry is not present in the file, the default entry is kept
   143  		err = tomlSettings.NewDecoder(f).Decode(&config)
   144  		// Add file name to errors that have a line number.
   145  		if _, ok := err.(*toml.LineError); ok {
   146  			err = errors.New(filepath + ", " + err.Error())
   147  		}
   148  	}
   149  	return config, err
   150  }
   151  
   152  //override the current config with whatever is provided through the command line
   153  //most values are not allowed a zero value (empty string), if not otherwise noted
   154  func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config {
   155  
   156  	if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" {
   157  		currentConfig.BzzAccount = keyid
   158  	}
   159  
   160  	if chbookaddr := ctx.GlobalString(ChequebookAddrFlag.Name); chbookaddr != "" {
   161  		currentConfig.Contract = common.HexToAddress(chbookaddr)
   162  	}
   163  
   164  	if networkid := ctx.GlobalString(SwarmNetworkIdFlag.Name); networkid != "" {
   165  		if id, _ := strconv.Atoi(networkid); id != 0 {
   166  			currentConfig.NetworkId = uint64(id)
   167  		}
   168  	}
   169  
   170  	if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
   171  		if datadir := ctx.GlobalString(utils.DataDirFlag.Name); datadir != "" {
   172  			currentConfig.Path = datadir
   173  		}
   174  	}
   175  
   176  	bzzport := ctx.GlobalString(SwarmPortFlag.Name)
   177  	if len(bzzport) > 0 {
   178  		currentConfig.Port = bzzport
   179  	}
   180  
   181  	if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" {
   182  		currentConfig.ListenAddr = bzzaddr
   183  	}
   184  
   185  	if ctx.GlobalIsSet(SwarmSwapEnabledFlag.Name) {
   186  		currentConfig.SwapEnabled = true
   187  	}
   188  
   189  	if ctx.GlobalIsSet(SwarmSyncEnabledFlag.Name) {
   190  		currentConfig.SyncEnabled = true
   191  	}
   192  
   193  	currentConfig.SwapApi = ctx.GlobalString(SwarmSwapAPIFlag.Name)
   194  	if currentConfig.SwapEnabled && currentConfig.SwapApi == "" {
   195  		utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
   196  	}
   197  
   198  	if ctx.GlobalIsSet(EnsAPIFlag.Name) {
   199  		ensAPIs := ctx.GlobalStringSlice(EnsAPIFlag.Name)
   200  		// preserve backward compatibility to disable ENS with --ens-api=""
   201  		if len(ensAPIs) == 1 && ensAPIs[0] == "" {
   202  			ensAPIs = nil
   203  		}
   204  		currentConfig.EnsAPIs = ensAPIs
   205  	}
   206  
   207  	if ensaddr := ctx.GlobalString(DeprecatedEnsAddrFlag.Name); ensaddr != "" {
   208  		currentConfig.EnsRoot = common.HexToAddress(ensaddr)
   209  	}
   210  
   211  	if cors := ctx.GlobalString(CorsStringFlag.Name); cors != "" {
   212  		currentConfig.Cors = cors
   213  	}
   214  
   215  	if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
   216  		currentConfig.BootNodes = ctx.GlobalString(utils.BootnodesFlag.Name)
   217  	}
   218  
   219  	return currentConfig
   220  
   221  }
   222  
   223  //override the current config with whatver is provided in environment variables
   224  //most values are not allowed a zero value (empty string), if not otherwise noted
   225  func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
   226  
   227  	if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" {
   228  		currentConfig.BzzAccount = keyid
   229  	}
   230  
   231  	if chbookaddr := os.Getenv(SWARM_ENV_CHEQUEBOOK_ADDR); chbookaddr != "" {
   232  		currentConfig.Contract = common.HexToAddress(chbookaddr)
   233  	}
   234  
   235  	if networkid := os.Getenv(SWARM_ENV_NETWORK_ID); networkid != "" {
   236  		if id, _ := strconv.Atoi(networkid); id != 0 {
   237  			currentConfig.NetworkId = uint64(id)
   238  		}
   239  	}
   240  
   241  	if datadir := os.Getenv(GETH_ENV_DATADIR); datadir != "" {
   242  		currentConfig.Path = datadir
   243  	}
   244  
   245  	bzzport := os.Getenv(SWARM_ENV_PORT)
   246  	if len(bzzport) > 0 {
   247  		currentConfig.Port = bzzport
   248  	}
   249  
   250  	if bzzaddr := os.Getenv(SWARM_ENV_LISTEN_ADDR); bzzaddr != "" {
   251  		currentConfig.ListenAddr = bzzaddr
   252  	}
   253  
   254  	if swapenable := os.Getenv(SWARM_ENV_SWAP_ENABLE); swapenable != "" {
   255  		if swap, err := strconv.ParseBool(swapenable); err != nil {
   256  			currentConfig.SwapEnabled = swap
   257  		}
   258  	}
   259  
   260  	if syncenable := os.Getenv(SWARM_ENV_SYNC_ENABLE); syncenable != "" {
   261  		if sync, err := strconv.ParseBool(syncenable); err != nil {
   262  			currentConfig.SyncEnabled = sync
   263  		}
   264  	}
   265  
   266  	if swapapi := os.Getenv(SWARM_ENV_SWAP_API); swapapi != "" {
   267  		currentConfig.SwapApi = swapapi
   268  	}
   269  
   270  	if currentConfig.SwapEnabled && currentConfig.SwapApi == "" {
   271  		utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
   272  	}
   273  
   274  	if ensapi := os.Getenv(SWARM_ENV_ENS_API); ensapi != "" {
   275  		currentConfig.EnsAPIs = strings.Split(ensapi, ",")
   276  	}
   277  
   278  	if ensaddr := os.Getenv(SWARM_ENV_ENS_ADDR); ensaddr != "" {
   279  		currentConfig.EnsRoot = common.HexToAddress(ensaddr)
   280  	}
   281  
   282  	if cors := os.Getenv(SWARM_ENV_CORS); cors != "" {
   283  		currentConfig.Cors = cors
   284  	}
   285  
   286  	if bootnodes := os.Getenv(SWARM_ENV_BOOTNODES); bootnodes != "" {
   287  		currentConfig.BootNodes = bootnodes
   288  	}
   289  
   290  	return currentConfig
   291  }
   292  
   293  // dumpConfig is the dumpconfig command.
   294  // writes a default config to STDOUT
   295  func dumpConfig(ctx *cli.Context) error {
   296  	cfg, err := buildConfig(ctx)
   297  	if err != nil {
   298  		utils.Fatalf(fmt.Sprintf("Uh oh - dumpconfig triggered an error %v", err))
   299  	}
   300  	comment := ""
   301  	out, err := tomlSettings.Marshal(&cfg)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	io.WriteString(os.Stdout, comment)
   306  	os.Stdout.Write(out)
   307  	return nil
   308  }
   309  
   310  //deprecated flags checked here
   311  func checkDeprecated(ctx *cli.Context) {
   312  	// exit if the deprecated --ethapi flag is set
   313  	if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" {
   314  		utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.")
   315  	}
   316  	// warn if --ens-api flag is set
   317  	if ctx.GlobalString(DeprecatedEnsAddrFlag.Name) != "" {
   318  		log.Warn("--ens-addr is no longer a valid command line flag, please use --ens-api to specify contract address.")
   319  	}
   320  }
   321  
   322  //validate configuration parameters
   323  func validateConfig(cfg *bzzapi.Config) (err error) {
   324  	for _, ensAPI := range cfg.EnsAPIs {
   325  		if ensAPI != "" {
   326  			if err := validateEnsAPIs(ensAPI); err != nil {
   327  				return fmt.Errorf("invalid format [tld:][contract-addr@]url for ENS API endpoint configuration %q: %v", ensAPI, err)
   328  			}
   329  		}
   330  	}
   331  	return nil
   332  }
   333  
   334  //validate EnsAPIs configuration parameter
   335  func validateEnsAPIs(s string) (err error) {
   336  	// missing contract address
   337  	if strings.HasPrefix(s, "@") {
   338  		return errors.New("missing contract address")
   339  	}
   340  	// missing url
   341  	if strings.HasSuffix(s, "@") {
   342  		return errors.New("missing url")
   343  	}
   344  	// missing tld
   345  	if strings.HasPrefix(s, ":") {
   346  		return errors.New("missing tld")
   347  	}
   348  	// missing url
   349  	if strings.HasSuffix(s, ":") {
   350  		return errors.New("missing url")
   351  	}
   352  	return nil
   353  }
   354  
   355  //print a Config as string
   356  func printConfig(config *bzzapi.Config) string {
   357  	out, err := tomlSettings.Marshal(&config)
   358  	if err != nil {
   359  		return fmt.Sprintf("Something is not right with the configuration: %v", err)
   360  	}
   361  	return string(out)
   362  }