github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/node/node.go (about) 1 // Copyright 2019 The ebakus/go-ebakus Authors 2 // This file is part of the ebakus/go-ebakus library. 3 // 4 // The ebakus/go-ebakus 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 ebakus/go-ebakus 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 ebakus/go-ebakus library. If not, see <http://www.gnu.org/licenses/>. 16 17 package node 18 19 import ( 20 "errors" 21 "fmt" 22 "net" 23 "os" 24 "path/filepath" 25 "reflect" 26 "strings" 27 "sync" 28 29 "github.com/ebakus/ebakusdb" 30 31 "github.com/ebakus/go-ebakus/accounts" 32 "github.com/ebakus/go-ebakus/core/rawdb" 33 "github.com/ebakus/go-ebakus/ethdb" 34 "github.com/ebakus/go-ebakus/event" 35 "github.com/ebakus/go-ebakus/internal/debug" 36 "github.com/ebakus/go-ebakus/log" 37 "github.com/ebakus/go-ebakus/p2p" 38 "github.com/ebakus/go-ebakus/rpc" 39 "github.com/prometheus/tsdb/fileutil" 40 ) 41 42 // Node is a container on which services can be registered. 43 type Node struct { 44 eventmux *event.TypeMux // Event multiplexer used between the services of a stack 45 config *Config 46 accman *accounts.Manager 47 48 ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop 49 instanceDirLock fileutil.Releaser // prevents concurrent use of instance directory 50 51 serverConfig p2p.Config 52 server *p2p.Server // Currently running P2P networking layer 53 54 serviceFuncs []ServiceConstructor // Service constructors (in dependency order) 55 services map[reflect.Type]Service // Currently running services 56 57 rpcAPIs []rpc.API // List of APIs currently provided by the node 58 inprocHandler *rpc.Server // In-process RPC request handler to process the API requests 59 60 ipcEndpoint string // IPC endpoint to listen at (empty = IPC disabled) 61 ipcListener net.Listener // IPC RPC listener socket to serve API requests 62 ipcHandler *rpc.Server // IPC RPC request handler to process the API requests 63 64 httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled) 65 httpWhitelist []string // HTTP RPC modules to allow through this endpoint 66 httpListener net.Listener // HTTP RPC listener socket to server API requests 67 httpHandler *rpc.Server // HTTP RPC request handler to process the API requests 68 69 wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled) 70 wsListener net.Listener // Websocket RPC listener socket to server API requests 71 wsHandler *rpc.Server // Websocket RPC request handler to process the API requests 72 73 stop chan struct{} // Channel to wait for termination notifications 74 lock sync.RWMutex 75 76 log log.Logger 77 } 78 79 // New creates a new P2P node, ready for protocol registration. 80 func New(conf *Config) (*Node, error) { 81 // Copy config and resolve the datadir so future changes to the current 82 // working directory don't affect the node. 83 confCopy := *conf 84 conf = &confCopy 85 if conf.DataDir != "" { 86 absdatadir, err := filepath.Abs(conf.DataDir) 87 if err != nil { 88 return nil, err 89 } 90 conf.DataDir = absdatadir 91 } 92 // Ensure that the instance name doesn't cause weird conflicts with 93 // other files in the data directory. 94 if strings.ContainsAny(conf.Name, `/\`) { 95 return nil, errors.New(`Config.Name must not contain '/' or '\'`) 96 } 97 if conf.Name == datadirDefaultKeyStore { 98 return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`) 99 } 100 if strings.HasSuffix(conf.Name, ".ipc") { 101 return nil, errors.New(`Config.Name cannot end in ".ipc"`) 102 } 103 // Ensure that the AccountManager method works before the node has started. 104 // We rely on this in cmd/ebakus. 105 am, ephemeralKeystore, err := makeAccountManager(conf) 106 if err != nil { 107 return nil, err 108 } 109 if conf.Logger == nil { 110 conf.Logger = log.New() 111 } 112 // Note: any interaction with Config that would create/touch files 113 // in the data directory or instance directory is delayed until Start. 114 return &Node{ 115 accman: am, 116 ephemeralKeystore: ephemeralKeystore, 117 config: conf, 118 serviceFuncs: []ServiceConstructor{}, 119 ipcEndpoint: conf.IPCEndpoint(), 120 httpEndpoint: conf.HTTPEndpoint(), 121 wsEndpoint: conf.WSEndpoint(), 122 eventmux: new(event.TypeMux), 123 log: conf.Logger, 124 }, nil 125 } 126 127 // Close stops the Node and releases resources acquired in 128 // Node constructor New. 129 func (n *Node) Close() error { 130 var errs []error 131 132 // Terminate all subsystems and collect any errors 133 if err := n.Stop(); err != nil && err != ErrNodeStopped { 134 errs = append(errs, err) 135 } 136 if err := n.accman.Close(); err != nil { 137 errs = append(errs, err) 138 } 139 // Report any errors that might have occurred 140 switch len(errs) { 141 case 0: 142 return nil 143 case 1: 144 return errs[0] 145 default: 146 return fmt.Errorf("%v", errs) 147 } 148 } 149 150 // Register injects a new service into the node's stack. The service created by 151 // the passed constructor must be unique in its type with regard to sibling ones. 152 func (n *Node) Register(constructor ServiceConstructor) error { 153 n.lock.Lock() 154 defer n.lock.Unlock() 155 156 if n.server != nil { 157 return ErrNodeRunning 158 } 159 n.serviceFuncs = append(n.serviceFuncs, constructor) 160 return nil 161 } 162 163 // Start create a live P2P node and starts running it. 164 func (n *Node) Start() error { 165 n.lock.Lock() 166 defer n.lock.Unlock() 167 168 // Short circuit if the node's already running 169 if n.server != nil { 170 return ErrNodeRunning 171 } 172 if err := n.openDataDir(); err != nil { 173 return err 174 } 175 176 // Initialize the p2p server. This creates the node key and 177 // discovery databases. 178 n.serverConfig = n.config.P2P 179 n.serverConfig.PrivateKey = n.config.NodeKey() 180 n.serverConfig.Name = n.config.NodeName() 181 n.serverConfig.Logger = n.log 182 if n.serverConfig.StaticNodes == nil { 183 n.serverConfig.StaticNodes = n.config.StaticNodes() 184 } 185 if n.serverConfig.TrustedNodes == nil { 186 n.serverConfig.TrustedNodes = n.config.TrustedNodes() 187 } 188 if n.serverConfig.NodeDatabase == "" { 189 n.serverConfig.NodeDatabase = n.config.NodeDB() 190 } 191 running := &p2p.Server{Config: n.serverConfig} 192 n.log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name) 193 194 // Otherwise copy and specialize the P2P configuration 195 services := make(map[reflect.Type]Service) 196 for _, constructor := range n.serviceFuncs { 197 // Create a new context for the particular service 198 ctx := &ServiceContext{ 199 config: n.config, 200 services: make(map[reflect.Type]Service), 201 EventMux: n.eventmux, 202 AccountManager: n.accman, 203 } 204 for kind, s := range services { // copy needed for threaded access 205 ctx.services[kind] = s 206 } 207 // Construct and save the service 208 service, err := constructor(ctx) 209 if err != nil { 210 return err 211 } 212 kind := reflect.TypeOf(service) 213 if _, exists := services[kind]; exists { 214 return &DuplicateServiceError{Kind: kind} 215 } 216 services[kind] = service 217 } 218 // Gather the protocols and start the freshly assembled P2P server 219 for _, service := range services { 220 running.Protocols = append(running.Protocols, service.Protocols()...) 221 } 222 if err := running.Start(); err != nil { 223 return convertFileLockError(err) 224 } 225 // Start each of the services 226 var started []reflect.Type 227 for kind, service := range services { 228 // Start the next service, stopping all previous upon failure 229 if err := service.Start(running); err != nil { 230 for _, kind := range started { 231 services[kind].Stop() 232 } 233 running.Stop() 234 235 return err 236 } 237 // Mark the service started for potential cleanup 238 started = append(started, kind) 239 } 240 // Lastly start the configured RPC interfaces 241 if err := n.startRPC(services); err != nil { 242 for _, service := range services { 243 service.Stop() 244 } 245 running.Stop() 246 return err 247 } 248 // Finish initializing the startup 249 n.services = services 250 n.server = running 251 n.stop = make(chan struct{}) 252 return nil 253 } 254 255 // Config returns the configuration of node. 256 func (n *Node) Config() *Config { 257 return n.config 258 } 259 260 func (n *Node) openDataDir() error { 261 if n.config.DataDir == "" { 262 return nil // ephemeral 263 } 264 265 instdir := filepath.Join(n.config.DataDir, n.config.name()) 266 if err := os.MkdirAll(instdir, 0700); err != nil { 267 return err 268 } 269 // Lock the instance directory to prevent concurrent use by another instance as well as 270 // accidental use of the instance directory as a database. 271 release, _, err := fileutil.Flock(filepath.Join(instdir, "LOCK")) 272 if err != nil { 273 return convertFileLockError(err) 274 } 275 n.instanceDirLock = release 276 return nil 277 } 278 279 // startRPC is a helper method to start all the various RPC endpoint during node 280 // startup. It's not meant to be called at any time afterwards as it makes certain 281 // assumptions about the state of the node. 282 func (n *Node) startRPC(services map[reflect.Type]Service) error { 283 // Gather all the possible APIs to surface 284 apis := n.apis() 285 for _, service := range services { 286 apis = append(apis, service.APIs()...) 287 } 288 // Start the various API endpoints, terminating all in case of errors 289 if err := n.startInProc(apis); err != nil { 290 return err 291 } 292 if err := n.startIPC(apis); err != nil { 293 n.stopInProc() 294 return err 295 } 296 if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts, n.config.HTTPTimeouts); err != nil { 297 n.stopIPC() 298 n.stopInProc() 299 return err 300 } 301 if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil { 302 n.stopHTTP() 303 n.stopIPC() 304 n.stopInProc() 305 return err 306 } 307 // All API endpoints started successfully 308 n.rpcAPIs = apis 309 return nil 310 } 311 312 // startInProc initializes an in-process RPC endpoint. 313 func (n *Node) startInProc(apis []rpc.API) error { 314 // Register all the APIs exposed by the services 315 handler := rpc.NewServer() 316 for _, api := range apis { 317 if err := handler.RegisterName(api.Namespace, api.Service); err != nil { 318 return err 319 } 320 n.log.Debug("InProc registered", "namespace", api.Namespace) 321 } 322 n.inprocHandler = handler 323 return nil 324 } 325 326 // stopInProc terminates the in-process RPC endpoint. 327 func (n *Node) stopInProc() { 328 if n.inprocHandler != nil { 329 n.inprocHandler.Stop() 330 n.inprocHandler = nil 331 } 332 } 333 334 // startIPC initializes and starts the IPC RPC endpoint. 335 func (n *Node) startIPC(apis []rpc.API) error { 336 if n.ipcEndpoint == "" { 337 return nil // IPC disabled. 338 } 339 listener, handler, err := rpc.StartIPCEndpoint(n.ipcEndpoint, apis) 340 if err != nil { 341 return err 342 } 343 n.ipcListener = listener 344 n.ipcHandler = handler 345 n.log.Info("IPC endpoint opened", "url", n.ipcEndpoint) 346 return nil 347 } 348 349 // stopIPC terminates the IPC RPC endpoint. 350 func (n *Node) stopIPC() { 351 if n.ipcListener != nil { 352 n.ipcListener.Close() 353 n.ipcListener = nil 354 355 n.log.Info("IPC endpoint closed", "url", n.ipcEndpoint) 356 } 357 if n.ipcHandler != nil { 358 n.ipcHandler.Stop() 359 n.ipcHandler = nil 360 } 361 } 362 363 // startHTTP initializes and starts the HTTP RPC endpoint. 364 func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error { 365 // Short circuit if the HTTP endpoint isn't being exposed 366 if endpoint == "" { 367 return nil 368 } 369 listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts) 370 if err != nil { 371 return err 372 } 373 n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ",")) 374 // All listeners booted successfully 375 n.httpEndpoint = endpoint 376 n.httpListener = listener 377 n.httpHandler = handler 378 379 return nil 380 } 381 382 // stopHTTP terminates the HTTP RPC endpoint. 383 func (n *Node) stopHTTP() { 384 if n.httpListener != nil { 385 n.httpListener.Close() 386 n.httpListener = nil 387 388 n.log.Info("HTTP endpoint closed", "url", fmt.Sprintf("http://%s", n.httpEndpoint)) 389 } 390 if n.httpHandler != nil { 391 n.httpHandler.Stop() 392 n.httpHandler = nil 393 } 394 } 395 396 // startWS initializes and starts the websocket RPC endpoint. 397 func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error { 398 // Short circuit if the WS endpoint isn't being exposed 399 if endpoint == "" { 400 return nil 401 } 402 listener, handler, err := rpc.StartWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll) 403 if err != nil { 404 return err 405 } 406 n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr())) 407 // All listeners booted successfully 408 n.wsEndpoint = endpoint 409 n.wsListener = listener 410 n.wsHandler = handler 411 412 return nil 413 } 414 415 // stopWS terminates the websocket RPC endpoint. 416 func (n *Node) stopWS() { 417 if n.wsListener != nil { 418 n.wsListener.Close() 419 n.wsListener = nil 420 421 n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%s", n.wsEndpoint)) 422 } 423 if n.wsHandler != nil { 424 n.wsHandler.Stop() 425 n.wsHandler = nil 426 } 427 } 428 429 // Stop terminates a running node along with all it's services. In the node was 430 // not started, an error is returned. 431 func (n *Node) Stop() error { 432 n.lock.Lock() 433 defer n.lock.Unlock() 434 435 // Short circuit if the node's not running 436 if n.server == nil { 437 return ErrNodeStopped 438 } 439 440 // Terminate the API, services and the p2p server. 441 n.stopWS() 442 n.stopHTTP() 443 n.stopIPC() 444 n.rpcAPIs = nil 445 failure := &StopError{ 446 Services: make(map[reflect.Type]error), 447 } 448 for kind, service := range n.services { 449 if err := service.Stop(); err != nil { 450 failure.Services[kind] = err 451 } 452 } 453 n.server.Stop() 454 n.services = nil 455 n.server = nil 456 457 // Release instance directory lock. 458 if n.instanceDirLock != nil { 459 if err := n.instanceDirLock.Release(); err != nil { 460 n.log.Error("Can't release datadir lock", "err", err) 461 } 462 n.instanceDirLock = nil 463 } 464 465 // unblock n.Wait 466 close(n.stop) 467 468 // Remove the keystore if it was created ephemerally. 469 var keystoreErr error 470 if n.ephemeralKeystore != "" { 471 keystoreErr = os.RemoveAll(n.ephemeralKeystore) 472 } 473 474 if len(failure.Services) > 0 { 475 return failure 476 } 477 if keystoreErr != nil { 478 return keystoreErr 479 } 480 return nil 481 } 482 483 // Wait blocks the thread until the node is stopped. If the node is not running 484 // at the time of invocation, the method immediately returns. 485 func (n *Node) Wait() { 486 n.lock.RLock() 487 if n.server == nil { 488 n.lock.RUnlock() 489 return 490 } 491 stop := n.stop 492 n.lock.RUnlock() 493 494 <-stop 495 } 496 497 // Restart terminates a running node and boots up a new one in its place. If the 498 // node isn't running, an error is returned. 499 func (n *Node) Restart() error { 500 if err := n.Stop(); err != nil { 501 return err 502 } 503 if err := n.Start(); err != nil { 504 return err 505 } 506 return nil 507 } 508 509 // Attach creates an RPC client attached to an in-process API handler. 510 func (n *Node) Attach() (*rpc.Client, error) { 511 n.lock.RLock() 512 defer n.lock.RUnlock() 513 514 if n.server == nil { 515 return nil, ErrNodeStopped 516 } 517 return rpc.DialInProc(n.inprocHandler), nil 518 } 519 520 // RPCHandler returns the in-process RPC request handler. 521 func (n *Node) RPCHandler() (*rpc.Server, error) { 522 n.lock.RLock() 523 defer n.lock.RUnlock() 524 525 if n.inprocHandler == nil { 526 return nil, ErrNodeStopped 527 } 528 return n.inprocHandler, nil 529 } 530 531 // Server retrieves the currently running P2P network layer. This method is meant 532 // only to inspect fields of the currently running server, life cycle management 533 // should be left to this Node entity. 534 func (n *Node) Server() *p2p.Server { 535 n.lock.RLock() 536 defer n.lock.RUnlock() 537 538 return n.server 539 } 540 541 // Service retrieves a currently running service registered of a specific type. 542 func (n *Node) Service(service interface{}) error { 543 n.lock.RLock() 544 defer n.lock.RUnlock() 545 546 // Short circuit if the node's not running 547 if n.server == nil { 548 return ErrNodeStopped 549 } 550 // Otherwise try to find the service to return 551 element := reflect.ValueOf(service).Elem() 552 if running, ok := n.services[element.Type()]; ok { 553 element.Set(reflect.ValueOf(running)) 554 return nil 555 } 556 return ErrServiceUnknown 557 } 558 559 // DataDir retrieves the current datadir used by the protocol stack. 560 // Deprecated: No files should be stored in this directory, use InstanceDir instead. 561 func (n *Node) DataDir() string { 562 return n.config.DataDir 563 } 564 565 // InstanceDir retrieves the instance directory used by the protocol stack. 566 func (n *Node) InstanceDir() string { 567 return n.config.instanceDir() 568 } 569 570 // AccountManager retrieves the account manager used by the protocol stack. 571 func (n *Node) AccountManager() *accounts.Manager { 572 return n.accman 573 } 574 575 // IPCEndpoint retrieves the current IPC endpoint used by the protocol stack. 576 func (n *Node) IPCEndpoint() string { 577 return n.ipcEndpoint 578 } 579 580 // HTTPEndpoint retrieves the current HTTP endpoint used by the protocol stack. 581 func (n *Node) HTTPEndpoint() string { 582 n.lock.Lock() 583 defer n.lock.Unlock() 584 585 if n.httpListener != nil { 586 return n.httpListener.Addr().String() 587 } 588 return n.httpEndpoint 589 } 590 591 // WSEndpoint retrieves the current WS endpoint used by the protocol stack. 592 func (n *Node) WSEndpoint() string { 593 n.lock.Lock() 594 defer n.lock.Unlock() 595 596 if n.wsListener != nil { 597 return n.wsListener.Addr().String() 598 } 599 return n.wsEndpoint 600 } 601 602 // EventMux retrieves the event multiplexer used by all the network services in 603 // the current protocol stack. 604 func (n *Node) EventMux() *event.TypeMux { 605 return n.eventmux 606 } 607 608 // OpenDatabase opens an existing database with the given name (or creates one if no 609 // previous can be found) from within the node's instance directory. If the node is 610 // ephemeral, a memory database is returned. 611 func (n *Node) OpenDatabase(name string, cache, handles int, namespace string) (ethdb.Database, error) { 612 if n.config.DataDir == "" { 613 return rawdb.NewMemoryDatabase(), nil 614 } 615 return rawdb.NewLevelDBDatabase(n.config.ResolvePath(name), cache, handles, namespace) 616 } 617 618 // OpenDatabaseWithFreezer opens an existing database with the given name (or 619 // creates one if no previous can be found) from within the node's data directory, 620 // also attaching a chain freezer to it that moves ancient chain data from the 621 // database to immutable append-only files. If the node is an ephemeral one, a 622 // memory database is returned. 623 func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, namespace string) (ethdb.Database, error) { 624 if n.config.DataDir == "" { 625 return rawdb.NewMemoryDatabase(), nil 626 } 627 root := n.config.ResolvePath(name) 628 629 switch { 630 case freezer == "": 631 freezer = filepath.Join(root, "ancient") 632 case !filepath.IsAbs(freezer): 633 freezer = n.config.ResolvePath(freezer) 634 } 635 return rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace) 636 } 637 638 func (n *Node) OpenEbakusDatabase(name string) (*ebakusdb.DB, error) { 639 if n.config.DataDir == "" { 640 return nil, nil // TODO: Implement a memory only version for tests 641 } 642 643 db, err := ebakusdb.Open(n.config.ResolvePath(name), 0666, nil) 644 if err != nil { 645 return nil, err 646 } 647 648 // Assign custom value encoders to the db 649 db.SetCustomEncoder(GobMarshal, GobUnmarshal) 650 651 return db, nil 652 } 653 654 // ResolvePath returns the absolute path of a resource in the instance directory. 655 func (n *Node) ResolvePath(x string) string { 656 return n.config.ResolvePath(x) 657 } 658 659 // apis returns the collection of RPC descriptors this node offers. 660 func (n *Node) apis() []rpc.API { 661 return []rpc.API{ 662 { 663 Namespace: "admin", 664 Version: "1.0", 665 Service: NewPrivateAdminAPI(n), 666 }, { 667 Namespace: "admin", 668 Version: "1.0", 669 Service: NewPublicAdminAPI(n), 670 Public: true, 671 }, { 672 Namespace: "debug", 673 Version: "1.0", 674 Service: debug.Handler, 675 }, { 676 Namespace: "web3", 677 Version: "1.0", 678 Service: NewPublicWeb3API(n), 679 Public: true, 680 }, 681 } 682 }