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