github.com/dominant-strategies/go-quai@v0.28.2/node/config.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser 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 // The go-ethereum library 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 Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package node 18 19 import ( 20 "crypto/ecdsa" 21 "fmt" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strings" 26 "sync" 27 28 "github.com/dominant-strategies/go-quai/common" 29 "github.com/dominant-strategies/go-quai/crypto" 30 "github.com/dominant-strategies/go-quai/log" 31 "github.com/dominant-strategies/go-quai/p2p" 32 "github.com/dominant-strategies/go-quai/p2p/enode" 33 "github.com/dominant-strategies/go-quai/rpc" 34 ) 35 36 const ( 37 datadirPrivateKey = "nodekey" // Path within the datadir to the node's private key 38 datadirDefaultKeyStore = "keystore" // Path within the datadir to the keystore 39 datadirStaticNodes = "static-nodes.json" // Path within the datadir to the static node list 40 datadirTrustedNodes = "trusted-nodes.json" // Path within the datadir to the trusted node list 41 datadirNodeDatabase = "nodes" // Path within the datadir to store the node infos 42 ) 43 44 // Config represents a small collection of configuration values to fine tune the 45 // P2P network layer of a protocol stack. These values can be further extended by 46 // all registered services. 47 type Config struct { 48 // Name sets the instance name of the node. It must not contain the / character and is 49 // used in the devp2p node identifier. The instance name of go-quai is "go-quai". If no 50 // value is specified, the basename of the current executable is used. 51 Name string `toml:"-"` 52 53 // UserIdent, if set, is used as an additional component in the devp2p node identifier. 54 UserIdent string `toml:",omitempty"` 55 56 // Version should be set to the version number of the program. It is used 57 // in the devp2p node identifier. 58 Version string `toml:"-"` 59 60 // DataDir is the file system folder the node should use for any data storage 61 // requirements. The configured data directory will not be directly shared with 62 // registered services, instead those can use utility methods to create/access 63 // databases or flat files. This enables ephemeral nodes which can fully reside 64 // in memory. 65 DataDir string 66 67 // Configuration of peer-to-peer networking. 68 P2P p2p.Config 69 70 // KeyStoreDir is the file system folder that contains private keys. The directory can 71 // be specified as a relative path, in which case it is resolved relative to the 72 // current directory. 73 // 74 // If KeyStoreDir is empty, the default location is the "keystore" subdirectory of 75 // DataDir. If DataDir is unspecified and KeyStoreDir is empty, an ephemeral directory 76 // is created by New and destroyed when the node is stopped. 77 KeyStoreDir string `toml:",omitempty"` 78 79 // ExternalSigner specifies an external URI for a clef-type signer 80 ExternalSigner string `toml:",omitempty"` 81 82 // UseLightweightKDF lowers the memory and CPU requirements of the key store 83 // scrypt KDF at the expense of security. 84 UseLightweightKDF bool `toml:",omitempty"` 85 86 // InsecureUnlockAllowed allows user to unlock accounts in unsafe http environment. 87 InsecureUnlockAllowed bool `toml:",omitempty"` 88 89 // NoUSB disables hardware wallet monitoring and connectivity. 90 NoUSB bool `toml:",omitempty"` 91 92 // USB enables hardware wallet monitoring and connectivity. 93 USB bool `toml:",omitempty"` 94 95 // HTTPHost is the host interface on which to start the HTTP RPC server. If this 96 // field is empty, no HTTP API endpoint will be started. 97 HTTPHost string 98 99 // HTTPPort is the TCP port number on which to start the HTTP RPC server. The 100 // default zero value is/ valid and will pick a port number randomly (useful 101 // for ephemeral nodes). 102 HTTPPort int `toml:",omitempty"` 103 104 // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting 105 // clients. Please be aware that CORS is a browser enforced security, it's fully 106 // useless for custom HTTP clients. 107 HTTPCors []string `toml:",omitempty"` 108 109 // HTTPVirtualHosts is the list of virtual hostnames which are allowed on incoming requests. 110 // This is by default {'localhost'}. Using this prevents attacks like 111 // DNS rebinding, which bypasses SOP by simply masquerading as being within the same 112 // origin. These attacks do not utilize CORS, since they are not cross-domain. 113 // By explicitly checking the Host-header, the server will not allow requests 114 // made against the server with a malicious host domain. 115 // Requests using ip address directly are not affected 116 HTTPVirtualHosts []string `toml:",omitempty"` 117 118 // HTTPModules is a list of API modules to expose via the HTTP RPC interface. 119 // If the module list is empty, all RPC API endpoints designated public will be 120 // exposed. 121 HTTPModules []string 122 123 // HTTPTimeouts allows for customization of the timeout values used by the HTTP RPC 124 // interface. 125 HTTPTimeouts rpc.HTTPTimeouts 126 127 // HTTPPathPrefix specifies a path prefix on which http-rpc is to be served. 128 HTTPPathPrefix string `toml:",omitempty"` 129 130 // WSHost is the host interface on which to start the websocket RPC server. If 131 // this field is empty, no websocket API endpoint will be started. 132 WSHost string 133 134 // WSPort is the TCP port number on which to start the websocket RPC server. The 135 // default zero value is/ valid and will pick a port number randomly (useful for 136 // ephemeral nodes). 137 WSPort int `toml:",omitempty"` 138 139 // WSPathPrefix specifies a path prefix on which ws-rpc is to be served. 140 WSPathPrefix string `toml:",omitempty"` 141 142 // WSOrigins is the list of domain to accept websocket requests from. Please be 143 // aware that the server can only act upon the HTTP request the client sends and 144 // cannot verify the validity of the request header. 145 WSOrigins []string `toml:",omitempty"` 146 147 // WSModules is a list of API modules to expose via the websocket RPC interface. 148 // If the module list is empty, all RPC API endpoints designated public will be 149 // exposed. 150 WSModules []string 151 152 // WSExposeAll exposes all API modules via the WebSocket RPC interface rather 153 // than just the public ones. 154 // 155 // *WARNING* Only set this if the node is running in a trusted network, exposing 156 // private APIs to untrusted users is a major security risk. 157 WSExposeAll bool `toml:",omitempty"` 158 159 // Logger is a custom logger to use with the p2p.Server. 160 Logger *log.Logger `toml:",omitempty"` 161 162 staticNodesWarning bool 163 trustedNodesWarning bool 164 oldQuaiResourceWarning bool 165 166 // AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC. 167 AllowUnprotectedTxs bool `toml:",omitempty"` 168 169 // JWTSecret is the path to the hex-encoded jwt secret. 170 JWTSecret string `toml:",omitempty"` 171 172 // EnablePersonal enables the deprecated personal namespace. 173 EnablePersonal bool `toml:"-"` 174 175 DBEngine string `toml:",omitempty"` 176 } 177 178 // NodeDB returns the path to the discovery node database. 179 func (c *Config) NodeDB() string { 180 if c.DataDir == "" { 181 return "" // ephemeral 182 } 183 return c.ResolvePath(datadirNodeDatabase) 184 } 185 186 // HTTPEndpoint resolves an HTTP endpoint based on the configured host interface 187 // and port parameters. 188 func (c *Config) HTTPEndpoint() string { 189 if c.HTTPHost == "" { 190 return "" 191 } 192 return fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort) 193 } 194 195 // DefaultHTTPEndpoint returns the HTTP endpoint used by default. 196 func DefaultHTTPEndpoint() string { 197 config := &Config{HTTPHost: DefaultHTTPHost, HTTPPort: DefaultHTTPPort} 198 return config.HTTPEndpoint() 199 } 200 201 // WSEndpoint resolves a websocket endpoint based on the configured host interface 202 // and port parameters. 203 func (c *Config) WSEndpoint() string { 204 if c.WSHost == "" { 205 return "" 206 } 207 return fmt.Sprintf("%s:%d", c.WSHost, c.WSPort) 208 } 209 210 // DefaultWSEndpoint returns the websocket endpoint used by default. 211 func DefaultWSEndpoint() string { 212 config := &Config{WSHost: DefaultWSHost, WSPort: DefaultWSPort} 213 return config.WSEndpoint() 214 } 215 216 // ExtRPCEnabled returns the indicator whether node enables the external 217 // RPC(http, ws). 218 func (c *Config) ExtRPCEnabled() bool { 219 return c.HTTPHost != "" || c.WSHost != "" 220 } 221 222 // NodeName returns the devp2p node identifier. 223 func (c *Config) NodeName() string { 224 name := c.name() 225 if c.UserIdent != "" { 226 name += "/" + c.UserIdent 227 } 228 if c.Version != "" { 229 name += "/" + c.Version 230 } 231 name += "/" + runtime.GOOS + "-" + runtime.GOARCH 232 name += "/" + runtime.Version() 233 return name 234 } 235 236 func (c *Config) name() string { 237 if c.Name == "" { 238 progname := strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe") 239 if progname == "" { 240 panic("empty executable name, set Config.Name") 241 } 242 return progname 243 } 244 return c.Name 245 } 246 247 // These resources are resolved differently for "go-quai" instances. 248 var isOldQuaiResource = map[string]bool{ 249 "chaindata": true, 250 "nodes": true, 251 "nodekey": true, 252 "static-nodes.json": false, // no warning for these because they have their 253 "trusted-nodes.json": false, // own separate warning. 254 } 255 256 // ResolvePath resolves path in the instance directory. 257 func (c *Config) ResolvePath(path string) string { 258 if filepath.IsAbs(path) { 259 return path 260 } 261 if c.DataDir == "" { 262 return "" 263 } 264 return filepath.Join(c.instanceDir(), path) 265 } 266 267 func (c *Config) instanceDir() string { 268 if c.DataDir == "" { 269 return "" 270 } 271 return filepath.Join(c.DataDir, c.name()) 272 } 273 274 // NodeKey retrieves the currently configured private key of the node, checking 275 // first any manually set key, falling back to the one found in the configured 276 // data folder. If no key can be found, a new one is generated. 277 func (c *Config) NodeKey() *ecdsa.PrivateKey { 278 // Use any specifically configured key. 279 if c.P2P.PrivateKey != nil { 280 return c.P2P.PrivateKey 281 } 282 // Generate ephemeral key if no datadir is being used. 283 if c.DataDir == "" { 284 key, err := crypto.GenerateKey() 285 if err != nil { 286 log.Fatal(fmt.Sprintf("Failed to generate ephemeral node key: %v", err)) 287 } 288 return key 289 } 290 291 keyfile := c.ResolvePath(datadirPrivateKey) 292 if key, err := crypto.LoadECDSA(keyfile); err == nil { 293 return key 294 } 295 // No persistent key found, generate and store a new one. 296 key, err := crypto.GenerateKey() 297 if err != nil { 298 log.Fatal(fmt.Sprintf("Failed to generate node key: %v", err)) 299 } 300 instanceDir := filepath.Join(c.DataDir, c.name()) 301 if err := os.MkdirAll(instanceDir, 0700); err != nil { 302 log.Error(fmt.Sprintf("Failed to persist node key: %v", err)) 303 return key 304 } 305 keyfile = filepath.Join(instanceDir, datadirPrivateKey) 306 if err := crypto.SaveECDSA(keyfile, key); err != nil { 307 log.Error(fmt.Sprintf("Failed to persist node key: %v", err)) 308 } 309 return key 310 } 311 312 // StaticNodes returns a list of node enode URLs configured as static nodes. 313 func (c *Config) StaticNodes() []*enode.Node { 314 return c.parsePersistentNodes(&c.staticNodesWarning, c.ResolvePath(datadirStaticNodes)) 315 } 316 317 // TrustedNodes returns a list of node enode URLs configured as trusted nodes. 318 func (c *Config) TrustedNodes() []*enode.Node { 319 return c.parsePersistentNodes(&c.trustedNodesWarning, c.ResolvePath(datadirTrustedNodes)) 320 } 321 322 // parsePersistentNodes parses a list of discovery node URLs loaded from a .json 323 // file from within the data directory. 324 func (c *Config) parsePersistentNodes(w *bool, path string) []*enode.Node { 325 // Short circuit if no node config is present 326 if c.DataDir == "" { 327 return nil 328 } 329 if _, err := os.Stat(path); err != nil { 330 return nil 331 } 332 c.warnOnce(w, "Found deprecated node list file %s, please use the TOML config file instead.", path) 333 334 // Load the nodes from the config file. 335 var nodelist []string 336 if err := common.LoadJSON(path, &nodelist); err != nil { 337 log.Error(fmt.Sprintf("Can't load node list file: %v", err)) 338 return nil 339 } 340 // Interpret the list as a discovery node array 341 var nodes []*enode.Node 342 for _, url := range nodelist { 343 if url == "" { 344 continue 345 } 346 node, err := enode.Parse(enode.ValidSchemes, url) 347 if err != nil { 348 log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err)) 349 continue 350 } 351 nodes = append(nodes, node) 352 } 353 return nodes 354 } 355 356 var warnLock sync.Mutex 357 358 func (c *Config) warnOnce(w *bool, format string, args ...interface{}) { 359 warnLock.Lock() 360 defer warnLock.Unlock() 361 362 if *w { 363 return 364 } 365 l := c.Logger 366 if l == nil { 367 l = &log.Log 368 } 369 l.Warn(fmt.Sprintf(format, args...)) 370 *w = true 371 }