github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/simulations/network_test.go (about)

     1  //  Copyright 2018 The go-ethereum Authors
     2  //  Copyright 2019 The go-aigar Authors
     3  //  This file is part of the go-aigar library.
     4  //
     5  //  The go-aigar library is free software: you can redistribute it and/or modify
     6  //  it under the terms of the GNU Lesser General Public License as published by
     7  //  the Free Software Foundation, either version 3 of the License, or
     8  //  (at your option) any later version.
     9  //
    10  //  The go-aigar library is distributed in the hope that it will be useful,
    11  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  //  GNU Lesser General Public License for more details.
    14  //
    15  //  You should have received a copy of the GNU Lesser General Public License
    16  //  along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package simulations
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"reflect"
    26  	"strconv"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/AigarNetwork/aigar/log"
    32  	"github.com/AigarNetwork/aigar/node"
    33  	"github.com/AigarNetwork/aigar/p2p/enode"
    34  	"github.com/AigarNetwork/aigar/p2p/simulations/adapters"
    35  )
    36  
    37  // Tests that a created snapshot with a minimal service only contains the expected connections
    38  // and that a network when loaded with this snapshot only contains those same connections
    39  func TestSnapshot(t *testing.T) {
    40  
    41  	// PART I
    42  	// create snapshot from ring network
    43  
    44  	// this is a minimal service, whose protocol will take exactly one message OR close of connection before quitting
    45  	adapter := adapters.NewSimAdapter(adapters.Services{
    46  		"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
    47  			return NewNoopService(nil), nil
    48  		},
    49  	})
    50  
    51  	// create network
    52  	network := NewNetwork(adapter, &NetworkConfig{
    53  		DefaultService: "noopwoop",
    54  	})
    55  	// \todo consider making a member of network, set to true threadsafe when shutdown
    56  	runningOne := true
    57  	defer func() {
    58  		if runningOne {
    59  			network.Shutdown()
    60  		}
    61  	}()
    62  
    63  	// create and start nodes
    64  	nodeCount := 20
    65  	ids := make([]enode.ID, nodeCount)
    66  	for i := 0; i < nodeCount; i++ {
    67  		conf := adapters.RandomNodeConfig()
    68  		node, err := network.NewNodeWithConfig(conf)
    69  		if err != nil {
    70  			t.Fatalf("error creating node: %s", err)
    71  		}
    72  		if err := network.Start(node.ID()); err != nil {
    73  			t.Fatalf("error starting node: %s", err)
    74  		}
    75  		ids[i] = node.ID()
    76  	}
    77  
    78  	// subscribe to peer events
    79  	evC := make(chan *Event)
    80  	sub := network.Events().Subscribe(evC)
    81  	defer sub.Unsubscribe()
    82  
    83  	// connect nodes in a ring
    84  	// spawn separate thread to avoid deadlock in the event listeners
    85  	go func() {
    86  		for i, id := range ids {
    87  			peerID := ids[(i+1)%len(ids)]
    88  			if err := network.Connect(id, peerID); err != nil {
    89  				t.Fatal(err)
    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 ev := <-evC:
   105  			if ev.Type == EventTypeConn && !ev.Control {
   106  
   107  				// fail on any disconnect
   108  				if !ev.Conn.Up {
   109  					t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
   110  				}
   111  				checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
   112  				checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
   113  				connEventCount--
   114  				log.Debug("ev", "count", connEventCount)
   115  				if connEventCount == 0 {
   116  					break OUTER
   117  				}
   118  			}
   119  		}
   120  	}
   121  
   122  	// create snapshot of current network
   123  	snap, err := network.Snapshot()
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	j, err := json.Marshal(snap)
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	log.Debug("snapshot taken", "nodes", len(snap.Nodes), "conns", len(snap.Conns), "json", string(j))
   132  
   133  	// verify that the snap element numbers check out
   134  	if len(checkIds) != len(snap.Conns) || len(checkIds) != len(snap.Nodes) {
   135  		t.Fatalf("snapshot wrong node,conn counts %d,%d != %d", len(snap.Nodes), len(snap.Conns), len(checkIds))
   136  	}
   137  
   138  	// shut down sim network
   139  	runningOne = false
   140  	sub.Unsubscribe()
   141  	network.Shutdown()
   142  
   143  	// check that we have all the expected connections in the snapshot
   144  	for nodid, nodConns := range checkIds {
   145  		for _, nodConn := range nodConns {
   146  			var match bool
   147  			for _, snapConn := range snap.Conns {
   148  				if snapConn.One == nodid && snapConn.Other == nodConn {
   149  					match = true
   150  					break
   151  				} else if snapConn.Other == nodid && snapConn.One == nodConn {
   152  					match = true
   153  					break
   154  				}
   155  			}
   156  			if !match {
   157  				t.Fatalf("snapshot missing conn %v -> %v", nodid, nodConn)
   158  			}
   159  		}
   160  	}
   161  	log.Info("snapshot checked")
   162  
   163  	// PART II
   164  	// load snapshot and verify that exactly same connections are formed
   165  
   166  	adapter = adapters.NewSimAdapter(adapters.Services{
   167  		"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
   168  			return NewNoopService(nil), nil
   169  		},
   170  	})
   171  	network = NewNetwork(adapter, &NetworkConfig{
   172  		DefaultService: "noopwoop",
   173  	})
   174  	defer func() {
   175  		network.Shutdown()
   176  	}()
   177  
   178  	// subscribe to peer events
   179  	// every node up and conn up event will generate one additional control event
   180  	// therefore multiply the count by two
   181  	evC = make(chan *Event, (len(snap.Conns)*2)+(len(snap.Nodes)*2))
   182  	sub = network.Events().Subscribe(evC)
   183  	defer sub.Unsubscribe()
   184  
   185  	// load the snapshot
   186  	// spawn separate thread to avoid deadlock in the event listeners
   187  	err = network.Load(snap)
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  
   192  	// collect connection events up to expected number
   193  	ctx, cancel = context.WithTimeout(context.TODO(), time.Second*3)
   194  	defer cancel()
   195  
   196  	connEventCount = nodeCount
   197  
   198  OuterTwo:
   199  	for {
   200  		select {
   201  		case <-ctx.Done():
   202  			t.Fatal(ctx.Err())
   203  		case ev := <-evC:
   204  			if ev.Type == EventTypeConn && !ev.Control {
   205  
   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.Services{
   258  				"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, 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.Services{
   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.Services{
   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 creat 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.Services{
   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 creat 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.Services{
   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.Services{
   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.Services{
   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  
   695  	// stop timer to discard setup time pollution
   696  	args := strings.Split(b.Name(), "/")
   697  	nodeCount, err := strconv.ParseInt(args[2], 10, 16)
   698  	if err != nil {
   699  		b.Fatal(err)
   700  	}
   701  
   702  	for i := 0; i < b.N; i++ {
   703  		// this is a minimal service, whose protocol will close a channel upon run of protocol
   704  		// making it possible to bench the time it takes for the service to start and protocol actually to be run
   705  		protoCMap := make(map[enode.ID]map[enode.ID]chan struct{})
   706  		adapter := adapters.NewSimAdapter(adapters.Services{
   707  			"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
   708  				protoCMap[ctx.Config.ID] = make(map[enode.ID]chan struct{})
   709  				svc := NewNoopService(protoCMap[ctx.Config.ID])
   710  				return svc, nil
   711  			},
   712  		})
   713  
   714  		// create network
   715  		network := NewNetwork(adapter, &NetworkConfig{
   716  			DefaultService: "noopwoop",
   717  		})
   718  		defer network.Shutdown()
   719  
   720  		// create and start nodes
   721  		ids := make([]enode.ID, nodeCount)
   722  		for i := 0; i < int(nodeCount); i++ {
   723  			conf := adapters.RandomNodeConfig()
   724  			node, err := network.NewNodeWithConfig(conf)
   725  			if err != nil {
   726  				b.Fatalf("error creating node: %s", err)
   727  			}
   728  			if err := network.Start(node.ID()); err != nil {
   729  				b.Fatalf("error starting node: %s", err)
   730  			}
   731  			ids[i] = node.ID()
   732  		}
   733  
   734  		// ready, set, go
   735  		b.ResetTimer()
   736  
   737  		// connect nodes in a ring
   738  		for i, id := range ids {
   739  			peerID := ids[(i+1)%len(ids)]
   740  			if err := network.Connect(id, peerID); err != nil {
   741  				b.Fatal(err)
   742  			}
   743  		}
   744  
   745  		// wait for all protocols to signal to close down
   746  		ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
   747  		defer cancel()
   748  		for nodid, peers := range protoCMap {
   749  			for peerid, peerC := range peers {
   750  				log.Debug("getting ", "node", nodid, "peer", peerid)
   751  				select {
   752  				case <-ctx.Done():
   753  					b.Fatal(ctx.Err())
   754  				case <-peerC:
   755  				}
   756  			}
   757  		}
   758  	}
   759  }
   760  
   761  func TestNode_UnmarshalJSON(t *testing.T) {
   762  	t.Run(
   763  		"test unmarshal of Node up field",
   764  		func(t *testing.T) {
   765  			runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONUpField())
   766  		},
   767  	)
   768  	t.Run(
   769  		"test unmarshal of Node Config field",
   770  		func(t *testing.T) {
   771  			runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONConfigField())
   772  		},
   773  	)
   774  }
   775  
   776  func runNodeUnmarshalJSON(t *testing.T, tests []nodeUnmarshalTestCase) {
   777  	t.Helper()
   778  	for _, tt := range tests {
   779  		t.Run(tt.name, func(t *testing.T) {
   780  			var got Node
   781  			if err := got.UnmarshalJSON([]byte(tt.marshaled)); err != nil {
   782  				expectErrorMessageToContain(t, err, tt.wantErr)
   783  			}
   784  			expectNodeEquality(t, got, tt.want)
   785  		})
   786  	}
   787  }
   788  
   789  type nodeUnmarshalTestCase struct {
   790  	name      string
   791  	marshaled string
   792  	want      Node
   793  	wantErr   string
   794  }
   795  
   796  func expectErrorMessageToContain(t *testing.T, got error, want string) {
   797  	t.Helper()
   798  	if got == nil && want == "" {
   799  		return
   800  	}
   801  
   802  	if got == nil && want != "" {
   803  		t.Errorf("error was expected, got: nil, want: %v", want)
   804  		return
   805  	}
   806  
   807  	if !strings.Contains(got.Error(), want) {
   808  		t.Errorf(
   809  			"unexpected error message, got  %v, want: %v",
   810  			want,
   811  			got,
   812  		)
   813  	}
   814  }
   815  
   816  func expectNodeEquality(t *testing.T, got Node, want Node) {
   817  	t.Helper()
   818  	if !reflect.DeepEqual(got, want) {
   819  		t.Errorf("Node.UnmarshalJSON() = %v, want %v", got, want)
   820  	}
   821  }
   822  
   823  func casesNodeUnmarshalJSONUpField() []nodeUnmarshalTestCase {
   824  	return []nodeUnmarshalTestCase{
   825  		{
   826  			name:      "empty json",
   827  			marshaled: "{}",
   828  			want: Node{
   829  				up: false,
   830  			},
   831  		},
   832  		{
   833  			name:      "a stopped node",
   834  			marshaled: "{\"up\": false}",
   835  			want: Node{
   836  				up: false,
   837  			},
   838  		},
   839  		{
   840  			name:      "a running node",
   841  			marshaled: "{\"up\": true}",
   842  			want: Node{
   843  				up: true,
   844  			},
   845  		},
   846  		{
   847  			name:      "invalid JSON value on valid key",
   848  			marshaled: "{\"up\": foo}",
   849  			wantErr:   "invalid character",
   850  		},
   851  		{
   852  			name:      "invalid JSON key and value",
   853  			marshaled: "{foo: bar}",
   854  			wantErr:   "invalid character",
   855  		},
   856  		{
   857  			name:      "bool value expected but got something else (string)",
   858  			marshaled: "{\"up\": \"true\"}",
   859  			wantErr:   "cannot unmarshal string into Go struct",
   860  		},
   861  	}
   862  }
   863  
   864  func casesNodeUnmarshalJSONConfigField() []nodeUnmarshalTestCase {
   865  	// Don't do a big fuss around testing, as adapters.NodeConfig should
   866  	// handle it's own serialization. Just do a sanity check.
   867  	return []nodeUnmarshalTestCase{
   868  		{
   869  			name:      "Config field is omitted",
   870  			marshaled: "{}",
   871  			want: Node{
   872  				Config: nil,
   873  			},
   874  		},
   875  		{
   876  			name:      "Config field is nil",
   877  			marshaled: "{\"config\": nil}",
   878  			want: Node{
   879  				Config: nil,
   880  			},
   881  		},
   882  		{
   883  			name:      "a non default Config field",
   884  			marshaled: "{\"config\":{\"name\":\"node_ecdd0\",\"port\":44665}}",
   885  			want: Node{
   886  				Config: &adapters.NodeConfig{
   887  					Name: "node_ecdd0",
   888  					Port: 44665,
   889  				},
   890  			},
   891  		},
   892  	}
   893  }