github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/node/node_test.go (about)

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