github.com/jimmyx0x/go-ethereum@v1.10.28/les/vflux/client/serverpool_test.go (about)

     1  // Copyright 2020 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 client
    18  
    19  import (
    20  	"math/rand"
    21  	"strconv"
    22  	"sync"
    23  	"sync/atomic"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common/mclock"
    28  	"github.com/ethereum/go-ethereum/ethdb"
    29  	"github.com/ethereum/go-ethereum/ethdb/memorydb"
    30  	"github.com/ethereum/go-ethereum/p2p/enode"
    31  	"github.com/ethereum/go-ethereum/p2p/enr"
    32  )
    33  
    34  const (
    35  	spTestNodes  = 1000
    36  	spTestTarget = 5
    37  	spTestLength = 10000
    38  	spMinTotal   = 40000
    39  	spMaxTotal   = 50000
    40  )
    41  
    42  func testNodeID(i int) enode.ID {
    43  	return enode.ID{42, byte(i % 256), byte(i / 256)}
    44  }
    45  
    46  func testNodeIndex(id enode.ID) int {
    47  	if id[0] != 42 {
    48  		return -1
    49  	}
    50  	return int(id[1]) + int(id[2])*256
    51  }
    52  
    53  type ServerPoolTest struct {
    54  	db                   ethdb.KeyValueStore
    55  	clock                *mclock.Simulated
    56  	quit                 chan chan struct{}
    57  	preNeg, preNegFail   bool
    58  	sp                   *ServerPool
    59  	spi                  enode.Iterator
    60  	input                enode.Iterator
    61  	testNodes            []spTestNode
    62  	trusted              []string
    63  	waitCount, waitEnded int32
    64  
    65  	// preNegLock protects the cycle counter, testNodes list and its connected field
    66  	// (accessed from both the main thread and the preNeg callback)
    67  	preNegLock sync.Mutex
    68  	queryWg    *sync.WaitGroup // a new wait group is created each time the simulation is started
    69  	stopping   bool            // stopping avoid calling queryWg.Add after queryWg.Wait
    70  
    71  	cycle, conn, servedConn  int
    72  	serviceCycles, dialCount int
    73  	disconnect               map[int][]int
    74  }
    75  
    76  type spTestNode struct {
    77  	connectCycles, waitCycles int
    78  	nextConnCycle, totalConn  int
    79  	connected, service        bool
    80  	node                      *enode.Node
    81  }
    82  
    83  func newServerPoolTest(preNeg, preNegFail bool) *ServerPoolTest {
    84  	nodes := make([]*enode.Node, spTestNodes)
    85  	for i := range nodes {
    86  		nodes[i] = enode.SignNull(&enr.Record{}, testNodeID(i))
    87  	}
    88  	return &ServerPoolTest{
    89  		clock:      &mclock.Simulated{},
    90  		db:         memorydb.New(),
    91  		input:      enode.CycleNodes(nodes),
    92  		testNodes:  make([]spTestNode, spTestNodes),
    93  		preNeg:     preNeg,
    94  		preNegFail: preNegFail,
    95  	}
    96  }
    97  
    98  func (s *ServerPoolTest) beginWait() {
    99  	// ensure that dialIterator and the maximal number of pre-neg queries are not all stuck in a waiting state
   100  	for atomic.AddInt32(&s.waitCount, 1) > preNegLimit {
   101  		atomic.AddInt32(&s.waitCount, -1)
   102  		s.clock.Run(time.Second)
   103  	}
   104  }
   105  
   106  func (s *ServerPoolTest) endWait() {
   107  	atomic.AddInt32(&s.waitCount, -1)
   108  	atomic.AddInt32(&s.waitEnded, 1)
   109  }
   110  
   111  func (s *ServerPoolTest) addTrusted(i int) {
   112  	s.trusted = append(s.trusted, enode.SignNull(&enr.Record{}, testNodeID(i)).String())
   113  }
   114  
   115  func (s *ServerPoolTest) start() {
   116  	var testQuery QueryFunc
   117  	s.queryWg = new(sync.WaitGroup)
   118  	if s.preNeg {
   119  		testQuery = func(node *enode.Node) int {
   120  			s.preNegLock.Lock()
   121  			if s.stopping {
   122  				s.preNegLock.Unlock()
   123  				return 0
   124  			}
   125  			s.queryWg.Add(1)
   126  			idx := testNodeIndex(node.ID())
   127  			n := &s.testNodes[idx]
   128  			canConnect := !n.connected && n.connectCycles != 0 && s.cycle >= n.nextConnCycle
   129  			s.preNegLock.Unlock()
   130  			defer s.queryWg.Done()
   131  
   132  			if s.preNegFail {
   133  				// simulate a scenario where UDP queries never work
   134  				s.beginWait()
   135  				s.clock.Sleep(time.Second * 5)
   136  				s.endWait()
   137  				return -1
   138  			}
   139  			switch idx % 3 {
   140  			case 0:
   141  				// pre-neg returns true only if connection is possible
   142  				if canConnect {
   143  					return 1
   144  				}
   145  				return 0
   146  			case 1:
   147  				// pre-neg returns true but connection might still fail
   148  				return 1
   149  			case 2:
   150  				// pre-neg returns true if connection is possible, otherwise timeout (node unresponsive)
   151  				if canConnect {
   152  					return 1
   153  				}
   154  				s.beginWait()
   155  				s.clock.Sleep(time.Second * 5)
   156  				s.endWait()
   157  				return -1
   158  			}
   159  			return -1
   160  		}
   161  	}
   162  
   163  	requestList := make([]RequestInfo, testReqTypes)
   164  	for i := range requestList {
   165  		requestList[i] = RequestInfo{Name: "testreq" + strconv.Itoa(i), InitAmount: 1, InitValue: 1}
   166  	}
   167  
   168  	s.sp, s.spi = NewServerPool(s.db, []byte("sp:"), 0, testQuery, s.clock, s.trusted, requestList)
   169  	s.sp.AddSource(s.input)
   170  	s.sp.validSchemes = enode.ValidSchemesForTesting
   171  	s.sp.unixTime = func() int64 { return int64(s.clock.Now()) / int64(time.Second) }
   172  	s.disconnect = make(map[int][]int)
   173  	s.sp.Start()
   174  	s.quit = make(chan chan struct{})
   175  	go func() {
   176  		last := int32(-1)
   177  		for {
   178  			select {
   179  			case <-time.After(time.Millisecond * 100):
   180  				c := atomic.LoadInt32(&s.waitEnded)
   181  				if c == last {
   182  					// advance clock if test is stuck (might happen in rare cases)
   183  					s.clock.Run(time.Second)
   184  				}
   185  				last = c
   186  			case quit := <-s.quit:
   187  				close(quit)
   188  				return
   189  			}
   190  		}
   191  	}()
   192  }
   193  
   194  func (s *ServerPoolTest) stop() {
   195  	// disable further queries and wait if one is currently running
   196  	s.preNegLock.Lock()
   197  	s.stopping = true
   198  	s.preNegLock.Unlock()
   199  	s.queryWg.Wait()
   200  
   201  	quit := make(chan struct{})
   202  	s.quit <- quit
   203  	<-quit
   204  	s.sp.Stop()
   205  	s.spi.Close()
   206  	s.preNegLock.Lock()
   207  	s.stopping = false
   208  	s.preNegLock.Unlock()
   209  	for i := range s.testNodes {
   210  		n := &s.testNodes[i]
   211  		if n.connected {
   212  			n.totalConn += s.cycle
   213  		}
   214  		n.connected = false
   215  		n.node = nil
   216  		n.nextConnCycle = 0
   217  	}
   218  	s.conn, s.servedConn = 0, 0
   219  }
   220  
   221  func (s *ServerPoolTest) run() {
   222  	for count := spTestLength; count > 0; count-- {
   223  		if dcList := s.disconnect[s.cycle]; dcList != nil {
   224  			for _, idx := range dcList {
   225  				n := &s.testNodes[idx]
   226  				s.sp.UnregisterNode(n.node)
   227  				n.totalConn += s.cycle
   228  				s.preNegLock.Lock()
   229  				n.connected = false
   230  				s.preNegLock.Unlock()
   231  				n.node = nil
   232  				s.conn--
   233  				if n.service {
   234  					s.servedConn--
   235  				}
   236  				n.nextConnCycle = s.cycle + n.waitCycles
   237  			}
   238  			delete(s.disconnect, s.cycle)
   239  		}
   240  		if s.conn < spTestTarget {
   241  			s.dialCount++
   242  			s.beginWait()
   243  			s.spi.Next()
   244  			s.endWait()
   245  			dial := s.spi.Node()
   246  			id := dial.ID()
   247  			idx := testNodeIndex(id)
   248  			n := &s.testNodes[idx]
   249  			if !n.connected && n.connectCycles != 0 && s.cycle >= n.nextConnCycle {
   250  				s.conn++
   251  				if n.service {
   252  					s.servedConn++
   253  				}
   254  				n.totalConn -= s.cycle
   255  				s.preNegLock.Lock()
   256  				n.connected = true
   257  				s.preNegLock.Unlock()
   258  				dc := s.cycle + n.connectCycles
   259  				s.disconnect[dc] = append(s.disconnect[dc], idx)
   260  				n.node = dial
   261  				nv, _ := s.sp.RegisterNode(n.node)
   262  				if n.service {
   263  					nv.Served([]ServedRequest{{ReqType: 0, Amount: 100}}, 0)
   264  				}
   265  			}
   266  		}
   267  		s.serviceCycles += s.servedConn
   268  		s.clock.Run(time.Second)
   269  		s.preNegLock.Lock()
   270  		s.cycle++
   271  		s.preNegLock.Unlock()
   272  	}
   273  }
   274  
   275  func (s *ServerPoolTest) setNodes(count, conn, wait int, service, trusted bool) (res []int) {
   276  	for ; count > 0; count-- {
   277  		idx := rand.Intn(spTestNodes)
   278  		for s.testNodes[idx].connectCycles != 0 || s.testNodes[idx].connected {
   279  			idx = rand.Intn(spTestNodes)
   280  		}
   281  		res = append(res, idx)
   282  		s.preNegLock.Lock()
   283  		s.testNodes[idx] = spTestNode{
   284  			connectCycles: conn,
   285  			waitCycles:    wait,
   286  			service:       service,
   287  		}
   288  		s.preNegLock.Unlock()
   289  		if trusted {
   290  			s.addTrusted(idx)
   291  		}
   292  	}
   293  	return
   294  }
   295  
   296  func (s *ServerPoolTest) resetNodes() {
   297  	for i, n := range s.testNodes {
   298  		if n.connected {
   299  			n.totalConn += s.cycle
   300  			s.sp.UnregisterNode(n.node)
   301  		}
   302  		s.preNegLock.Lock()
   303  		s.testNodes[i] = spTestNode{totalConn: n.totalConn}
   304  		s.preNegLock.Unlock()
   305  	}
   306  	s.conn, s.servedConn = 0, 0
   307  	s.disconnect = make(map[int][]int)
   308  	s.trusted = nil
   309  }
   310  
   311  func (s *ServerPoolTest) checkNodes(t *testing.T, nodes []int) {
   312  	var sum int
   313  	for _, idx := range nodes {
   314  		n := &s.testNodes[idx]
   315  		if n.connected {
   316  			n.totalConn += s.cycle
   317  		}
   318  		sum += n.totalConn
   319  		n.totalConn = 0
   320  		if n.connected {
   321  			n.totalConn -= s.cycle
   322  		}
   323  	}
   324  	if sum < spMinTotal || sum > spMaxTotal {
   325  		t.Errorf("Total connection amount %d outside expected range %d to %d", sum, spMinTotal, spMaxTotal)
   326  	}
   327  }
   328  
   329  func TestServerPool(t *testing.T)               { testServerPool(t, false, false) }
   330  func TestServerPoolWithPreNeg(t *testing.T)     { testServerPool(t, true, false) }
   331  func TestServerPoolWithPreNegFail(t *testing.T) { testServerPool(t, true, true) }
   332  func testServerPool(t *testing.T, preNeg, fail bool) {
   333  	s := newServerPoolTest(preNeg, fail)
   334  	nodes := s.setNodes(100, 200, 200, true, false)
   335  	s.setNodes(100, 20, 20, false, false)
   336  	s.start()
   337  	s.run()
   338  	s.stop()
   339  	s.checkNodes(t, nodes)
   340  }
   341  
   342  func TestServerPoolChangedNodes(t *testing.T)           { testServerPoolChangedNodes(t, false) }
   343  func TestServerPoolChangedNodesWithPreNeg(t *testing.T) { testServerPoolChangedNodes(t, true) }
   344  func testServerPoolChangedNodes(t *testing.T, preNeg bool) {
   345  	s := newServerPoolTest(preNeg, false)
   346  	nodes := s.setNodes(100, 200, 200, true, false)
   347  	s.setNodes(100, 20, 20, false, false)
   348  	s.start()
   349  	s.run()
   350  	s.checkNodes(t, nodes)
   351  	for i := 0; i < 3; i++ {
   352  		s.resetNodes()
   353  		nodes := s.setNodes(100, 200, 200, true, false)
   354  		s.setNodes(100, 20, 20, false, false)
   355  		s.run()
   356  		s.checkNodes(t, nodes)
   357  	}
   358  	s.stop()
   359  }
   360  
   361  func TestServerPoolRestartNoDiscovery(t *testing.T) { testServerPoolRestartNoDiscovery(t, false) }
   362  func TestServerPoolRestartNoDiscoveryWithPreNeg(t *testing.T) {
   363  	testServerPoolRestartNoDiscovery(t, true)
   364  }
   365  func testServerPoolRestartNoDiscovery(t *testing.T, preNeg bool) {
   366  	s := newServerPoolTest(preNeg, false)
   367  	nodes := s.setNodes(100, 200, 200, true, false)
   368  	s.setNodes(100, 20, 20, false, false)
   369  	s.start()
   370  	s.run()
   371  	s.stop()
   372  	s.checkNodes(t, nodes)
   373  	s.input = nil
   374  	s.start()
   375  	s.run()
   376  	s.stop()
   377  	s.checkNodes(t, nodes)
   378  }
   379  
   380  func TestServerPoolTrustedNoDiscovery(t *testing.T) { testServerPoolTrustedNoDiscovery(t, false) }
   381  func TestServerPoolTrustedNoDiscoveryWithPreNeg(t *testing.T) {
   382  	testServerPoolTrustedNoDiscovery(t, true)
   383  }
   384  func testServerPoolTrustedNoDiscovery(t *testing.T, preNeg bool) {
   385  	s := newServerPoolTest(preNeg, false)
   386  	trusted := s.setNodes(200, 200, 200, true, true)
   387  	s.input = nil
   388  	s.start()
   389  	s.run()
   390  	s.stop()
   391  	s.checkNodes(t, trusted)
   392  }