Merge pull request #737 from JukLee0ira/fix-node

node, p2p/simulations: fix node.Node AccountsManager leak
This commit is contained in:
Daniel Liu 2024-11-25 16:35:40 +08:00 committed by GitHub
commit 017dcdd756
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 105 additions and 15 deletions

View file

@ -203,6 +203,8 @@ func initGenesis(ctx *cli.Context) error {
// Open an initialise both full and light databases // Open an initialise both full and light databases
stack, _ := makeFullNode(ctx) stack, _ := makeFullNode(ctx)
defer stack.Close()
for _, name := range []string{"chaindata", "lightchaindata"} { for _, name := range []string{"chaindata", "lightchaindata"} {
chaindb, err := stack.OpenDatabase(name, 0, 0, "") chaindb, err := stack.OpenDatabase(name, 0, 0, "")
if err != nil { if err != nil {
@ -228,6 +230,8 @@ func importChain(ctx *cli.Context) error {
go metrics.CollectProcessMetrics(3 * time.Second) go metrics.CollectProcessMetrics(3 * time.Second)
stack, _ := makeFullNode(ctx) stack, _ := makeFullNode(ctx)
defer stack.Close()
chain, chainDb := utils.MakeChain(ctx, stack) chain, chainDb := utils.MakeChain(ctx, stack)
defer chainDb.Close() defer chainDb.Close()
@ -319,6 +323,8 @@ func exportChain(ctx *cli.Context) error {
utils.Fatalf("This command requires an argument.") utils.Fatalf("This command requires an argument.")
} }
stack, _ := makeFullNode(ctx) stack, _ := makeFullNode(ctx)
defer stack.Close()
chain, db := utils.MakeChain(ctx, stack) chain, db := utils.MakeChain(ctx, stack)
defer db.Close() defer db.Close()
start := time.Now() start := time.Now()
@ -353,6 +359,8 @@ func importPreimages(ctx *cli.Context) error {
utils.Fatalf("This command requires an argument.") utils.Fatalf("This command requires an argument.")
} }
stack, _ := makeFullNode(ctx) stack, _ := makeFullNode(ctx)
defer stack.Close()
diskdb := utils.MakeChainDatabase(ctx, stack) diskdb := utils.MakeChainDatabase(ctx, stack)
defer diskdb.Close() defer diskdb.Close()
@ -370,6 +378,8 @@ func exportPreimages(ctx *cli.Context) error {
utils.Fatalf("This command requires an argument.") utils.Fatalf("This command requires an argument.")
} }
stack, _ := makeFullNode(ctx) stack, _ := makeFullNode(ctx)
defer stack.Close()
diskdb := utils.MakeChainDatabase(ctx, stack) diskdb := utils.MakeChainDatabase(ctx, stack)
defer diskdb.Close() defer diskdb.Close()
@ -388,6 +398,8 @@ func copyDb(ctx *cli.Context) error {
} }
// Initialize a new chain for the running node to sync into // Initialize a new chain for the running node to sync into
stack, _ := makeFullNode(ctx) stack, _ := makeFullNode(ctx)
defer stack.Close()
chain, chainDb := utils.MakeChain(ctx, stack) chain, chainDb := utils.MakeChain(ctx, stack)
defer chainDb.Close() defer chainDb.Close()
@ -461,6 +473,8 @@ func removeDB(ctx *cli.Context) error {
func dump(ctx *cli.Context) error { func dump(ctx *cli.Context) error {
stack, _ := makeFullNode(ctx) stack, _ := makeFullNode(ctx)
defer stack.Close()
chain, chainDb := utils.MakeChain(ctx, stack) chain, chainDb := utils.MakeChain(ctx, stack)
defer chainDb.Close() defer chainDb.Close()

View file

@ -79,7 +79,7 @@ func localConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags // Create and start the node based on the CLI flags
node, cfg := makeFullNode(ctx) node, cfg := makeFullNode(ctx)
startNode(ctx, node, cfg) startNode(ctx, node, cfg)
defer node.Stop() defer node.Close()
// Attach to the newly started node and start the JavaScript console // Attach to the newly started node and start the JavaScript console
client, err := node.Attach() client, err := node.Attach()
@ -181,7 +181,7 @@ func ephemeralConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags // Create and start the node based on the CLI flags
node, cfg := makeFullNode(ctx) node, cfg := makeFullNode(ctx)
startNode(ctx, node, cfg) startNode(ctx, node, cfg)
defer node.Stop() defer node.Close()
// Attach to the newly started node and start the JavaScript console // Attach to the newly started node and start the JavaScript console
client, err := node.Attach() client, err := node.Attach()

View file

@ -226,6 +226,7 @@ func main() {
// blocking mode, waiting for it to be shut down. // blocking mode, waiting for it to be shut down.
func XDC(ctx *cli.Context) error { func XDC(ctx *cli.Context) error {
node, cfg := makeFullNode(ctx) node, cfg := makeFullNode(ctx)
defer node.Close()
startNode(ctx, node, cfg) startNode(ctx, node, cfg)
node.Wait() node.Wait()
return nil return nil

View file

@ -287,7 +287,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
// close terminates the Ethereum connection and tears down the faucet. // close terminates the Ethereum connection and tears down the faucet.
func (f *faucet) close() error { func (f *faucet) close() error {
return f.stack.Stop() return f.stack.Close()
} }
// listenAndServe registers the HTTP handlers for the faucet and boots it up // listenAndServe registers the HTTP handlers for the faucet and boots it up

View file

@ -153,8 +153,8 @@ func (env *tester) Close(t *testing.T) {
if err := env.console.Stop(false); err != nil { if err := env.console.Stop(false); err != nil {
t.Errorf("failed to stop embedded console: %v", err) t.Errorf("failed to stop embedded console: %v", err)
} }
if err := env.stack.Stop(); err != nil { if err := env.stack.Close(); err != nil {
t.Errorf("failed to stop embedded node: %v", err) t.Errorf("failed to tear down embedded node: %v", err)
} }
os.RemoveAll(env.workspace) os.RemoveAll(env.workspace)
} }

View file

@ -37,14 +37,22 @@ func TestDatadirCreation(t *testing.T) {
} }
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
if _, err := New(&Config{DataDir: dir}); err != nil { node, err := New(&Config{DataDir: dir})
if err != nil {
t.Fatalf("failed to create stack with existing datadir: %v", err) t.Fatalf("failed to create stack with existing datadir: %v", err)
} }
if err := node.Close(); err != nil {
t.Fatalf("failed to close node: %v", err)
}
// Generate a long non-existing datadir path and check that it gets created by a node // Generate a long non-existing datadir path and check that it gets created by a node
dir = filepath.Join(dir, "a", "b", "c", "d", "e", "f") dir = filepath.Join(dir, "a", "b", "c", "d", "e", "f")
if _, err := New(&Config{DataDir: dir}); err != nil { node, err = New(&Config{DataDir: dir})
if err != nil {
t.Fatalf("failed to create stack with creatable datadir: %v", err) t.Fatalf("failed to create stack with creatable datadir: %v", err)
} }
if err := node.Close(); err != nil {
t.Fatalf("failed to close node: %v", err)
}
if _, err := os.Stat(dir); err != nil { if _, err := os.Stat(dir); err != nil {
t.Fatalf("freshly created datadir not accessible: %v", err) t.Fatalf("freshly created datadir not accessible: %v", err)
} }
@ -56,8 +64,12 @@ func TestDatadirCreation(t *testing.T) {
defer os.Remove(file.Name()) defer os.Remove(file.Name())
dir = filepath.Join(file.Name(), "invalid/path") dir = filepath.Join(file.Name(), "invalid/path")
if _, err := New(&Config{DataDir: dir}); err == nil { node, err = New(&Config{DataDir: dir})
if err == nil {
t.Fatalf("protocol stack created with an invalid datadir") t.Fatalf("protocol stack created with an invalid datadir")
if err := node.Close(); err != nil {
t.Fatalf("failed to close node: %v", err)
}
} }
} }

View file

@ -131,6 +131,29 @@ func New(conf *Config) (*Node, error) {
}, nil }, nil
} }
// Close stops the Node and releases resources acquired in
// Node constructor New.
func (n *Node) Close() error {
var errs []error
// Terminate all subsystems and collect any errors
if err := n.Stop(); err != nil && err != ErrNodeStopped {
errs = append(errs, err)
}
if err := n.accman.Close(); err != nil {
errs = append(errs, err)
}
// Report any errors that might have occurred
switch len(errs) {
case 0:
return nil
case 1:
return errs[0]
default:
return fmt.Errorf("%v", errs)
}
}
// Register injects a new service into the node's stack. The service created by // Register injects a new service into the node's stack. The service created by
// the passed constructor must be unique in its type with regard to sibling ones. // the passed constructor must be unique in its type with regard to sibling ones.
func (n *Node) Register(constructor ServiceConstructor) error { func (n *Node) Register(constructor ServiceConstructor) error {

View file

@ -29,10 +29,10 @@ import (
// life cycle management. // life cycle management.
// //
// The following methods are needed to implement a node.Service: // The following methods are needed to implement a node.Service:
// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on // - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on
// - APIs() []rpc.API - api methods the service wants to expose on rpc channels // - APIs() []rpc.API - api methods the service wants to expose on rpc channels
// - Start() error - method invoked when the node is ready to start the service // - Start() error - method invoked when the node is ready to start the service
// - Stop() error - method invoked when the node terminates the service // - Stop() error - method invoked when the node terminates the service
type SampleService struct{} type SampleService struct{}
func (s *SampleService) Protocols() []p2p.Protocol { return nil } func (s *SampleService) Protocols() []p2p.Protocol { return nil }
@ -47,6 +47,8 @@ func ExampleService() {
if err != nil { if err != nil {
log.Fatalf("Failed to create network node: %v", err) log.Fatalf("Failed to create network node: %v", err)
} }
defer stack.Close()
// Create and register a simple network service. This is done through the definition // Create and register a simple network service. This is done through the definition
// of a node.ServiceConstructor that will instantiate a node.Service. The reason for // of a node.ServiceConstructor that will instantiate a node.Service. The reason for
// the factory method approach is to support service restarts without relying on the // the factory method approach is to support service restarts without relying on the

View file

@ -47,6 +47,8 @@ func TestNodeLifeCycle(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Ensure that a stopped node can be stopped again // Ensure that a stopped node can be stopped again
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
if err := stack.Stop(); err != ErrNodeStopped { if err := stack.Stop(); err != ErrNodeStopped {
@ -89,6 +91,8 @@ func TestNodeUsedDataDir(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create original protocol stack: %v", err) t.Fatalf("failed to create original protocol stack: %v", err)
} }
defer original.Close()
if err := original.Start(); err != nil { if err := original.Start(); err != nil {
t.Fatalf("failed to start original protocol stack: %v", err) t.Fatalf("failed to start original protocol stack: %v", err)
} }
@ -99,6 +103,8 @@ func TestNodeUsedDataDir(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create duplicate protocol stack: %v", err) t.Fatalf("failed to create duplicate protocol stack: %v", err)
} }
defer duplicate.Close()
if err := duplicate.Start(); err != ErrDatadirUsed { if err := duplicate.Start(); err != ErrDatadirUsed {
t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed) t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed)
} }
@ -110,6 +116,8 @@ func TestServiceRegistry(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Register a batch of unique services and ensure they start successfully // Register a batch of unique services and ensure they start successfully
services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC} services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC}
for i, constructor := range services { for i, constructor := range services {
@ -142,6 +150,8 @@ func TestServiceLifeCycle(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Register a batch of life-cycle instrumented services // Register a batch of life-cycle instrumented services
services := map[string]InstrumentingWrapper{ services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA, "A": InstrumentedServiceMakerA,
@ -192,6 +202,8 @@ func TestServiceRestarts(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Define a service that does not support restarts // Define a service that does not support restarts
var ( var (
running bool running bool
@ -240,6 +252,8 @@ func TestServiceConstructionAbortion(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Define a batch of good services // Define a batch of good services
services := map[string]InstrumentingWrapper{ services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA, "A": InstrumentedServiceMakerA,
@ -287,6 +301,8 @@ func TestServiceStartupAbortion(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Register a batch of good services // Register a batch of good services
services := map[string]InstrumentingWrapper{ services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA, "A": InstrumentedServiceMakerA,
@ -340,6 +356,8 @@ func TestServiceTerminationGuarantee(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Register a batch of good services // Register a batch of good services
services := map[string]InstrumentingWrapper{ services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA, "A": InstrumentedServiceMakerA,
@ -415,6 +433,8 @@ func TestServiceRetrieval(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
if err := stack.Register(NewNoopService); err != nil { if err := stack.Register(NewNoopService); err != nil {
t.Fatalf("noop service registration failed: %v", err) t.Fatalf("noop service registration failed: %v", err)
} }
@ -450,6 +470,8 @@ func TestProtocolGather(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Register a batch of services with some configured number of protocols // Register a batch of services with some configured number of protocols
services := map[string]struct { services := map[string]struct {
Count int Count int
@ -506,6 +528,8 @@ func TestAPIGather(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Register a batch of services with some configured APIs // Register a batch of services with some configured APIs
calls := make(chan string, 1) calls := make(chan string, 1)
makeAPI := func(result string) *OneMethodAPI { makeAPI := func(result string) *OneMethodAPI {

View file

@ -67,6 +67,7 @@ func TestContextServices(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create protocol stack: %v", err) t.Fatalf("failed to create protocol stack: %v", err)
} }
defer stack.Close()
// Define a verifier that ensures a NoopA is before it and NoopB after // Define a verifier that ensures a NoopA is before it and NoopB after
verifier := func(ctx *ServiceContext) (Service, error) { verifier := func(ctx *ServiceContext) (Service, error) {
var objA *NoopServiceA var objA *NoopServiceA

View file

@ -160,6 +160,12 @@ type SimNode struct {
connected map[discover.NodeID]bool connected map[discover.NodeID]bool
} }
// Close closes the underlaying node.Node to release
// acquired resources.
func (sn *SimNode) Close() error {
return sn.node.Close()
}
// Addr returns the node's discovery address // Addr returns the node's discovery address
func (sn *SimNode) Addr() []byte { func (sn *SimNode) Addr() []byte {
return []byte(sn.Node().String()) return []byte(sn.Node().String())

View file

@ -21,6 +21,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"sync" "sync"
"time" "time"
@ -497,12 +498,18 @@ func (net *Network) Shutdown() {
if err := node.Stop(); err != nil { if err := node.Stop(); err != nil {
log.Warn(fmt.Sprintf("error stopping node %s", node.ID().TerminalString()), "err", err) log.Warn(fmt.Sprintf("error stopping node %s", node.ID().TerminalString()), "err", err)
} }
// If the node has the close method, call it.
if closer, ok := node.Node.(io.Closer); ok {
if err := closer.Close(); err != nil {
log.Warn("Can't close node", "id", node.ID(), "err", err)
}
}
} }
close(net.quitc) close(net.quitc)
} }
//Reset resets all network properties: // Reset resets all network properties:
//emtpies the nodes and the connection list // emtpies the nodes and the connection list
func (net *Network) Reset() { func (net *Network) Reset() {
net.lock.Lock() net.lock.Lock()
defer net.lock.Unlock() defer net.lock.Unlock()