github.com/number571/tendermint@v0.34.11-gost/internal/blockchain/v0/pool_test.go (about) 1 package v0 2 3 import ( 4 "fmt" 5 mrand "math/rand" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 "github.com/number571/tendermint/libs/log" 13 tmrand "github.com/number571/tendermint/libs/rand" 14 "github.com/number571/tendermint/types" 15 ) 16 17 func init() { 18 peerTimeout = 2 * time.Second 19 } 20 21 type testPeer struct { 22 id types.NodeID 23 base int64 24 height int64 25 inputChan chan inputData // make sure each peer's data is sequential 26 } 27 28 type inputData struct { 29 t *testing.T 30 pool *BlockPool 31 request BlockRequest 32 } 33 34 func (p testPeer) runInputRoutine() { 35 go func() { 36 for input := range p.inputChan { 37 p.simulateInput(input) 38 } 39 }() 40 } 41 42 // Request desired, pretend like we got the block immediately. 43 func (p testPeer) simulateInput(input inputData) { 44 block := &types.Block{Header: types.Header{Height: input.request.Height}} 45 input.pool.AddBlock(input.request.PeerID, block, 123) 46 // TODO: uncommenting this creates a race which is detected by: 47 // https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856 48 // see: https://github.com/number571/tendermint/issues/3390#issue-418379890 49 // input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height) 50 } 51 52 type testPeers map[types.NodeID]testPeer 53 54 func (ps testPeers) start() { 55 for _, v := range ps { 56 v.runInputRoutine() 57 } 58 } 59 60 func (ps testPeers) stop() { 61 for _, v := range ps { 62 close(v.inputChan) 63 } 64 } 65 66 func makePeers(numPeers int, minHeight, maxHeight int64) testPeers { 67 peers := make(testPeers, numPeers) 68 for i := 0; i < numPeers; i++ { 69 peerID := types.NodeID(tmrand.Str(12)) 70 height := minHeight + mrand.Int63n(maxHeight-minHeight) 71 base := minHeight + int64(i) 72 if base > height { 73 base = height 74 } 75 peers[peerID] = testPeer{peerID, base, height, make(chan inputData, 10)} 76 } 77 return peers 78 } 79 80 func TestBlockPoolBasic(t *testing.T) { 81 start := int64(42) 82 peers := makePeers(10, start+1, 1000) 83 errorsCh := make(chan peerError, 1000) 84 requestsCh := make(chan BlockRequest, 1000) 85 pool := NewBlockPool(start, requestsCh, errorsCh) 86 pool.SetLogger(log.TestingLogger()) 87 88 err := pool.Start() 89 if err != nil { 90 t.Error(err) 91 } 92 93 t.Cleanup(func() { 94 if err := pool.Stop(); err != nil { 95 t.Error(err) 96 } 97 }) 98 99 peers.start() 100 defer peers.stop() 101 102 // Introduce each peer. 103 go func() { 104 for _, peer := range peers { 105 pool.SetPeerRange(peer.id, peer.base, peer.height) 106 } 107 }() 108 109 // Start a goroutine to pull blocks 110 go func() { 111 for { 112 if !pool.IsRunning() { 113 return 114 } 115 first, second := pool.PeekTwoBlocks() 116 if first != nil && second != nil { 117 pool.PopRequest() 118 } else { 119 time.Sleep(1 * time.Second) 120 } 121 } 122 }() 123 124 // Pull from channels 125 for { 126 select { 127 case err := <-errorsCh: 128 t.Error(err) 129 case request := <-requestsCh: 130 t.Logf("Pulled new BlockRequest %v", request) 131 if request.Height == 300 { 132 return // Done! 133 } 134 135 peers[request.PeerID].inputChan <- inputData{t, pool, request} 136 } 137 } 138 } 139 140 func TestBlockPoolTimeout(t *testing.T) { 141 start := int64(42) 142 peers := makePeers(10, start+1, 1000) 143 errorsCh := make(chan peerError, 1000) 144 requestsCh := make(chan BlockRequest, 1000) 145 pool := NewBlockPool(start, requestsCh, errorsCh) 146 pool.SetLogger(log.TestingLogger()) 147 err := pool.Start() 148 if err != nil { 149 t.Error(err) 150 } 151 t.Cleanup(func() { 152 if err := pool.Stop(); err != nil { 153 t.Error(err) 154 } 155 }) 156 157 for _, peer := range peers { 158 t.Logf("Peer %v", peer.id) 159 } 160 161 // Introduce each peer. 162 go func() { 163 for _, peer := range peers { 164 pool.SetPeerRange(peer.id, peer.base, peer.height) 165 } 166 }() 167 168 // Start a goroutine to pull blocks 169 go func() { 170 for { 171 if !pool.IsRunning() { 172 return 173 } 174 first, second := pool.PeekTwoBlocks() 175 if first != nil && second != nil { 176 pool.PopRequest() 177 } else { 178 time.Sleep(1 * time.Second) 179 } 180 } 181 }() 182 183 // Pull from channels 184 counter := 0 185 timedOut := map[types.NodeID]struct{}{} 186 for { 187 select { 188 case err := <-errorsCh: 189 t.Log(err) 190 // consider error to be always timeout here 191 if _, ok := timedOut[err.peerID]; !ok { 192 counter++ 193 if counter == len(peers) { 194 return // Done! 195 } 196 } 197 case request := <-requestsCh: 198 t.Logf("Pulled new BlockRequest %+v", request) 199 } 200 } 201 } 202 203 func TestBlockPoolRemovePeer(t *testing.T) { 204 peers := make(testPeers, 10) 205 for i := 0; i < 10; i++ { 206 peerID := types.NodeID(fmt.Sprintf("%d", i+1)) 207 height := int64(i + 1) 208 peers[peerID] = testPeer{peerID, 0, height, make(chan inputData)} 209 } 210 requestsCh := make(chan BlockRequest) 211 errorsCh := make(chan peerError) 212 213 pool := NewBlockPool(1, requestsCh, errorsCh) 214 pool.SetLogger(log.TestingLogger()) 215 err := pool.Start() 216 require.NoError(t, err) 217 t.Cleanup(func() { 218 if err := pool.Stop(); err != nil { 219 t.Error(err) 220 } 221 }) 222 223 // add peers 224 for peerID, peer := range peers { 225 pool.SetPeerRange(peerID, peer.base, peer.height) 226 } 227 assert.EqualValues(t, 10, pool.MaxPeerHeight()) 228 229 // remove not-existing peer 230 assert.NotPanics(t, func() { pool.RemovePeer(types.NodeID("Superman")) }) 231 232 // remove peer with biggest height 233 pool.RemovePeer(types.NodeID("10")) 234 assert.EqualValues(t, 9, pool.MaxPeerHeight()) 235 236 // remove all peers 237 for peerID := range peers { 238 pool.RemovePeer(peerID) 239 } 240 241 assert.EqualValues(t, 0, pool.MaxPeerHeight()) 242 }