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 }