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 }