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