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  }