github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/eth/downloader/peer.go (about) 1 // Copyright 2015 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 // Contains the active peer-set of the downloader, maintaining both failures 18 // as well as reputation metrics to prioritize the block retrievals. 19 20 package downloader 21 22 import ( 23 "errors" 24 "math/big" 25 "sync" 26 "time" 27 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/eth/protocols/eth" 30 "github.com/ethereum/go-ethereum/event" 31 "github.com/ethereum/go-ethereum/log" 32 "github.com/ethereum/go-ethereum/p2p/msgrate" 33 ) 34 35 const ( 36 maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items 37 ) 38 39 var ( 40 errAlreadyRegistered = errors.New("peer is already registered") 41 errNotRegistered = errors.New("peer is not registered") 42 ) 43 44 // peerConnection represents an active peer from which hashes and blocks are retrieved. 45 type peerConnection struct { 46 id string // Unique identifier of the peer 47 48 rates *msgrate.Tracker // Tracker to hone in on the number of items retrievable per second 49 lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously) 50 51 peer Peer 52 53 version uint // Eth protocol version number to switch strategies 54 log log.Logger // Contextual logger to add extra infos to peer logs 55 lock sync.RWMutex 56 } 57 58 // Peer encapsulates the methods required to synchronise with a remote full peer. 59 type Peer interface { 60 Head() (common.Hash, *big.Int) 61 RequestHeadersByHash(common.Hash, int, int, bool, chan *eth.Response) (*eth.Request, error) 62 RequestHeadersByNumber(uint64, int, int, bool, chan *eth.Response) (*eth.Request, error) 63 64 RequestBodies([]common.Hash, chan *eth.Response) (*eth.Request, error) 65 RequestReceipts([]common.Hash, chan *eth.Response) (*eth.Request, error) 66 } 67 68 // newPeerConnection creates a new downloader peer. 69 func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection { 70 return &peerConnection{ 71 id: id, 72 lacking: make(map[common.Hash]struct{}), 73 peer: peer, 74 version: version, 75 log: logger, 76 } 77 } 78 79 // Reset clears the internal state of a peer entity. 80 func (p *peerConnection) Reset() { 81 p.lock.Lock() 82 defer p.lock.Unlock() 83 84 p.lacking = make(map[common.Hash]struct{}) 85 } 86 87 // UpdateHeaderRate updates the peer's estimated header retrieval throughput with 88 // the current measurement. 89 func (p *peerConnection) UpdateHeaderRate(delivered int, elapsed time.Duration) { 90 p.rates.Update(eth.BlockHeadersMsg, elapsed, delivered) 91 } 92 93 // UpdateBodyRate updates the peer's estimated body retrieval throughput with the 94 // current measurement. 95 func (p *peerConnection) UpdateBodyRate(delivered int, elapsed time.Duration) { 96 p.rates.Update(eth.BlockBodiesMsg, elapsed, delivered) 97 } 98 99 // UpdateReceiptRate updates the peer's estimated receipt retrieval throughput 100 // with the current measurement. 101 func (p *peerConnection) UpdateReceiptRate(delivered int, elapsed time.Duration) { 102 p.rates.Update(eth.ReceiptsMsg, elapsed, delivered) 103 } 104 105 // HeaderCapacity retrieves the peer's header download allowance based on its 106 // previously discovered throughput. 107 func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int { 108 cap := p.rates.Capacity(eth.BlockHeadersMsg, targetRTT) 109 if cap > MaxHeaderFetch { 110 cap = MaxHeaderFetch 111 } 112 return cap 113 } 114 115 // BodyCapacity retrieves the peer's body download allowance based on its 116 // previously discovered throughput. 117 func (p *peerConnection) BodyCapacity(targetRTT time.Duration) int { 118 cap := p.rates.Capacity(eth.BlockBodiesMsg, targetRTT) 119 if cap > MaxBlockFetch { 120 cap = MaxBlockFetch 121 } 122 return cap 123 } 124 125 // ReceiptCapacity retrieves the peers receipt download allowance based on its 126 // previously discovered throughput. 127 func (p *peerConnection) ReceiptCapacity(targetRTT time.Duration) int { 128 cap := p.rates.Capacity(eth.ReceiptsMsg, targetRTT) 129 if cap > MaxReceiptFetch { 130 cap = MaxReceiptFetch 131 } 132 return cap 133 } 134 135 // MarkLacking appends a new entity to the set of items (blocks, receipts, states) 136 // that a peer is known not to have (i.e. have been requested before). If the 137 // set reaches its maximum allowed capacity, items are randomly dropped off. 138 func (p *peerConnection) MarkLacking(hash common.Hash) { 139 p.lock.Lock() 140 defer p.lock.Unlock() 141 142 for len(p.lacking) >= maxLackingHashes { 143 for drop := range p.lacking { 144 delete(p.lacking, drop) 145 break 146 } 147 } 148 p.lacking[hash] = struct{}{} 149 } 150 151 // Lacks retrieves whether the hash of a blockchain item is on the peers lacking 152 // list (i.e. whether we know that the peer does not have it). 153 func (p *peerConnection) Lacks(hash common.Hash) bool { 154 p.lock.RLock() 155 defer p.lock.RUnlock() 156 157 _, ok := p.lacking[hash] 158 return ok 159 } 160 161 // peeringEvent is sent on the peer event feed when a remote peer connects or 162 // disconnects. 163 type peeringEvent struct { 164 peer *peerConnection 165 join bool 166 } 167 168 // peerSet represents the collection of active peer participating in the chain 169 // download procedure. 170 type peerSet struct { 171 peers map[string]*peerConnection 172 rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat 173 events event.Feed // Feed to publish peer lifecycle events on 174 175 lock sync.RWMutex 176 } 177 178 // newPeerSet creates a new peer set top track the active download sources. 179 func newPeerSet() *peerSet { 180 return &peerSet{ 181 peers: make(map[string]*peerConnection), 182 rates: msgrate.NewTrackers(log.New("proto", "eth")), 183 } 184 } 185 186 // SubscribeEvents subscribes to peer arrival and departure events. 187 func (ps *peerSet) SubscribeEvents(ch chan<- *peeringEvent) event.Subscription { 188 return ps.events.Subscribe(ch) 189 } 190 191 // Reset iterates over the current peer set, and resets each of the known peers 192 // to prepare for a next batch of block retrieval. 193 func (ps *peerSet) Reset() { 194 ps.lock.RLock() 195 defer ps.lock.RUnlock() 196 197 for _, peer := range ps.peers { 198 peer.Reset() 199 } 200 } 201 202 // Register injects a new peer into the working set, or returns an error if the 203 // peer is already known. 204 // 205 // The method also sets the starting throughput values of the new peer to the 206 // average of all existing peers, to give it a realistic chance of being used 207 // for data retrievals. 208 func (ps *peerSet) Register(p *peerConnection) error { 209 // Register the new peer with some meaningful defaults 210 ps.lock.Lock() 211 if _, ok := ps.peers[p.id]; ok { 212 ps.lock.Unlock() 213 return errAlreadyRegistered 214 } 215 p.rates = msgrate.NewTracker(ps.rates.MeanCapacities(), ps.rates.MedianRoundTrip()) 216 if err := ps.rates.Track(p.id, p.rates); err != nil { 217 ps.lock.Unlock() 218 return err 219 } 220 ps.peers[p.id] = p 221 ps.lock.Unlock() 222 223 ps.events.Send(&peeringEvent{peer: p, join: true}) 224 return nil 225 } 226 227 // Unregister removes a remote peer from the active set, disabling any further 228 // actions to/from that particular entity. 229 func (ps *peerSet) Unregister(id string) error { 230 ps.lock.Lock() 231 p, ok := ps.peers[id] 232 if !ok { 233 ps.lock.Unlock() 234 return errNotRegistered 235 } 236 delete(ps.peers, id) 237 ps.rates.Untrack(id) 238 ps.lock.Unlock() 239 240 ps.events.Send(&peeringEvent{peer: p, join: false}) 241 return nil 242 } 243 244 // Peer retrieves the registered peer with the given id. 245 func (ps *peerSet) Peer(id string) *peerConnection { 246 ps.lock.RLock() 247 defer ps.lock.RUnlock() 248 249 return ps.peers[id] 250 } 251 252 // Len returns if the current number of peers in the set. 253 func (ps *peerSet) Len() int { 254 ps.lock.RLock() 255 defer ps.lock.RUnlock() 256 257 return len(ps.peers) 258 } 259 260 // AllPeers retrieves a flat list of all the peers within the set. 261 func (ps *peerSet) AllPeers() []*peerConnection { 262 ps.lock.RLock() 263 defer ps.lock.RUnlock() 264 265 list := make([]*peerConnection, 0, len(ps.peers)) 266 for _, p := range ps.peers { 267 list = append(list, p) 268 } 269 return list 270 } 271 272 // peerCapacitySort implements sort.Interface. 273 // It sorts peer connections by capacity (descending). 274 type peerCapacitySort struct { 275 peers []*peerConnection 276 caps []int 277 } 278 279 func (ps *peerCapacitySort) Len() int { 280 return len(ps.peers) 281 } 282 283 func (ps *peerCapacitySort) Less(i, j int) bool { 284 return ps.caps[i] > ps.caps[j] 285 } 286 287 func (ps *peerCapacitySort) Swap(i, j int) { 288 ps.peers[i], ps.peers[j] = ps.peers[j], ps.peers[i] 289 ps.caps[i], ps.caps[j] = ps.caps[j], ps.caps[i] 290 }