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