aboutsummaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
Diffstat (limited to 'node')
-rw-r--r--node/api.go83
-rw-r--r--node/config.go42
-rw-r--r--node/defaults.go5
-rw-r--r--node/errors.go11
-rw-r--r--node/node.go255
-rw-r--r--node/service.go135
6 files changed, 226 insertions, 305 deletions
diff --git a/node/api.go b/node/api.go
index 5e93580..4589d25 100644
--- a/node/api.go
+++ b/node/api.go
@@ -21,28 +21,48 @@ import (
"fmt"
//"strings"
+ "github.com/ava-labs/coreth/internal/debug"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/crypto"
- "github.com/ava-labs/go-ethereum/p2p"
- "github.com/ava-labs/go-ethereum/p2p/enode"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/p2p/enode"
)
-// PrivateAdminAPI is the collection of administrative API methods exposed only
-// over a secure RPC channel.
-type PrivateAdminAPI struct {
- node *Node // Node interfaced by this API
+// apis returns the collection of built-in RPC APIs.
+func (n *Node) apis() []rpc.API {
+ return []rpc.API{
+ {
+ Namespace: "admin",
+ Version: "1.0",
+ Service: &privateAdminAPI{n},
+ }, {
+ Namespace: "admin",
+ Version: "1.0",
+ Service: &publicAdminAPI{n},
+ Public: true,
+ }, {
+ Namespace: "debug",
+ Version: "1.0",
+ Service: debug.Handler,
+ }, {
+ Namespace: "web3",
+ Version: "1.0",
+ Service: &publicWeb3API{n},
+ Public: true,
+ },
+ }
}
-// NewPrivateAdminAPI creates a new API definition for the private admin methods
-// of the node itself.
-func NewPrivateAdminAPI(node *Node) *PrivateAdminAPI {
- return &PrivateAdminAPI{node: node}
+// privateAdminAPI is the collection of administrative API methods exposed only
+// over a secure RPC channel.
+type privateAdminAPI struct {
+ node *Node // Node interfaced by this API
}
// AddPeer requests connecting to a remote node, and also maintaining the new
// connection at all times, even reconnecting if it is lost.
-func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
+func (api *privateAdminAPI) AddPeer(url string) (bool, error) {
// Make sure the server is running, fail otherwise
server := api.node.Server()
if server == nil {
@@ -58,7 +78,7 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
}
// RemovePeer disconnects from a remote node if the connection exists
-func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
+func (api *privateAdminAPI) RemovePeer(url string) (bool, error) {
// Make sure the server is running, fail otherwise
server := api.node.Server()
if server == nil {
@@ -74,7 +94,7 @@ func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
}
// AddTrustedPeer allows a remote node to always connect, even if slots are full
-func (api *PrivateAdminAPI) AddTrustedPeer(url string) (bool, error) {
+func (api *privateAdminAPI) AddTrustedPeer(url string) (bool, error) {
// Make sure the server is running, fail otherwise
server := api.node.Server()
if server == nil {
@@ -90,7 +110,7 @@ func (api *PrivateAdminAPI) AddTrustedPeer(url string) (bool, error) {
// RemoveTrustedPeer removes a remote node from the trusted peer set, but it
// does not disconnect it automatically.
-func (api *PrivateAdminAPI) RemoveTrustedPeer(url string) (bool, error) {
+func (api *privateAdminAPI) RemoveTrustedPeer(url string) (bool, error) {
// Make sure the server is running, fail otherwise
server := api.node.Server()
if server == nil {
@@ -106,7 +126,7 @@ func (api *PrivateAdminAPI) RemoveTrustedPeer(url string) (bool, error) {
// PeerEvents creates an RPC subscription which receives peer events from the
// node's p2p.Server
-func (api *PrivateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) {
+func (api *privateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) {
// Make sure the server is running, fail otherwise
server := api.node.Server()
if server == nil {
@@ -142,21 +162,15 @@ func (api *PrivateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription,
return rpcSub, nil
}
-// PublicAdminAPI is the collection of administrative API methods exposed over
+// publicAdminAPI is the collection of administrative API methods exposed over
// both secure and unsecure RPC channels.
-type PublicAdminAPI struct {
+type publicAdminAPI struct {
node *Node // Node interfaced by this API
}
-// NewPublicAdminAPI creates a new API definition for the public admin methods
-// of the node itself.
-func NewPublicAdminAPI(node *Node) *PublicAdminAPI {
- return &PublicAdminAPI{node: node}
-}
-
// Peers retrieves all the information we know about each individual peer at the
// protocol granularity.
-func (api *PublicAdminAPI) Peers() ([]*p2p.PeerInfo, error) {
+func (api *publicAdminAPI) Peers() ([]*p2p.PeerInfo, error) {
server := api.node.Server()
if server == nil {
return nil, ErrNodeStopped
@@ -166,7 +180,7 @@ func (api *PublicAdminAPI) Peers() ([]*p2p.PeerInfo, error) {
// NodeInfo retrieves all the information we know about the host node at the
// protocol granularity.
-func (api *PublicAdminAPI) NodeInfo() (*p2p.NodeInfo, error) {
+func (api *publicAdminAPI) NodeInfo() (*p2p.NodeInfo, error) {
server := api.node.Server()
if server == nil {
return nil, ErrNodeStopped
@@ -175,27 +189,22 @@ func (api *PublicAdminAPI) NodeInfo() (*p2p.NodeInfo, error) {
}
// Datadir retrieves the current data directory the node is using.
-func (api *PublicAdminAPI) Datadir() string {
+func (api *publicAdminAPI) Datadir() string {
return api.node.DataDir()
}
-// PublicWeb3API offers helper utils
-type PublicWeb3API struct {
+// publicWeb3API offers helper utils
+type publicWeb3API struct {
stack *Node
}
-// NewPublicWeb3API creates a new Web3Service instance
-func NewPublicWeb3API(stack *Node) *PublicWeb3API {
- return &PublicWeb3API{stack}
-}
-
// ClientVersion returns the node name
-func (s *PublicWeb3API) ClientVersion() string {
+func (s *publicWeb3API) ClientVersion() string {
return s.stack.Server().Name
}
// Sha3 applies the ethereum sha3 implementation on the input.
// It assumes the input is hex encoded.
-func (s *PublicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes {
+func (s *publicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes {
return crypto.Keccak256(input)
}
diff --git a/node/config.go b/node/config.go
index b8ada53..57bb7a1 100644
--- a/node/config.go
+++ b/node/config.go
@@ -32,11 +32,11 @@ import (
"github.com/ava-labs/coreth/accounts/scwallet"
//"github.com/ava-labs/coreth/accounts/usbwallet"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/crypto"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/p2p"
- "github.com/ava-labs/go-ethereum/p2p/enode"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/p2p/enode"
)
const (
@@ -83,7 +83,7 @@ type Config struct {
KeyStoreDir string `toml:",omitempty"`
// ExternalSigner specifies an external URI for a clef-type signer
- ExternalSigner string `toml:"omitempty"`
+ ExternalSigner string `toml:",omitempty"`
// UseLightweightKDF lowers the memory and CPU requirements of the key store
// scrypt KDF at the expense of security.
@@ -102,11 +102,11 @@ type Config struct {
// a simple file name, it is placed inside the data directory (or on the root
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
// relative), then that specific path is enforced. An empty path disables IPC.
- IPCPath string `toml:",omitempty"`
+ IPCPath string
// HTTPHost is the host interface on which to start the HTTP RPC server. If this
// field is empty, no HTTP API endpoint will be started.
- HTTPHost string `toml:",omitempty"`
+ HTTPHost string
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful
@@ -130,7 +130,7 @@ type Config struct {
// HTTPModules is a list of API modules to expose via the HTTP RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
- HTTPModules []string `toml:",omitempty"`
+ HTTPModules []string
// HTTPTimeouts allows for customization of the timeout values used by the HTTP RPC
// interface.
@@ -138,7 +138,7 @@ type Config struct {
// WSHost is the host interface on which to start the websocket RPC server. If
// this field is empty, no websocket API endpoint will be started.
- WSHost string `toml:",omitempty"`
+ WSHost string
// WSPort is the TCP port number on which to start the websocket RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful for
@@ -153,7 +153,7 @@ type Config struct {
// WSModules is a list of API modules to expose via the websocket RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
- WSModules []string `toml:",omitempty"`
+ WSModules []string
// WSExposeAll exposes all API modules via the WebSocket RPC interface rather
// than just the public ones.
@@ -162,15 +162,6 @@ type Config struct {
// private APIs to untrusted users is a major security risk.
WSExposeAll bool `toml:",omitempty"`
- // GraphQLHost is the host interface on which to start the GraphQL server. If this
- // field is empty, no GraphQL API endpoint will be started.
- GraphQLHost string `toml:",omitempty"`
-
- // GraphQLPort is the TCP port number on which to start the GraphQL server. The
- // default zero value is/ valid and will pick a port number randomly (useful
- // for ephemeral nodes).
- GraphQLPort int `toml:",omitempty"`
-
// GraphQLCors is the Cross-Origin Resource Sharing header to send to requesting
// clients. Please be aware that CORS is a browser enforced security, it's fully
// useless for custom HTTP clients.
@@ -247,15 +238,6 @@ func (c *Config) HTTPEndpoint() string {
return fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort)
}
-// GraphQLEndpoint resolves a GraphQL endpoint based on the configured host interface
-// and port parameters.
-func (c *Config) GraphQLEndpoint() string {
- if c.GraphQLHost == "" {
- return ""
- }
- return fmt.Sprintf("%s:%d", c.GraphQLHost, c.GraphQLPort)
-}
-
// DefaultHTTPEndpoint returns the HTTP endpoint used by default.
func DefaultHTTPEndpoint() string {
config := &Config{HTTPHost: DefaultHTTPHost, HTTPPort: DefaultHTTPPort}
@@ -280,7 +262,7 @@ func DefaultWSEndpoint() string {
// ExtRPCEnabled returns the indicator whether node enables the external
// RPC(http, ws or graphql).
func (c *Config) ExtRPCEnabled() bool {
- return c.HTTPHost != "" || c.WSHost != "" || c.GraphQLHost != ""
+ return c.HTTPHost != "" || c.WSHost != ""
}
// NodeName returns the devp2p node identifier.
diff --git a/node/defaults.go b/node/defaults.go
index 1d277e2..b2c4d0b 100644
--- a/node/defaults.go
+++ b/node/defaults.go
@@ -23,8 +23,8 @@ import (
"runtime"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/p2p"
- "github.com/ava-labs/go-ethereum/p2p/nat"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/p2p/nat"
)
const (
@@ -45,7 +45,6 @@ var DefaultConfig = Config{
HTTPTimeouts: rpc.DefaultHTTPTimeouts,
WSPort: DefaultWSPort,
WSModules: []string{"net", "web3"},
- GraphQLPort: DefaultGraphQLPort,
GraphQLVirtualHosts: []string{"localhost"},
P2P: p2p.Config{
ListenAddr: ":30303",
diff --git a/node/errors.go b/node/errors.go
index 2e0dadc..67547bf 100644
--- a/node/errors.go
+++ b/node/errors.go
@@ -39,17 +39,6 @@ func convertFileLockError(err error) error {
return err
}
-// DuplicateServiceError is returned during Node startup if a registered service
-// constructor returns a service of the same type that was already started.
-type DuplicateServiceError struct {
- Kind reflect.Type
-}
-
-// Error generates a textual representation of the duplicate service error.
-func (e *DuplicateServiceError) Error() string {
- return fmt.Sprintf("duplicate service: %v", e.Kind)
-}
-
// StopError is returned if a Node fails to stop either any of its registered
// services or itself.
type StopError struct {
diff --git a/node/node.go b/node/node.go
index d2a212b..3ed89ed 100644
--- a/node/node.go
+++ b/node/node.go
@@ -18,44 +18,64 @@ package node
import (
"errors"
+ "os"
"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/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/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
+ eventmux *event.TypeMux
+ config *Config
+ accman *accounts.Manager
+ log log.Logger
+ ephemKeystore string // if non-empty, the key directory that will be removed by Stop
+ dirLock fileutil.Releaser // prevents concurrent use of instance directory
+ stop chan struct{} // Channel to wait for termination notifications
+ server *p2p.Server // Currently running P2P networking layer
+ startStopLock sync.Mutex // Start/Stop are protected by an additional lock
+ state int // Tracks state of node lifecycle
+ lock sync.Mutex
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
+ databases map[*closeTrackingDB]struct{} // All open databases
+}
- log log.Logger
+const (
+ initializingState = iota
+ runningState
+ closedState
+)
+
+func (n *Node) openDataDir() error {
+ if n.config.DataDir == "" {
+ return nil // ephemeral
+ }
+
+ instdir := filepath.Join(n.config.DataDir, n.config.name())
+ if err := os.MkdirAll(instdir, 0700); err != nil {
+ return err
+ }
+ // Lock the instance directory to prevent concurrent use by another instance as well as
+ // accidental use of the instance directory as a database.
+ release, _, err := fileutil.Flock(filepath.Join(instdir, "LOCK"))
+ if err != nil {
+ return convertFileLockError(err)
+ }
+ n.dirLock = release
+ return nil
}
// New creates a new P2P node, ready for protocol registration.
@@ -71,6 +91,10 @@ func New(conf *Config) (*Node, error) {
}
conf.DataDir = absdatadir
}
+ if conf.Logger == nil {
+ conf.Logger = log.New()
+ }
+
// Ensure that the instance name doesn't cause weird conflicts with
// other files in the data directory.
if strings.ContainsAny(conf.Name, `/\`) {
@@ -82,25 +106,50 @@ func New(conf *Config) (*Node, error) {
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.
+
+ node := &Node{
+ config: conf,
+ inprocHandler: rpc.NewServer(),
+ eventmux: new(event.TypeMux),
+ log: conf.Logger,
+ stop: make(chan struct{}),
+ server: &p2p.Server{Config: conf.P2P},
+ databases: make(map[*closeTrackingDB]struct{}),
+ }
+
+ // Register built-in APIs.
+ node.rpcAPIs = append(node.rpcAPIs, node.apis()...)
+
+ // Acquire the instance directory lock.
+ if err := node.openDataDir(); err != nil {
+ return nil, err
+ }
+ // 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()
+ node.accman = am
+ node.ephemKeystore = ephemeralKeystore
+
+ // Initialize the p2p server. This creates the node key and discovery databases.
+ node.server.Config.PrivateKey = node.config.NodeKey()
+ node.server.Config.Name = node.config.NodeName()
+ node.server.Config.Logger = node.log
+ if node.server.Config.StaticNodes == nil {
+ node.server.Config.StaticNodes = node.config.StaticNodes()
+ }
+ if node.server.Config.TrustedNodes == nil {
+ node.server.Config.TrustedNodes = node.config.TrustedNodes()
}
- // 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
+ if node.server.Config.NodeDatabase == "" {
+ node.server.Config.NodeDatabase = node.config.NodeDB()
+ }
+
+ // Configure RPC servers.
+
+ return node, nil
}
// Config returns the configuration of node.
@@ -109,33 +158,15 @@ func (n *Node) Config() *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.
+// only to inspect fields of the currently running server. Callers should not
+// start or stop the returned server.
func (n *Node) Server() *p2p.Server {
- n.lock.RLock()
- defer n.lock.RUnlock()
+ n.lock.Lock()
+ defer n.lock.Unlock()
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 {
@@ -162,10 +193,24 @@ func (n *Node) EventMux() *event.TypeMux {
// 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) {
+ n.lock.Lock()
+ defer n.lock.Unlock()
+ if n.state == closedState {
+ return nil, ErrNodeStopped
+ }
+
+ var db ethdb.Database
+ var err error
if n.config.DataDir == "" {
- return rawdb.NewMemoryDatabase(), nil
+ db = rawdb.NewMemoryDatabase()
+ } else {
+ db, err = rawdb.NewLevelDBDatabase(n.ResolvePath(name), cache, handles, namespace)
}
- return rawdb.NewLevelDBDatabase(n.config.ResolvePath(name), cache, handles, namespace)
+
+ if err == nil {
+ db = n.wrapDatabase(db)
+ }
+ return db, err
}
// OpenDatabaseWithFreezer opens an existing database with the given name (or
@@ -174,18 +219,31 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string) (
// 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) {
+ n.lock.Lock()
+ defer n.lock.Unlock()
+ if n.state == closedState {
+ return nil, ErrNodeStopped
+ }
+
+ var db ethdb.Database
+ var err error
if n.config.DataDir == "" {
- return rawdb.NewMemoryDatabase(), nil
+ db = rawdb.NewMemoryDatabase()
+ } else {
+ root := n.ResolvePath(name)
+ switch {
+ case freezer == "":
+ freezer = filepath.Join(root, "ancient")
+ case !filepath.IsAbs(freezer):
+ freezer = n.ResolvePath(freezer)
+ }
+ db, err = rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace)
}
- root := n.config.ResolvePath(name)
- switch {
- case freezer == "":
- freezer = filepath.Join(root, "ancient")
- case !filepath.IsAbs(freezer):
- freezer = n.config.ResolvePath(freezer)
+ if err == nil {
+ db = n.wrapDatabase(db)
}
- return rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace)
+ return db, err
}
// ResolvePath returns the absolute path of a resource in the instance directory.
@@ -193,27 +251,46 @@ 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,
- },
+// closeTrackingDB wraps the Close method of a database. When the database is closed by the
+// service, the wrapper removes it from the node's database map. This ensures that Node
+// won't auto-close the database if it is closed by the service that opened it.
+type closeTrackingDB struct {
+ ethdb.Database
+ n *Node
+}
+
+func (db *closeTrackingDB) Close() error {
+ db.n.lock.Lock()
+ delete(db.n.databases, db)
+ db.n.lock.Unlock()
+ return db.Database.Close()
+}
+
+// wrapDatabase ensures the database will be auto-closed when Node is closed.
+func (n *Node) wrapDatabase(db ethdb.Database) ethdb.Database {
+ wrapper := &closeTrackingDB{db, n}
+ n.databases[wrapper] = struct{}{}
+ return wrapper
+}
+
+// closeDatabases closes all open databases.
+func (n *Node) closeDatabases() (errors []error) {
+ for db := range n.databases {
+ delete(n.databases, db)
+ if err := db.Database.Close(); err != nil {
+ errors = append(errors, err)
+ }
+ }
+ return errors
+}
+
+// RegisterAPIs registers the APIs a service provides on the node.
+func (n *Node) RegisterAPIs(apis []rpc.API) {
+ n.lock.Lock()
+ defer n.lock.Unlock()
+
+ if n.state != initializingState {
+ panic("can't register APIs on running/stopped node")
}
+ n.rpcAPIs = append(n.rpcAPIs, apis...)
}
diff --git a/node/service.go b/node/service.go
deleted file mode 100644
index 7b3a4ff..0000000
--- a/node/service.go
+++ /dev/null
@@ -1,135 +0,0 @@
-// 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 <http://www.gnu.org/licenses/>.
-
-package node
-
-import (
- "path/filepath"
- "reflect"
-
- "github.com/ava-labs/coreth/accounts"
- "github.com/ava-labs/coreth/core/rawdb"
- "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/p2p"
-)
-
-// ServiceContext is a collection of service independent options inherited from
-// the protocol stack, that is passed to all constructors to be optionally used;
-// as well as utility methods to operate on the service environment.
-type ServiceContext struct {
- config *Config
- services map[reflect.Type]Service // Index of the already constructed services
- EventMux *event.TypeMux // Event multiplexer used for decoupled notifications
- AccountManager *accounts.Manager // Account manager created by the node.
-}
-
-// OpenDatabase opens an existing database with the given name (or creates one
-// if no previous can be found) from within the node's data directory. If the
-// node is an ephemeral one, a memory database is returned.
-func (ctx *ServiceContext) OpenDatabase(name string, cache int, handles int, namespace string) (ethdb.Database, error) {
- if ctx.config.DataDir == "" {
- return rawdb.NewMemoryDatabase(), nil
- }
- return rawdb.NewLevelDBDatabase(ctx.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 (ctx *ServiceContext) OpenDatabaseWithFreezer(name string, cache int, handles int, freezer string, namespace string) (ethdb.Database, error) {
- if ctx.config.DataDir == "" {
- return rawdb.NewMemoryDatabase(), nil
- }
- root := ctx.config.ResolvePath(name)
-
- switch {
- case freezer == "":
- freezer = filepath.Join(root, "ancient")
- case !filepath.IsAbs(freezer):
- freezer = ctx.config.ResolvePath(freezer)
- }
- return rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace)
-}
-
-// ResolvePath resolves a user path into the data directory if that was relative
-// and if the user actually uses persistent storage. It will return an empty string
-// for emphemeral storage and the user's own input for absolute paths.
-func (ctx *ServiceContext) ResolvePath(path string) string {
- return ctx.config.ResolvePath(path)
-}
-
-// Service retrieves a currently running service registered of a specific type.
-func (ctx *ServiceContext) Service(service interface{}) error {
- element := reflect.ValueOf(service).Elem()
- if running, ok := ctx.services[element.Type()]; ok {
- element.Set(reflect.ValueOf(running))
- return nil
- }
- return ErrServiceUnknown
-}
-
-// ExtRPCEnabled returns the indicator whether node enables the external
-// RPC(http, ws or graphql).
-func (ctx *ServiceContext) ExtRPCEnabled() bool {
- return ctx.config.ExtRPCEnabled()
-}
-
-func NewServiceContext(cfg *Config, mux *event.TypeMux) (ServiceContext, string, error) {
- if cfg == nil {
- cfg = &Config{}
- }
- am, ep, err := makeAccountManager(cfg)
- return ServiceContext{
- config: cfg,
- services: make(map[reflect.Type]Service),
- EventMux: mux,
- AccountManager: am,
- }, ep, err
-}
-
-// ServiceConstructor is the function signature of the constructors needed to be
-// registered for service instantiation.
-type ServiceConstructor func(ctx *ServiceContext) (Service, error)
-
-// Service is an individual protocol that can be registered into a node.
-//
-// Notes:
-//
-// • Service life-cycle management is delegated to the node. The service is allowed to
-// initialize itself upon creation, but no goroutines should be spun up outside of the
-// Start method.
-//
-// • Restart logic is not required as the node will create a fresh instance
-// every time a service is started.
-type Service interface {
- // Protocols retrieves the P2P protocols the service wishes to start.
- Protocols() []p2p.Protocol
-
- // APIs retrieves the list of RPC descriptors the service provides
- APIs() []rpc.API
-
- // Start is called after all services have been constructed and the networking
- // layer was also initialized to spawn any goroutines required by the service.
- Start(server *p2p.Server) error
-
- // Stop terminates all goroutines belonging to the service, blocking until they
- // are all terminated.
- Stop() error
-}