github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/node/config.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:40</date>
    10  //</624450102047543296>
    11  
    12  
    13  package node
    14  
    15  import (
    16  	"crypto/ecdsa"
    17  	"fmt"
    18  	"io/ioutil"
    19  	"os"
    20  	"path/filepath"
    21  	"runtime"
    22  	"strings"
    23  	"sync"
    24  
    25  	"github.com/ethereum/go-ethereum/accounts"
    26  	"github.com/ethereum/go-ethereum/accounts/keystore"
    27  	"github.com/ethereum/go-ethereum/accounts/usbwallet"
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/crypto"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/p2p"
    32  	"github.com/ethereum/go-ethereum/p2p/enode"
    33  	"github.com/ethereum/go-ethereum/rpc"
    34  )
    35  
    36  const (
    37  datadirPrivateKey      = "nodekey"            //datadir中指向节点私钥的路径
    38  datadirDefaultKeyStore = "keystore"           //到密钥库的datadir中的路径
    39  datadirStaticNodes     = "static-nodes.json"  //到静态节点列表的datadir中的路径
    40  datadirTrustedNodes    = "trusted-nodes.json" //datadir中到受信任节点列表的路径
    41  datadirNodeDatabase    = "nodes"              //datadir中存储节点信息的路径
    42  )
    43  
    44  //config表示一个小的配置值集合,用于微调
    45  //协议栈的P2P网络层。这些值可以进一步扩展为
    46  //所有注册服务。
    47  type Config struct {
    48  //名称设置节点的实例名称。它不能包含/字符,并且
    49  //在devp2p节点标识符中使用。geth的实例名为“geth”。如果没有
    50  //如果指定了值,则使用当前可执行文件的基名称。
    51  	Name string `toml:"-"`
    52  
    53  //如果设置了userIdent,则用作devp2p节点标识符中的附加组件。
    54  	UserIdent string `toml:",omitempty"`
    55  
    56  //版本应设置为程序的版本号。它被使用
    57  //在devp2p节点标识符中。
    58  	Version string `toml:"-"`
    59  
    60  //datadir是节点应用于任何数据存储的文件系统文件夹。
    61  //要求。配置的数据目录将不会直接与共享
    62  //注册的服务,而这些服务可以使用实用工具方法来创建/访问
    63  //数据库或平面文件。这将启用可以完全驻留的临时节点
    64  //在记忆中。
    65  	DataDir string
    66  
    67  //对等网络的配置。
    68  	P2P p2p.Config
    69  
    70  //keystoredir是包含私钥的文件系统文件夹。目录可以
    71  //被指定为相对路径,在这种情况下,它是相对于
    72  //当前目录。
    73  //
    74  //如果keystoredir为空,则默认位置是的“keystore”子目录。
    75  //DATADIR如果未指定datadir且keystoredir为空,则为临时目录
    76  //由new创建并在节点停止时销毁。
    77  	KeyStoreDir string `toml:",omitempty"`
    78  
    79  //uselightweightkdf降低了密钥存储的内存和CPU要求
    80  //以牺牲安全为代价加密kdf。
    81  	UseLightweightKDF bool `toml:",omitempty"`
    82  
    83  //nousb禁用硬件钱包监控和连接。
    84  	NoUSB bool `toml:",omitempty"`
    85  
    86  //ipcpath是放置ipc端点的请求位置。如果路径是
    87  //一个简单的文件名,它放在数据目录(或根目录)中
    88  //窗口上的管道路径),但是如果它是可解析的路径名(绝对或
    89  //相对),然后强制执行该特定路径。空路径禁用IPC。
    90  	IPCPath string `toml:",omitempty"`
    91  
    92  //http host是启动HTTP RPC服务器的主机接口。如果这样
    93  //字段为空,不会启动HTTP API终结点。
    94  	HTTPHost string `toml:",omitempty"`
    95  
    96  //http port是启动HTTP RPC服务器的TCP端口号。这个
    97  //默认的零值是/有效的,将随机选择端口号(有用
    98  //对于季节性节点)。
    99  	HTTPPort int `toml:",omitempty"`
   100  
   101  //httpcors是要发送到请求的跨源资源共享头
   102  //客户。请注意,CORS是一种浏览器强制的安全性,它完全
   103  //对自定义HTTP客户端无效。
   104  	HTTPCors []string `toml:",omitempty"`
   105  
   106  //httpvirtualhosts是传入请求上允许的虚拟主机名列表。
   107  //默认为“localhost”。使用它可以防止像
   108  //DNS重新绑定,它通过简单地伪装成在同一个sop中绕过了sop
   109  //起源。这些攻击不使用CORS,因为它们不是跨域的。
   110  //通过显式检查主机头,服务器将不允许请求
   111  //针对具有恶意主机域的服务器。
   112  //直接使用IP地址的请求不受影响
   113  	HTTPVirtualHosts []string `toml:",omitempty"`
   114  
   115  //http modules是要通过HTTP RPC接口公开的API模块列表。
   116  //如果模块列表为空,则所有指定为public的RPC API端点都将
   117  //暴露的。
   118  	HTTPModules []string `toml:",omitempty"`
   119  
   120  //HTTPTimeouts允许自定义HTTP RPC使用的超时值
   121  //接口。
   122  	HTTPTimeouts rpc.HTTPTimeouts
   123  
   124  //wshost是启动WebSocket RPC服务器的主机接口。如果
   125  //此字段为空,将不启动WebSocket API终结点。
   126  	WSHost string `toml:",omitempty"`
   127  
   128  //wsport是启动WebSocket RPC服务器的TCP端口号。这个
   129  //默认的零值是/有效的,将随机选择一个端口号(用于
   130  //短暂的节点)。
   131  	WSPort int `toml:",omitempty"`
   132  
   133  //wsorigins是接受WebSocket请求的域列表。请
   134  //注意,服务器只能根据客户机发送的HTTP请求进行操作,并且
   135  //无法验证请求头的有效性。
   136  	WSOrigins []string `toml:",omitempty"`
   137  
   138  //wsmodules是要通过WebSocket RPC接口公开的API模块列表。
   139  //如果模块列表为空,则所有指定为public的RPC API端点都将
   140  //暴露的。
   141  	WSModules []string `toml:",omitempty"`
   142  
   143  //wsexposeall通过websocket rpc接口公开所有api模块,而不是
   144  //而不仅仅是公众。
   145  //
   146  //*警告*仅当节点在受信任的网络中运行时设置此选项,显示
   147  //对不受信任用户的私有API是一个主要的安全风险。
   148  	WSExposeAll bool `toml:",omitempty"`
   149  
   150  //logger是用于p2p.server的自定义记录器。
   151  	Logger log.Logger `toml:",omitempty"`
   152  
   153  	staticNodesWarning     bool
   154  	trustedNodesWarning    bool
   155  	oldGethResourceWarning bool
   156  }
   157  
   158  //ipc endpoint根据配置的值解析IPC端点,考虑
   159  //帐户设置数据文件夹以及我们当前的指定平台
   160  //继续运行。
   161  func (c *Config) IPCEndpoint() string {
   162  //如果未启用仪表板组合仪表,则短路
   163  	if c.IPCPath == "" {
   164  		return ""
   165  	}
   166  //在窗户上,我们只能使用普通的顶层管道。
   167  	if runtime.GOOS == "windows" {
   168  		if strings.HasPrefix(c.IPCPath, `\\.\pipe\`) {
   169  			return c.IPCPath
   170  		}
   171  		return `\\.\pipe\` + c.IPCPath
   172  	}
   173  //将名称解析为数据目录完整路径,否则
   174  	if filepath.Base(c.IPCPath) == c.IPCPath {
   175  		if c.DataDir == "" {
   176  			return filepath.Join(os.TempDir(), c.IPCPath)
   177  		}
   178  		return filepath.Join(c.DataDir, c.IPCPath)
   179  	}
   180  	return c.IPCPath
   181  }
   182  
   183  //nodedb返回发现节点数据库的路径。
   184  func (c *Config) NodeDB() string {
   185  	if c.DataDir == "" {
   186  return "" //短暂的
   187  	}
   188  	return c.ResolvePath(datadirNodeDatabase)
   189  }
   190  
   191  //defaultipcendpoint返回默认情况下使用的IPC路径。
   192  func DefaultIPCEndpoint(clientIdentifier string) string {
   193  	if clientIdentifier == "" {
   194  		clientIdentifier = strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe")
   195  		if clientIdentifier == "" {
   196  			panic("empty executable name")
   197  		}
   198  	}
   199  	config := &Config{DataDir: DefaultDataDir(), IPCPath: clientIdentifier + ".ipc"}
   200  	return config.IPCEndpoint()
   201  }
   202  
   203  //http endpoint基于配置的主机接口解析HTTP端点
   204  //和端口参数。
   205  func (c *Config) HTTPEndpoint() string {
   206  	if c.HTTPHost == "" {
   207  		return ""
   208  	}
   209  	return fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort)
   210  }
   211  
   212  //default http endpoint返回默认情况下使用的HTTP端点。
   213  func DefaultHTTPEndpoint() string {
   214  	config := &Config{HTTPHost: DefaultHTTPHost, HTTPPort: DefaultHTTPPort}
   215  	return config.HTTPEndpoint()
   216  }
   217  
   218  //wsendpoint基于配置的主机接口解析WebSocket终结点
   219  //和端口参数。
   220  func (c *Config) WSEndpoint() string {
   221  	if c.WSHost == "" {
   222  		return ""
   223  	}
   224  	return fmt.Sprintf("%s:%d", c.WSHost, c.WSPort)
   225  }
   226  
   227  //defaultwsendpoint返回默认情况下使用的WebSocket端点。
   228  func DefaultWSEndpoint() string {
   229  	config := &Config{WSHost: DefaultWSHost, WSPort: DefaultWSPort}
   230  	return config.WSEndpoint()
   231  }
   232  
   233  //nodename返回devp2p节点标识符。
   234  func (c *Config) NodeName() string {
   235  	name := c.name()
   236  //向后兼容性:以前的版本使用的标题是“geth”,请保留。
   237  	if name == "geth" || name == "geth-testnet" {
   238  		name = "Geth"
   239  	}
   240  	if c.UserIdent != "" {
   241  		name += "/" + c.UserIdent
   242  	}
   243  	if c.Version != "" {
   244  		name += "/v" + c.Version
   245  	}
   246  	name += "/" + runtime.GOOS + "-" + runtime.GOARCH
   247  	name += "/" + runtime.Version()
   248  	return name
   249  }
   250  
   251  func (c *Config) name() string {
   252  	if c.Name == "" {
   253  		progname := strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe")
   254  		if progname == "" {
   255  			panic("empty executable name, set Config.Name")
   256  		}
   257  		return progname
   258  	}
   259  	return c.Name
   260  }
   261  
   262  //对于“geth”实例,这些资源的解析方式不同。
   263  var isOldGethResource = map[string]bool{
   264  	"chaindata":          true,
   265  	"nodes":              true,
   266  	"nodekey":            true,
   267  "static-nodes.json":  false, //没有警告,因为他们有
   268  "trusted-nodes.json": false, //单独警告。
   269  }
   270  
   271  //resolvepath解析实例目录中的路径。
   272  func (c *Config) ResolvePath(path string) string {
   273  	if filepath.IsAbs(path) {
   274  		return path
   275  	}
   276  	if c.DataDir == "" {
   277  		return ""
   278  	}
   279  //向后兼容性:确保创建了数据目录文件
   280  //如果存在,则使用by geth 1.4。
   281  	if warn, isOld := isOldGethResource[path]; isOld {
   282  		oldpath := ""
   283  		if c.name() == "geth" {
   284  			oldpath = filepath.Join(c.DataDir, path)
   285  		}
   286  		if oldpath != "" && common.FileExist(oldpath) {
   287  			if warn {
   288  				c.warnOnce(&c.oldGethResourceWarning, "Using deprecated resource file %s, please move this file to the 'geth' subdirectory of datadir.", oldpath)
   289  			}
   290  			return oldpath
   291  		}
   292  	}
   293  	return filepath.Join(c.instanceDir(), path)
   294  }
   295  
   296  func (c *Config) instanceDir() string {
   297  	if c.DataDir == "" {
   298  		return ""
   299  	}
   300  	return filepath.Join(c.DataDir, c.name())
   301  }
   302  
   303  //node key检索当前配置的节点私钥,检查
   304  //首先是任何手动设置的键,返回到配置的
   305  //数据文件夹。如果找不到密钥,则生成一个新的密钥。
   306  func (c *Config) NodeKey() *ecdsa.PrivateKey {
   307  //使用任何特定配置的密钥。
   308  	if c.P2P.PrivateKey != nil {
   309  		return c.P2P.PrivateKey
   310  	}
   311  //如果没有使用datadir,则生成临时密钥。
   312  	if c.DataDir == "" {
   313  		key, err := crypto.GenerateKey()
   314  		if err != nil {
   315  			log.Crit(fmt.Sprintf("Failed to generate ephemeral node key: %v", err))
   316  		}
   317  		return key
   318  	}
   319  
   320  	keyfile := c.ResolvePath(datadirPrivateKey)
   321  	if key, err := crypto.LoadECDSA(keyfile); err == nil {
   322  		return key
   323  	}
   324  //找不到持久密钥,生成并存储新密钥。
   325  	key, err := crypto.GenerateKey()
   326  	if err != nil {
   327  		log.Crit(fmt.Sprintf("Failed to generate node key: %v", err))
   328  	}
   329  	instanceDir := filepath.Join(c.DataDir, c.name())
   330  	if err := os.MkdirAll(instanceDir, 0700); err != nil {
   331  		log.Error(fmt.Sprintf("Failed to persist node key: %v", err))
   332  		return key
   333  	}
   334  	keyfile = filepath.Join(instanceDir, datadirPrivateKey)
   335  	if err := crypto.SaveECDSA(keyfile, key); err != nil {
   336  		log.Error(fmt.Sprintf("Failed to persist node key: %v", err))
   337  	}
   338  	return key
   339  }
   340  
   341  //static nodes返回配置为静态节点的节点enode URL的列表。
   342  func (c *Config) StaticNodes() []*enode.Node {
   343  	return c.parsePersistentNodes(&c.staticNodesWarning, c.ResolvePath(datadirStaticNodes))
   344  }
   345  
   346  //trusted nodes返回配置为受信任节点的节点enode URL的列表。
   347  func (c *Config) TrustedNodes() []*enode.Node {
   348  	return c.parsePersistentNodes(&c.trustedNodesWarning, c.ResolvePath(datadirTrustedNodes))
   349  }
   350  
   351  //ParsePersistentNodes分析从.json加载的发现节点URL列表
   352  //数据目录中的文件。
   353  func (c *Config) parsePersistentNodes(w *bool, path string) []*enode.Node {
   354  //如果不存在节点配置,则短路
   355  	if c.DataDir == "" {
   356  		return nil
   357  	}
   358  	if _, err := os.Stat(path); err != nil {
   359  		return nil
   360  	}
   361  	c.warnOnce(w, "Found deprecated node list file %s, please use the TOML config file instead.", path)
   362  
   363  //从配置文件加载节点。
   364  	var nodelist []string
   365  	if err := common.LoadJSON(path, &nodelist); err != nil {
   366  		log.Error(fmt.Sprintf("Can't load node list file: %v", err))
   367  		return nil
   368  	}
   369  //将列表解释为发现节点数组
   370  	var nodes []*enode.Node
   371  	for _, url := range nodelist {
   372  		if url == "" {
   373  			continue
   374  		}
   375  		node, err := enode.ParseV4(url)
   376  		if err != nil {
   377  			log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err))
   378  			continue
   379  		}
   380  		nodes = append(nodes, node)
   381  	}
   382  	return nodes
   383  }
   384  
   385  //accountconfig确定scrypt和keydirectory的设置
   386  func (c *Config) AccountConfig() (int, int, string, error) {
   387  	scryptN := keystore.StandardScryptN
   388  	scryptP := keystore.StandardScryptP
   389  	if c.UseLightweightKDF {
   390  		scryptN = keystore.LightScryptN
   391  		scryptP = keystore.LightScryptP
   392  	}
   393  
   394  	var (
   395  		keydir string
   396  		err    error
   397  	)
   398  	switch {
   399  	case filepath.IsAbs(c.KeyStoreDir):
   400  		keydir = c.KeyStoreDir
   401  	case c.DataDir != "":
   402  		if c.KeyStoreDir == "" {
   403  			keydir = filepath.Join(c.DataDir, datadirDefaultKeyStore)
   404  		} else {
   405  			keydir, err = filepath.Abs(c.KeyStoreDir)
   406  		}
   407  	case c.KeyStoreDir != "":
   408  		keydir, err = filepath.Abs(c.KeyStoreDir)
   409  	}
   410  	return scryptN, scryptP, keydir, err
   411  }
   412  
   413  func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
   414  	scryptN, scryptP, keydir, err := conf.AccountConfig()
   415  	var ephemeral string
   416  	if keydir == "" {
   417  //没有datadir。
   418  		keydir, err = ioutil.TempDir("", "go-ethereum-keystore")
   419  		ephemeral = keydir
   420  	}
   421  
   422  	if err != nil {
   423  		return nil, "", err
   424  	}
   425  	if err := os.MkdirAll(keydir, 0700); err != nil {
   426  		return nil, "", err
   427  	}
   428  //集合客户经理和支持的后端
   429  	backends := []accounts.Backend{
   430  		keystore.NewKeyStore(keydir, scryptN, scryptP),
   431  	}
   432  	if !conf.NoUSB {
   433  //启动用于分类帐硬件钱包的USB集线器
   434  		if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
   435  			log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
   436  		} else {
   437  			backends = append(backends, ledgerhub)
   438  		}
   439  //启动Trezor硬件钱包的USB集线器
   440  		if trezorhub, err := usbwallet.NewTrezorHub(); err != nil {
   441  			log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
   442  		} else {
   443  			backends = append(backends, trezorhub)
   444  		}
   445  	}
   446  	return accounts.NewManager(backends...), ephemeral, nil
   447  }
   448  
   449  var warnLock sync.Mutex
   450  
   451  func (c *Config) warnOnce(w *bool, format string, args ...interface{}) {
   452  	warnLock.Lock()
   453  	defer warnLock.Unlock()
   454  
   455  	if *w {
   456  		return
   457  	}
   458  	l := c.Logger
   459  	if l == nil {
   460  		l = log.Root()
   461  	}
   462  	l.Warn(fmt.Sprintf(format, args...))
   463  	*w = true
   464  }
   465