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