github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/network/networkid_test.go (about) 1 // Copyleft 2018 The susy-graviton Authors 2 // This file is part of the susy-graviton library. 3 // 4 // The susy-graviton 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 susy-graviton library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MSRCHANTABILITY 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 susy-graviton library. If not, see <http://www.gnu.org/licenses/>. 16 17 package network 18 19 import ( 20 "bytes" 21 "context" 22 "flag" 23 "fmt" 24 "math/rand" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/susy-go/susy-graviton/log" 30 "github.com/susy-go/susy-graviton/node" 31 "github.com/susy-go/susy-graviton/p2p" 32 "github.com/susy-go/susy-graviton/p2p/enode" 33 "github.com/susy-go/susy-graviton/p2p/simulations" 34 "github.com/susy-go/susy-graviton/p2p/simulations/adapters" 35 "github.com/susy-go/susy-graviton/rpc" 36 ) 37 38 var ( 39 currentNetworkID int 40 cnt int 41 nodeMap map[int][]enode.ID 42 kademlias map[enode.ID]*Kademlia 43 ) 44 45 const ( 46 NumberOfNets = 4 47 MaxTimeout = 15 * time.Second 48 ) 49 50 func init() { 51 flag.Parse() 52 rand.Seed(time.Now().Unix()) 53 } 54 55 /* 56 Run the network ID test. 57 The test creates one simulations.Network instance, 58 a number of nodes, then connects nodes with each other in this network. 59 60 Each node gets a network ID assigned according to the number of networks. 61 Having more network IDs is just arbitrary in order to exclude 62 false positives. 63 64 Nodes should only connect with other nodes with the same network ID. 65 After the setup phase, the test checks on each node if it has the 66 expected node connections (excluding those not sharing the network ID). 67 */ 68 func TestNetworkID(t *testing.T) { 69 log.Debug("Start test") 70 //arbitrarily set the number of nodes. It could be any number 71 numNodes := 24 72 //the nodeMap maps all nodes (slice value) with the same network ID (key) 73 nodeMap = make(map[int][]enode.ID) 74 //set up the network and connect nodes 75 net, err := setupNetwork(numNodes) 76 if err != nil { 77 t.Fatalf("Error setting up network: %v", err) 78 } 79 //let's sleep to ensure all nodes are connected 80 time.Sleep(1 * time.Second) 81 // shutdown the the network to avoid race conditions 82 // on accessing kademlias global map while network nodes 83 // are accepting messages 84 net.Shutdown() 85 //for each group sharing the same network ID... 86 for _, netIDGroup := range nodeMap { 87 log.Trace("netIDGroup size", "size", len(netIDGroup)) 88 //...check that their size of the kademlia is of the expected size 89 //the assumption is that it should be the size of the group minus 1 (the node itself) 90 for _, node := range netIDGroup { 91 if kademlias[node].addrs.Size() != len(netIDGroup)-1 { 92 t.Fatalf("Kademlia size has not expected peer size. Kademlia size: %d, expected size: %d", kademlias[node].addrs.Size(), len(netIDGroup)-1) 93 } 94 kademlias[node].EachAddr(nil, 0, func(addr *BzzAddr, _ int) bool { 95 found := false 96 for _, nd := range netIDGroup { 97 if bytes.Equal(kademlias[nd].BaseAddr(), addr.Address()) { 98 found = true 99 } 100 } 101 if !found { 102 t.Fatalf("Expected node not found for node %s", node.String()) 103 } 104 return true 105 }) 106 } 107 } 108 log.Info("Test terminated successfully") 109 } 110 111 // setup simulated network with bzz/discovery and pss services. 112 // connects nodes in a circle 113 // if allowRaw is set, omission of builtin pss encryption is enabled (see PssParams) 114 func setupNetwork(numnodes int) (net *simulations.Network, err error) { 115 log.Debug("Setting up network") 116 quitC := make(chan struct{}) 117 errc := make(chan error) 118 nodes := make([]*simulations.Node, numnodes) 119 if numnodes < 16 { 120 return nil, fmt.Errorf("Minimum sixteen nodes in network") 121 } 122 adapter := adapters.NewSimAdapter(newServices()) 123 //create the network 124 net = simulations.NewNetwork(adapter, &simulations.NetworkConfig{ 125 ID: "NetworkIdTestNet", 126 DefaultService: "bzz", 127 }) 128 log.Debug("Creating networks and nodes") 129 130 var connCount int 131 132 //create nodes and connect them to each other 133 for i := 0; i < numnodes; i++ { 134 log.Trace("iteration: ", "i", i) 135 nodeconf := adapters.RandomNodeConfig() 136 nodes[i], err = net.NewNodeWithConfig(nodeconf) 137 if err != nil { 138 return nil, fmt.Errorf("error creating node %d: %v", i, err) 139 } 140 err = net.Start(nodes[i].ID()) 141 if err != nil { 142 return nil, fmt.Errorf("error starting node %d: %v", i, err) 143 } 144 client, err := nodes[i].Client() 145 if err != nil { 146 return nil, fmt.Errorf("create node %d rpc client fail: %v", i, err) 147 } 148 //now setup and start event watching in order to know when we can upload 149 ctx, watchCancel := context.WithTimeout(context.Background(), MaxTimeout) 150 defer watchCancel() 151 watchSubscriptionEvents(ctx, nodes[i].ID(), client, errc, quitC) 152 //on every iteration we connect to all previous ones 153 for k := i - 1; k >= 0; k-- { 154 connCount++ 155 log.Debug(fmt.Sprintf("Connecting node %d with node %d; connection count is %d", i, k, connCount)) 156 err = net.Connect(nodes[i].ID(), nodes[k].ID()) 157 if err != nil { 158 if !strings.Contains(err.Error(), "already connected") { 159 return nil, fmt.Errorf("error connecting nodes: %v", err) 160 } 161 } 162 } 163 } 164 //now wait until the number of expected subscriptions has been finished 165 //`watchSubscriptionEvents` will write with a `nil` value to errc 166 for err := range errc { 167 if err != nil { 168 return nil, err 169 } 170 //`nil` received, decrement count 171 connCount-- 172 log.Trace("count down", "cnt", connCount) 173 //all subscriptions received 174 if connCount == 0 { 175 close(quitC) 176 break 177 } 178 } 179 log.Debug("Network setup phase terminated") 180 return net, nil 181 } 182 183 func newServices() adapters.Services { 184 kademlias = make(map[enode.ID]*Kademlia) 185 kademlia := func(id enode.ID) *Kademlia { 186 if k, ok := kademlias[id]; ok { 187 return k 188 } 189 params := NewKadParams() 190 params.NeighbourhoodSize = 2 191 params.MaxBinSize = 3 192 params.MinBinSize = 1 193 params.MaxRetries = 1000 194 params.RetryExponent = 2 195 params.RetryInterval = 1000000 196 kademlias[id] = NewKademlia(id[:], params) 197 return kademlias[id] 198 } 199 return adapters.Services{ 200 "bzz": func(ctx *adapters.ServiceContext) (node.Service, error) { 201 addr := NewAddr(ctx.Config.Node()) 202 hp := NewHiveParams() 203 hp.Discovery = false 204 cnt++ 205 //assign the network ID 206 currentNetworkID = cnt % NumberOfNets 207 if ok := nodeMap[currentNetworkID]; ok == nil { 208 nodeMap[currentNetworkID] = make([]enode.ID, 0) 209 } 210 //add this node to the group sharing the same network ID 211 nodeMap[currentNetworkID] = append(nodeMap[currentNetworkID], ctx.Config.ID) 212 log.Debug("current network ID:", "id", currentNetworkID) 213 config := &BzzConfig{ 214 OverlayAddr: addr.Over(), 215 UnderlayAddr: addr.Under(), 216 HiveParams: hp, 217 NetworkID: uint64(currentNetworkID), 218 } 219 return NewBzz(config, kademlia(ctx.Config.ID), nil, nil, nil), nil 220 }, 221 } 222 } 223 224 func watchSubscriptionEvents(ctx context.Context, id enode.ID, client *rpc.Client, errc chan error, quitC chan struct{}) { 225 events := make(chan *p2p.PeerEvent) 226 sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents") 227 if err != nil { 228 log.Error(err.Error()) 229 errc <- fmt.Errorf("error getting peer events for node %v: %s", id, err) 230 return 231 } 232 go func() { 233 defer func() { 234 sub.Unsubscribe() 235 log.Trace("watch subscription events: unsubscribe", "id", id) 236 }() 237 238 for { 239 select { 240 case <-quitC: 241 return 242 case <-ctx.Done(): 243 select { 244 case errc <- ctx.Err(): 245 case <-quitC: 246 } 247 return 248 case e := <-events: 249 if e.Type == p2p.PeerEventTypeAdd { 250 errc <- nil 251 } 252 case err := <-sub.Err(): 253 if err != nil { 254 select { 255 case errc <- fmt.Errorf("error getting peer events for node %v: %v", id, err): 256 case <-quitC: 257 } 258 return 259 } 260 } 261 } 262 }() 263 }