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 }