github.com/phillinzzz/newBsc@v1.1.6/node/node.go (about) 1 // Copyright 2015 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 "errors" 21 "fmt" 22 "net/http" 23 "os" 24 "path" 25 "path/filepath" 26 "reflect" 27 "strings" 28 "sync" 29 30 "github.com/phillinzzz/newBsc/accounts" 31 "github.com/phillinzzz/newBsc/core/rawdb" 32 "github.com/phillinzzz/newBsc/ethdb" 33 "github.com/phillinzzz/newBsc/ethdb/leveldb" 34 "github.com/phillinzzz/newBsc/event" 35 "github.com/phillinzzz/newBsc/log" 36 "github.com/phillinzzz/newBsc/p2p" 37 "github.com/phillinzzz/newBsc/rpc" 38 "github.com/prometheus/tsdb/fileutil" 39 ) 40 41 // Node is a container on which services can be registered. 42 type Node struct { 43 eventmux *event.TypeMux 44 config *Config 45 accman *accounts.Manager 46 log log.Logger 47 ephemKeystore string // if non-empty, the key directory that will be removed by Stop 48 dirLock fileutil.Releaser // prevents concurrent use of instance directory 49 stop chan struct{} // Channel to wait for termination notifications 50 server *p2p.Server // Currently running P2P networking layer 51 startStopLock sync.Mutex // Start/Stop are protected by an additional lock 52 state int // Tracks state of node lifecycle 53 54 lock sync.Mutex 55 lifecycles []Lifecycle // All registered backends, services, and auxiliary services that have a lifecycle 56 rpcAPIs []rpc.API // List of APIs currently provided by the node 57 http *httpServer // 58 ws *httpServer // 59 ipc *ipcServer // Stores information about the ipc http server 60 inprocHandler *rpc.Server // In-process RPC request handler to process the API requests 61 62 databases map[*closeTrackingDB]struct{} // All open databases 63 } 64 65 const ( 66 initializingState = iota 67 runningState 68 closedState 69 ) 70 71 // New creates a new P2P node, ready for protocol registration. 72 func New(conf *Config) (*Node, error) { 73 // Copy config and resolve the datadir so future changes to the current 74 // working directory don't affect the node. 75 confCopy := *conf 76 conf = &confCopy 77 if conf.DataDir != "" { 78 absdatadir, err := filepath.Abs(conf.DataDir) 79 if err != nil { 80 return nil, err 81 } 82 conf.DataDir = absdatadir 83 } 84 if conf.LogConfig != nil { 85 logFilePath := "" 86 if conf.LogConfig.FileRoot == "" { 87 logFilePath = path.Join(conf.DataDir, conf.LogConfig.FilePath) 88 } else { 89 logFilePath = path.Join(conf.LogConfig.FileRoot, conf.LogConfig.FilePath) 90 } 91 log.Root().SetHandler(log.NewFileLvlHandler(logFilePath, conf.LogConfig.MaxBytesSize, conf.LogConfig.Level)) 92 } 93 if conf.Logger == nil { 94 conf.Logger = log.New() 95 } 96 97 // Ensure that the instance name doesn't cause weird conflicts with 98 // other files in the data directory. 99 if strings.ContainsAny(conf.Name, `/\`) { 100 return nil, errors.New(`Config.Name must not contain '/' or '\'`) 101 } 102 if conf.Name == datadirDefaultKeyStore { 103 return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`) 104 } 105 if strings.HasSuffix(conf.Name, ".ipc") { 106 return nil, errors.New(`Config.Name cannot end in ".ipc"`) 107 } 108 109 node := &Node{ 110 config: conf, 111 inprocHandler: rpc.NewServer(), 112 eventmux: new(event.TypeMux), 113 log: conf.Logger, 114 stop: make(chan struct{}), 115 server: &p2p.Server{Config: conf.P2P}, 116 databases: make(map[*closeTrackingDB]struct{}), 117 } 118 119 // Register built-in APIs. 120 node.rpcAPIs = append(node.rpcAPIs, node.apis()...) 121 122 // Acquire the instance directory lock. 123 if err := node.openDataDir(); err != nil { 124 return nil, err 125 } 126 // Ensure that the AccountManager method works before the node has started. We rely on 127 // this in cmd/geth. 128 am, ephemeralKeystore, err := makeAccountManager(conf) 129 if err != nil { 130 return nil, err 131 } 132 node.accman = am 133 node.ephemKeystore = ephemeralKeystore 134 135 // Initialize the p2p server. This creates the node key and discovery databases. 136 node.server.Config.PrivateKey = node.config.NodeKey() 137 node.server.Config.Name = node.config.NodeName() 138 node.server.Config.Logger = node.log 139 if node.server.Config.StaticNodes == nil { 140 node.server.Config.StaticNodes = node.config.StaticNodes() 141 } 142 if node.server.Config.TrustedNodes == nil { 143 node.server.Config.TrustedNodes = node.config.TrustedNodes() 144 } 145 if node.server.Config.NodeDatabase == "" { 146 node.server.Config.NodeDatabase = node.config.NodeDB() 147 } 148 149 // Check HTTP/WS prefixes are valid. 150 if err := validatePrefix("HTTP", conf.HTTPPathPrefix); err != nil { 151 return nil, err 152 } 153 if err := validatePrefix("WebSocket", conf.WSPathPrefix); err != nil { 154 return nil, err 155 } 156 157 // Configure RPC servers. 158 node.http = newHTTPServer(node.log, conf.HTTPTimeouts) 159 node.ws = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts) 160 node.ipc = newIPCServer(node.log, conf.IPCEndpoint()) 161 162 return node, nil 163 } 164 165 // Start starts all registered lifecycles, RPC services and p2p networking. 166 // Node can only be started once. 167 func (n *Node) Start() error { 168 n.startStopLock.Lock() 169 defer n.startStopLock.Unlock() 170 171 n.lock.Lock() 172 switch n.state { 173 case runningState: 174 n.lock.Unlock() 175 return ErrNodeRunning 176 case closedState: 177 n.lock.Unlock() 178 return ErrNodeStopped 179 } 180 n.state = runningState 181 // open networking and RPC endpoints 182 err := n.openEndpoints() 183 lifecycles := make([]Lifecycle, len(n.lifecycles)) 184 copy(lifecycles, n.lifecycles) 185 n.lock.Unlock() 186 187 // Check if endpoint startup failed. 188 if err != nil { 189 n.doClose(nil) 190 return err 191 } 192 // Start all registered lifecycles. 193 var started []Lifecycle 194 for _, lifecycle := range lifecycles { 195 if err = lifecycle.Start(); err != nil { 196 break 197 } 198 started = append(started, lifecycle) 199 } 200 // Check if any lifecycle failed to start. 201 if err != nil { 202 n.stopServices(started) 203 n.doClose(nil) 204 } 205 return err 206 } 207 208 // Close stops the Node and releases resources acquired in 209 // Node constructor New. 210 func (n *Node) Close() error { 211 n.startStopLock.Lock() 212 defer n.startStopLock.Unlock() 213 214 n.lock.Lock() 215 state := n.state 216 n.lock.Unlock() 217 switch state { 218 case initializingState: 219 // The node was never started. 220 return n.doClose(nil) 221 case runningState: 222 // The node was started, release resources acquired by Start(). 223 var errs []error 224 if err := n.stopServices(n.lifecycles); err != nil { 225 errs = append(errs, err) 226 } 227 return n.doClose(errs) 228 case closedState: 229 return ErrNodeStopped 230 default: 231 panic(fmt.Sprintf("node is in unknown state %d", state)) 232 } 233 } 234 235 // doClose releases resources acquired by New(), collecting errors. 236 func (n *Node) doClose(errs []error) error { 237 // Close databases. This needs the lock because it needs to 238 // synchronize with OpenDatabase*. 239 n.lock.Lock() 240 n.state = closedState 241 errs = append(errs, n.closeDatabases()...) 242 n.lock.Unlock() 243 244 if err := n.accman.Close(); err != nil { 245 errs = append(errs, err) 246 } 247 if n.ephemKeystore != "" { 248 if err := os.RemoveAll(n.ephemKeystore); err != nil { 249 errs = append(errs, err) 250 } 251 } 252 253 // Release instance directory lock. 254 n.closeDataDir() 255 256 // Unblock n.Wait. 257 close(n.stop) 258 259 // Report any errors that might have occurred. 260 switch len(errs) { 261 case 0: 262 return nil 263 case 1: 264 return errs[0] 265 default: 266 return fmt.Errorf("%v", errs) 267 } 268 } 269 270 // openEndpoints starts all network and RPC endpoints. 271 func (n *Node) openEndpoints() error { 272 // start networking endpoints 273 n.log.Info("Starting peer-to-peer node", "instance", n.server.Name) 274 if err := n.server.Start(); err != nil { 275 return convertFileLockError(err) 276 } 277 // start RPC endpoints 278 err := n.startRPC() 279 if err != nil { 280 n.stopRPC() 281 n.server.Stop() 282 } 283 return err 284 } 285 286 // containsLifecycle checks if 'lfs' contains 'l'. 287 func containsLifecycle(lfs []Lifecycle, l Lifecycle) bool { 288 for _, obj := range lfs { 289 if obj == l { 290 return true 291 } 292 } 293 return false 294 } 295 296 // stopServices terminates running services, RPC and p2p networking. 297 // It is the inverse of Start. 298 func (n *Node) stopServices(running []Lifecycle) error { 299 n.stopRPC() 300 301 // Stop running lifecycles in reverse order. 302 failure := &StopError{Services: make(map[reflect.Type]error)} 303 for i := len(running) - 1; i >= 0; i-- { 304 if err := running[i].Stop(); err != nil { 305 failure.Services[reflect.TypeOf(running[i])] = err 306 } 307 } 308 309 // Stop p2p networking. 310 n.server.Stop() 311 312 if len(failure.Services) > 0 { 313 return failure 314 } 315 return nil 316 } 317 318 func (n *Node) openDataDir() error { 319 if n.config.DataDir == "" { 320 return nil // ephemeral 321 } 322 323 instdir := filepath.Join(n.config.DataDir, n.config.name()) 324 if err := os.MkdirAll(instdir, 0700); err != nil { 325 return err 326 } 327 // Lock the instance directory to prevent concurrent use by another instance as well as 328 // accidental use of the instance directory as a database. 329 release, _, err := fileutil.Flock(filepath.Join(instdir, "LOCK")) 330 if err != nil { 331 return convertFileLockError(err) 332 } 333 n.dirLock = release 334 return nil 335 } 336 337 func (n *Node) closeDataDir() { 338 // Release instance directory lock. 339 if n.dirLock != nil { 340 if err := n.dirLock.Release(); err != nil { 341 n.log.Error("Can't release datadir lock", "err", err) 342 } 343 n.dirLock = nil 344 } 345 } 346 347 // configureRPC is a helper method to configure all the various RPC endpoints during node 348 // startup. It's not meant to be called at any time afterwards as it makes certain 349 // assumptions about the state of the node. 350 func (n *Node) startRPC() error { 351 if err := n.startInProc(); err != nil { 352 return err 353 } 354 355 // Configure IPC. 356 if n.ipc.endpoint != "" { 357 if err := n.ipc.start(n.rpcAPIs); err != nil { 358 return err 359 } 360 } 361 362 // Configure HTTP. 363 if n.config.HTTPHost != "" { 364 config := httpConfig{ 365 CorsAllowedOrigins: n.config.HTTPCors, 366 Vhosts: n.config.HTTPVirtualHosts, 367 Modules: n.config.HTTPModules, 368 prefix: n.config.HTTPPathPrefix, 369 } 370 if err := n.http.setListenAddr(n.config.HTTPHost, n.config.HTTPPort); err != nil { 371 return err 372 } 373 if err := n.http.enableRPC(n.rpcAPIs, config); err != nil { 374 return err 375 } 376 } 377 378 // Configure WebSocket. 379 if n.config.WSHost != "" { 380 server := n.wsServerForPort(n.config.WSPort) 381 config := wsConfig{ 382 Modules: n.config.WSModules, 383 Origins: n.config.WSOrigins, 384 prefix: n.config.WSPathPrefix, 385 } 386 if err := server.setListenAddr(n.config.WSHost, n.config.WSPort); err != nil { 387 return err 388 } 389 if err := server.enableWS(n.rpcAPIs, config); err != nil { 390 return err 391 } 392 } 393 394 if err := n.http.start(); err != nil { 395 return err 396 } 397 return n.ws.start() 398 } 399 400 func (n *Node) wsServerForPort(port int) *httpServer { 401 if n.config.HTTPHost == "" || n.http.port == port { 402 return n.http 403 } 404 return n.ws 405 } 406 407 func (n *Node) stopRPC() { 408 n.http.stop() 409 n.ws.stop() 410 n.ipc.stop() 411 n.stopInProc() 412 } 413 414 // startInProc registers all RPC APIs on the inproc server. 415 func (n *Node) startInProc() error { 416 for _, api := range n.rpcAPIs { 417 if err := n.inprocHandler.RegisterName(api.Namespace, api.Service); err != nil { 418 return err 419 } 420 } 421 return nil 422 } 423 424 // stopInProc terminates the in-process RPC endpoint. 425 func (n *Node) stopInProc() { 426 n.inprocHandler.Stop() 427 } 428 429 // Wait blocks until the node is closed. 430 func (n *Node) Wait() { 431 <-n.stop 432 } 433 434 // RegisterLifecycle registers the given Lifecycle on the node. 435 func (n *Node) RegisterLifecycle(lifecycle Lifecycle) { 436 n.lock.Lock() 437 defer n.lock.Unlock() 438 439 if n.state != initializingState { 440 panic("can't register lifecycle on running/stopped node") 441 } 442 if containsLifecycle(n.lifecycles, lifecycle) { 443 panic(fmt.Sprintf("attempt to register lifecycle %T more than once", lifecycle)) 444 } 445 n.lifecycles = append(n.lifecycles, lifecycle) 446 } 447 448 // RegisterProtocols adds backend's protocols to the node's p2p server. 449 func (n *Node) RegisterProtocols(protocols []p2p.Protocol) { 450 n.lock.Lock() 451 defer n.lock.Unlock() 452 453 if n.state != initializingState { 454 panic("can't register protocols on running/stopped node") 455 } 456 n.server.Protocols = append(n.server.Protocols, protocols...) 457 } 458 459 // RegisterAPIs registers the APIs a service provides on the node. 460 func (n *Node) RegisterAPIs(apis []rpc.API) { 461 n.lock.Lock() 462 defer n.lock.Unlock() 463 464 if n.state != initializingState { 465 panic("can't register APIs on running/stopped node") 466 } 467 n.rpcAPIs = append(n.rpcAPIs, apis...) 468 } 469 470 // RegisterHandler mounts a handler on the given path on the canonical HTTP server. 471 // 472 // The name of the handler is shown in a log message when the HTTP server starts 473 // and should be a descriptive term for the service provided by the handler. 474 func (n *Node) RegisterHandler(name, path string, handler http.Handler) { 475 n.lock.Lock() 476 defer n.lock.Unlock() 477 478 if n.state != initializingState { 479 panic("can't register HTTP handler on running/stopped node") 480 } 481 482 n.http.mux.Handle(path, handler) 483 n.http.handlerNames[path] = name 484 } 485 486 // Attach creates an RPC client attached to an in-process API handler. 487 func (n *Node) Attach() (*rpc.Client, error) { 488 return rpc.DialInProc(n.inprocHandler), nil 489 } 490 491 // RPCHandler returns the in-process RPC request handler. 492 func (n *Node) RPCHandler() (*rpc.Server, error) { 493 n.lock.Lock() 494 defer n.lock.Unlock() 495 496 if n.state == closedState { 497 return nil, ErrNodeStopped 498 } 499 return n.inprocHandler, nil 500 } 501 502 // Config returns the configuration of node. 503 func (n *Node) Config() *Config { 504 return n.config 505 } 506 507 // Server retrieves the currently running P2P network layer. This method is meant 508 // only to inspect fields of the currently running server. Callers should not 509 // start or stop the returned server. 510 func (n *Node) Server() *p2p.Server { 511 n.lock.Lock() 512 defer n.lock.Unlock() 513 514 return n.server 515 } 516 517 // DataDir retrieves the current datadir used by the protocol stack. 518 // Deprecated: No files should be stored in this directory, use InstanceDir instead. 519 func (n *Node) DataDir() string { 520 return n.config.DataDir 521 } 522 523 // InstanceDir retrieves the instance directory used by the protocol stack. 524 func (n *Node) InstanceDir() string { 525 return n.config.instanceDir() 526 } 527 528 // AccountManager retrieves the account manager used by the protocol stack. 529 func (n *Node) AccountManager() *accounts.Manager { 530 return n.accman 531 } 532 533 // IPCEndpoint retrieves the current IPC endpoint used by the protocol stack. 534 func (n *Node) IPCEndpoint() string { 535 return n.ipc.endpoint 536 } 537 538 // HTTPEndpoint returns the URL of the HTTP server. Note that this URL does not 539 // contain the JSON-RPC path prefix set by HTTPPathPrefix. 540 func (n *Node) HTTPEndpoint() string { 541 return "http://" + n.http.listenAddr() 542 } 543 544 // WSEndpoint returns the current JSON-RPC over WebSocket endpoint. 545 func (n *Node) WSEndpoint() string { 546 if n.http.wsAllowed() { 547 return "ws://" + n.http.listenAddr() + n.http.wsConfig.prefix 548 } 549 return "ws://" + n.ws.listenAddr() + n.ws.wsConfig.prefix 550 } 551 552 // EventMux retrieves the event multiplexer used by all the network services in 553 // the current protocol stack. 554 func (n *Node) EventMux() *event.TypeMux { 555 return n.eventmux 556 } 557 558 // OpenDatabase opens an existing database with the given name (or creates one if no 559 // previous can be found) from within the node's instance directory. If the node is 560 // ephemeral, a memory database is returned. 561 func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, readonly bool) (ethdb.Database, error) { 562 n.lock.Lock() 563 defer n.lock.Unlock() 564 if n.state == closedState { 565 return nil, ErrNodeStopped 566 } 567 568 var db ethdb.Database 569 var err error 570 if n.config.DataDir == "" { 571 db = rawdb.NewMemoryDatabase() 572 } else { 573 db, err = rawdb.NewLevelDBDatabase(n.ResolvePath(name), cache, handles, namespace, readonly) 574 } 575 576 if err == nil { 577 db = n.wrapDatabase(db) 578 } 579 return db, err 580 } 581 582 func (n *Node) OpenAndMergeDatabase(name string, cache, handles int, freezer, diff, namespace string, readonly, persistDiff bool) (ethdb.Database, error) { 583 chainDB, err := n.OpenDatabaseWithFreezer(name, cache, handles, freezer, namespace, readonly) 584 if err != nil { 585 return nil, err 586 } 587 if persistDiff { 588 diffStore, err := n.OpenDiffDatabase(name, handles, diff, namespace, readonly) 589 if err != nil { 590 chainDB.Close() 591 return nil, err 592 } 593 chainDB.SetDiffStore(diffStore) 594 } 595 return chainDB, nil 596 } 597 598 // OpenDatabaseWithFreezer opens an existing database with the given name (or 599 // creates one if no previous can be found) from within the node's data directory, 600 // also attaching a chain freezer to it that moves ancient chain data from the 601 // database to immutable append-only files. If the node is an ephemeral one, a 602 // memory database is returned. 603 func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, namespace string, readonly bool) (ethdb.Database, error) { 604 n.lock.Lock() 605 defer n.lock.Unlock() 606 if n.state == closedState { 607 return nil, ErrNodeStopped 608 } 609 610 var db ethdb.Database 611 var err error 612 if n.config.DataDir == "" { 613 db = rawdb.NewMemoryDatabase() 614 } else { 615 root := n.ResolvePath(name) 616 switch { 617 case freezer == "": 618 freezer = filepath.Join(root, "ancient") 619 case !filepath.IsAbs(freezer): 620 freezer = n.ResolvePath(freezer) 621 } 622 db, err = rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace, readonly) 623 } 624 625 if err == nil { 626 db = n.wrapDatabase(db) 627 } 628 return db, err 629 } 630 631 func (n *Node) OpenDiffDatabase(name string, handles int, diff, namespace string, readonly bool) (*leveldb.Database, error) { 632 n.lock.Lock() 633 defer n.lock.Unlock() 634 if n.state == closedState { 635 return nil, ErrNodeStopped 636 } 637 638 var db *leveldb.Database 639 var err error 640 if n.config.DataDir == "" { 641 panic("datadir is missing") 642 } 643 root := n.ResolvePath(name) 644 switch { 645 case diff == "": 646 diff = filepath.Join(root, "diff") 647 case !filepath.IsAbs(diff): 648 diff = n.ResolvePath(diff) 649 } 650 db, err = leveldb.New(diff, 0, handles, namespace, readonly) 651 652 return db, err 653 } 654 655 // ResolvePath returns the absolute path of a resource in the instance directory. 656 func (n *Node) ResolvePath(x string) string { 657 return n.config.ResolvePath(x) 658 } 659 660 // closeTrackingDB wraps the Close method of a database. When the database is closed by the 661 // service, the wrapper removes it from the node's database map. This ensures that Node 662 // won't auto-close the database if it is closed by the service that opened it. 663 type closeTrackingDB struct { 664 ethdb.Database 665 n *Node 666 } 667 668 func (db *closeTrackingDB) Close() error { 669 db.n.lock.Lock() 670 delete(db.n.databases, db) 671 db.n.lock.Unlock() 672 return db.Database.Close() 673 } 674 675 // wrapDatabase ensures the database will be auto-closed when Node is closed. 676 func (n *Node) wrapDatabase(db ethdb.Database) ethdb.Database { 677 wrapper := &closeTrackingDB{db, n} 678 n.databases[wrapper] = struct{}{} 679 return wrapper 680 } 681 682 // closeDatabases closes all open databases. 683 func (n *Node) closeDatabases() (errors []error) { 684 for db := range n.databases { 685 delete(n.databases, db) 686 if err := db.Database.Close(); err != nil { 687 errors = append(errors, err) 688 } 689 } 690 return errors 691 }