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