github.com/gnattishness/bazel-go-ethereum@v0.0.0-20190929123618-7022a154f56d/les/clientpool_test.go (about) 1 // Copyright 2019 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 "fmt" 21 "math/rand" 22 "testing" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common/mclock" 26 "github.com/ethereum/go-ethereum/core/rawdb" 27 "github.com/ethereum/go-ethereum/p2p/enode" 28 ) 29 30 func TestClientPoolL10C100Free(t *testing.T) { 31 testClientPool(t, 10, 100, 0, true) 32 } 33 34 func TestClientPoolL40C200Free(t *testing.T) { 35 testClientPool(t, 40, 200, 0, true) 36 } 37 38 func TestClientPoolL100C300Free(t *testing.T) { 39 testClientPool(t, 100, 300, 0, true) 40 } 41 42 func TestClientPoolL10C100P4(t *testing.T) { 43 testClientPool(t, 10, 100, 4, false) 44 } 45 46 func TestClientPoolL40C200P30(t *testing.T) { 47 testClientPool(t, 40, 200, 30, false) 48 } 49 50 func TestClientPoolL100C300P20(t *testing.T) { 51 testClientPool(t, 100, 300, 20, false) 52 } 53 54 const testClientPoolTicks = 500000 55 56 type poolTestPeer int 57 58 func (i poolTestPeer) ID() enode.ID { 59 return enode.ID{byte(i % 256), byte(i >> 8)} 60 } 61 62 func (i poolTestPeer) freeClientId() string { 63 return fmt.Sprintf("addr #%d", i) 64 } 65 66 func (i poolTestPeer) updateCapacity(uint64) {} 67 68 func testClientPool(t *testing.T, connLimit, clientCount, paidCount int, randomDisconnect bool) { 69 rand.Seed(time.Now().UnixNano()) 70 var ( 71 clock mclock.Simulated 72 db = rawdb.NewMemoryDatabase() 73 connected = make([]bool, clientCount) 74 connTicks = make([]int, clientCount) 75 disconnCh = make(chan int, clientCount) 76 disconnFn = func(id enode.ID) { 77 disconnCh <- int(id[0]) + int(id[1])<<8 78 } 79 pool = newClientPool(db, 1, 10000, &clock, disconnFn) 80 ) 81 pool.setLimits(connLimit, uint64(connLimit)) 82 pool.setPriceFactors(priceFactors{1, 0, 1}, priceFactors{1, 0, 1}) 83 84 // pool should accept new peers up to its connected limit 85 for i := 0; i < connLimit; i++ { 86 if pool.connect(poolTestPeer(i), 0) { 87 connected[i] = true 88 } else { 89 t.Fatalf("Test peer #%d rejected", i) 90 } 91 } 92 // since all accepted peers are new and should not be kicked out, the next one should be rejected 93 if pool.connect(poolTestPeer(connLimit), 0) { 94 connected[connLimit] = true 95 t.Fatalf("Peer accepted over connected limit") 96 } 97 98 // randomly connect and disconnect peers, expect to have a similar total connection time at the end 99 for tickCounter := 0; tickCounter < testClientPoolTicks; tickCounter++ { 100 clock.Run(1 * time.Second) 101 //time.Sleep(time.Microsecond * 100) 102 103 if tickCounter == testClientPoolTicks/4 { 104 // give a positive balance to some of the peers 105 amount := uint64(testClientPoolTicks / 2 * 1000000000) // enough for half of the simulation period 106 for i := 0; i < paidCount; i++ { 107 pool.addBalance(poolTestPeer(i).ID(), amount, false) 108 } 109 } 110 111 i := rand.Intn(clientCount) 112 if connected[i] { 113 if randomDisconnect { 114 pool.disconnect(poolTestPeer(i)) 115 connected[i] = false 116 connTicks[i] += tickCounter 117 } 118 } else { 119 if pool.connect(poolTestPeer(i), 0) { 120 connected[i] = true 121 connTicks[i] -= tickCounter 122 } 123 } 124 pollDisconnects: 125 for { 126 select { 127 case i := <-disconnCh: 128 pool.disconnect(poolTestPeer(i)) 129 if connected[i] { 130 connTicks[i] += tickCounter 131 connected[i] = false 132 } 133 default: 134 break pollDisconnects 135 } 136 } 137 } 138 139 expTicks := testClientPoolTicks/2*connLimit/clientCount + testClientPoolTicks/2*(connLimit-paidCount)/(clientCount-paidCount) 140 expMin := expTicks - expTicks/10 141 expMax := expTicks + expTicks/10 142 paidTicks := testClientPoolTicks/2*connLimit/clientCount + testClientPoolTicks/2 143 paidMin := paidTicks - paidTicks/10 144 paidMax := paidTicks + paidTicks/10 145 146 // check if the total connected time of peers are all in the expected range 147 for i, c := range connected { 148 if c { 149 connTicks[i] += testClientPoolTicks 150 } 151 min, max := expMin, expMax 152 if i < paidCount { 153 // expect a higher amount for clients with a positive balance 154 min, max = paidMin, paidMax 155 } 156 if connTicks[i] < min || connTicks[i] > max { 157 t.Errorf("Total connected time of test node #%d (%d) outside expected range (%d to %d)", i, connTicks[i], min, max) 158 } 159 } 160 161 // a previously unknown peer should be accepted now 162 if !pool.connect(poolTestPeer(54321), 0) { 163 t.Fatalf("Previously unknown peer rejected") 164 } 165 166 // close and restart pool 167 pool.stop() 168 pool = newClientPool(db, 1, 10000, &clock, func(id enode.ID) {}) 169 pool.setLimits(connLimit, uint64(connLimit)) 170 171 // try connecting all known peers (connLimit should be filled up) 172 for i := 0; i < clientCount; i++ { 173 pool.connect(poolTestPeer(i), 0) 174 } 175 // expect pool to remember known nodes and kick out one of them to accept a new one 176 if !pool.connect(poolTestPeer(54322), 0) { 177 t.Errorf("Previously unknown peer rejected after restarting pool") 178 } 179 pool.stop() 180 }