github.com/niluplatform/go-nilu@v1.7.4-0.20200912082737-a0cb0776d52c/cmd/swarm/config.go (about)

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