github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/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 "encoding/json" 22 "fmt" 23 "net" 24 "os" 25 "path/filepath" 26 "runtime" 27 "strings" 28 29 "github.com/ethereumproject/go-ethereum/common" 30 "github.com/ethereumproject/go-ethereum/crypto" 31 "github.com/ethereumproject/go-ethereum/logger" 32 "github.com/ethereumproject/go-ethereum/logger/glog" 33 "github.com/ethereumproject/go-ethereum/p2p/discover" 34 "github.com/ethereumproject/go-ethereum/p2p/nat" 35 "github.com/spf13/afero" 36 ) 37 38 var ( 39 datadirPrivateKey = "nodekey" // Path within the datadir to the node's private key 40 datadirStaticNodes = "static-nodes.json" // Path within the datadir to the static node list 41 datadirTrustedNodes = "trusted-nodes.json" // Path within the datadir to the trusted node list 42 datadirNodeDatabase = "nodes" // Path within the datadir to store the node infos 43 ) 44 45 // fs wraps afero.FS, used as a type of it's own so that we can take it's address 46 // and set a zero-value default. 47 type fs struct { 48 afero.Fs 49 } 50 51 // Config represents a small collection of configuration values to fine tune the 52 // P2P network layer of a protocol stack. These values can be further extended by 53 // all registered services. 54 type Config struct { 55 // DataDir is the file system folder the node should use for any data storage 56 // requirements. The configured data directory will not be directly shared with 57 // registered services, instead those can use utility methods to create/access 58 // databases or flat files. This enables ephemeral nodes which can fully reside 59 // in memory. 60 DataDir string 61 62 // IPCPath is the requested location to place the IPC endpoint. If the path is 63 // a simple file name, it is placed inside the chaindata directory (or on the root 64 // pipe path on Windows), whereas if it's a resolvable path name (absolute or 65 // relative), then that specific path is enforced. An empty path disables IPC. 66 IPCPath string 67 68 // fs is an abstracted file system. 69 // In normal use, it points to a thin wrapper around the standard os FS package, 70 // and can be swapped for an abstracted in-mem map during tests, which helps 71 // ensure test reliability by removing sometimes-laggy OS FS R/Ws. 72 // The var is currently private because it is not set to inMem outside of this package tests. 73 fs *fs 74 75 // This field should be a valid secp256k1 private key that will be used for both 76 // remote peer identification as well as network traffic encryption. If no key 77 // is configured, the preset one is loaded from the data dir, generating it if 78 // needed. 79 PrivateKey *ecdsa.PrivateKey 80 81 // Name sets the node name of this server. 82 Name string 83 84 // NoDiscovery specifies whether the peer discovery mechanism should be started 85 // or not. Disabling is usually useful for protocol debugging (manual topology). 86 NoDiscovery bool 87 88 // Bootstrap nodes used to establish connectivity with the rest of the network. 89 BootstrapNodes []*discover.Node 90 91 // Network interface address on which the node should listen for inbound peers. 92 ListenAddr string 93 94 // If set to a non-nil value, the given NAT port mapper is used to make the 95 // listening port available to the Internet. 96 NAT nat.Interface 97 98 // If Dialer is set to a non-nil value, the given Dialer is used to dial outbound 99 // peer connections. 100 Dialer *net.Dialer 101 102 // If NoDial is true, the node will not dial any peers. 103 NoDial bool 104 105 // MaxPeers is the maximum number of peers that can be connected. If this is 106 // set to zero, then only the configured static and trusted peers can connect. 107 MaxPeers int 108 109 // MaxPendingPeers is the maximum number of peers that can be pending in the 110 // handshake phase, counted separately for inbound and outbound connections. 111 // Zero defaults to preset values. 112 MaxPendingPeers int 113 114 // HTTPHost is the host interface on which to start the HTTP RPC server. If this 115 // field is empty, no HTTP API endpoint will be started. 116 HTTPHost string 117 118 // HTTPPort is the TCP port number on which to start the HTTP RPC server. The 119 // default zero value is/ valid and will pick a port number randomly (useful 120 // for ephemeral nodes). 121 HTTPPort int 122 123 // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting 124 // clients. Please be aware that CORS is a browser enforced security, it's fully 125 // useless for custom HTTP clients. 126 HTTPCors string 127 128 // HTTPModules is a list of API modules to expose via the HTTP RPC interface. 129 // If the module list is empty, all RPC API endpoints designated public will be 130 // exposed. 131 HTTPModules []string 132 133 // WSHost is the host interface on which to start the websocket RPC server. If 134 // this field is empty, no websocket API endpoint will be started. 135 WSHost string 136 137 // WSPort is the TCP port number on which to start the websocket RPC server. The 138 // default zero value is/ valid and will pick a port number randomly (useful for 139 // ephemeral nodes). 140 WSPort int 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 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 153 // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into 154 // account the set data folders as well as the designated platform we're currently 155 // running on. 156 func (c *Config) IPCEndpoint() string { 157 // Short circuit if IPC has not been enabled 158 if c.IPCPath == "" { 159 return "" 160 } 161 // just for safety, but should be already initialized 162 if c.fs == nil { 163 c.fs = &fs{afero.NewOsFs()} 164 } 165 // On windows we can only use plain top-level pipes 166 if runtime.GOOS == "windows" { 167 if strings.HasPrefix(c.IPCPath, `\\.\pipe\`) { 168 return c.IPCPath 169 } 170 return `\\.\pipe\` + c.IPCPath 171 } 172 // Resolve names into the data directory full paths otherwise 173 if filepath.Base(c.IPCPath) == c.IPCPath { 174 if c.DataDir == "" { 175 // Use afs.MkdirAll instead of afero.TempDir because with the latter, 176 // afero creates temporary™ sub dir under the normal temp dir. Not sure if bug or expected, but 177 // just using plain os temp dir is ok. 178 // Anyways, all afero ~is doing~ do should do is just mkdir -p the os-specific tmp dir path anyway 179 tempDir := os.TempDir() 180 err := c.fs.MkdirAll(tempDir, os.ModeTemporary) 181 if err != nil && !os.IsExist(err) { 182 glog.Fatal(err) 183 } 184 return filepath.Join(tempDir, c.IPCPath) 185 } 186 return filepath.Join(c.DataDir, c.IPCPath) 187 } 188 return c.IPCPath 189 } 190 191 // DefaultIPCEndpoint returns the IPC path used by default. 192 func DefaultIPCEndpoint(chainDir string) string { 193 config := &Config{DataDir: chainDir, IPCPath: common.DefaultIPCSocket} 194 return config.IPCEndpoint() 195 } 196 197 // HTTPEndpoint resolves an HTTP endpoint based on the configured host interface 198 // and port parameters. 199 func (c *Config) HTTPEndpoint() string { 200 if c.HTTPHost == "" { 201 return "" 202 } 203 return fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort) 204 } 205 206 // WSEndpoint resolves an websocket endpoint based on the configured host interface 207 // and port parameters. 208 func (c *Config) WSEndpoint() string { 209 if c.WSHost == "" { 210 return "" 211 } 212 return fmt.Sprintf("%s:%d", c.WSHost, c.WSPort) 213 } 214 215 // NodeKey retrieves the currently configured private key of the node, checking 216 // first any manually set key, falling back to the one found in the configured 217 // data folder. If no key can be found, a new one is generated. 218 func (c *Config) NodeKey() *ecdsa.PrivateKey { 219 // Use any specifically configured key 220 if c.PrivateKey != nil { 221 return c.PrivateKey 222 } 223 // just for safety, but should be already initialized 224 if c.fs == nil { 225 c.fs = &fs{afero.NewOsFs()} 226 } 227 // Generate ephemeral key if no datadir is being used 228 if c.DataDir == "" { 229 key, err := crypto.GenerateKey() 230 if err != nil { 231 glog.Fatalf("Failed to generate ephemeral node key: %v", err) 232 } 233 return key 234 } 235 // Fall back to persistent key from the data directory 236 keyfile := filepath.Join(c.DataDir, datadirPrivateKey) 237 f, err := c.fs.Open(keyfile) 238 if err == nil { 239 // file open error was nil, attempt to load key, fatal on any error 240 defer f.Close() 241 key, err := crypto.LoadECDSA(f) 242 if err == nil { 243 return key 244 } 245 246 glog.Fatalf("could not load key file: %v", err) 247 } 248 249 // there was an error opening an existing key file; there's nothing we can do about this 250 if !os.IsNotExist(err) { 251 glog.Fatalf("could not load key file: %v", err) 252 return nil 253 } 254 255 // No key file found, generate and store a new one 256 key, err := crypto.GenerateKey() 257 if err != nil { 258 glog.Fatalf("Failed to generate node key: %v", err) 259 } 260 261 f, err = c.fs.Create(keyfile) 262 if err != nil { 263 glog.Fatalf("failed to open node key file: %v", err) 264 } 265 defer f.Close() 266 if _, err := crypto.WriteECDSAKey(f, key); err != nil { 267 glog.V(logger.Error).Infof("Failed to persist node key: %v", err) 268 } 269 return key 270 } 271 272 // StaticNodes returns a list of node enode URLs configured as static nodes. 273 func (c *Config) StaticNodes() []*discover.Node { 274 return c.parsePersistentNodes(datadirStaticNodes) 275 } 276 277 // TrusterNodes returns a list of node enode URLs configured as trusted nodes. 278 func (c *Config) TrusterNodes() []*discover.Node { 279 return c.parsePersistentNodes(datadirTrustedNodes) 280 } 281 282 // parsePersistentNodes parses a list of discovery node URLs loaded from a .json 283 // file from within the data directory. 284 func (c *Config) parsePersistentNodes(file string) []*discover.Node { 285 // Short circuit if no node config is present 286 if c.DataDir == "" { 287 return nil 288 } 289 // just for safety, but should be already initialized 290 if c.fs == nil { 291 c.fs = &fs{afero.NewOsFs()} 292 } 293 path := filepath.Join(c.DataDir, file) 294 if _, err := c.fs.Stat(path); err != nil { 295 return nil 296 } 297 // Load the nodes from the config file 298 blob, err := afero.ReadFile(c.fs, path) 299 if err != nil { 300 glog.V(logger.Error).Infof("Failed to access nodes: %v", err) 301 return nil 302 } 303 nodelist := []string{} 304 if err := json.Unmarshal(blob, &nodelist); err != nil { 305 glog.V(logger.Error).Infof("Failed to load nodes: %v", err) 306 return nil 307 } 308 // Interpret the list as a discovery node array 309 var nodes []*discover.Node 310 for _, url := range nodelist { 311 if url == "" { 312 continue 313 } 314 node, err := discover.ParseNode(url) 315 if err != nil { 316 glog.V(logger.Error).Infof("Node URL %s: %v\n", url, err) 317 continue 318 } 319 nodes = append(nodes, node) 320 } 321 return nodes 322 }