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