github.com/theQRL/go-zond@v0.2.1/cmd/gzond/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 "bufio" 21 "errors" 22 "fmt" 23 "os" 24 "reflect" 25 "runtime" 26 "strings" 27 "unicode" 28 29 "github.com/naoina/toml" 30 "github.com/theQRL/go-zond/accounts" 31 "github.com/theQRL/go-zond/accounts/external" 32 "github.com/theQRL/go-zond/accounts/keystore" 33 "github.com/theQRL/go-zond/cmd/utils" 34 "github.com/theQRL/go-zond/common" 35 "github.com/theQRL/go-zond/common/hexutil" 36 "github.com/theQRL/go-zond/internal/flags" 37 "github.com/theQRL/go-zond/internal/version" 38 "github.com/theQRL/go-zond/internal/zondapi" 39 "github.com/theQRL/go-zond/log" 40 "github.com/theQRL/go-zond/metrics" 41 "github.com/theQRL/go-zond/node" 42 "github.com/theQRL/go-zond/params" 43 "github.com/theQRL/go-zond/zond/catalyst" 44 "github.com/theQRL/go-zond/zond/zondconfig" 45 "github.com/urfave/cli/v2" 46 ) 47 48 var ( 49 dumpConfigCommand = &cli.Command{ 50 Action: dumpConfig, 51 Name: "dumpconfig", 52 Usage: "Export configuration values in a TOML format", 53 ArgsUsage: "<dumpfile (optional)>", 54 Flags: flags.Merge(nodeFlags, rpcFlags), 55 Description: `Export configuration values in TOML format (to stdout by default).`, 56 } 57 58 configFileFlag = &cli.StringFlag{ 59 Name: "config", 60 Usage: "TOML configuration file", 61 Category: flags.ZondCategory, 62 } 63 ) 64 65 // These settings ensure that TOML keys use the same names as Go struct fields. 66 var tomlSettings = toml.Config{ 67 NormFieldName: func(rt reflect.Type, key string) string { 68 return key 69 }, 70 FieldToKey: func(rt reflect.Type, field string) string { 71 return field 72 }, 73 MissingField: func(rt reflect.Type, field string) error { 74 id := fmt.Sprintf("%s.%s", rt.String(), field) 75 if deprecated(id) { 76 log.Warn("Config field is deprecated and won't have an effect", "name", id) 77 return nil 78 } 79 var link string 80 if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" { 81 link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name()) 82 } 83 return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link) 84 }, 85 } 86 87 type ethstatsConfig struct { 88 URL string `toml:",omitempty"` 89 } 90 91 type gzondConfig struct { 92 Zond zondconfig.Config 93 Node node.Config 94 Zondstats ethstatsConfig 95 Metrics metrics.Config 96 } 97 98 func loadConfig(file string, cfg *gzondConfig) error { 99 f, err := os.Open(file) 100 if err != nil { 101 return err 102 } 103 defer f.Close() 104 105 err = tomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg) 106 // Add file name to errors that have a line number. 107 if _, ok := err.(*toml.LineError); ok { 108 err = errors.New(file + ", " + err.Error()) 109 } 110 return err 111 } 112 113 func defaultNodeConfig() node.Config { 114 git, _ := version.VCS() 115 cfg := node.DefaultConfig 116 cfg.Name = clientIdentifier 117 cfg.Version = params.VersionWithCommit(git.Commit, git.Date) 118 cfg.HTTPModules = append(cfg.HTTPModules, "zond") 119 cfg.WSModules = append(cfg.WSModules, "zond") 120 cfg.IPCPath = "gzond.ipc" 121 return cfg 122 } 123 124 // loadBaseConfig loads the gzondConfig based on the given command line 125 // parameters and config file. 126 func loadBaseConfig(ctx *cli.Context) gzondConfig { 127 // Load defaults. 128 cfg := gzondConfig{ 129 Zond: zondconfig.Defaults, 130 Node: defaultNodeConfig(), 131 Metrics: metrics.DefaultConfig, 132 } 133 134 // Load config file. 135 if file := ctx.String(configFileFlag.Name); file != "" { 136 if err := loadConfig(file, &cfg); err != nil { 137 utils.Fatalf("%v", err) 138 } 139 } 140 141 // Apply flags. 142 utils.SetNodeConfig(ctx, &cfg.Node) 143 return cfg 144 } 145 146 // makeConfigNode loads gzond configuration and creates a blank node instance. 147 func makeConfigNode(ctx *cli.Context) (*node.Node, gzondConfig) { 148 cfg := loadBaseConfig(ctx) 149 stack, err := node.New(&cfg.Node) 150 if err != nil { 151 utils.Fatalf("Failed to create the protocol stack: %v", err) 152 } 153 // Node doesn't by default populate account manager backends 154 if err := setAccountManagerBackends(stack.Config(), stack.AccountManager(), stack.KeyStoreDir()); err != nil { 155 utils.Fatalf("Failed to set account manager backends: %v", err) 156 } 157 158 utils.SetZondConfig(ctx, stack, &cfg.Zond) 159 if ctx.IsSet(utils.ZondStatsURLFlag.Name) { 160 cfg.Zondstats.URL = ctx.String(utils.ZondStatsURLFlag.Name) 161 } 162 applyMetricConfig(ctx, &cfg) 163 164 return stack, cfg 165 } 166 167 // makeFullNode loads gzond configuration and creates the Zond backend. 168 func makeFullNode(ctx *cli.Context) (*node.Node, zondapi.Backend) { 169 stack, cfg := makeConfigNode(ctx) 170 backend, zond := utils.RegisterZondService(stack, &cfg.Zond) 171 172 // Create gauge with gzond system and build information 173 if zond != nil { 174 var protos []string 175 for _, p := range zond.Protocols() { 176 protos = append(protos, fmt.Sprintf("%v/%d", p.Name, p.Version)) 177 } 178 metrics.NewRegisteredGaugeInfo("gzond/info", nil).Update(metrics.GaugeInfoValue{ 179 "arch": runtime.GOARCH, 180 "os": runtime.GOOS, 181 "version": cfg.Node.Version, 182 "protocols": strings.Join(protos, ","), 183 }) 184 } 185 186 // Configure log filter RPC API. 187 filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Zond) 188 189 // Configure GraphQL if requested. 190 if ctx.IsSet(utils.GraphQLEnabledFlag.Name) { 191 utils.RegisterGraphQLService(stack, backend, filterSystem, &cfg.Node) 192 } 193 194 // Add the Zond Stats daemon if requested. 195 if cfg.Zondstats.URL != "" { 196 utils.RegisterZondStatsService(stack, backend, cfg.Zondstats.URL) 197 } 198 199 // Configure full-sync tester service if requested 200 if ctx.IsSet(utils.SyncTargetFlag.Name) { 201 hex := hexutil.MustDecode(ctx.String(utils.SyncTargetFlag.Name)) 202 if len(hex) != common.HashLength { 203 utils.Fatalf("invalid sync target length: have %d, want %d", len(hex), common.HashLength) 204 } 205 utils.RegisterFullSyncTester(stack, zond, common.BytesToHash(hex)) 206 } 207 208 // Start the dev mode if requested, or launch the engine API for 209 // interacting with external consensus client. 210 if ctx.IsSet(utils.DeveloperFlag.Name) { 211 simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), zond) 212 if err != nil { 213 utils.Fatalf("failed to register dev mode catalyst service: %v", err) 214 } 215 catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon) 216 stack.RegisterLifecycle(simBeacon) 217 } else { 218 err := catalyst.Register(stack, zond) 219 if err != nil { 220 utils.Fatalf("failed to register catalyst service: %v", err) 221 } 222 } 223 return stack, backend 224 } 225 226 // dumpConfig is the dumpconfig command. 227 func dumpConfig(ctx *cli.Context) error { 228 _, cfg := makeConfigNode(ctx) 229 comment := "" 230 231 if cfg.Zond.Genesis != nil { 232 cfg.Zond.Genesis = nil 233 comment += "# Note: this config doesn't contain the genesis block.\n\n" 234 } 235 236 out, err := tomlSettings.Marshal(&cfg) 237 if err != nil { 238 return err 239 } 240 241 dump := os.Stdout 242 if ctx.NArg() > 0 { 243 dump, err = os.OpenFile(ctx.Args().Get(0), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) 244 if err != nil { 245 return err 246 } 247 defer dump.Close() 248 } 249 dump.WriteString(comment) 250 dump.Write(out) 251 252 return nil 253 } 254 255 func applyMetricConfig(ctx *cli.Context, cfg *gzondConfig) { 256 if ctx.IsSet(utils.MetricsEnabledFlag.Name) { 257 cfg.Metrics.Enabled = ctx.Bool(utils.MetricsEnabledFlag.Name) 258 } 259 if ctx.IsSet(utils.MetricsEnabledExpensiveFlag.Name) { 260 cfg.Metrics.EnabledExpensive = ctx.Bool(utils.MetricsEnabledExpensiveFlag.Name) 261 } 262 if ctx.IsSet(utils.MetricsHTTPFlag.Name) { 263 cfg.Metrics.HTTP = ctx.String(utils.MetricsHTTPFlag.Name) 264 } 265 if ctx.IsSet(utils.MetricsPortFlag.Name) { 266 cfg.Metrics.Port = ctx.Int(utils.MetricsPortFlag.Name) 267 } 268 if ctx.IsSet(utils.MetricsEnableInfluxDBFlag.Name) { 269 cfg.Metrics.EnableInfluxDB = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name) 270 } 271 if ctx.IsSet(utils.MetricsInfluxDBEndpointFlag.Name) { 272 cfg.Metrics.InfluxDBEndpoint = ctx.String(utils.MetricsInfluxDBEndpointFlag.Name) 273 } 274 if ctx.IsSet(utils.MetricsInfluxDBDatabaseFlag.Name) { 275 cfg.Metrics.InfluxDBDatabase = ctx.String(utils.MetricsInfluxDBDatabaseFlag.Name) 276 } 277 if ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) { 278 cfg.Metrics.InfluxDBUsername = ctx.String(utils.MetricsInfluxDBUsernameFlag.Name) 279 } 280 if ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name) { 281 cfg.Metrics.InfluxDBPassword = ctx.String(utils.MetricsInfluxDBPasswordFlag.Name) 282 } 283 if ctx.IsSet(utils.MetricsInfluxDBTagsFlag.Name) { 284 cfg.Metrics.InfluxDBTags = ctx.String(utils.MetricsInfluxDBTagsFlag.Name) 285 } 286 if ctx.IsSet(utils.MetricsEnableInfluxDBV2Flag.Name) { 287 cfg.Metrics.EnableInfluxDBV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name) 288 } 289 if ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) { 290 cfg.Metrics.InfluxDBToken = ctx.String(utils.MetricsInfluxDBTokenFlag.Name) 291 } 292 if ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name) { 293 cfg.Metrics.InfluxDBBucket = ctx.String(utils.MetricsInfluxDBBucketFlag.Name) 294 } 295 if ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) { 296 cfg.Metrics.InfluxDBOrganization = ctx.String(utils.MetricsInfluxDBOrganizationFlag.Name) 297 } 298 } 299 300 func deprecated(field string) bool { 301 switch field { 302 default: 303 return false 304 } 305 } 306 307 func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir string) error { 308 scryptN := keystore.StandardScryptN 309 scryptP := keystore.StandardScryptP 310 if conf.UseLightweightKDF { 311 scryptN = keystore.LightScryptN 312 scryptP = keystore.LightScryptP 313 } 314 315 // Assemble the supported backends 316 if len(conf.ExternalSigner) > 0 { 317 log.Info("Using external signer", "url", conf.ExternalSigner) 318 if extBackend, err := external.NewExternalBackend(conf.ExternalSigner); err == nil { 319 am.AddBackend(extBackend) 320 return nil 321 } else { 322 return fmt.Errorf("error connecting to external signer: %v", err) 323 } 324 } 325 326 // For now, we're using EITHER external signer OR local signers. 327 // If/when we implement some form of lockfile for USB and keystore wallets, 328 // we can have both, but it's very confusing for the user to see the same 329 // accounts in both externally and locally, plus very racey. 330 am.AddBackend(keystore.NewKeyStore(keydir, scryptN, scryptP)) 331 // TODO(now.youtrack.cloud/issue/TGZ-4) 332 /* 333 if conf.USB { 334 // Start a USB hub for Ledger hardware wallets 335 if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil { 336 log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err)) 337 } else { 338 am.AddBackend(ledgerhub) 339 } 340 // Start a USB hub for Trezor hardware wallets (HID version) 341 if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil { 342 log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err)) 343 } else { 344 am.AddBackend(trezorhub) 345 } 346 // Start a USB hub for Trezor hardware wallets (WebUSB version) 347 if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil { 348 log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err)) 349 } else { 350 am.AddBackend(trezorhub) 351 } 352 } 353 if len(conf.SmartCardDaemonPath) > 0 { 354 // Start a smart card hub 355 if schub, err := scwallet.NewHub(conf.SmartCardDaemonPath, scwallet.Scheme, keydir); err != nil { 356 log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err)) 357 } else { 358 am.AddBackend(schub) 359 } 360 } 361 */ 362 363 return nil 364 }