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