github.com/okex/exchain@v1.8.0/libs/tendermint/blockchain/v1/pool.go (about) 1 package v1 2 3 import ( 4 "sort" 5 6 "github.com/okex/exchain/libs/tendermint/libs/log" 7 "github.com/okex/exchain/libs/tendermint/p2p" 8 "github.com/okex/exchain/libs/tendermint/types" 9 ) 10 11 // BlockPool keeps track of the fast sync peers, block requests and block responses. 12 type BlockPool struct { 13 logger log.Logger 14 // Set of peers that have sent status responses, with height bigger than pool.Height 15 peers map[p2p.ID]*BpPeer 16 // Set of block heights and the corresponding peers from where a block response is expected or has been received. 17 blocks map[int64]p2p.ID 18 19 plannedRequests map[int64]struct{} // list of blocks to be assigned peers for blockRequest 20 nextRequestHeight int64 // next height to be added to plannedRequests 21 22 Height int64 // height of next block to execute 23 MaxPeerHeight int64 // maximum height of all peers 24 toBcR bcReactor 25 } 26 27 // NewBlockPool creates a new BlockPool. 28 func NewBlockPool(height int64, toBcR bcReactor) *BlockPool { 29 return &BlockPool{ 30 Height: height, 31 MaxPeerHeight: 0, 32 peers: make(map[p2p.ID]*BpPeer), 33 blocks: make(map[int64]p2p.ID), 34 plannedRequests: make(map[int64]struct{}), 35 nextRequestHeight: height, 36 toBcR: toBcR, 37 } 38 } 39 40 // SetLogger sets the logger of the pool. 41 func (pool *BlockPool) SetLogger(l log.Logger) { 42 pool.logger = l 43 } 44 45 // ReachedMaxHeight check if the pool has reached the maximum peer height. 46 func (pool *BlockPool) ReachedMaxHeight() bool { 47 return pool.Height >= pool.MaxPeerHeight 48 } 49 50 func (pool *BlockPool) rescheduleRequest(peerID p2p.ID, height int64) { 51 pool.logger.Info("reschedule requests made to peer for height ", "peerID", peerID, "height", height) 52 pool.plannedRequests[height] = struct{}{} 53 delete(pool.blocks, height) 54 pool.peers[peerID].RemoveBlock(height) 55 } 56 57 // Updates the pool's max height. If no peers are left MaxPeerHeight is set to 0. 58 func (pool *BlockPool) updateMaxPeerHeight() { 59 var newMax int64 60 for _, peer := range pool.peers { 61 peerHeight := peer.Height 62 if peerHeight > newMax { 63 newMax = peerHeight 64 } 65 } 66 pool.MaxPeerHeight = newMax 67 } 68 69 // UpdatePeer adds a new peer or updates an existing peer with a new base and height. 70 // If a peer is short it is not added. 71 func (pool *BlockPool) UpdatePeer(peerID p2p.ID, base int64, height int64) error { 72 73 peer := pool.peers[peerID] 74 75 if peer == nil { 76 if height < pool.Height { 77 pool.logger.Info("Peer height too small", 78 "peer", peerID, "height", height, "fsm_height", pool.Height) 79 return errPeerTooShort 80 } 81 // Add new peer. 82 peer = NewBpPeer(peerID, base, height, pool.toBcR.sendPeerError, nil) 83 peer.SetLogger(pool.logger.With("peer", peerID)) 84 pool.peers[peerID] = peer 85 pool.logger.Info("added peer", "peerID", peerID, "base", base, "height", height, "num_peers", len(pool.peers)) 86 } else { 87 // Check if peer is lowering its height. This is not allowed. 88 if height < peer.Height { 89 pool.RemovePeer(peerID, errPeerLowersItsHeight) 90 return errPeerLowersItsHeight 91 } 92 // Update existing peer. 93 peer.Base = base 94 peer.Height = height 95 } 96 97 // Update the pool's MaxPeerHeight if needed. 98 pool.updateMaxPeerHeight() 99 100 return nil 101 } 102 103 // Cleans and deletes the peer. Recomputes the max peer height. 104 func (pool *BlockPool) deletePeer(peer *BpPeer) { 105 if peer == nil { 106 return 107 } 108 peer.Cleanup() 109 delete(pool.peers, peer.ID) 110 111 if peer.Height == pool.MaxPeerHeight { 112 pool.updateMaxPeerHeight() 113 } 114 } 115 116 // RemovePeer removes the blocks and requests from the peer, reschedules them and deletes the peer. 117 func (pool *BlockPool) RemovePeer(peerID p2p.ID, err error) { 118 peer := pool.peers[peerID] 119 if peer == nil { 120 return 121 } 122 pool.logger.Info("removing peer", "peerID", peerID, "error", err) 123 124 // Reschedule the block requests made to the peer, or received and not processed yet. 125 // Note that some of the requests may be removed further down. 126 for h := range pool.peers[peerID].blocks { 127 pool.rescheduleRequest(peerID, h) 128 } 129 130 oldMaxPeerHeight := pool.MaxPeerHeight 131 // Delete the peer. This operation may result in the pool's MaxPeerHeight being lowered. 132 pool.deletePeer(peer) 133 134 // Check if the pool's MaxPeerHeight has been lowered. 135 // This may happen if the tallest peer has been removed. 136 if oldMaxPeerHeight > pool.MaxPeerHeight { 137 // Remove any planned requests for heights over the new MaxPeerHeight. 138 for h := range pool.plannedRequests { 139 if h > pool.MaxPeerHeight { 140 delete(pool.plannedRequests, h) 141 } 142 } 143 // Adjust the nextRequestHeight to the new max plus one. 144 if pool.nextRequestHeight > pool.MaxPeerHeight { 145 pool.nextRequestHeight = pool.MaxPeerHeight + 1 146 } 147 } 148 } 149 150 func (pool *BlockPool) removeShortPeers() { 151 for _, peer := range pool.peers { 152 if peer.Height < pool.Height { 153 pool.RemovePeer(peer.ID, nil) 154 } 155 } 156 } 157 158 func (pool *BlockPool) removeBadPeers() { 159 pool.removeShortPeers() 160 for _, peer := range pool.peers { 161 if err := peer.CheckRate(); err != nil { 162 pool.RemovePeer(peer.ID, err) 163 pool.toBcR.sendPeerError(err, peer.ID) 164 } 165 } 166 } 167 168 // MakeNextRequests creates more requests if the block pool is running low. 169 func (pool *BlockPool) MakeNextRequests(maxNumRequests int) { 170 heights := pool.makeRequestBatch(maxNumRequests) 171 if len(heights) != 0 { 172 pool.logger.Info("makeNextRequests will make following requests", 173 "number", len(heights), "heights", heights) 174 } 175 176 for _, height := range heights { 177 h := int64(height) 178 if !pool.sendRequest(h) { 179 // If a good peer was not found for sending the request at height h then return, 180 // as it shouldn't be possible to find a peer for h+1. 181 return 182 } 183 delete(pool.plannedRequests, h) 184 } 185 } 186 187 // Makes a batch of requests sorted by height such that the block pool has up to maxNumRequests entries. 188 func (pool *BlockPool) makeRequestBatch(maxNumRequests int) []int { 189 pool.removeBadPeers() 190 // At this point pool.requests may include heights for requests to be redone due to removal of peers: 191 // - peers timed out or were removed by switch 192 // - FSM timed out on waiting to advance the block execution due to missing blocks at h or h+1 193 // Determine the number of requests needed by subtracting the number of requests already made from the maximum 194 // allowed 195 numNeeded := maxNumRequests - len(pool.blocks) 196 for len(pool.plannedRequests) < numNeeded { 197 if pool.nextRequestHeight > pool.MaxPeerHeight { 198 break 199 } 200 pool.plannedRequests[pool.nextRequestHeight] = struct{}{} 201 pool.nextRequestHeight++ 202 } 203 204 heights := make([]int, 0, len(pool.plannedRequests)) 205 for k := range pool.plannedRequests { 206 heights = append(heights, int(k)) 207 } 208 sort.Ints(heights) 209 return heights 210 } 211 212 func (pool *BlockPool) sendRequest(height int64) bool { 213 for _, peer := range pool.peers { 214 if peer.NumPendingBlockRequests >= maxRequestsPerPeer { 215 continue 216 } 217 if peer.Base > height || peer.Height < height { 218 continue 219 } 220 221 err := pool.toBcR.sendBlockRequest(peer.ID, height) 222 if err == errNilPeerForBlockRequest { 223 // Switch does not have this peer, remove it and continue to look for another peer. 224 pool.logger.Error("switch does not have peer..removing peer selected for height", "peer", 225 peer.ID, "height", height) 226 pool.RemovePeer(peer.ID, err) 227 continue 228 } 229 230 if err == errSendQueueFull { 231 pool.logger.Error("peer queue is full", "peer", peer.ID, "height", height) 232 continue 233 } 234 235 pool.logger.Info("assigned request to peer", "peer", peer.ID, "height", height) 236 237 pool.blocks[height] = peer.ID 238 peer.RequestSent(height) 239 240 return true 241 } 242 pool.logger.Error("could not find peer to send request for block at height", "height", height) 243 return false 244 } 245 246 // AddBlock validates that the block comes from the peer it was expected from and stores it in the 'blocks' map. 247 func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int) error { 248 peer, ok := pool.peers[peerID] 249 if !ok { 250 pool.logger.Error("block from unknown peer", "height", block.Height, "peer", peerID) 251 return errBadDataFromPeer 252 } 253 if wantPeerID, ok := pool.blocks[block.Height]; ok && wantPeerID != peerID { 254 pool.logger.Error("block received from wrong peer", "height", block.Height, 255 "peer", peerID, "expected_peer", wantPeerID) 256 return errBadDataFromPeer 257 } 258 259 return peer.AddBlock(block, blockSize) 260 } 261 262 // BlockData stores the peer responsible to deliver a block and the actual block if delivered. 263 type BlockData struct { 264 block *types.Block 265 peer *BpPeer 266 } 267 268 // BlockAndPeerAtHeight retrieves the block and delivery peer at specified height. 269 // Returns errMissingBlock if a block was not found 270 func (pool *BlockPool) BlockAndPeerAtHeight(height int64) (bData *BlockData, err error) { 271 peerID := pool.blocks[height] 272 peer := pool.peers[peerID] 273 if peer == nil { 274 return nil, errMissingBlock 275 } 276 277 block, err := peer.BlockAtHeight(height) 278 if err != nil { 279 return nil, err 280 } 281 282 return &BlockData{peer: peer, block: block}, nil 283 } 284 285 // FirstTwoBlocksAndPeers returns the blocks and the delivery peers at pool's height H and H+1. 286 func (pool *BlockPool) FirstTwoBlocksAndPeers() (first, second *BlockData, err error) { 287 first, err = pool.BlockAndPeerAtHeight(pool.Height) 288 second, err2 := pool.BlockAndPeerAtHeight(pool.Height + 1) 289 if err == nil { 290 err = err2 291 } 292 return 293 } 294 295 // InvalidateFirstTwoBlocks removes the peers that sent us the first two blocks, blocks are removed by RemovePeer(). 296 func (pool *BlockPool) InvalidateFirstTwoBlocks(err error) { 297 first, err1 := pool.BlockAndPeerAtHeight(pool.Height) 298 second, err2 := pool.BlockAndPeerAtHeight(pool.Height + 1) 299 300 if err1 == nil { 301 pool.RemovePeer(first.peer.ID, err) 302 } 303 if err2 == nil { 304 pool.RemovePeer(second.peer.ID, err) 305 } 306 } 307 308 // ProcessedCurrentHeightBlock performs cleanup after a block is processed. It removes block at pool height and 309 // the peers that are now short. 310 func (pool *BlockPool) ProcessedCurrentHeightBlock() { 311 peerID, peerOk := pool.blocks[pool.Height] 312 if peerOk { 313 pool.peers[peerID].RemoveBlock(pool.Height) 314 } 315 delete(pool.blocks, pool.Height) 316 pool.logger.Debug("removed block at height", "height", pool.Height) 317 pool.Height++ 318 pool.removeShortPeers() 319 } 320 321 // RemovePeerAtCurrentHeights checks if a block at pool's height H exists and if not, it removes the 322 // delivery peer and returns. If a block at height H exists then the check and peer removal is done for H+1. 323 // This function is called when the FSM is not able to make progress for some time. 324 // This happens if either the block H or H+1 have not been delivered. 325 func (pool *BlockPool) RemovePeerAtCurrentHeights(err error) { 326 peerID := pool.blocks[pool.Height] 327 peer, ok := pool.peers[peerID] 328 if ok { 329 if _, err := peer.BlockAtHeight(pool.Height); err != nil { 330 pool.logger.Info("remove peer that hasn't sent block at pool.Height", 331 "peer", peerID, "height", pool.Height) 332 pool.RemovePeer(peerID, err) 333 return 334 } 335 } 336 peerID = pool.blocks[pool.Height+1] 337 peer, ok = pool.peers[peerID] 338 if ok { 339 if _, err := peer.BlockAtHeight(pool.Height + 1); err != nil { 340 pool.logger.Info("remove peer that hasn't sent block at pool.Height+1", 341 "peer", peerID, "height", pool.Height+1) 342 pool.RemovePeer(peerID, err) 343 return 344 } 345 } 346 } 347 348 // Cleanup performs pool and peer cleanup 349 func (pool *BlockPool) Cleanup() { 350 for id, peer := range pool.peers { 351 peer.Cleanup() 352 delete(pool.peers, id) 353 } 354 pool.plannedRequests = make(map[int64]struct{}) 355 pool.blocks = make(map[int64]p2p.ID) 356 pool.nextRequestHeight = 0 357 pool.Height = 0 358 pool.MaxPeerHeight = 0 359 } 360 361 // NumPeers returns the number of peers in the pool 362 func (pool *BlockPool) NumPeers() int { 363 return len(pool.peers) 364 } 365 366 // NeedsBlocks returns true if more blocks are required. 367 func (pool *BlockPool) NeedsBlocks() bool { 368 return len(pool.blocks) < maxNumRequests 369 }