github.com/dominant-strategies/go-quai@v0.28.2/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 "sort" 26 "sync" 27 "sync/atomic" 28 "time" 29 30 "github.com/dominant-strategies/go-quai/common" 31 "github.com/dominant-strategies/go-quai/eth/protocols/eth" 32 "github.com/dominant-strategies/go-quai/event" 33 "github.com/dominant-strategies/go-quai/log" 34 "github.com/dominant-strategies/go-quai/p2p/msgrate" 35 ) 36 37 const ( 38 maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items 39 ) 40 41 var ( 42 errAlreadyFetching = errors.New("already fetching blocks from peer") 43 errAlreadyRegistered = errors.New("peer is already registered") 44 errNotRegistered = errors.New("peer is not registered") 45 ) 46 47 // peerConnection represents an active peer from which hashes and blocks are retrieved. 48 type peerConnection struct { 49 id string // Unique identifier of the peer 50 51 headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1) 52 blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1) 53 54 headerStarted time.Time // Time instance when the last header fetch was started 55 blockStarted time.Time // Time instance when the last block (body) fetch was started 56 57 rates *msgrate.Tracker // Tracker to hone in on the number of items retrievable per second 58 lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously) 59 60 peer Peer 61 62 version uint // Eth protocol version number to switch strategies 63 log log.Logger // Contextual logger to add extra infos to peer logs 64 lock sync.RWMutex 65 } 66 67 // LightPeer encapsulates the methods required to synchronise with a remote light peer. 68 type LightPeer interface { 69 Head() (common.Hash, *big.Int, *big.Int, time.Time) 70 RequestHeadersByHash(common.Hash, int, uint64, bool, bool) error 71 RequestHeadersByNumber(uint64, int, uint64, uint64, bool, bool) error 72 } 73 74 // Peer encapsulates the methods required to synchronise with a remote full peer. 75 type Peer interface { 76 LightPeer 77 RequestBodies([]common.Hash) error 78 } 79 80 // newPeerConnection creates a new downloader peer. 81 func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection { 82 return &peerConnection{ 83 id: id, 84 lacking: make(map[common.Hash]struct{}), 85 peer: peer, 86 version: version, 87 log: logger, 88 } 89 } 90 91 // Reset clears the internal state of a peer entity. 92 func (p *peerConnection) Reset() { 93 p.lock.Lock() 94 defer p.lock.Unlock() 95 96 atomic.StoreInt32(&p.headerIdle, 0) 97 atomic.StoreInt32(&p.blockIdle, 0) 98 99 p.lacking = make(map[common.Hash]struct{}) 100 } 101 102 // PeerConnection ID returns the unique identifier of the peer. 103 func (p *peerConnection) ID() string { 104 return p.id 105 } 106 107 // Tracker returns the message rate trackers of the peer. 108 func (p *peerConnection) Tracker() *msgrate.Tracker { 109 return p.rates 110 } 111 112 // Peer returns the underlying peer entity. 113 func (p *peerConnection) Peer() Peer { 114 return p.peer 115 } 116 117 // FetchHeaders sends a header retrieval request to the remote peer. 118 func (p *peerConnection) FetchHeaders(from uint64, count int) error { 119 // Short circuit if the peer is already fetching 120 if !atomic.CompareAndSwapInt32(&p.headerIdle, 0, 1) { 121 return errAlreadyFetching 122 } 123 p.headerStarted = time.Now() 124 125 // In the case of prime the required amount is the PrimeSKeletonDist which is the 126 // distance between the skeleton headers. 127 if common.NodeLocation.Context() == common.PRIME_CTX { 128 // Issue the header retrieval request (absolute upwards without gaps) 129 go p.peer.RequestHeadersByNumber(from, PrimeSkeletonDist, 1, 0, false, true) 130 } else { 131 // Issue the header retrieval request (absolute upwards without gaps) 132 go p.peer.RequestHeadersByNumber(from, count, 1, 0, false, true) 133 } 134 135 return nil 136 } 137 138 // FetchBodies sends a block body retrieval request to the remote peer. 139 func (p *peerConnection) FetchBodies(request *fetchRequest) error { 140 // Short circuit if the peer is already fetching 141 if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) { 142 return errAlreadyFetching 143 } 144 p.blockStarted = time.Now() 145 146 go func() { 147 // Convert the header set to a retrievable slice 148 hashes := make([]common.Hash, 0, len(request.Headers)) 149 for _, header := range request.Headers { 150 hashes = append(hashes, header.Hash()) 151 } 152 p.peer.RequestBodies(hashes) 153 }() 154 155 return nil 156 } 157 158 // SetHeadersIdle sets the peer to idle, allowing it to execute new header retrieval 159 // requests. Its estimated header retrieval throughput is updated with that measured 160 // just now. 161 func (p *peerConnection) SetHeadersIdle(delivered int, deliveryTime time.Time) { 162 p.rates.Update(eth.BlockHeadersMsg, deliveryTime.Sub(p.headerStarted), delivered) 163 atomic.StoreInt32(&p.headerIdle, 0) 164 } 165 166 // SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval 167 // requests. Its estimated body retrieval throughput is updated with that measured 168 // just now. 169 func (p *peerConnection) SetBodiesIdle(delivered int, deliveryTime time.Time) { 170 p.rates.Update(eth.BlockBodiesMsg, deliveryTime.Sub(p.blockStarted), delivered) 171 atomic.StoreInt32(&p.blockIdle, 0) 172 } 173 174 // HeaderCapacity retrieves the peers header download allowance based on its 175 // previously discovered throughput. 176 func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int { 177 cap := p.rates.Capacity(eth.BlockHeadersMsg, targetRTT) 178 if cap > MaxHeaderFetch { 179 cap = MaxHeaderFetch 180 } 181 return cap 182 } 183 184 // BlockCapacity retrieves the peers block download allowance based on its 185 // previously discovered throughput. 186 func (p *peerConnection) BlockCapacity(targetRTT time.Duration) int { 187 cap := p.rates.Capacity(eth.BlockBodiesMsg, targetRTT) 188 if cap > MaxBlockFetch { 189 cap = MaxBlockFetch 190 } 191 return cap 192 } 193 194 // MarkLacking appends a new entity to the set of items (blocks) 195 // that a peer is known not to have (i.e. have been requested before). If the 196 // set reaches its maximum allowed capacity, items are randomly dropped off. 197 func (p *peerConnection) MarkLacking(hash common.Hash) { 198 p.lock.Lock() 199 defer p.lock.Unlock() 200 201 for len(p.lacking) >= maxLackingHashes { 202 for drop := range p.lacking { 203 delete(p.lacking, drop) 204 break 205 } 206 } 207 p.lacking[hash] = struct{}{} 208 } 209 210 // Lacks retrieves whether the hash of a blockchain item is on the peers lacking 211 // list (i.e. whether we know that the peer does not have it). 212 func (p *peerConnection) Lacks(hash common.Hash) bool { 213 p.lock.RLock() 214 defer p.lock.RUnlock() 215 216 _, ok := p.lacking[hash] 217 return ok 218 } 219 220 // peerSet represents the collection of active peer participating in the chain 221 // download procedure. 222 type peerSet struct { 223 peers map[string]*peerConnection 224 rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat 225 226 newPeerFeed event.Feed 227 peerDropFeed event.Feed 228 229 lock sync.RWMutex 230 } 231 232 // newPeerSet creates a new peer set top track the active download sources. 233 func newPeerSet() *peerSet { 234 return &peerSet{ 235 peers: make(map[string]*peerConnection), 236 rates: msgrate.NewTrackers(&log.Log), 237 } 238 } 239 240 // SubscribeNewPeers subscribes to peer arrival events. 241 func (ps *peerSet) SubscribeNewPeers(ch chan<- *peerConnection) event.Subscription { 242 return ps.newPeerFeed.Subscribe(ch) 243 } 244 245 // SubscribePeerDrops subscribes to peer departure events. 246 func (ps *peerSet) SubscribePeerDrops(ch chan<- *peerConnection) event.Subscription { 247 return ps.peerDropFeed.Subscribe(ch) 248 } 249 250 // Reset iterates over the current peer set, and resets each of the known peers 251 // to prepare for a next batch of block retrieval. 252 func (ps *peerSet) Reset() { 253 ps.lock.RLock() 254 defer ps.lock.RUnlock() 255 256 for _, peer := range ps.peers { 257 peer.Reset() 258 } 259 } 260 261 // Register injects a new peer into the working set, or returns an error if the 262 // peer is already known. 263 // 264 // The method also sets the starting throughput values of the new peer to the 265 // average of all existing peers, to give it a realistic chance of being used 266 // for data retrievals. 267 func (ps *peerSet) Register(p *peerConnection) error { 268 // Register the new peer with some meaningful defaults 269 ps.lock.Lock() 270 if _, ok := ps.peers[p.id]; ok { 271 ps.lock.Unlock() 272 return errAlreadyRegistered 273 } 274 p.rates = msgrate.NewTracker(ps.rates.MeanCapacities(), ps.rates.MedianRoundTrip()) 275 if err := ps.rates.Track(p.id, p.rates); err != nil { 276 return err 277 } 278 ps.peers[p.id] = p 279 ps.lock.Unlock() 280 281 ps.newPeerFeed.Send(p) 282 return nil 283 } 284 285 // Unregister removes a remote peer from the active set, disabling any further 286 // actions to/from that particular entity. 287 func (ps *peerSet) Unregister(id string) error { 288 ps.lock.Lock() 289 p, ok := ps.peers[id] 290 if !ok { 291 ps.lock.Unlock() 292 return errNotRegistered 293 } 294 delete(ps.peers, id) 295 ps.rates.Untrack(id) 296 ps.lock.Unlock() 297 298 ps.peerDropFeed.Send(p) 299 return nil 300 } 301 302 // Peer retrieves the registered peer with the given id. 303 func (ps *peerSet) Peer(id string) *peerConnection { 304 ps.lock.RLock() 305 defer ps.lock.RUnlock() 306 307 return ps.peers[id] 308 } 309 310 // Len returns if the current number of peers in the set. 311 func (ps *peerSet) Len() int { 312 ps.lock.RLock() 313 defer ps.lock.RUnlock() 314 315 return len(ps.peers) 316 } 317 318 // AllPeers retrieves a flat list of all the peers within the set. 319 func (ps *peerSet) AllPeers() []*peerConnection { 320 ps.lock.RLock() 321 defer ps.lock.RUnlock() 322 323 list := make([]*peerConnection, 0, len(ps.peers)) 324 for _, p := range ps.peers { 325 list = append(list, p) 326 } 327 return list 328 } 329 330 // HeaderIdlePeers retrieves a flat list of all the currently header-idle peers 331 // within the active peer set, ordered by their reputation. 332 func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) { 333 idle := func(p *peerConnection) bool { 334 return atomic.LoadInt32(&p.headerIdle) == 0 335 } 336 throughput := func(p *peerConnection) int { 337 return p.rates.Capacity(eth.BlockHeadersMsg, time.Second) 338 } 339 return ps.idlePeers(eth.QUAI1, eth.QUAI2, idle, throughput) 340 } 341 342 // BodyIdlePeers retrieves a flat list of all the currently body-idle peers within 343 // the active peer set, ordered by their reputation. 344 func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) { 345 idle := func(p *peerConnection) bool { 346 return atomic.LoadInt32(&p.blockIdle) == 0 347 } 348 throughput := func(p *peerConnection) int { 349 return p.rates.Capacity(eth.BlockBodiesMsg, time.Second) 350 } 351 return ps.idlePeers(eth.QUAI1, eth.QUAI2, idle, throughput) 352 } 353 354 // idlePeers retrieves a flat list of all currently idle peers satisfying the 355 // protocol version constraints, using the provided function to check idleness. 356 // The resulting set of peers are sorted by their capacity. 357 func (ps *peerSet) idlePeers(minProtocol, maxProtocol uint, idleCheck func(*peerConnection) bool, capacity func(*peerConnection) int) ([]*peerConnection, int) { 358 ps.lock.RLock() 359 defer ps.lock.RUnlock() 360 361 var ( 362 total = 0 363 idle = make([]*peerConnection, 0, len(ps.peers)) 364 tps = make([]int, 0, len(ps.peers)) 365 ) 366 for _, p := range ps.peers { 367 if p.version >= minProtocol && p.version <= maxProtocol { 368 if idleCheck(p) { 369 idle = append(idle, p) 370 tps = append(tps, capacity(p)) 371 } 372 total++ 373 } 374 } 375 376 // And sort them 377 sortPeers := &peerCapacitySort{idle, tps} 378 sort.Sort(sortPeers) 379 return sortPeers.p, total 380 } 381 382 // peerCapacitySort implements sort.Interface. 383 // It sorts peer connections by capacity (descending). 384 type peerCapacitySort struct { 385 p []*peerConnection 386 tp []int 387 } 388 389 func (ps *peerCapacitySort) Len() int { 390 return len(ps.p) 391 } 392 393 func (ps *peerCapacitySort) Less(i, j int) bool { 394 return ps.tp[i] > ps.tp[j] 395 } 396 397 func (ps *peerCapacitySort) Swap(i, j int) { 398 ps.p[i], ps.p[j] = ps.p[j], ps.p[i] 399 ps.tp[i], ps.tp[j] = ps.tp[j], ps.tp[i] 400 }