github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/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 "github.com/intfoundation/intchain/rpc" 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 "runtime" 27 "strings" 28 29 "github.com/intfoundation/intchain/accounts" 30 "github.com/intfoundation/intchain/accounts/keystore" 31 "github.com/intfoundation/intchain/accounts/usbwallet" 32 "github.com/intfoundation/intchain/common" 33 "github.com/intfoundation/intchain/crypto" 34 "github.com/intfoundation/intchain/log" 35 "github.com/intfoundation/intchain/p2p" 36 "github.com/intfoundation/intchain/p2p/discover" 37 ) 38 39 const ( 40 datadirPrivateKey = "nodekey" // Path within the datadir to the node's private key 41 datadirDefaultKeyStore = "keystore" // Path within the datadir to the keystore 42 datadirStaticNodes = "static-nodes.json" // Path within the datadir to the static node list 43 datadirTrustedNodes = "trusted-nodes.json" // Path within the datadir to the trusted node list 44 datadirNodeDatabase = "nodes" // Path within the datadir to store the node infos 45 ) 46 47 // Config represents a small collection of configuration values to fine tune the 48 // P2P network layer of a protocol stack. These values can be further extended by 49 // all registered services. 50 type Config struct { 51 // Name sets the instance name of the node. It must not contain the / character and is 52 // used in the devp2p node identifier. The instance name of intchain is "intchain". If no 53 // value is specified, the basename of the current executable is used. 54 Name string `toml:"-"` 55 56 ChainId string `toml:",omitempty"` 57 58 // UserIdent, if set, is used as an additional component in the devp2p node identifier. 59 UserIdent string `toml:",omitempty"` 60 61 // Version should be set to the version number of the program. It is used 62 // in the devp2p node identifier. 63 Version string `toml:"-"` 64 65 // GeneralDataDir is the file system folder for general information 66 GeneralDataDir string 67 68 // DataDir is the file system folder the node should use for any data storage 69 // requirements. The configured data directory will not be directly shared with 70 // registered services, instead those can use utility methods to create/access 71 // databases or flat files. This enables ephemeral nodes which can fully reside 72 // in memory. 73 DataDir string 74 75 // Configuration of peer-to-peer networking. 76 P2P p2p.Config 77 78 // KeyStoreDir is the file system folder that contains private keys. The directory can 79 // be specified as a relative path, in which case it is resolved relative to the 80 // current directory. 81 // 82 // If KeyStoreDir is empty, the default location is the "keystore" subdirectory of 83 // DataDir. If DataDir is unspecified and KeyStoreDir is empty, an ephemeral directory 84 // is created by New and destroyed when the node is stopped. 85 KeyStoreDir string `toml:",omitempty"` 86 87 // UseLightweightKDF lowers the memory and CPU requirements of the key store 88 // scrypt KDF at the expense of security. 89 UseLightweightKDF bool `toml:",omitempty"` 90 91 // NoUSB disables hardware wallet monitoring and connectivity. 92 NoUSB bool `toml:",omitempty"` 93 94 // IPCPath is the requested location to place the IPC endpoint. If the path is 95 // a simple file name, it is placed inside the data directory (or on the root 96 // pipe path on Windows), whereas if it's a resolvable path name (absolute or 97 // relative), then that specific path is enforced. An empty path disables IPC. 98 IPCPath string `toml:",omitempty"` 99 100 // HTTPHost is the host interface on which to start the HTTP RPC server. If this 101 // field is empty, no HTTP API endpoint will be started. 102 HTTPHost string `toml:",omitempty"` 103 104 // HTTPPort is the TCP port number on which to start the HTTP RPC server. The 105 // default zero value is/ valid and will pick a port number randomly (useful 106 // for ephemeral nodes). 107 HTTPPort int `toml:",omitempty"` 108 109 // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting 110 // clients. Please be aware that CORS is a browser enforced security, it's fully 111 // useless for custom HTTP clients. 112 HTTPCors []string `toml:",omitempty"` 113 114 // HTTPVirtualHosts is the list of virtual hostnames which are allowed on incoming requests. 115 // This is by default {'localhost'}. Using this prevents attacks like 116 // DNS rebinding, which bypasses SOP by simply masquerading as being within the same 117 // origin. These attacks do not utilize CORS, since they are not cross-domain. 118 // By explicitly checking the Host-header, the server will not allow requests 119 // made against the server with a malicious host domain. 120 // Requests using ip address directly are not affected 121 HTTPVirtualHosts []string `toml:",omitempty"` 122 123 // HTTPModules is a list of API modules to expose via the HTTP RPC interface. 124 // If the module list is empty, all RPC API endpoints designated public will be 125 // exposed. 126 HTTPModules []string `toml:",omitempty"` 127 128 // HTTPTimeouts allows for customization of the timeout values used by the HTTP RPC 129 // interface. 130 HTTPTimeouts rpc.HTTPTimeouts 131 132 // WSHost is the host interface on which to start the websocket RPC server. If 133 // this field is empty, no websocket API endpoint will be started. 134 WSHost string `toml:",omitempty"` 135 136 // WSPort is the TCP port number on which to start the websocket RPC server. The 137 // default zero value is/ valid and will pick a port number randomly (useful for 138 // ephemeral nodes). 139 WSPort int `toml:",omitempty"` 140 141 // WSOrigins is the list of domain to accept websocket requests from. Please be 142 // aware that the server can only act upon the HTTP request the client sends and 143 // cannot verify the validity of the request header. 144 WSOrigins []string `toml:",omitempty"` 145 146 // WSModules is a list of API modules to expose via the websocket RPC interface. 147 // If the module list is empty, all RPC API endpoints designated public will be 148 // exposed. 149 WSModules []string `toml:",omitempty"` 150 151 // WSExposeAll exposes all API modules via the WebSocket RPC interface rather 152 // than just the public ones. 153 // 154 // *WARNING* Only set this if the node is running in a trusted network, exposing 155 // private APIs to untrusted users is a major security risk. 156 WSExposeAll bool `toml:",omitempty"` 157 158 // Logger is a custom logger to use with the p2p.Server. 159 Logger log.Logger `toml:",omitempty"` 160 } 161 162 // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into 163 // account the set data folders as well as the designated platform we're currently 164 // running on. 165 func (c *Config) IPCEndpoint() string { 166 // Short circuit if IPC has not been enabled 167 if c.IPCPath == "" { 168 return "" 169 } 170 // On windows we can only use plain top-level pipes 171 if runtime.GOOS == "windows" { 172 if strings.HasPrefix(c.IPCPath, `\\.\pipe\`) { 173 return c.IPCPath 174 } 175 return `\\.\pipe\` + c.ChainId + `\` + c.IPCPath 176 } 177 // Resolve names into the data directory full paths otherwise 178 if filepath.Base(c.IPCPath) == c.IPCPath { 179 if c.DataDir == "" { 180 return filepath.Join(os.TempDir(), c.IPCPath) 181 } 182 return filepath.Join(c.DataDir, c.IPCPath) 183 } 184 return c.IPCPath 185 } 186 187 // NodeDB returns the path to the discovery node database. 188 func (c *Config) NodeDB() string { 189 if c.GeneralDataDir == "" { 190 return "" // ephemeral 191 } 192 return c.ResolvePath(datadirNodeDatabase) 193 } 194 195 // DefaultIPCEndpoint returns the IPC path used by default. 196 func DefaultIPCEndpoint(clientIdentifier string) string { 197 if clientIdentifier == "" { 198 clientIdentifier = strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe") 199 if clientIdentifier == "" { 200 panic("empty executable name") 201 } 202 } 203 config := &Config{DataDir: DefaultDataDir(), IPCPath: clientIdentifier + ".ipc"} 204 return config.IPCEndpoint() 205 } 206 207 // HTTPEndpoint resolves an HTTP endpoint based on the configured host interface 208 // and port parameters. 209 func (c *Config) HTTPEndpoint() string { 210 if c.HTTPHost == "" { 211 return "" 212 } 213 return fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort) 214 } 215 216 // DefaultHTTPEndpoint returns the HTTP endpoint used by default. 217 func DefaultHTTPEndpoint() string { 218 config := &Config{HTTPHost: DefaultHTTPHost, HTTPPort: DefaultHTTPPort} 219 return config.HTTPEndpoint() 220 } 221 222 // WSEndpoint resolves an websocket endpoint based on the configured host interface 223 // and port parameters. 224 func (c *Config) WSEndpoint() string { 225 if c.WSHost == "" { 226 return "" 227 } 228 return fmt.Sprintf("%s:%d", c.WSHost, c.WSPort) 229 } 230 231 // DefaultWSEndpoint returns the websocket endpoint used by default. 232 func DefaultWSEndpoint() string { 233 config := &Config{WSHost: DefaultWSHost, WSPort: DefaultWSPort} 234 return config.WSEndpoint() 235 } 236 237 // NodeName returns the devp2p node identifier. 238 func (c *Config) NodeName() string { 239 name := c.name() 240 // Backwards compatibility: previous versions used title-cased "Geth", keep that. 241 //if name == "intchain" || name == "intchain-testnet" { 242 // name = "intchain" 243 //} 244 if c.UserIdent != "" { 245 name += "/" + c.UserIdent 246 } 247 if c.Version != "" { 248 name += "/v" + c.Version 249 } 250 name += "/" + runtime.GOOS + "-" + runtime.GOARCH 251 name += "/" + runtime.Version() 252 return name 253 } 254 255 func (c *Config) name() string { 256 if c.Name == "" { 257 progname := strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe") 258 if progname == "" { 259 panic("empty executable name, set Config.Name") 260 } 261 return progname 262 } 263 return c.Name 264 } 265 266 // These resources are resolved differently for "intchain" instances. 267 var isOldGethResource = map[string]bool{ 268 "chaindata": true, 269 "nodes": true, 270 "nodekey": true, 271 "static-nodes.json": true, 272 "trusted-nodes.json": true, 273 } 274 275 var isGeneralResource = map[string]bool{ 276 "nodes": true, 277 "nodekey": true, 278 "static-nodes.json": true, 279 "trusted-nodes.json": true, 280 } 281 282 // ResolvePath resolves path in the instance directory. 283 func (c *Config) ResolvePath(path string) string { 284 if filepath.IsAbs(path) { 285 return path 286 } 287 if c.DataDir == "" { 288 return "" 289 } 290 // Backwards-compatibility: ensure that data directory files created 291 if c.name() == "intchain" && isOldGethResource[path] { 292 oldpath := "" 293 if c.Name == "intchain" { 294 oldpath = filepath.Join(c.DataDir, path) 295 } 296 if oldpath != "" && common.FileExist(oldpath) { 297 // TODO: print warning 298 return oldpath 299 } 300 } 301 302 if isGeneralResource[path] { 303 return filepath.Join(c.GeneralDataDir, path) 304 } 305 306 return filepath.Join(c.instanceDir(), path) 307 } 308 309 func (c *Config) instanceDir() string { 310 if c.DataDir == "" { 311 return "" 312 } 313 314 return filepath.Join(c.DataDir, c.name()) 315 } 316 317 // NodeKey retrieves the currently configured private key of the node, checking 318 // first any manually set key, falling back to the one found in the configured 319 // data folder. If no key can be found, a new one is generated. 320 func (c *Config) NodeKey() *ecdsa.PrivateKey { 321 // Use any specifically configured key. 322 if c.P2P.PrivateKey != nil { 323 return c.P2P.PrivateKey 324 } 325 // Generate ephemeral key if no datadir is being used. 326 if c.GeneralDataDir == "" { 327 key, err := crypto.GenerateKey() 328 if err != nil { 329 log.Crit(fmt.Sprintf("Failed to generate ephemeral node key: %v", err)) 330 } 331 return key 332 } 333 334 keyfile := c.ResolvePath(datadirPrivateKey) 335 if key, err := crypto.LoadECDSA(keyfile); err == nil { 336 return key 337 } 338 // No persistent key found, generate and store a new one. 339 key, err := crypto.GenerateKey() 340 if err != nil { 341 log.Crit(fmt.Sprintf("Failed to generate node key: %v", err)) 342 } 343 /* 344 instanceDir := filepath.Join(c.DataDir, c.name()) 345 if err := os.MkdirAll(instanceDir, 0700); err != nil { 346 log.Error(fmt.Sprintf("Failed to persist node key: %v", err)) 347 return key 348 } 349 keyfile = filepath.Join(instanceDir, datadirPrivateKey) 350 */ 351 if err := os.MkdirAll(c.GeneralDataDir, 0700); err != nil { 352 log.Error(fmt.Sprintf("Failed to persist node key: %v", err)) 353 return key 354 } 355 keyfile = c.ResolvePath(datadirPrivateKey) 356 357 if err := crypto.SaveECDSA(keyfile, key); err != nil { 358 log.Error(fmt.Sprintf("Failed to persist node key: %v", err)) 359 } 360 return key 361 } 362 363 // StaticNodes returns a list of node enode URLs configured as static nodes. 364 func (c *Config) StaticNodes() []*discover.Node { 365 return c.parsePersistentNodes(c.ResolvePath(datadirStaticNodes)) 366 } 367 368 // TrustedNodes returns a list of node enode URLs configured as trusted nodes. 369 func (c *Config) TrustedNodes() []*discover.Node { 370 return c.parsePersistentNodes(c.ResolvePath(datadirTrustedNodes)) 371 } 372 373 // parsePersistentNodes parses a list of discovery node URLs loaded from a .json 374 // file from within the data directory. 375 func (c *Config) parsePersistentNodes(path string) []*discover.Node { 376 // Short circuit if no node config is present 377 if c.DataDir == "" { 378 return nil 379 } 380 if _, err := os.Stat(path); err != nil { 381 return nil 382 } 383 // Load the nodes from the config file. 384 var nodelist []string 385 if err := common.LoadJSON(path, &nodelist); err != nil { 386 log.Error(fmt.Sprintf("Can't load node file %s: %v", path, err)) 387 return nil 388 } 389 // Interpret the list as a discovery node array 390 var nodes []*discover.Node 391 for _, url := range nodelist { 392 if url == "" { 393 continue 394 } 395 node, err := discover.ParseNode(url) 396 if err != nil { 397 log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err)) 398 continue 399 } 400 nodes = append(nodes, node) 401 } 402 return nodes 403 } 404 405 // AccountConfig determines the settings for scrypt and keydirectory 406 func (c *Config) AccountConfig() (int, int, string, error) { 407 scryptN := keystore.StandardScryptN 408 scryptP := keystore.StandardScryptP 409 if c.UseLightweightKDF { 410 scryptN = keystore.LightScryptN 411 scryptP = keystore.LightScryptP 412 } 413 414 var ( 415 keydir string 416 err error 417 ) 418 switch { 419 case filepath.IsAbs(c.KeyStoreDir): 420 keydir = c.KeyStoreDir 421 case c.DataDir != "": 422 if c.KeyStoreDir == "" { 423 keydir = filepath.Join(c.DataDir, datadirDefaultKeyStore) 424 } else { 425 keydir, err = filepath.Abs(c.KeyStoreDir) 426 } 427 case c.KeyStoreDir != "": 428 keydir, err = filepath.Abs(c.KeyStoreDir) 429 } 430 return scryptN, scryptP, keydir, err 431 } 432 433 func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { 434 scryptN, scryptP, keydir, err := conf.AccountConfig() 435 var ephemeral string 436 if keydir == "" { 437 // There is no datadir. 438 keydir, err = ioutil.TempDir("", "intchain-keystore") 439 ephemeral = keydir 440 } 441 442 if err != nil { 443 return nil, "", err 444 } 445 if err := os.MkdirAll(keydir, 0700); err != nil { 446 return nil, "", err 447 } 448 // Assemble the account manager and supported backends 449 backends := []accounts.Backend{ 450 keystore.NewKeyStore(keydir, scryptN, scryptP), 451 } 452 if !conf.NoUSB { 453 // Start a USB hub for Ledger hardware wallets 454 if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil { 455 log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err)) 456 } else { 457 backends = append(backends, ledgerhub) 458 } 459 // Start a USB hub for Trezor hardware wallets 460 if trezorhub, err := usbwallet.NewTrezorHub(); err != nil { 461 log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err)) 462 } else { 463 backends = append(backends, trezorhub) 464 } 465 } 466 return accounts.NewManager(backends...), ephemeral, nil 467 }