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