github.com/klaytn/klaytn@v1.12.1/networks/p2p/simulations/network_test.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2017 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from p2p/simulations/network_test.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package simulations
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/klaytn/klaytn/networks/p2p/discover"
    30  	"github.com/klaytn/klaytn/networks/p2p/simulations/adapters"
    31  )
    32  
    33  // TestNetworkSimulation creates a multi-node simulation network with each node
    34  // connected in a ring topology, checks that all nodes successfully handshake
    35  // with each other and that a snapshot fully represents the desired topology
    36  func TestNetworkSimulation(t *testing.T) {
    37  	// create simulation network with 20 testService nodes
    38  	adapter := adapters.NewSimAdapter(adapters.Services{
    39  		"test": newTestService,
    40  	})
    41  	network := NewNetwork(adapter, &NetworkConfig{
    42  		DefaultService: "test",
    43  	})
    44  	defer network.Shutdown()
    45  	nodeCount := 20
    46  	ids := make([]discover.NodeID, nodeCount)
    47  	for i := 0; i < nodeCount; i++ {
    48  		conf := adapters.RandomNodeConfig()
    49  		node, err := network.NewNodeWithConfig(conf)
    50  		if err != nil {
    51  			t.Fatalf("error creating node: %s", err)
    52  		}
    53  		if err := network.Start(node.ID()); err != nil {
    54  			t.Fatalf("error starting node: %s", err)
    55  		}
    56  		ids[i] = node.ID()
    57  	}
    58  
    59  	// perform a check which connects the nodes in a ring (so each node is
    60  	// connected to exactly two peers) and then checks that all nodes
    61  	// performed two handshakes by checking their peerCount
    62  	action := func(_ context.Context) error {
    63  		for i, id := range ids {
    64  			peerID := ids[(i+1)%len(ids)]
    65  			if err := network.Connect(id, peerID); err != nil {
    66  				return err
    67  			}
    68  		}
    69  		return nil
    70  	}
    71  	check := func(ctx context.Context, id discover.NodeID) (bool, error) {
    72  		// check we haven't run out of time
    73  		select {
    74  		case <-ctx.Done():
    75  			return false, ctx.Err()
    76  		default:
    77  		}
    78  
    79  		// get the node
    80  		node := network.GetNode(id)
    81  		if node == nil {
    82  			return false, fmt.Errorf("unknown node: %s", id)
    83  		}
    84  
    85  		// check it has exactly two peers
    86  		client, err := node.Client()
    87  		if err != nil {
    88  			return false, err
    89  		}
    90  		var peerCount int64
    91  		if err := client.CallContext(ctx, &peerCount, "test_peerCount"); err != nil {
    92  			return false, err
    93  		}
    94  		switch {
    95  		case peerCount < 2:
    96  			return false, nil
    97  		case peerCount == 2:
    98  			return true, nil
    99  		default:
   100  			return false, fmt.Errorf("unexpected peerCount: %d", peerCount)
   101  		}
   102  	}
   103  
   104  	timeout := 30 * time.Second
   105  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   106  	defer cancel()
   107  
   108  	// trigger a check every 100ms
   109  	trigger := make(chan discover.NodeID)
   110  	go triggerChecks(ctx, ids, trigger, 100*time.Millisecond)
   111  
   112  	result := NewSimulation(network).Run(ctx, &Step{
   113  		Action:  action,
   114  		Trigger: trigger,
   115  		Expect: &Expectation{
   116  			Nodes: ids,
   117  			Check: check,
   118  		},
   119  	})
   120  	if result.Error != nil {
   121  		t.Fatalf("simulation failed: %s", result.Error)
   122  	}
   123  
   124  	// take a network snapshot and check it contains the correct topology
   125  	snap, err := network.Snapshot()
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	if len(snap.Nodes) != nodeCount {
   130  		t.Fatalf("expected snapshot to contain %d nodes, got %d", nodeCount, len(snap.Nodes))
   131  	}
   132  	if len(snap.Conns) != nodeCount {
   133  		t.Fatalf("expected snapshot to contain %d connections, got %d", nodeCount, len(snap.Conns))
   134  	}
   135  	for i, id := range ids {
   136  		conn := snap.Conns[i]
   137  		if conn.One != id {
   138  			t.Fatalf("expected conn[%d].One to be %s, got %s", i, id, conn.One)
   139  		}
   140  		peerID := ids[(i+1)%len(ids)]
   141  		if conn.Other != peerID {
   142  			t.Fatalf("expected conn[%d].Other to be %s, got %s", i, peerID, conn.Other)
   143  		}
   144  	}
   145  }
   146  
   147  func triggerChecks(ctx context.Context, ids []discover.NodeID, trigger chan discover.NodeID, interval time.Duration) {
   148  	tick := time.NewTicker(interval)
   149  	defer tick.Stop()
   150  	for {
   151  		select {
   152  		case <-tick.C:
   153  			for _, id := range ids {
   154  				select {
   155  				case trigger <- id:
   156  				case <-ctx.Done():
   157  					return
   158  				}
   159  			}
   160  		case <-ctx.Done():
   161  			return
   162  		}
   163  	}
   164  }