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 }