github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/cmd/swarm/config.go (about)

     1  // Copyright 2017 The Spectrum Authors
     2  // This file is part of Spectrum.
     3  //
     4  // Spectrum 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  // Spectrum 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 Spectrum. 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  	"unicode"
    27  
    28  	cli "gopkg.in/urfave/cli.v1"
    29  
    30  	"github.com/SmartMeshFoundation/Spectrum/cmd/utils"
    31  	"github.com/SmartMeshFoundation/Spectrum/common"
    32  	"github.com/SmartMeshFoundation/Spectrum/log"
    33  	"github.com/SmartMeshFoundation/Spectrum/node"
    34  	"github.com/naoina/toml"
    35  
    36  	bzzapi "github.com/SmartMeshFoundation/Spectrum/swarm/api"
    37  )
    38  
    39  var (
    40  	//flag definition for the dumpconfig command
    41  	DumpConfigCommand = cli.Command{
    42  		Action:      utils.MigrateFlags(dumpConfig),
    43  		Name:        "dumpconfig",
    44  		Usage:       "Show configuration values",
    45  		ArgsUsage:   "",
    46  		Flags:       app.Flags,
    47  		Category:    "MISCELLANEOUS COMMANDS",
    48  		Description: `The dumpconfig command shows configuration values.`,
    49  	}
    50  
    51  	//flag definition for the config file command
    52  	SwarmTomlConfigPathFlag = cli.StringFlag{
    53  		Name:  "config",
    54  		Usage: "TOML configuration file",
    55  	}
    56  )
    57  
    58  //constants for environment variables
    59  const (
    60  	SWARM_ENV_CHEQUEBOOK_ADDR = "SWARM_CHEQUEBOOK_ADDR"
    61  	SWARM_ENV_ACCOUNT         = "SWARM_ACCOUNT"
    62  	SWARM_ENV_LISTEN_ADDR     = "SWARM_LISTEN_ADDR"
    63  	SWARM_ENV_PORT            = "SWARM_PORT"
    64  	SWARM_ENV_NETWORK_ID      = "SWARM_NETWORK_ID"
    65  	SWARM_ENV_SWAP_ENABLE     = "SWARM_SWAP_ENABLE"
    66  	SWARM_ENV_SWAP_API        = "SWARM_SWAP_API"
    67  	SWARM_ENV_SYNC_ENABLE     = "SWARM_SYNC_ENABLE"
    68  	SWARM_ENV_ENS_API         = "SWARM_ENS_API"
    69  	SWARM_ENV_ENS_ADDR        = "SWARM_ENS_ADDR"
    70  	SWARM_ENV_CORS            = "SWARM_CORS"
    71  	SWARM_ENV_BOOTNODES       = "SWARM_BOOTNODES"
    72  	GETH_ENV_DATADIR          = "GETH_DATADIR"
    73  )
    74  
    75  // These settings ensure that TOML keys use the same names as Go struct fields.
    76  var tomlSettings = toml.Config{
    77  	NormFieldName: func(rt reflect.Type, key string) string {
    78  		return key
    79  	},
    80  	FieldToKey: func(rt reflect.Type, field string) string {
    81  		return field
    82  	},
    83  	MissingField: func(rt reflect.Type, field string) error {
    84  		link := ""
    85  		if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
    86  			link = fmt.Sprintf(", check github.com/SmartMeshFoundation/Spectrum/swarm/api/config.go for available fields")
    87  		}
    88  		return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link)
    89  	},
    90  }
    91  
    92  //before booting the swarm node, build the configuration
    93  func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) {
    94  	//check for deprecated flags
    95  	checkDeprecated(ctx)
    96  	//start by creating a default config
    97  	config = bzzapi.NewDefaultConfig()
    98  	//first load settings from config file (if provided)
    99  	config, err = configFileOverride(config, ctx)
   100  	//override settings provided by environment variables
   101  	config = envVarsOverride(config)
   102  	//override settings provided by command line
   103  	config = cmdLineOverride(config, ctx)
   104  
   105  	return
   106  }
   107  
   108  //finally, after the configuration build phase is finished, initialize
   109  func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context) {
   110  	//at this point, all vars should be set in the Config
   111  	//get the account for the provided swarm account
   112  	prvkey := getAccount(config.BzzAccount, ctx, stack)
   113  	//set the resolved config path (geth --datadir)
   114  	config.Path = stack.InstanceDir()
   115  	//finally, initialize the configuration
   116  	config.Init(prvkey)
   117  	//configuration phase completed here
   118  	log.Debug("Starting Swarm with the following parameters:")
   119  	//after having created the config, print it to screen
   120  	log.Debug(printConfig(config))
   121  }
   122  
   123  //override the current config with whatever is in the config file, if a config file has been provided
   124  func configFileOverride(config *bzzapi.Config, ctx *cli.Context) (*bzzapi.Config, error) {
   125  	var err error
   126  
   127  	//only do something if the -config flag has been set
   128  	if ctx.GlobalIsSet(SwarmTomlConfigPathFlag.Name) {
   129  		var filepath string
   130  		if filepath = ctx.GlobalString(SwarmTomlConfigPathFlag.Name); filepath == "" {
   131  			utils.Fatalf("Config file flag provided with invalid file path")
   132  		}
   133  		f, err := os.Open(filepath)
   134  		if err != nil {
   135  			return nil, err
   136  		}
   137  		defer f.Close()
   138  
   139  		//decode the TOML file into a Config struct
   140  		//note that we are decoding into the existing defaultConfig;
   141  		//if an entry is not present in the file, the default entry is kept
   142  		err = tomlSettings.NewDecoder(f).Decode(&config)
   143  		// Add file name to errors that have a line number.
   144  		if _, ok := err.(*toml.LineError); ok {
   145  			err = errors.New(filepath + ", " + err.Error())
   146  		}
   147  	}
   148  	return config, err
   149  }
   150  
   151  //override the current config with whatever is provided through the command line
   152  //most values are not allowed a zero value (empty string), if not otherwise noted
   153  func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config {
   154  
   155  	if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" {
   156  		currentConfig.BzzAccount = keyid
   157  	}
   158  
   159  	if chbookaddr := ctx.GlobalString(ChequebookAddrFlag.Name); chbookaddr != "" {
   160  		currentConfig.Contract = common.HexToAddress(chbookaddr)
   161  	}
   162  
   163  	if networkid := ctx.GlobalString(SwarmNetworkIdFlag.Name); networkid != "" {
   164  		if id, _ := strconv.Atoi(networkid); id != 0 {
   165  			currentConfig.NetworkId = uint64(id)
   166  		}
   167  	}
   168  
   169  	if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
   170  		if datadir := ctx.GlobalString(utils.DataDirFlag.Name); datadir != "" {
   171  			currentConfig.Path = datadir
   172  		}
   173  	}
   174  
   175  	bzzport := ctx.GlobalString(SwarmPortFlag.Name)
   176  	if len(bzzport) > 0 {
   177  		currentConfig.Port = bzzport
   178  	}
   179  
   180  	if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" {
   181  		currentConfig.ListenAddr = bzzaddr
   182  	}
   183  
   184  	if ctx.GlobalIsSet(SwarmSwapEnabledFlag.Name) {
   185  		currentConfig.SwapEnabled = true
   186  	}
   187  
   188  	if ctx.GlobalIsSet(SwarmSyncEnabledFlag.Name) {
   189  		currentConfig.SyncEnabled = true
   190  	}
   191  
   192  	currentConfig.SwapApi = ctx.GlobalString(SwarmSwapAPIFlag.Name)
   193  	if currentConfig.SwapEnabled && currentConfig.SwapApi == "" {
   194  		utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
   195  	}
   196  
   197  	//EnsApi can be set to "", so can't check for empty string, as it is allowed!
   198  	if ctx.GlobalIsSet(EnsAPIFlag.Name) {
   199  		currentConfig.EnsApi = ctx.GlobalString(EnsAPIFlag.Name)
   200  	}
   201  
   202  	if ensaddr := ctx.GlobalString(EnsAddrFlag.Name); ensaddr != "" {
   203  		currentConfig.EnsRoot = common.HexToAddress(ensaddr)
   204  	}
   205  
   206  	if cors := ctx.GlobalString(CorsStringFlag.Name); cors != "" {
   207  		currentConfig.Cors = cors
   208  	}
   209  
   210  	if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
   211  		currentConfig.BootNodes = ctx.GlobalString(utils.BootnodesFlag.Name)
   212  	}
   213  
   214  	return currentConfig
   215  
   216  }
   217  
   218  //override the current config with whatver is provided in environment variables
   219  //most values are not allowed a zero value (empty string), if not otherwise noted
   220  func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
   221  
   222  	if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" {
   223  		currentConfig.BzzAccount = keyid
   224  	}
   225  
   226  	if chbookaddr := os.Getenv(SWARM_ENV_CHEQUEBOOK_ADDR); chbookaddr != "" {
   227  		currentConfig.Contract = common.HexToAddress(chbookaddr)
   228  	}
   229  
   230  	if networkid := os.Getenv(SWARM_ENV_NETWORK_ID); networkid != "" {
   231  		if id, _ := strconv.Atoi(networkid); id != 0 {
   232  			currentConfig.NetworkId = uint64(id)
   233  		}
   234  	}
   235  
   236  	if datadir := os.Getenv(GETH_ENV_DATADIR); datadir != "" {
   237  		currentConfig.Path = datadir
   238  	}
   239  
   240  	bzzport := os.Getenv(SWARM_ENV_PORT)
   241  	if len(bzzport) > 0 {
   242  		currentConfig.Port = bzzport
   243  	}
   244  
   245  	if bzzaddr := os.Getenv(SWARM_ENV_LISTEN_ADDR); bzzaddr != "" {
   246  		currentConfig.ListenAddr = bzzaddr
   247  	}
   248  
   249  	if swapenable := os.Getenv(SWARM_ENV_SWAP_ENABLE); swapenable != "" {
   250  		if swap, err := strconv.ParseBool(swapenable); err != nil {
   251  			currentConfig.SwapEnabled = swap
   252  		}
   253  	}
   254  
   255  	if syncenable := os.Getenv(SWARM_ENV_SYNC_ENABLE); syncenable != "" {
   256  		if sync, err := strconv.ParseBool(syncenable); err != nil {
   257  			currentConfig.SyncEnabled = sync
   258  		}
   259  	}
   260  
   261  	if swapapi := os.Getenv(SWARM_ENV_SWAP_API); swapapi != "" {
   262  		currentConfig.SwapApi = swapapi
   263  	}
   264  
   265  	if currentConfig.SwapEnabled && currentConfig.SwapApi == "" {
   266  		utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
   267  	}
   268  
   269  	//EnsApi can be set to "", so can't check for empty string, as it is allowed
   270  	if ensapi, exists := os.LookupEnv(SWARM_ENV_ENS_API); exists {
   271  		currentConfig.EnsApi = ensapi
   272  	}
   273  
   274  	if ensaddr := os.Getenv(SWARM_ENV_ENS_ADDR); ensaddr != "" {
   275  		currentConfig.EnsRoot = common.HexToAddress(ensaddr)
   276  	}
   277  
   278  	if cors := os.Getenv(SWARM_ENV_CORS); cors != "" {
   279  		currentConfig.Cors = cors
   280  	}
   281  
   282  	if bootnodes := os.Getenv(SWARM_ENV_BOOTNODES); bootnodes != "" {
   283  		currentConfig.BootNodes = bootnodes
   284  	}
   285  
   286  	return currentConfig
   287  }
   288  
   289  // dumpConfig is the dumpconfig command.
   290  // writes a default config to STDOUT
   291  func dumpConfig(ctx *cli.Context) error {
   292  	cfg, err := buildConfig(ctx)
   293  	if err != nil {
   294  		utils.Fatalf(fmt.Sprintf("Uh oh - dumpconfig triggered an error %v", err))
   295  	}
   296  	comment := ""
   297  	out, err := tomlSettings.Marshal(&cfg)
   298  	if err != nil {
   299  		return err
   300  	}
   301  	io.WriteString(os.Stdout, comment)
   302  	os.Stdout.Write(out)
   303  	return nil
   304  }
   305  
   306  //deprecated flags checked here
   307  func checkDeprecated(ctx *cli.Context) {
   308  	// exit if the deprecated --ethapi flag is set
   309  	if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" {
   310  		utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.")
   311  	}
   312  }
   313  
   314  //print a Config as string
   315  func printConfig(config *bzzapi.Config) string {
   316  	out, err := tomlSettings.Marshal(&config)
   317  	if err != nil {
   318  		return (fmt.Sprintf("Something is not right with the configuration: %v", err))
   319  	}
   320  	return string(out)
   321  }