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