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 }