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