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  }