github.com/DFWallet/tendermint-cosmos@v0.0.2/blockchain/v1/peer.go (about) 1 package v1 2 3 import ( 4 "fmt" 5 "math" 6 "time" 7 8 flow "github.com/DFWallet/tendermint-cosmos/libs/flowrate" 9 "github.com/DFWallet/tendermint-cosmos/libs/log" 10 "github.com/DFWallet/tendermint-cosmos/p2p" 11 "github.com/DFWallet/tendermint-cosmos/types" 12 ) 13 14 //-------- 15 // Peer 16 17 // BpPeerParams stores the peer parameters that are used when creating a peer. 18 type BpPeerParams struct { 19 timeout time.Duration 20 minRecvRate int64 21 sampleRate time.Duration 22 windowSize time.Duration 23 } 24 25 // BpPeer is the datastructure associated with a fast sync peer. 26 type BpPeer struct { 27 logger log.Logger 28 ID p2p.ID 29 30 Base int64 // the peer reported base 31 Height int64 // the peer reported height 32 NumPendingBlockRequests int // number of requests still waiting for block responses 33 blocks map[int64]*types.Block // blocks received or expected to be received from this peer 34 blockResponseTimer *time.Timer 35 recvMonitor *flow.Monitor 36 params *BpPeerParams // parameters for timer and monitor 37 38 onErr func(err error, peerID p2p.ID) // function to call on error 39 } 40 41 // NewBpPeer creates a new peer. 42 func NewBpPeer(peerID p2p.ID, base int64, height int64, 43 onErr func(err error, peerID p2p.ID), params *BpPeerParams) *BpPeer { 44 45 if params == nil { 46 params = BpPeerDefaultParams() 47 } 48 return &BpPeer{ 49 ID: peerID, 50 Base: base, 51 Height: height, 52 blocks: make(map[int64]*types.Block, maxRequestsPerPeer), 53 logger: log.NewNopLogger(), 54 onErr: onErr, 55 params: params, 56 } 57 } 58 59 // String returns a string representation of a peer. 60 func (peer *BpPeer) String() string { 61 return fmt.Sprintf("peer: %v height: %v pending: %v", peer.ID, peer.Height, peer.NumPendingBlockRequests) 62 } 63 64 // SetLogger sets the logger of the peer. 65 func (peer *BpPeer) SetLogger(l log.Logger) { 66 peer.logger = l 67 } 68 69 // Cleanup performs cleanup of the peer, removes blocks, requests, stops timer and monitor. 70 func (peer *BpPeer) Cleanup() { 71 if peer.blockResponseTimer != nil { 72 peer.blockResponseTimer.Stop() 73 } 74 if peer.NumPendingBlockRequests != 0 { 75 peer.logger.Info("peer with pending requests is being cleaned", "peer", peer.ID) 76 } 77 if len(peer.blocks)-peer.NumPendingBlockRequests != 0 { 78 peer.logger.Info("peer with pending blocks is being cleaned", "peer", peer.ID) 79 } 80 for h := range peer.blocks { 81 delete(peer.blocks, h) 82 } 83 peer.NumPendingBlockRequests = 0 84 peer.recvMonitor = nil 85 } 86 87 // BlockAtHeight returns the block at a given height if available and errMissingBlock otherwise. 88 func (peer *BpPeer) BlockAtHeight(height int64) (*types.Block, error) { 89 block, ok := peer.blocks[height] 90 if !ok { 91 return nil, errMissingBlock 92 } 93 if block == nil { 94 return nil, errMissingBlock 95 } 96 return peer.blocks[height], nil 97 } 98 99 // AddBlock adds a block at peer level. Block must be non-nil and recvSize a positive integer 100 // The peer must have a pending request for this block. 101 func (peer *BpPeer) AddBlock(block *types.Block, recvSize int) error { 102 if block == nil || recvSize < 0 { 103 panic("bad parameters") 104 } 105 existingBlock, ok := peer.blocks[block.Height] 106 if !ok { 107 peer.logger.Error("unsolicited block", "blockHeight", block.Height, "peer", peer.ID) 108 return errMissingBlock 109 } 110 if existingBlock != nil { 111 peer.logger.Error("already have a block for height", "height", block.Height) 112 return errDuplicateBlock 113 } 114 if peer.NumPendingBlockRequests == 0 { 115 panic("peer does not have pending requests") 116 } 117 peer.blocks[block.Height] = block 118 peer.NumPendingBlockRequests-- 119 if peer.NumPendingBlockRequests == 0 { 120 peer.stopMonitor() 121 peer.stopBlockResponseTimer() 122 } else { 123 peer.recvMonitor.Update(recvSize) 124 peer.resetBlockResponseTimer() 125 } 126 return nil 127 } 128 129 // RemoveBlock removes the block of given height 130 func (peer *BpPeer) RemoveBlock(height int64) { 131 delete(peer.blocks, height) 132 } 133 134 // RequestSent records that a request was sent, and starts the peer timer and monitor if needed. 135 func (peer *BpPeer) RequestSent(height int64) { 136 peer.blocks[height] = nil 137 138 if peer.NumPendingBlockRequests == 0 { 139 peer.startMonitor() 140 peer.resetBlockResponseTimer() 141 } 142 peer.NumPendingBlockRequests++ 143 } 144 145 // CheckRate verifies that the response rate of the peer is acceptable (higher than the minimum allowed). 146 func (peer *BpPeer) CheckRate() error { 147 if peer.NumPendingBlockRequests == 0 { 148 return nil 149 } 150 curRate := peer.recvMonitor.Status().CurRate 151 // curRate can be 0 on start 152 if curRate != 0 && curRate < peer.params.minRecvRate { 153 err := errSlowPeer 154 peer.logger.Error("SendTimeout", "peer", peer, 155 "reason", err, 156 "curRate", fmt.Sprintf("%d KB/s", curRate/1024), 157 "minRate", fmt.Sprintf("%d KB/s", peer.params.minRecvRate/1024)) 158 return err 159 } 160 return nil 161 } 162 163 func (peer *BpPeer) onTimeout() { 164 peer.onErr(errNoPeerResponse, peer.ID) 165 } 166 167 func (peer *BpPeer) stopMonitor() { 168 peer.recvMonitor.Done() 169 peer.recvMonitor = nil 170 } 171 172 func (peer *BpPeer) startMonitor() { 173 peer.recvMonitor = flow.New(peer.params.sampleRate, peer.params.windowSize) 174 initialValue := float64(peer.params.minRecvRate) * math.E 175 peer.recvMonitor.SetREMA(initialValue) 176 } 177 178 func (peer *BpPeer) resetBlockResponseTimer() { 179 if peer.blockResponseTimer == nil { 180 peer.blockResponseTimer = time.AfterFunc(peer.params.timeout, peer.onTimeout) 181 } else { 182 peer.blockResponseTimer.Reset(peer.params.timeout) 183 } 184 } 185 186 func (peer *BpPeer) stopBlockResponseTimer() bool { 187 if peer.blockResponseTimer == nil { 188 return false 189 } 190 return peer.blockResponseTimer.Stop() 191 } 192 193 // BpPeerDefaultParams returns the default peer parameters. 194 func BpPeerDefaultParams() *BpPeerParams { 195 return &BpPeerParams{ 196 // Timeout for a peer to respond to a block request. 197 timeout: 15 * time.Second, 198 199 // Minimum recv rate to ensure we're receiving blocks from a peer fast 200 // enough. If a peer is not sending data at at least that rate, we 201 // consider them to have timedout and we disconnect. 202 // 203 // Assuming a DSL connection (not a good choice) 128 Kbps (upload) ~ 15 KB/s, 204 // sending data across atlantic ~ 7.5 KB/s. 205 minRecvRate: int64(7680), 206 207 // Monitor parameters 208 sampleRate: time.Second, 209 windowSize: 40 * time.Second, 210 } 211 }