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