github.com/ethereum/go-ethereum@v1.14.3/node/node_test.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  	"io"
    23  	"net"
    24  	"net/http"
    25  	"reflect"
    26  	"slices"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/ethereum/go-ethereum/crypto"
    31  	"github.com/ethereum/go-ethereum/ethdb"
    32  	"github.com/ethereum/go-ethereum/p2p"
    33  	"github.com/ethereum/go-ethereum/rpc"
    34  
    35  	"github.com/stretchr/testify/assert"
    36  )
    37  
    38  var (
    39  	testNodeKey, _ = crypto.GenerateKey()
    40  )
    41  
    42  func testNodeConfig() *Config {
    43  	return &Config{
    44  		Name: "test node",
    45  		P2P:  p2p.Config{PrivateKey: testNodeKey},
    46  	}
    47  }
    48  
    49  // Tests that an empty protocol stack can be closed more than once.
    50  func TestNodeCloseMultipleTimes(t *testing.T) {
    51  	stack, err := New(testNodeConfig())
    52  	if err != nil {
    53  		t.Fatalf("failed to create protocol stack: %v", err)
    54  	}
    55  	stack.Close()
    56  
    57  	// Ensure that a stopped node can be stopped again
    58  	for i := 0; i < 3; i++ {
    59  		if err := stack.Close(); err != ErrNodeStopped {
    60  			t.Fatalf("iter %d: stop failure mismatch: have %v, want %v", i, err, ErrNodeStopped)
    61  		}
    62  	}
    63  }
    64  
    65  func TestNodeStartMultipleTimes(t *testing.T) {
    66  	stack, err := New(testNodeConfig())
    67  	if err != nil {
    68  		t.Fatalf("failed to create protocol stack: %v", err)
    69  	}
    70  
    71  	// Ensure that a node can be successfully started, but only once
    72  	if err := stack.Start(); err != nil {
    73  		t.Fatalf("failed to start node: %v", err)
    74  	}
    75  	if err := stack.Start(); err != ErrNodeRunning {
    76  		t.Fatalf("start failure mismatch: have %v, want %v ", err, ErrNodeRunning)
    77  	}
    78  	// Ensure that a node can be stopped, but only once
    79  	if err := stack.Close(); err != nil {
    80  		t.Fatalf("failed to stop node: %v", err)
    81  	}
    82  	if err := stack.Close(); err != ErrNodeStopped {
    83  		t.Fatalf("stop failure mismatch: have %v, want %v ", err, ErrNodeStopped)
    84  	}
    85  }
    86  
    87  // Tests that if the data dir is already in use, an appropriate error is returned.
    88  func TestNodeUsedDataDir(t *testing.T) {
    89  	// Create a temporary folder to use as the data directory
    90  	dir := t.TempDir()
    91  
    92  	// Create a new node based on the data directory
    93  	original, err := New(&Config{DataDir: dir})
    94  	if err != nil {
    95  		t.Fatalf("failed to create original protocol stack: %v", err)
    96  	}
    97  	defer original.Close()
    98  	if err := original.Start(); err != nil {
    99  		t.Fatalf("failed to start original protocol stack: %v", err)
   100  	}
   101  
   102  	// Create a second node based on the same data directory and ensure failure
   103  	_, err = New(&Config{DataDir: dir})
   104  	if err != ErrDatadirUsed {
   105  		t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed)
   106  	}
   107  }
   108  
   109  // Tests whether a Lifecycle can be registered.
   110  func TestLifecycleRegistry_Successful(t *testing.T) {
   111  	stack, err := New(testNodeConfig())
   112  	if err != nil {
   113  		t.Fatalf("failed to create protocol stack: %v", err)
   114  	}
   115  	defer stack.Close()
   116  
   117  	noop := NewNoop()
   118  	stack.RegisterLifecycle(noop)
   119  
   120  	if !slices.Contains(stack.lifecycles, Lifecycle(noop)) {
   121  		t.Fatalf("lifecycle was not properly registered on the node, %v", err)
   122  	}
   123  }
   124  
   125  // Tests whether a service's protocols can be registered properly on the node's p2p server.
   126  func TestRegisterProtocols(t *testing.T) {
   127  	stack, err := New(testNodeConfig())
   128  	if err != nil {
   129  		t.Fatalf("failed to create protocol stack: %v", err)
   130  	}
   131  	defer stack.Close()
   132  
   133  	fs, err := NewFullService(stack)
   134  	if err != nil {
   135  		t.Fatalf("could not create full service: %v", err)
   136  	}
   137  
   138  	for _, protocol := range fs.Protocols() {
   139  		if !containsProtocol(stack.server.Protocols, protocol) {
   140  			t.Fatalf("protocol %v was not successfully registered", protocol)
   141  		}
   142  	}
   143  
   144  	for _, api := range fs.APIs() {
   145  		if !containsAPI(stack.rpcAPIs, api) {
   146  			t.Fatalf("api %v was not successfully registered", api)
   147  		}
   148  	}
   149  }
   150  
   151  // This test checks that open databases are closed with node.
   152  func TestNodeCloseClosesDB(t *testing.T) {
   153  	stack, _ := New(testNodeConfig())
   154  	defer stack.Close()
   155  
   156  	db, err := stack.OpenDatabase("mydb", 0, 0, "", false)
   157  	if err != nil {
   158  		t.Fatal("can't open DB:", err)
   159  	}
   160  	if err = db.Put([]byte{}, []byte{}); err != nil {
   161  		t.Fatal("can't Put on open DB:", err)
   162  	}
   163  
   164  	stack.Close()
   165  	if err = db.Put([]byte{}, []byte{}); err == nil {
   166  		t.Fatal("Put succeeded after node is closed")
   167  	}
   168  }
   169  
   170  // This test checks that OpenDatabase can be used from within a Lifecycle Start method.
   171  func TestNodeOpenDatabaseFromLifecycleStart(t *testing.T) {
   172  	stack, _ := New(testNodeConfig())
   173  	defer stack.Close()
   174  
   175  	var db ethdb.Database
   176  	var err error
   177  	stack.RegisterLifecycle(&InstrumentedService{
   178  		startHook: func() {
   179  			db, err = stack.OpenDatabase("mydb", 0, 0, "", false)
   180  			if err != nil {
   181  				t.Fatal("can't open DB:", err)
   182  			}
   183  		},
   184  		stopHook: func() {
   185  			db.Close()
   186  		},
   187  	})
   188  
   189  	stack.Start()
   190  	stack.Close()
   191  }
   192  
   193  // This test checks that OpenDatabase can be used from within a Lifecycle Stop method.
   194  func TestNodeOpenDatabaseFromLifecycleStop(t *testing.T) {
   195  	stack, _ := New(testNodeConfig())
   196  	defer stack.Close()
   197  
   198  	stack.RegisterLifecycle(&InstrumentedService{
   199  		stopHook: func() {
   200  			db, err := stack.OpenDatabase("mydb", 0, 0, "", false)
   201  			if err != nil {
   202  				t.Fatal("can't open DB:", err)
   203  			}
   204  			db.Close()
   205  		},
   206  	})
   207  
   208  	stack.Start()
   209  	stack.Close()
   210  }
   211  
   212  // Tests that registered Lifecycles get started and stopped correctly.
   213  func TestLifecycleLifeCycle(t *testing.T) {
   214  	stack, _ := New(testNodeConfig())
   215  	defer stack.Close()
   216  
   217  	started := make(map[string]bool)
   218  	stopped := make(map[string]bool)
   219  
   220  	// Create a batch of instrumented services
   221  	lifecycles := map[string]Lifecycle{
   222  		"A": &InstrumentedService{
   223  			startHook: func() { started["A"] = true },
   224  			stopHook:  func() { stopped["A"] = true },
   225  		},
   226  		"B": &InstrumentedService{
   227  			startHook: func() { started["B"] = true },
   228  			stopHook:  func() { stopped["B"] = true },
   229  		},
   230  		"C": &InstrumentedService{
   231  			startHook: func() { started["C"] = true },
   232  			stopHook:  func() { stopped["C"] = true },
   233  		},
   234  	}
   235  	// register lifecycles on node
   236  	for _, lifecycle := range lifecycles {
   237  		stack.RegisterLifecycle(lifecycle)
   238  	}
   239  	// Start the node and check that all services are running
   240  	if err := stack.Start(); err != nil {
   241  		t.Fatalf("failed to start protocol stack: %v", err)
   242  	}
   243  	for id := range lifecycles {
   244  		if !started[id] {
   245  			t.Fatalf("service %s: freshly started service not running", id)
   246  		}
   247  		if stopped[id] {
   248  			t.Fatalf("service %s: freshly started service already stopped", id)
   249  		}
   250  	}
   251  	// Stop the node and check that all services have been stopped
   252  	if err := stack.Close(); err != nil {
   253  		t.Fatalf("failed to stop protocol stack: %v", err)
   254  	}
   255  	for id := range lifecycles {
   256  		if !stopped[id] {
   257  			t.Fatalf("service %s: freshly terminated service still running", id)
   258  		}
   259  	}
   260  }
   261  
   262  // Tests that if a Lifecycle fails to start, all others started before it will be
   263  // shut down.
   264  func TestLifecycleStartupError(t *testing.T) {
   265  	stack, err := New(testNodeConfig())
   266  	if err != nil {
   267  		t.Fatalf("failed to create protocol stack: %v", err)
   268  	}
   269  	defer stack.Close()
   270  
   271  	started := make(map[string]bool)
   272  	stopped := make(map[string]bool)
   273  
   274  	// Create a batch of instrumented services
   275  	lifecycles := map[string]Lifecycle{
   276  		"A": &InstrumentedService{
   277  			startHook: func() { started["A"] = true },
   278  			stopHook:  func() { stopped["A"] = true },
   279  		},
   280  		"B": &InstrumentedService{
   281  			startHook: func() { started["B"] = true },
   282  			stopHook:  func() { stopped["B"] = true },
   283  		},
   284  		"C": &InstrumentedService{
   285  			startHook: func() { started["C"] = true },
   286  			stopHook:  func() { stopped["C"] = true },
   287  		},
   288  	}
   289  	// register lifecycles on node
   290  	for _, lifecycle := range lifecycles {
   291  		stack.RegisterLifecycle(lifecycle)
   292  	}
   293  
   294  	// Register a service that fails to construct itself
   295  	failure := errors.New("fail")
   296  	failer := &InstrumentedService{start: failure}
   297  	stack.RegisterLifecycle(failer)
   298  
   299  	// Start the protocol stack and ensure all started services stop
   300  	if err := stack.Start(); err != failure {
   301  		t.Fatalf("stack startup failure mismatch: have %v, want %v", err, failure)
   302  	}
   303  	for id := range lifecycles {
   304  		if started[id] && !stopped[id] {
   305  			t.Fatalf("service %s: started but not stopped", id)
   306  		}
   307  		delete(started, id)
   308  		delete(stopped, id)
   309  	}
   310  }
   311  
   312  // Tests that even if a registered Lifecycle fails to shut down cleanly, it does
   313  // not influence the rest of the shutdown invocations.
   314  func TestLifecycleTerminationGuarantee(t *testing.T) {
   315  	stack, err := New(testNodeConfig())
   316  	if err != nil {
   317  		t.Fatalf("failed to create protocol stack: %v", err)
   318  	}
   319  	defer stack.Close()
   320  
   321  	started := make(map[string]bool)
   322  	stopped := make(map[string]bool)
   323  
   324  	// Create a batch of instrumented services
   325  	lifecycles := map[string]Lifecycle{
   326  		"A": &InstrumentedService{
   327  			startHook: func() { started["A"] = true },
   328  			stopHook:  func() { stopped["A"] = true },
   329  		},
   330  		"B": &InstrumentedService{
   331  			startHook: func() { started["B"] = true },
   332  			stopHook:  func() { stopped["B"] = true },
   333  		},
   334  		"C": &InstrumentedService{
   335  			startHook: func() { started["C"] = true },
   336  			stopHook:  func() { stopped["C"] = true },
   337  		},
   338  	}
   339  	// register lifecycles on node
   340  	for _, lifecycle := range lifecycles {
   341  		stack.RegisterLifecycle(lifecycle)
   342  	}
   343  
   344  	// Register a service that fails to shot down cleanly
   345  	failure := errors.New("fail")
   346  	failer := &InstrumentedService{stop: failure}
   347  	stack.RegisterLifecycle(failer)
   348  
   349  	// Start the protocol stack, and ensure that a failing shut down terminates all
   350  	// Start the stack and make sure all is online
   351  	if err := stack.Start(); err != nil {
   352  		t.Fatalf("failed to start protocol stack: %v", err)
   353  	}
   354  	for id := range lifecycles {
   355  		if !started[id] {
   356  			t.Fatalf("service %s: service not running", id)
   357  		}
   358  		if stopped[id] {
   359  			t.Fatalf("service %s: service already stopped", id)
   360  		}
   361  	}
   362  	// Stop the stack, verify failure and check all terminations
   363  	err = stack.Close()
   364  	if err, ok := err.(*StopError); !ok {
   365  		t.Fatalf("termination failure mismatch: have %v, want StopError", err)
   366  	} else {
   367  		failer := reflect.TypeOf(&InstrumentedService{})
   368  		if err.Services[failer] != failure {
   369  			t.Fatalf("failer termination failure mismatch: have %v, want %v", err.Services[failer], failure)
   370  		}
   371  		if len(err.Services) != 1 {
   372  			t.Fatalf("failure count mismatch: have %d, want %d", len(err.Services), 1)
   373  		}
   374  	}
   375  	for id := range lifecycles {
   376  		if !stopped[id] {
   377  			t.Fatalf("service %s: service not terminated", id)
   378  		}
   379  		delete(started, id)
   380  		delete(stopped, id)
   381  	}
   382  
   383  	stack.server = &p2p.Server{}
   384  	stack.server.PrivateKey = testNodeKey
   385  }
   386  
   387  // Tests whether a handler can be successfully mounted on the canonical HTTP server
   388  // on the given prefix
   389  func TestRegisterHandler_Successful(t *testing.T) {
   390  	node := createNode(t, 7878, 7979)
   391  	defer node.Close()
   392  	// create and mount handler
   393  	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   394  		w.Write([]byte("success"))
   395  	})
   396  	node.RegisterHandler("test", "/test", handler)
   397  
   398  	// start node
   399  	if err := node.Start(); err != nil {
   400  		t.Fatalf("could not start node: %v", err)
   401  	}
   402  
   403  	// create HTTP request
   404  	httpReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:7878/test", nil)
   405  	if err != nil {
   406  		t.Error("could not issue new http request ", err)
   407  	}
   408  
   409  	// check response
   410  	resp := doHTTPRequest(t, httpReq)
   411  	buf := make([]byte, 7)
   412  	_, err = io.ReadFull(resp.Body, buf)
   413  	if err != nil {
   414  		t.Fatalf("could not read response: %v", err)
   415  	}
   416  	assert.Equal(t, "success", string(buf))
   417  }
   418  
   419  // Tests whether websocket requests can be handled on the same port as a regular http server.
   420  func TestWebsocketHTTPOnSamePort_WebsocketRequest(t *testing.T) {
   421  	node := startHTTP(t, 0, 0)
   422  	defer node.Close()
   423  
   424  	ws := strings.Replace(node.HTTPEndpoint(), "http://", "ws://", 1)
   425  
   426  	if node.WSEndpoint() != ws {
   427  		t.Fatalf("endpoints should be the same")
   428  	}
   429  	if !checkRPC(ws) {
   430  		t.Fatalf("ws request failed")
   431  	}
   432  	if !checkRPC(node.HTTPEndpoint()) {
   433  		t.Fatalf("http request failed")
   434  	}
   435  }
   436  
   437  func TestWebsocketHTTPOnSeparatePort_WSRequest(t *testing.T) {
   438  	// try and get a free port
   439  	listener, err := net.Listen("tcp", "127.0.0.1:0")
   440  	if err != nil {
   441  		t.Fatal("can't listen:", err)
   442  	}
   443  	port := listener.Addr().(*net.TCPAddr).Port
   444  	listener.Close()
   445  
   446  	node := startHTTP(t, 0, port)
   447  	defer node.Close()
   448  
   449  	wsOnHTTP := strings.Replace(node.HTTPEndpoint(), "http://", "ws://", 1)
   450  	ws := fmt.Sprintf("ws://127.0.0.1:%d", port)
   451  
   452  	if node.WSEndpoint() == wsOnHTTP {
   453  		t.Fatalf("endpoints should not be the same")
   454  	}
   455  	// ensure ws endpoint matches the expected endpoint
   456  	if node.WSEndpoint() != ws {
   457  		t.Fatalf("ws endpoint is incorrect: expected %s, got %s", ws, node.WSEndpoint())
   458  	}
   459  
   460  	if !checkRPC(ws) {
   461  		t.Fatalf("ws request failed")
   462  	}
   463  	if !checkRPC(node.HTTPEndpoint()) {
   464  		t.Fatalf("http request failed")
   465  	}
   466  }
   467  
   468  type rpcPrefixTest struct {
   469  	httpPrefix, wsPrefix string
   470  	// These lists paths on which JSON-RPC should be served / not served.
   471  	wantHTTP   []string
   472  	wantNoHTTP []string
   473  	wantWS     []string
   474  	wantNoWS   []string
   475  }
   476  
   477  func TestNodeRPCPrefix(t *testing.T) {
   478  	t.Parallel()
   479  
   480  	tests := []rpcPrefixTest{
   481  		// both off
   482  		{
   483  			httpPrefix: "", wsPrefix: "",
   484  			wantHTTP:   []string{"/", "/?p=1"},
   485  			wantNoHTTP: []string{"/test", "/test?p=1"},
   486  			wantWS:     []string{"/", "/?p=1"},
   487  			wantNoWS:   []string{"/test", "/test?p=1"},
   488  		},
   489  		// only http prefix
   490  		{
   491  			httpPrefix: "/testprefix", wsPrefix: "",
   492  			wantHTTP:   []string{"/testprefix", "/testprefix?p=1", "/testprefix/x", "/testprefix/x?p=1"},
   493  			wantNoHTTP: []string{"/", "/?p=1", "/test", "/test?p=1"},
   494  			wantWS:     []string{"/", "/?p=1"},
   495  			wantNoWS:   []string{"/testprefix", "/testprefix?p=1", "/test", "/test?p=1"},
   496  		},
   497  		// only ws prefix
   498  		{
   499  			httpPrefix: "", wsPrefix: "/testprefix",
   500  			wantHTTP:   []string{"/", "/?p=1"},
   501  			wantNoHTTP: []string{"/testprefix", "/testprefix?p=1", "/test", "/test?p=1"},
   502  			wantWS:     []string{"/testprefix", "/testprefix?p=1", "/testprefix/x", "/testprefix/x?p=1"},
   503  			wantNoWS:   []string{"/", "/?p=1", "/test", "/test?p=1"},
   504  		},
   505  		// both set
   506  		{
   507  			httpPrefix: "/testprefix", wsPrefix: "/testprefix",
   508  			wantHTTP:   []string{"/testprefix", "/testprefix?p=1", "/testprefix/x", "/testprefix/x?p=1"},
   509  			wantNoHTTP: []string{"/", "/?p=1", "/test", "/test?p=1"},
   510  			wantWS:     []string{"/testprefix", "/testprefix?p=1", "/testprefix/x", "/testprefix/x?p=1"},
   511  			wantNoWS:   []string{"/", "/?p=1", "/test", "/test?p=1"},
   512  		},
   513  	}
   514  
   515  	for _, test := range tests {
   516  		test := test
   517  		name := fmt.Sprintf("http=%s ws=%s", test.httpPrefix, test.wsPrefix)
   518  		t.Run(name, func(t *testing.T) {
   519  			cfg := &Config{
   520  				HTTPHost:       "127.0.0.1",
   521  				HTTPPathPrefix: test.httpPrefix,
   522  				WSHost:         "127.0.0.1",
   523  				WSPathPrefix:   test.wsPrefix,
   524  			}
   525  			node, err := New(cfg)
   526  			if err != nil {
   527  				t.Fatal("can't create node:", err)
   528  			}
   529  			defer node.Close()
   530  			if err := node.Start(); err != nil {
   531  				t.Fatal("can't start node:", err)
   532  			}
   533  			test.check(t, node)
   534  		})
   535  	}
   536  }
   537  
   538  func (test rpcPrefixTest) check(t *testing.T, node *Node) {
   539  	t.Helper()
   540  	httpBase := "http://" + node.http.listenAddr()
   541  	wsBase := "ws://" + node.http.listenAddr()
   542  
   543  	if node.WSEndpoint() != wsBase+test.wsPrefix {
   544  		t.Errorf("Error: node has wrong WSEndpoint %q", node.WSEndpoint())
   545  	}
   546  
   547  	for _, path := range test.wantHTTP {
   548  		resp := rpcRequest(t, httpBase+path, testMethod)
   549  		if resp.StatusCode != 200 {
   550  			t.Errorf("Error: %s: bad status code %d, want 200", path, resp.StatusCode)
   551  		}
   552  	}
   553  	for _, path := range test.wantNoHTTP {
   554  		resp := rpcRequest(t, httpBase+path, testMethod)
   555  		if resp.StatusCode != 404 {
   556  			t.Errorf("Error: %s: bad status code %d, want 404", path, resp.StatusCode)
   557  		}
   558  	}
   559  	for _, path := range test.wantWS {
   560  		err := wsRequest(t, wsBase+path)
   561  		if err != nil {
   562  			t.Errorf("Error: %s: WebSocket connection failed: %v", path, err)
   563  		}
   564  	}
   565  	for _, path := range test.wantNoWS {
   566  		err := wsRequest(t, wsBase+path)
   567  		if err == nil {
   568  			t.Errorf("Error: %s: WebSocket connection succeeded for path in wantNoWS", path)
   569  		}
   570  	}
   571  }
   572  
   573  func createNode(t *testing.T, httpPort, wsPort int) *Node {
   574  	conf := &Config{
   575  		HTTPHost:     "127.0.0.1",
   576  		HTTPPort:     httpPort,
   577  		WSHost:       "127.0.0.1",
   578  		WSPort:       wsPort,
   579  		HTTPTimeouts: rpc.DefaultHTTPTimeouts,
   580  	}
   581  	node, err := New(conf)
   582  	if err != nil {
   583  		t.Fatalf("could not create a new node: %v", err)
   584  	}
   585  	return node
   586  }
   587  
   588  func startHTTP(t *testing.T, httpPort, wsPort int) *Node {
   589  	node := createNode(t, httpPort, wsPort)
   590  	err := node.Start()
   591  	if err != nil {
   592  		t.Fatalf("could not start http service on node: %v", err)
   593  	}
   594  
   595  	return node
   596  }
   597  
   598  func doHTTPRequest(t *testing.T, req *http.Request) *http.Response {
   599  	client := http.DefaultClient
   600  	resp, err := client.Do(req)
   601  	if err != nil {
   602  		t.Fatalf("could not issue a GET request to the given endpoint: %v", err)
   603  	}
   604  	t.Cleanup(func() { resp.Body.Close() })
   605  	return resp
   606  }
   607  
   608  func containsProtocol(stackProtocols []p2p.Protocol, protocol p2p.Protocol) bool {
   609  	for _, a := range stackProtocols {
   610  		if reflect.DeepEqual(a, protocol) {
   611  			return true
   612  		}
   613  	}
   614  	return false
   615  }
   616  
   617  func containsAPI(stackAPIs []rpc.API, api rpc.API) bool {
   618  	for _, a := range stackAPIs {
   619  		if reflect.DeepEqual(a, api) {
   620  			return true
   621  		}
   622  	}
   623  	return false
   624  }