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