github.com/cryptogateway/go-paymex@v0.0.0-20210204174735-96277fb1e602/les/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 les 18 19 import ( 20 "math/rand" 21 "sync/atomic" 22 "testing" 23 "time" 24 25 "github.com/cryptogateway/go-paymex/common/mclock" 26 "github.com/cryptogateway/go-paymex/ethdb" 27 "github.com/cryptogateway/go-paymex/ethdb/memorydb" 28 lpc "github.com/cryptogateway/go-paymex/les/lespay/client" 29 "github.com/cryptogateway/go-paymex/p2p" 30 "github.com/cryptogateway/go-paymex/p2p/enode" 31 "github.com/cryptogateway/go-paymex/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 struct{} 57 preNeg, preNegFail bool 58 vt *lpc.ValueTracker 59 sp *serverPool 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 peer *serverPeer 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 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)) 148 s.sp = newServerPool(s.db, []byte("serverpool:"), s.vt, 0, testQuery, s.clock, s.trusted) 149 s.sp.addSource(s.input) 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 }