github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/p2p/discv5/sim_test.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package discv5
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"math/rand"
    24  	"net"
    25  	"strconv"
    26  	"sync"
    27  	"sync/atomic"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/SmartMeshFoundation/Spectrum/common"
    32  )
    33  
    34  // In this test, nodes try to randomly resolve each other.
    35  func TestSimRandomResolve(t *testing.T) {
    36  	t.Skip("boring")
    37  	if runWithPlaygroundTime(t) {
    38  		return
    39  	}
    40  
    41  	sim := newSimulation()
    42  	bootnode := sim.launchNode(false)
    43  
    44  	// A new node joins every 10s.
    45  	launcher := time.NewTicker(10 * time.Second)
    46  	go func() {
    47  		for range launcher.C {
    48  			net := sim.launchNode(false)
    49  			go randomResolves(t, sim, net)
    50  			if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil {
    51  				panic(err)
    52  			}
    53  			fmt.Printf("launched @ %v: %x\n", time.Now(), net.Self().ID[:16])
    54  		}
    55  	}()
    56  
    57  	time.Sleep(3 * time.Hour)
    58  	launcher.Stop()
    59  	sim.shutdown()
    60  	sim.printStats()
    61  }
    62  
    63  func TestSimTopics(t *testing.T) {
    64  	t.Skip("NaCl test")
    65  	if runWithPlaygroundTime(t) {
    66  		return
    67  	}
    68  	sim := newSimulation()
    69  	bootnode := sim.launchNode(false)
    70  
    71  	go func() {
    72  		nets := make([]*Network, 1024)
    73  		for i := range nets {
    74  			net := sim.launchNode(false)
    75  			nets[i] = net
    76  			if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil {
    77  				panic(err)
    78  			}
    79  			time.Sleep(time.Second * 5)
    80  		}
    81  
    82  		for i, net := range nets {
    83  			if i < 256 {
    84  				stop := make(chan struct{})
    85  				go net.RegisterTopic(testTopic, stop)
    86  				go func() {
    87  					//time.Sleep(time.Second * 36000)
    88  					time.Sleep(time.Second * 40000)
    89  					close(stop)
    90  				}()
    91  				time.Sleep(time.Millisecond * 100)
    92  			}
    93  			//			time.Sleep(time.Second * 10)
    94  			//time.Sleep(time.Second)
    95  			/*if i%500 == 499 {
    96  				time.Sleep(time.Second * 9501)
    97  			} else {
    98  				time.Sleep(time.Second)
    99  			}*/
   100  		}
   101  	}()
   102  
   103  	// A new node joins every 10s.
   104  	/*	launcher := time.NewTicker(5 * time.Second)
   105  		cnt := 0
   106  		var printNet *Network
   107  		go func() {
   108  			for range launcher.C {
   109  				cnt++
   110  				if cnt <= 1000 {
   111  					log := false //(cnt == 500)
   112  					net := sim.launchNode(log)
   113  					if log {
   114  						printNet = net
   115  					}
   116  					if cnt > 500 {
   117  						go net.RegisterTopic(testTopic, nil)
   118  					}
   119  					if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil {
   120  						panic(err)
   121  					}
   122  				}
   123  				//fmt.Printf("launched @ %v: %x\n", time.Now(), net.Self().ID[:16])
   124  			}
   125  		}()
   126  	*/
   127  	time.Sleep(55000 * time.Second)
   128  	//launcher.Stop()
   129  	sim.shutdown()
   130  	//sim.printStats()
   131  	//printNet.log.printLogs()
   132  }
   133  
   134  /*func testHierarchicalTopics(i int) []Topic {
   135  	digits := strconv.FormatInt(int64(256+i/4), 4)
   136  	res := make([]Topic, 5)
   137  	for i, _ := range res {
   138  		res[i] = Topic("foo" + digits[1:i+1])
   139  	}
   140  	return res
   141  }*/
   142  
   143  func testHierarchicalTopics(i int) []Topic {
   144  	digits := strconv.FormatInt(int64(128+i/8), 2)
   145  	res := make([]Topic, 8)
   146  	for i := range res {
   147  		res[i] = Topic("foo" + digits[1:i+1])
   148  	}
   149  	return res
   150  }
   151  
   152  func TestSimTopicHierarchy(t *testing.T) {
   153  	t.Skip("NaCl test")
   154  	if runWithPlaygroundTime(t) {
   155  		return
   156  	}
   157  	sim := newSimulation()
   158  	bootnode := sim.launchNode(false)
   159  
   160  	go func() {
   161  		nets := make([]*Network, 1024)
   162  		for i := range nets {
   163  			net := sim.launchNode(false)
   164  			nets[i] = net
   165  			if err := net.SetFallbackNodes([]*Node{bootnode.Self()}); err != nil {
   166  				panic(err)
   167  			}
   168  			time.Sleep(time.Second * 5)
   169  		}
   170  
   171  		stop := make(chan struct{})
   172  		for i, net := range nets {
   173  			//if i < 256 {
   174  			for _, topic := range testHierarchicalTopics(i)[:5] {
   175  				//fmt.Println("reg", topic)
   176  				go net.RegisterTopic(topic, stop)
   177  			}
   178  			time.Sleep(time.Millisecond * 100)
   179  			//}
   180  		}
   181  		time.Sleep(time.Second * 90000)
   182  		close(stop)
   183  	}()
   184  
   185  	time.Sleep(100000 * time.Second)
   186  	sim.shutdown()
   187  }
   188  
   189  func randomResolves(t *testing.T, s *simulation, net *Network) {
   190  	randtime := func() time.Duration {
   191  		return time.Duration(rand.Intn(50)+20) * time.Second
   192  	}
   193  	lookup := func(target NodeID) bool {
   194  		result := net.Resolve(target)
   195  		return result != nil && result.ID == target
   196  	}
   197  
   198  	timer := time.NewTimer(randtime())
   199  	for {
   200  		select {
   201  		case <-timer.C:
   202  			target := s.randomNode().Self().ID
   203  			if !lookup(target) {
   204  				t.Errorf("node %x: target %x not found", net.Self().ID[:8], target[:8])
   205  			}
   206  			timer.Reset(randtime())
   207  		case <-net.closed:
   208  			return
   209  		}
   210  	}
   211  }
   212  
   213  type simulation struct {
   214  	mu      sync.RWMutex
   215  	nodes   map[NodeID]*Network
   216  	nodectr uint32
   217  }
   218  
   219  func newSimulation() *simulation {
   220  	return &simulation{nodes: make(map[NodeID]*Network)}
   221  }
   222  
   223  func (s *simulation) shutdown() {
   224  	s.mu.RLock()
   225  	alive := make([]*Network, 0, len(s.nodes))
   226  	for _, n := range s.nodes {
   227  		alive = append(alive, n)
   228  	}
   229  	defer s.mu.RUnlock()
   230  
   231  	for _, n := range alive {
   232  		n.Close()
   233  	}
   234  }
   235  
   236  func (s *simulation) printStats() {
   237  	s.mu.Lock()
   238  	defer s.mu.Unlock()
   239  	fmt.Println("node counter:", s.nodectr)
   240  	fmt.Println("alive nodes:", len(s.nodes))
   241  
   242  	// for _, n := range s.nodes {
   243  	// 	fmt.Printf("%x\n", n.tab.self.ID[:8])
   244  	// 	transport := n.conn.(*simTransport)
   245  	// 	fmt.Println("   joined:", transport.joinTime)
   246  	// 	fmt.Println("   sends:", transport.hashctr)
   247  	// 	fmt.Println("   table size:", n.tab.count)
   248  	// }
   249  
   250  	/*for _, n := range s.nodes {
   251  		fmt.Println()
   252  		fmt.Printf("*** Node %x\n", n.tab.self.ID[:8])
   253  		n.log.printLogs()
   254  	}*/
   255  
   256  }
   257  
   258  func (s *simulation) randomNode() *Network {
   259  	s.mu.Lock()
   260  	defer s.mu.Unlock()
   261  
   262  	n := rand.Intn(len(s.nodes))
   263  	for _, net := range s.nodes {
   264  		if n == 0 {
   265  			return net
   266  		}
   267  		n--
   268  	}
   269  	return nil
   270  }
   271  
   272  func (s *simulation) launchNode(log bool) *Network {
   273  	var (
   274  		num = s.nodectr
   275  		key = newkey()
   276  		id  = PubkeyID(&key.PublicKey)
   277  		ip  = make(net.IP, 4)
   278  	)
   279  	s.nodectr++
   280  	binary.BigEndian.PutUint32(ip, num)
   281  	ip[0] = 10
   282  	addr := &net.UDPAddr{IP: ip, Port: 30303}
   283  
   284  	transport := &simTransport{joinTime: time.Now(), sender: id, senderAddr: addr, sim: s, priv: key}
   285  	net, err := newNetwork(transport, key.PublicKey, nil, "<no database>", nil)
   286  	if err != nil {
   287  		panic("cannot launch new node: " + err.Error())
   288  	}
   289  
   290  	s.mu.Lock()
   291  	s.nodes[id] = net
   292  	s.mu.Unlock()
   293  
   294  	return net
   295  }
   296  
   297  func (s *simulation) dropNode(id NodeID) {
   298  	s.mu.Lock()
   299  	n := s.nodes[id]
   300  	delete(s.nodes, id)
   301  	s.mu.Unlock()
   302  
   303  	n.Close()
   304  }
   305  
   306  type simTransport struct {
   307  	joinTime   time.Time
   308  	sender     NodeID
   309  	senderAddr *net.UDPAddr
   310  	sim        *simulation
   311  	hashctr    uint64
   312  	priv       *ecdsa.PrivateKey
   313  }
   314  
   315  func (st *simTransport) localAddr() *net.UDPAddr {
   316  	return st.senderAddr
   317  }
   318  
   319  func (st *simTransport) Close() {}
   320  
   321  func (st *simTransport) send(remote *Node, ptype nodeEvent, data interface{}) (hash []byte) {
   322  	hash = st.nextHash()
   323  	var raw []byte
   324  	if ptype == pongPacket {
   325  		var err error
   326  		raw, _, err = encodePacket(st.priv, byte(ptype), data)
   327  		if err != nil {
   328  			panic(err)
   329  		}
   330  	}
   331  
   332  	st.sendPacket(remote.ID, ingressPacket{
   333  		remoteID:   st.sender,
   334  		remoteAddr: st.senderAddr,
   335  		hash:       hash,
   336  		ev:         ptype,
   337  		data:       data,
   338  		rawData:    raw,
   339  	})
   340  	return hash
   341  }
   342  
   343  func (st *simTransport) sendPing(remote *Node, remoteAddr *net.UDPAddr, topics []Topic) []byte {
   344  	hash := st.nextHash()
   345  	st.sendPacket(remote.ID, ingressPacket{
   346  		remoteID:   st.sender,
   347  		remoteAddr: st.senderAddr,
   348  		hash:       hash,
   349  		ev:         pingPacket,
   350  		data: &ping{
   351  			Version:    4,
   352  			From:       rpcEndpoint{IP: st.senderAddr.IP, UDP: uint16(st.senderAddr.Port), TCP: 30303},
   353  			To:         rpcEndpoint{IP: remoteAddr.IP, UDP: uint16(remoteAddr.Port), TCP: 30303},
   354  			Expiration: uint64(time.Now().Unix() + int64(expiration)),
   355  			Topics:     topics,
   356  		},
   357  	})
   358  	return hash
   359  }
   360  
   361  func (st *simTransport) sendPong(remote *Node, pingHash []byte) {
   362  	raddr := remote.addr()
   363  
   364  	st.sendPacket(remote.ID, ingressPacket{
   365  		remoteID:   st.sender,
   366  		remoteAddr: st.senderAddr,
   367  		hash:       st.nextHash(),
   368  		ev:         pongPacket,
   369  		data: &pong{
   370  			To:         rpcEndpoint{IP: raddr.IP, UDP: uint16(raddr.Port), TCP: 30303},
   371  			ReplyTok:   pingHash,
   372  			Expiration: uint64(time.Now().Unix() + int64(expiration)),
   373  		},
   374  	})
   375  }
   376  
   377  func (st *simTransport) sendFindnodeHash(remote *Node, target common.Hash) {
   378  	st.sendPacket(remote.ID, ingressPacket{
   379  		remoteID:   st.sender,
   380  		remoteAddr: st.senderAddr,
   381  		hash:       st.nextHash(),
   382  		ev:         findnodeHashPacket,
   383  		data: &findnodeHash{
   384  			Target:     target,
   385  			Expiration: uint64(time.Now().Unix() + int64(expiration)),
   386  		},
   387  	})
   388  }
   389  
   390  func (st *simTransport) sendTopicRegister(remote *Node, topics []Topic, idx int, pong []byte) {
   391  	//fmt.Println("send", topics, pong)
   392  	st.sendPacket(remote.ID, ingressPacket{
   393  		remoteID:   st.sender,
   394  		remoteAddr: st.senderAddr,
   395  		hash:       st.nextHash(),
   396  		ev:         topicRegisterPacket,
   397  		data: &topicRegister{
   398  			Topics: topics,
   399  			Idx:    uint(idx),
   400  			Pong:   pong,
   401  		},
   402  	})
   403  }
   404  
   405  func (st *simTransport) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node) {
   406  	rnodes := make([]rpcNode, len(nodes))
   407  	for i := range nodes {
   408  		rnodes[i] = nodeToRPC(nodes[i])
   409  	}
   410  	st.sendPacket(remote.ID, ingressPacket{
   411  		remoteID:   st.sender,
   412  		remoteAddr: st.senderAddr,
   413  		hash:       st.nextHash(),
   414  		ev:         topicNodesPacket,
   415  		data:       &topicNodes{Echo: queryHash, Nodes: rnodes},
   416  	})
   417  }
   418  
   419  func (st *simTransport) sendNeighbours(remote *Node, nodes []*Node) {
   420  	// TODO: send multiple packets
   421  	rnodes := make([]rpcNode, len(nodes))
   422  	for i := range nodes {
   423  		rnodes[i] = nodeToRPC(nodes[i])
   424  	}
   425  	st.sendPacket(remote.ID, ingressPacket{
   426  		remoteID:   st.sender,
   427  		remoteAddr: st.senderAddr,
   428  		hash:       st.nextHash(),
   429  		ev:         neighborsPacket,
   430  		data: &neighbors{
   431  			Nodes:      rnodes,
   432  			Expiration: uint64(time.Now().Unix() + int64(expiration)),
   433  		},
   434  	})
   435  }
   436  
   437  func (st *simTransport) nextHash() []byte {
   438  	v := atomic.AddUint64(&st.hashctr, 1)
   439  	var hash common.Hash
   440  	binary.BigEndian.PutUint64(hash[:], v)
   441  	return hash[:]
   442  }
   443  
   444  const packetLoss = 0 // 1/1000
   445  
   446  func (st *simTransport) sendPacket(remote NodeID, p ingressPacket) {
   447  	if rand.Int31n(1000) >= packetLoss {
   448  		st.sim.mu.RLock()
   449  		recipient := st.sim.nodes[remote]
   450  		st.sim.mu.RUnlock()
   451  
   452  		time.AfterFunc(200*time.Millisecond, func() {
   453  			recipient.reqReadPacket(p)
   454  		})
   455  	}
   456  }