// Copyright 2015 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package node import ( "errors" "path/filepath" "reflect" "strings" "sync" "github.com/ava-labs/coreth/accounts" "github.com/ava-labs/coreth/core/rawdb" "github.com/ava-labs/coreth/internal/debug" "github.com/ava-labs/coreth/rpc" "github.com/ava-labs/go-ethereum/ethdb" "github.com/ava-labs/go-ethereum/event" "github.com/ava-labs/go-ethereum/log" "github.com/ava-labs/go-ethereum/p2p" "github.com/prometheus/tsdb/fileutil" ) // Node is a container on which services can be registered. type Node struct { eventmux *event.TypeMux // Event multiplexer used between the services of a stack config *Config accman *accounts.Manager ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop instanceDirLock fileutil.Releaser // prevents concurrent use of instance directory serverConfig p2p.Config server *p2p.Server // Currently running P2P networking layer serviceFuncs []ServiceConstructor // Service constructors (in dependency order) services map[reflect.Type]Service // Currently running services rpcAPIs []rpc.API // List of APIs currently provided by the node inprocHandler *rpc.Server // In-process RPC request handler to process the API requests stop chan struct{} // Channel to wait for termination notifications lock sync.RWMutex log log.Logger } // New creates a new P2P node, ready for protocol registration. func New(conf *Config) (*Node, error) { // Copy config and resolve the datadir so future changes to the current // working directory don't affect the node. confCopy := *conf conf = &confCopy if conf.DataDir != "" { absdatadir, err := filepath.Abs(conf.DataDir) if err != nil { return nil, err } conf.DataDir = absdatadir } // Ensure that the instance name doesn't cause weird conflicts with // other files in the data directory. if strings.ContainsAny(conf.Name, `/\`) { return nil, errors.New(`Config.Name must not contain '/' or '\'`) } if conf.Name == datadirDefaultKeyStore { return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`) } if strings.HasSuffix(conf.Name, ".ipc") { return nil, errors.New(`Config.Name cannot end in ".ipc"`) } // Ensure that the AccountManager method works before the node has started. // We rely on this in cmd/geth. am, ephemeralKeystore, err := makeAccountManager(conf) if err != nil { return nil, err } if conf.Logger == nil { conf.Logger = log.New() } // Note: any interaction with Config that would create/touch files // in the data directory or instance directory is delayed until Start. return &Node{ accman: am, ephemeralKeystore: ephemeralKeystore, config: conf, serviceFuncs: []ServiceConstructor{}, eventmux: new(event.TypeMux), log: conf.Logger, }, nil } // Config returns the configuration of node. func (n *Node) Config() *Config { return n.config } // Server retrieves the currently running P2P network layer. This method is meant // only to inspect fields of the currently running server, life cycle management // should be left to this Node entity. func (n *Node) Server() *p2p.Server { n.lock.RLock() defer n.lock.RUnlock() return n.server } // Service retrieves a currently running service registered of a specific type. func (n *Node) Service(service interface{}) error { n.lock.RLock() defer n.lock.RUnlock() // Short circuit if the node's not running if n.server == nil { return ErrNodeStopped } // Otherwise try to find the service to return element := reflect.ValueOf(service).Elem() if running, ok := n.services[element.Type()]; ok { element.Set(reflect.ValueOf(running)) return nil } return ErrServiceUnknown } // DataDir retrieves the current datadir used by the protocol stack. // Deprecated: No files should be stored in this directory, use InstanceDir instead. func (n *Node) DataDir() string { return n.config.DataDir } // InstanceDir retrieves the instance directory used by the protocol stack. func (n *Node) InstanceDir() string { return n.config.instanceDir() } // AccountManager retrieves the account manager used by the protocol stack. func (n *Node) AccountManager() *accounts.Manager { return n.accman } // EventMux retrieves the event multiplexer used by all the network services in // the current protocol stack. func (n *Node) EventMux() *event.TypeMux { return n.eventmux } // OpenDatabase opens an existing database with the given name (or creates one if no // previous can be found) from within the node's instance directory. If the node is // ephemeral, a memory database is returned. func (n *Node) OpenDatabase(name string, cache, handles int, namespace string) (ethdb.Database, error) { if n.config.DataDir == "" { return rawdb.NewMemoryDatabase(), nil } return rawdb.NewLevelDBDatabase(n.config.ResolvePath(name), cache, handles, namespace) } // OpenDatabaseWithFreezer opens an existing database with the given name (or // creates one if no previous can be found) from within the node's data directory, // also attaching a chain freezer to it that moves ancient chain data from the // database to immutable append-only files. If the node is an ephemeral one, a // memory database is returned. func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, namespace string) (ethdb.Database, error) { if n.config.DataDir == "" { return rawdb.NewMemoryDatabase(), nil } root := n.config.ResolvePath(name) switch { case freezer == "": freezer = filepath.Join(root, "ancient") case !filepath.IsAbs(freezer): freezer = n.config.ResolvePath(freezer) } return rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace) } // ResolvePath returns the absolute path of a resource in the instance directory. func (n *Node) ResolvePath(x string) string { return n.config.ResolvePath(x) } // apis returns the collection of RPC descriptors this node offers. func (n *Node) apis() []rpc.API { return []rpc.API{ { Namespace: "admin", Version: "1.0", Service: NewPrivateAdminAPI(n), }, { Namespace: "admin", Version: "1.0", Service: NewPublicAdminAPI(n), Public: true, }, { Namespace: "debug", Version: "1.0", Service: debug.Handler, }, { Namespace: "web3", Version: "1.0", Service: NewPublicWeb3API(n), Public: true, }, } }