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 }