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