github.com/julesgoullee/go-ethereum@v1.9.7/p2p/simulations/network_test.go (about)

     1  // Copyright 2017 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 simulations
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"reflect"
    25  	"strconv"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/node"
    32  	"github.com/ethereum/go-ethereum/p2p/enode"
    33  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    34  )
    35  
    36  // Tests that a created snapshot with a minimal service only contains the expected connections
    37  // and that a network when loaded with this snapshot only contains those same connections
    38  func TestSnapshot(t *testing.T) {
    39  
    40  	// PART I
    41  	// create snapshot from ring network
    42  
    43  	// this is a minimal service, whose protocol will take exactly one message OR close of connection before quitting
    44  	adapter := adapters.NewSimAdapter(adapters.Services{
    45  		"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
    46  			return NewNoopService(nil), nil
    47  		},
    48  	})
    49  
    50  	// create network
    51  	network := NewNetwork(adapter, &NetworkConfig{
    52  		DefaultService: "noopwoop",
    53  	})
    54  	// \todo consider making a member of network, set to true threadsafe when shutdown
    55  	runningOne := true
    56  	defer func() {
    57  		if runningOne {
    58  			network.Shutdown()
    59  		}
    60  	}()
    61  
    62  	// create and start nodes
    63  	nodeCount := 20
    64  	ids := make([]enode.ID, nodeCount)
    65  	for i := 0; i < nodeCount; i++ {
    66  		conf := adapters.RandomNodeConfig()
    67  		node, err := network.NewNodeWithConfig(conf)
    68  		if err != nil {
    69  			t.Fatalf("error creating node: %s", err)
    70  		}
    71  		if err := network.Start(node.ID()); err != nil {
    72  			t.Fatalf("error starting node: %s", err)
    73  		}
    74  		ids[i] = node.ID()
    75  	}
    76  
    77  	// subscribe to peer events
    78  	evC := make(chan *Event)
    79  	sub := network.Events().Subscribe(evC)
    80  	defer sub.Unsubscribe()
    81  
    82  	// connect nodes in a ring
    83  	// spawn separate thread to avoid deadlock in the event listeners
    84  	go func() {
    85  		for i, id := range ids {
    86  			peerID := ids[(i+1)%len(ids)]
    87  			if err := network.Connect(id, peerID); err != nil {
    88  				t.Fatal(err)
    89  			}
    90  		}
    91  	}()
    92  
    93  	// collect connection events up to expected number
    94  	ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
    95  	defer cancel()
    96  	checkIds := make(map[enode.ID][]enode.ID)
    97  	connEventCount := nodeCount
    98  OUTER:
    99  	for {
   100  		select {
   101  		case <-ctx.Done():
   102  			t.Fatal(ctx.Err())
   103  		case ev := <-evC:
   104  			if ev.Type == EventTypeConn && !ev.Control {
   105  
   106  				// fail on any disconnect
   107  				if !ev.Conn.Up {
   108  					t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
   109  				}
   110  				checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
   111  				checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
   112  				connEventCount--
   113  				log.Debug("ev", "count", connEventCount)
   114  				if connEventCount == 0 {
   115  					break OUTER
   116  				}
   117  			}
   118  		}
   119  	}
   120  
   121  	// create snapshot of current network
   122  	snap, err := network.Snapshot()
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  	j, err := json.Marshal(snap)
   127  	if err != nil {
   128  		t.Fatal(err)
   129  	}
   130  	log.Debug("snapshot taken", "nodes", len(snap.Nodes), "conns", len(snap.Conns), "json", string(j))
   131  
   132  	// verify that the snap element numbers check out
   133  	if len(checkIds) != len(snap.Conns) || len(checkIds) != len(snap.Nodes) {
   134  		t.Fatalf("snapshot wrong node,conn counts %d,%d != %d", len(snap.Nodes), len(snap.Conns), len(checkIds))
   135  	}
   136  
   137  	// shut down sim network
   138  	runningOne = false
   139  	sub.Unsubscribe()
   140  	network.Shutdown()
   141  
   142  	// check that we have all the expected connections in the snapshot
   143  	for nodid, nodConns := range checkIds {
   144  		for _, nodConn := range nodConns {
   145  			var match bool
   146  			for _, snapConn := range snap.Conns {
   147  				if snapConn.One == nodid && snapConn.Other == nodConn {
   148  					match = true
   149  					break
   150  				} else if snapConn.Other == nodid && snapConn.One == nodConn {
   151  					match = true
   152  					break
   153  				}
   154  			}
   155  			if !match {
   156  				t.Fatalf("snapshot missing conn %v -> %v", nodid, nodConn)
   157  			}
   158  		}
   159  	}
   160  	log.Info("snapshot checked")
   161  
   162  	// PART II
   163  	// load snapshot and verify that exactly same connections are formed
   164  
   165  	adapter = adapters.NewSimAdapter(adapters.Services{
   166  		"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
   167  			return NewNoopService(nil), nil
   168  		},
   169  	})
   170  	network = NewNetwork(adapter, &NetworkConfig{
   171  		DefaultService: "noopwoop",
   172  	})
   173  	defer func() {
   174  		network.Shutdown()
   175  	}()
   176  
   177  	// subscribe to peer events
   178  	// every node up and conn up event will generate one additional control event
   179  	// therefore multiply the count by two
   180  	evC = make(chan *Event, (len(snap.Conns)*2)+(len(snap.Nodes)*2))
   181  	sub = network.Events().Subscribe(evC)
   182  	defer sub.Unsubscribe()
   183  
   184  	// load the snapshot
   185  	// spawn separate thread to avoid deadlock in the event listeners
   186  	err = network.Load(snap)
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	// collect connection events up to expected number
   192  	ctx, cancel = context.WithTimeout(context.TODO(), time.Second*3)
   193  	defer cancel()
   194  
   195  	connEventCount = nodeCount
   196  
   197  OuterTwo:
   198  	for {
   199  		select {
   200  		case <-ctx.Done():
   201  			t.Fatal(ctx.Err())
   202  		case ev := <-evC:
   203  			if ev.Type == EventTypeConn && !ev.Control {
   204  
   205  				// fail on any disconnect
   206  				if !ev.Conn.Up {
   207  					t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
   208  				}
   209  				log.Debug("conn", "on", ev.Conn.One, "other", ev.Conn.Other)
   210  				checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
   211  				checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
   212  				connEventCount--
   213  				log.Debug("ev", "count", connEventCount)
   214  				if connEventCount == 0 {
   215  					break OuterTwo
   216  				}
   217  			}
   218  		}
   219  	}
   220  
   221  	// check that we have all expected connections in the network
   222  	for _, snapConn := range snap.Conns {
   223  		var match bool
   224  		for nodid, nodConns := range checkIds {
   225  			for _, nodConn := range nodConns {
   226  				if snapConn.One == nodid && snapConn.Other == nodConn {
   227  					match = true
   228  					break
   229  				} else if snapConn.Other == nodid && snapConn.One == nodConn {
   230  					match = true
   231  					break
   232  				}
   233  			}
   234  		}
   235  		if !match {
   236  			t.Fatalf("network missing conn %v -> %v", snapConn.One, snapConn.Other)
   237  		}
   238  	}
   239  
   240  	// verify that network didn't generate any other additional connection events after the ones we have collected within a reasonable period of time
   241  	ctx, cancel = context.WithTimeout(context.TODO(), time.Second)
   242  	defer cancel()
   243  	select {
   244  	case <-ctx.Done():
   245  	case ev := <-evC:
   246  		if ev.Type == EventTypeConn {
   247  			t.Fatalf("Superfluous conn found %v -> %v", ev.Conn.One, ev.Conn.Other)
   248  		}
   249  	}
   250  
   251  	// This test validates if all connections from the snapshot
   252  	// are created in the network.
   253  	t.Run("conns after load", func(t *testing.T) {
   254  		// Create new network.
   255  		n := NewNetwork(
   256  			adapters.NewSimAdapter(adapters.Services{
   257  				"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
   258  					return NewNoopService(nil), nil
   259  				},
   260  			}),
   261  			&NetworkConfig{
   262  				DefaultService: "noopwoop",
   263  			},
   264  		)
   265  		defer n.Shutdown()
   266  
   267  		// Load the same snapshot.
   268  		err := n.Load(snap)
   269  		if err != nil {
   270  			t.Fatal(err)
   271  		}
   272  
   273  		// Check every connection from the snapshot
   274  		// if it is in the network, too.
   275  		for _, c := range snap.Conns {
   276  			if n.GetConn(c.One, c.Other) == nil {
   277  				t.Errorf("missing connection: %s -> %s", c.One, c.Other)
   278  			}
   279  		}
   280  	})
   281  }
   282  
   283  // TestNetworkSimulation creates a multi-node simulation network with each node
   284  // connected in a ring topology, checks that all nodes successfully handshake
   285  // with each other and that a snapshot fully represents the desired topology
   286  func TestNetworkSimulation(t *testing.T) {
   287  	// create simulation network with 20 testService nodes
   288  	adapter := adapters.NewSimAdapter(adapters.Services{
   289  		"test": newTestService,
   290  	})
   291  	network := NewNetwork(adapter, &NetworkConfig{
   292  		DefaultService: "test",
   293  	})
   294  	defer network.Shutdown()
   295  	nodeCount := 20
   296  	ids := make([]enode.ID, nodeCount)
   297  	for i := 0; i < nodeCount; i++ {
   298  		conf := adapters.RandomNodeConfig()
   299  		node, err := network.NewNodeWithConfig(conf)
   300  		if err != nil {
   301  			t.Fatalf("error creating node: %s", err)
   302  		}
   303  		if err := network.Start(node.ID()); err != nil {
   304  			t.Fatalf("error starting node: %s", err)
   305  		}
   306  		ids[i] = node.ID()
   307  	}
   308  
   309  	// perform a check which connects the nodes in a ring (so each node is
   310  	// connected to exactly two peers) and then checks that all nodes
   311  	// performed two handshakes by checking their peerCount
   312  	action := func(_ context.Context) error {
   313  		for i, id := range ids {
   314  			peerID := ids[(i+1)%len(ids)]
   315  			if err := network.Connect(id, peerID); err != nil {
   316  				return err
   317  			}
   318  		}
   319  		return nil
   320  	}
   321  	check := func(ctx context.Context, id enode.ID) (bool, error) {
   322  		// check we haven't run out of time
   323  		select {
   324  		case <-ctx.Done():
   325  			return false, ctx.Err()
   326  		default:
   327  		}
   328  
   329  		// get the node
   330  		node := network.GetNode(id)
   331  		if node == nil {
   332  			return false, fmt.Errorf("unknown node: %s", id)
   333  		}
   334  
   335  		// check it has exactly two peers
   336  		client, err := node.Client()
   337  		if err != nil {
   338  			return false, err
   339  		}
   340  		var peerCount int64
   341  		if err := client.CallContext(ctx, &peerCount, "test_peerCount"); err != nil {
   342  			return false, err
   343  		}
   344  		switch {
   345  		case peerCount < 2:
   346  			return false, nil
   347  		case peerCount == 2:
   348  			return true, nil
   349  		default:
   350  			return false, fmt.Errorf("unexpected peerCount: %d", peerCount)
   351  		}
   352  	}
   353  
   354  	timeout := 30 * time.Second
   355  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   356  	defer cancel()
   357  
   358  	// trigger a check every 100ms
   359  	trigger := make(chan enode.ID)
   360  	go triggerChecks(ctx, ids, trigger, 100*time.Millisecond)
   361  
   362  	result := NewSimulation(network).Run(ctx, &Step{
   363  		Action:  action,
   364  		Trigger: trigger,
   365  		Expect: &Expectation{
   366  			Nodes: ids,
   367  			Check: check,
   368  		},
   369  	})
   370  	if result.Error != nil {
   371  		t.Fatalf("simulation failed: %s", result.Error)
   372  	}
   373  
   374  	// take a network snapshot and check it contains the correct topology
   375  	snap, err := network.Snapshot()
   376  	if err != nil {
   377  		t.Fatal(err)
   378  	}
   379  	if len(snap.Nodes) != nodeCount {
   380  		t.Fatalf("expected snapshot to contain %d nodes, got %d", nodeCount, len(snap.Nodes))
   381  	}
   382  	if len(snap.Conns) != nodeCount {
   383  		t.Fatalf("expected snapshot to contain %d connections, got %d", nodeCount, len(snap.Conns))
   384  	}
   385  	for i, id := range ids {
   386  		conn := snap.Conns[i]
   387  		if conn.One != id {
   388  			t.Fatalf("expected conn[%d].One to be %s, got %s", i, id, conn.One)
   389  		}
   390  		peerID := ids[(i+1)%len(ids)]
   391  		if conn.Other != peerID {
   392  			t.Fatalf("expected conn[%d].Other to be %s, got %s", i, peerID, conn.Other)
   393  		}
   394  	}
   395  }
   396  
   397  func createTestNodes(count int, network *Network) (nodes []*Node, err error) {
   398  	for i := 0; i < count; i++ {
   399  		nodeConf := adapters.RandomNodeConfig()
   400  		node, err := network.NewNodeWithConfig(nodeConf)
   401  		if err != nil {
   402  			return nil, err
   403  		}
   404  		if err := network.Start(node.ID()); err != nil {
   405  			return nil, err
   406  		}
   407  
   408  		nodes = append(nodes, node)
   409  	}
   410  
   411  	return nodes, nil
   412  }
   413  
   414  func createTestNodesWithProperty(property string, count int, network *Network) (propertyNodes []*Node, err error) {
   415  	for i := 0; i < count; i++ {
   416  		nodeConf := adapters.RandomNodeConfig()
   417  		nodeConf.Properties = append(nodeConf.Properties, property)
   418  
   419  		node, err := network.NewNodeWithConfig(nodeConf)
   420  		if err != nil {
   421  			return nil, err
   422  		}
   423  		if err := network.Start(node.ID()); err != nil {
   424  			return nil, err
   425  		}
   426  
   427  		propertyNodes = append(propertyNodes, node)
   428  	}
   429  
   430  	return propertyNodes, nil
   431  }
   432  
   433  // TestGetNodeIDs creates a set of nodes and attempts to retrieve their IDs,.
   434  // It then tests again whilst excluding a node ID from being returned.
   435  // If a node ID is not returned, or more node IDs than expected are returned, the test fails.
   436  func TestGetNodeIDs(t *testing.T) {
   437  	adapter := adapters.NewSimAdapter(adapters.Services{
   438  		"test": newTestService,
   439  	})
   440  	network := NewNetwork(adapter, &NetworkConfig{
   441  		DefaultService: "test",
   442  	})
   443  	defer network.Shutdown()
   444  
   445  	numNodes := 5
   446  	nodes, err := createTestNodes(numNodes, network)
   447  	if err != nil {
   448  		t.Fatalf("Could not creat test nodes %v", err)
   449  	}
   450  
   451  	gotNodeIDs := network.GetNodeIDs()
   452  	if len(gotNodeIDs) != numNodes {
   453  		t.Fatalf("Expected %d nodes, got %d", numNodes, len(gotNodeIDs))
   454  	}
   455  
   456  	for _, node1 := range nodes {
   457  		match := false
   458  		for _, node2ID := range gotNodeIDs {
   459  			if bytes.Equal(node1.ID().Bytes(), node2ID.Bytes()) {
   460  				match = true
   461  				break
   462  			}
   463  		}
   464  
   465  		if !match {
   466  			t.Fatalf("A created node was not returned by GetNodes(), ID: %s", node1.ID().String())
   467  		}
   468  	}
   469  
   470  	excludeNodeID := nodes[3].ID()
   471  	gotNodeIDsExcl := network.GetNodeIDs(excludeNodeID)
   472  	if len(gotNodeIDsExcl) != numNodes-1 {
   473  		t.Fatalf("Expected one less node ID to be returned")
   474  	}
   475  	for _, nodeID := range gotNodeIDsExcl {
   476  		if bytes.Equal(excludeNodeID.Bytes(), nodeID.Bytes()) {
   477  			t.Fatalf("GetNodeIDs returned the node ID we excluded, ID: %s", nodeID.String())
   478  		}
   479  	}
   480  }
   481  
   482  // TestGetNodes creates a set of nodes and attempts to retrieve them again.
   483  // It then tests again whilst excluding a node from being returned.
   484  // If a node is not returned, or more nodes than expected are returned, the test fails.
   485  func TestGetNodes(t *testing.T) {
   486  	adapter := adapters.NewSimAdapter(adapters.Services{
   487  		"test": newTestService,
   488  	})
   489  	network := NewNetwork(adapter, &NetworkConfig{
   490  		DefaultService: "test",
   491  	})
   492  	defer network.Shutdown()
   493  
   494  	numNodes := 5
   495  	nodes, err := createTestNodes(numNodes, network)
   496  	if err != nil {
   497  		t.Fatalf("Could not creat test nodes %v", err)
   498  	}
   499  
   500  	gotNodes := network.GetNodes()
   501  	if len(gotNodes) != numNodes {
   502  		t.Fatalf("Expected %d nodes, got %d", numNodes, len(gotNodes))
   503  	}
   504  
   505  	for _, node1 := range nodes {
   506  		match := false
   507  		for _, node2 := range gotNodes {
   508  			if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) {
   509  				match = true
   510  				break
   511  			}
   512  		}
   513  
   514  		if !match {
   515  			t.Fatalf("A created node was not returned by GetNodes(), ID: %s", node1.ID().String())
   516  		}
   517  	}
   518  
   519  	excludeNodeID := nodes[3].ID()
   520  	gotNodesExcl := network.GetNodes(excludeNodeID)
   521  	if len(gotNodesExcl) != numNodes-1 {
   522  		t.Fatalf("Expected one less node to be returned")
   523  	}
   524  	for _, node := range gotNodesExcl {
   525  		if bytes.Equal(excludeNodeID.Bytes(), node.ID().Bytes()) {
   526  			t.Fatalf("GetNodes returned the node we excluded, ID: %s", node.ID().String())
   527  		}
   528  	}
   529  }
   530  
   531  // TestGetNodesByID creates a set of nodes and attempts to retrieve a subset of them by ID
   532  // If a node is not returned, or more nodes than expected are returned, the test fails.
   533  func TestGetNodesByID(t *testing.T) {
   534  	adapter := adapters.NewSimAdapter(adapters.Services{
   535  		"test": newTestService,
   536  	})
   537  	network := NewNetwork(adapter, &NetworkConfig{
   538  		DefaultService: "test",
   539  	})
   540  	defer network.Shutdown()
   541  
   542  	numNodes := 5
   543  	nodes, err := createTestNodes(numNodes, network)
   544  	if err != nil {
   545  		t.Fatalf("Could not create test nodes: %v", err)
   546  	}
   547  
   548  	numSubsetNodes := 2
   549  	subsetNodes := nodes[0:numSubsetNodes]
   550  	var subsetNodeIDs []enode.ID
   551  	for _, node := range subsetNodes {
   552  		subsetNodeIDs = append(subsetNodeIDs, node.ID())
   553  	}
   554  
   555  	gotNodesByID := network.GetNodesByID(subsetNodeIDs)
   556  	if len(gotNodesByID) != numSubsetNodes {
   557  		t.Fatalf("Expected %d nodes, got %d", numSubsetNodes, len(gotNodesByID))
   558  	}
   559  
   560  	for _, node1 := range subsetNodes {
   561  		match := false
   562  		for _, node2 := range gotNodesByID {
   563  			if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) {
   564  				match = true
   565  				break
   566  			}
   567  		}
   568  
   569  		if !match {
   570  			t.Fatalf("A created node was not returned by GetNodesByID(), ID: %s", node1.ID().String())
   571  		}
   572  	}
   573  }
   574  
   575  // TestGetNodesByProperty creates a subset of nodes with a property assigned.
   576  // GetNodesByProperty is then checked for correctness by comparing the nodes returned to those initially created.
   577  // If a node with a property is not found, or more nodes than expected are returned, the test fails.
   578  func TestGetNodesByProperty(t *testing.T) {
   579  	adapter := adapters.NewSimAdapter(adapters.Services{
   580  		"test": newTestService,
   581  	})
   582  	network := NewNetwork(adapter, &NetworkConfig{
   583  		DefaultService: "test",
   584  	})
   585  	defer network.Shutdown()
   586  
   587  	numNodes := 3
   588  	_, err := createTestNodes(numNodes, network)
   589  	if err != nil {
   590  		t.Fatalf("Failed to create nodes: %v", err)
   591  	}
   592  
   593  	numPropertyNodes := 3
   594  	propertyTest := "test"
   595  	propertyNodes, err := createTestNodesWithProperty(propertyTest, numPropertyNodes, network)
   596  	if err != nil {
   597  		t.Fatalf("Failed to create nodes with property: %v", err)
   598  	}
   599  
   600  	gotNodesByProperty := network.GetNodesByProperty(propertyTest)
   601  	if len(gotNodesByProperty) != numPropertyNodes {
   602  		t.Fatalf("Expected %d nodes with a property, got %d", numPropertyNodes, len(gotNodesByProperty))
   603  	}
   604  
   605  	for _, node1 := range propertyNodes {
   606  		match := false
   607  		for _, node2 := range gotNodesByProperty {
   608  			if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) {
   609  				match = true
   610  				break
   611  			}
   612  		}
   613  
   614  		if !match {
   615  			t.Fatalf("A created node with property was not returned by GetNodesByProperty(), ID: %s", node1.ID().String())
   616  		}
   617  	}
   618  }
   619  
   620  // TestGetNodeIDsByProperty creates a subset of nodes with a property assigned.
   621  // GetNodeIDsByProperty is then checked for correctness by comparing the node IDs returned to those initially created.
   622  // If a node ID with a property is not found, or more nodes IDs than expected are returned, the test fails.
   623  func TestGetNodeIDsByProperty(t *testing.T) {
   624  	adapter := adapters.NewSimAdapter(adapters.Services{
   625  		"test": newTestService,
   626  	})
   627  	network := NewNetwork(adapter, &NetworkConfig{
   628  		DefaultService: "test",
   629  	})
   630  	defer network.Shutdown()
   631  
   632  	numNodes := 3
   633  	_, err := createTestNodes(numNodes, network)
   634  	if err != nil {
   635  		t.Fatalf("Failed to create nodes: %v", err)
   636  	}
   637  
   638  	numPropertyNodes := 3
   639  	propertyTest := "test"
   640  	propertyNodes, err := createTestNodesWithProperty(propertyTest, numPropertyNodes, network)
   641  	if err != nil {
   642  		t.Fatalf("Failed to created nodes with property: %v", err)
   643  	}
   644  
   645  	gotNodeIDsByProperty := network.GetNodeIDsByProperty(propertyTest)
   646  	if len(gotNodeIDsByProperty) != numPropertyNodes {
   647  		t.Fatalf("Expected %d nodes with a property, got %d", numPropertyNodes, len(gotNodeIDsByProperty))
   648  	}
   649  
   650  	for _, node1 := range propertyNodes {
   651  		match := false
   652  		id1 := node1.ID()
   653  		for _, id2 := range gotNodeIDsByProperty {
   654  			if bytes.Equal(id1.Bytes(), id2.Bytes()) {
   655  				match = true
   656  				break
   657  			}
   658  		}
   659  
   660  		if !match {
   661  			t.Fatalf("Not all nodes IDs were returned by GetNodeIDsByProperty(), ID: %s", id1.String())
   662  		}
   663  	}
   664  }
   665  
   666  func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, interval time.Duration) {
   667  	tick := time.NewTicker(interval)
   668  	defer tick.Stop()
   669  	for {
   670  		select {
   671  		case <-tick.C:
   672  			for _, id := range ids {
   673  				select {
   674  				case trigger <- id:
   675  				case <-ctx.Done():
   676  					return
   677  				}
   678  			}
   679  		case <-ctx.Done():
   680  			return
   681  		}
   682  	}
   683  }
   684  
   685  // \todo: refactor to implement shapshots
   686  // and connect configuration methods once these are moved from
   687  // swarm/network/simulations/connect.go
   688  func BenchmarkMinimalService(b *testing.B) {
   689  	b.Run("ring/32", benchmarkMinimalServiceTmp)
   690  }
   691  
   692  func benchmarkMinimalServiceTmp(b *testing.B) {
   693  
   694  	// stop timer to discard setup time pollution
   695  	args := strings.Split(b.Name(), "/")
   696  	nodeCount, err := strconv.ParseInt(args[2], 10, 16)
   697  	if err != nil {
   698  		b.Fatal(err)
   699  	}
   700  
   701  	for i := 0; i < b.N; i++ {
   702  		// this is a minimal service, whose protocol will close a channel upon run of protocol
   703  		// making it possible to bench the time it takes for the service to start and protocol actually to be run
   704  		protoCMap := make(map[enode.ID]map[enode.ID]chan struct{})
   705  		adapter := adapters.NewSimAdapter(adapters.Services{
   706  			"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
   707  				protoCMap[ctx.Config.ID] = make(map[enode.ID]chan struct{})
   708  				svc := NewNoopService(protoCMap[ctx.Config.ID])
   709  				return svc, nil
   710  			},
   711  		})
   712  
   713  		// create network
   714  		network := NewNetwork(adapter, &NetworkConfig{
   715  			DefaultService: "noopwoop",
   716  		})
   717  		defer network.Shutdown()
   718  
   719  		// create and start nodes
   720  		ids := make([]enode.ID, nodeCount)
   721  		for i := 0; i < int(nodeCount); i++ {
   722  			conf := adapters.RandomNodeConfig()
   723  			node, err := network.NewNodeWithConfig(conf)
   724  			if err != nil {
   725  				b.Fatalf("error creating node: %s", err)
   726  			}
   727  			if err := network.Start(node.ID()); err != nil {
   728  				b.Fatalf("error starting node: %s", err)
   729  			}
   730  			ids[i] = node.ID()
   731  		}
   732  
   733  		// ready, set, go
   734  		b.ResetTimer()
   735  
   736  		// connect nodes in a ring
   737  		for i, id := range ids {
   738  			peerID := ids[(i+1)%len(ids)]
   739  			if err := network.Connect(id, peerID); err != nil {
   740  				b.Fatal(err)
   741  			}
   742  		}
   743  
   744  		// wait for all protocols to signal to close down
   745  		ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
   746  		defer cancel()
   747  		for nodid, peers := range protoCMap {
   748  			for peerid, peerC := range peers {
   749  				log.Debug("getting ", "node", nodid, "peer", peerid)
   750  				select {
   751  				case <-ctx.Done():
   752  					b.Fatal(ctx.Err())
   753  				case <-peerC:
   754  				}
   755  			}
   756  		}
   757  	}
   758  }
   759  
   760  func TestNode_UnmarshalJSON(t *testing.T) {
   761  	t.Run(
   762  		"test unmarshal of Node up field",
   763  		func(t *testing.T) {
   764  			runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONUpField())
   765  		},
   766  	)
   767  	t.Run(
   768  		"test unmarshal of Node Config field",
   769  		func(t *testing.T) {
   770  			runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONConfigField())
   771  		},
   772  	)
   773  }
   774  
   775  func runNodeUnmarshalJSON(t *testing.T, tests []nodeUnmarshalTestCase) {
   776  	t.Helper()
   777  	for _, tt := range tests {
   778  		t.Run(tt.name, func(t *testing.T) {
   779  			var got Node
   780  			if err := got.UnmarshalJSON([]byte(tt.marshaled)); err != nil {
   781  				expectErrorMessageToContain(t, err, tt.wantErr)
   782  			}
   783  			expectNodeEquality(t, got, tt.want)
   784  		})
   785  	}
   786  }
   787  
   788  type nodeUnmarshalTestCase struct {
   789  	name      string
   790  	marshaled string
   791  	want      Node
   792  	wantErr   string
   793  }
   794  
   795  func expectErrorMessageToContain(t *testing.T, got error, want string) {
   796  	t.Helper()
   797  	if got == nil && want == "" {
   798  		return
   799  	}
   800  
   801  	if got == nil && want != "" {
   802  		t.Errorf("error was expected, got: nil, want: %v", want)
   803  		return
   804  	}
   805  
   806  	if !strings.Contains(got.Error(), want) {
   807  		t.Errorf(
   808  			"unexpected error message, got  %v, want: %v",
   809  			want,
   810  			got,
   811  		)
   812  	}
   813  }
   814  
   815  func expectNodeEquality(t *testing.T, got Node, want Node) {
   816  	t.Helper()
   817  	if !reflect.DeepEqual(got, want) {
   818  		t.Errorf("Node.UnmarshalJSON() = %v, want %v", got, want)
   819  	}
   820  }
   821  
   822  func casesNodeUnmarshalJSONUpField() []nodeUnmarshalTestCase {
   823  	return []nodeUnmarshalTestCase{
   824  		{
   825  			name:      "empty json",
   826  			marshaled: "{}",
   827  			want: Node{
   828  				up: false,
   829  			},
   830  		},
   831  		{
   832  			name:      "a stopped node",
   833  			marshaled: "{\"up\": false}",
   834  			want: Node{
   835  				up: false,
   836  			},
   837  		},
   838  		{
   839  			name:      "a running node",
   840  			marshaled: "{\"up\": true}",
   841  			want: Node{
   842  				up: true,
   843  			},
   844  		},
   845  		{
   846  			name:      "invalid JSON value on valid key",
   847  			marshaled: "{\"up\": foo}",
   848  			wantErr:   "invalid character",
   849  		},
   850  		{
   851  			name:      "invalid JSON key and value",
   852  			marshaled: "{foo: bar}",
   853  			wantErr:   "invalid character",
   854  		},
   855  		{
   856  			name:      "bool value expected but got something else (string)",
   857  			marshaled: "{\"up\": \"true\"}",
   858  			wantErr:   "cannot unmarshal string into Go struct",
   859  		},
   860  	}
   861  }
   862  
   863  func casesNodeUnmarshalJSONConfigField() []nodeUnmarshalTestCase {
   864  	// Don't do a big fuss around testing, as adapters.NodeConfig should
   865  	// handle it's own serialization. Just do a sanity check.
   866  	return []nodeUnmarshalTestCase{
   867  		{
   868  			name:      "Config field is omitted",
   869  			marshaled: "{}",
   870  			want: Node{
   871  				Config: nil,
   872  			},
   873  		},
   874  		{
   875  			name:      "Config field is nil",
   876  			marshaled: "{\"config\": nil}",
   877  			want: Node{
   878  				Config: nil,
   879  			},
   880  		},
   881  		{
   882  			name:      "a non default Config field",
   883  			marshaled: "{\"config\":{\"name\":\"node_ecdd0\",\"port\":44665}}",
   884  			want: Node{
   885  				Config: &adapters.NodeConfig{
   886  					Name: "node_ecdd0",
   887  					Port: 44665,
   888  				},
   889  			},
   890  		},
   891  	}
   892  }