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