github.com/bcnmy/go-ethereum@v1.10.27/les/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/ethereum/go-ethereum/common" 31 "github.com/ethereum/go-ethereum/eth/protocols/eth" 32 "github.com/ethereum/go-ethereum/event" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/ethereum/go-ethereum/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 receiptIdle int32 // Current receipt activity state of the peer (idle = 0, active = 1) 54 stateIdle int32 // Current node data activity state of the peer (idle = 0, active = 1) 55 56 headerStarted time.Time // Time instance when the last header fetch was started 57 blockStarted time.Time // Time instance when the last block (body) fetch was started 58 receiptStarted time.Time // Time instance when the last receipt fetch was started 59 stateStarted time.Time // Time instance when the last node data fetch was started 60 61 rates *msgrate.Tracker // Tracker to hone in on the number of items retrievable per second 62 lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously) 63 64 peer Peer 65 66 version uint // Eth protocol version number to switch strategies 67 log log.Logger // Contextual logger to add extra infos to peer logs 68 lock sync.RWMutex 69 } 70 71 // LightPeer encapsulates the methods required to synchronise with a remote light peer. 72 type LightPeer interface { 73 Head() (common.Hash, *big.Int) 74 RequestHeadersByHash(common.Hash, int, int, bool) error 75 RequestHeadersByNumber(uint64, int, int, bool) error 76 } 77 78 // Peer encapsulates the methods required to synchronise with a remote full peer. 79 type Peer interface { 80 LightPeer 81 RequestBodies([]common.Hash) error 82 RequestReceipts([]common.Hash) error 83 RequestNodeData([]common.Hash) error 84 } 85 86 // lightPeerWrapper wraps a LightPeer struct, stubbing out the Peer-only methods. 87 type lightPeerWrapper struct { 88 peer LightPeer 89 } 90 91 func (w *lightPeerWrapper) Head() (common.Hash, *big.Int) { return w.peer.Head() } 92 func (w *lightPeerWrapper) RequestHeadersByHash(h common.Hash, amount int, skip int, reverse bool) error { 93 return w.peer.RequestHeadersByHash(h, amount, skip, reverse) 94 } 95 func (w *lightPeerWrapper) RequestHeadersByNumber(i uint64, amount int, skip int, reverse bool) error { 96 return w.peer.RequestHeadersByNumber(i, amount, skip, reverse) 97 } 98 func (w *lightPeerWrapper) RequestBodies([]common.Hash) error { 99 panic("RequestBodies not supported in light client mode sync") 100 } 101 func (w *lightPeerWrapper) RequestReceipts([]common.Hash) error { 102 panic("RequestReceipts not supported in light client mode sync") 103 } 104 func (w *lightPeerWrapper) RequestNodeData([]common.Hash) error { 105 panic("RequestNodeData not supported in light client mode sync") 106 } 107 108 // newPeerConnection creates a new downloader peer. 109 func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection { 110 return &peerConnection{ 111 id: id, 112 lacking: make(map[common.Hash]struct{}), 113 peer: peer, 114 version: version, 115 log: logger, 116 } 117 } 118 119 // Reset clears the internal state of a peer entity. 120 func (p *peerConnection) Reset() { 121 p.lock.Lock() 122 defer p.lock.Unlock() 123 124 atomic.StoreInt32(&p.headerIdle, 0) 125 atomic.StoreInt32(&p.blockIdle, 0) 126 atomic.StoreInt32(&p.receiptIdle, 0) 127 atomic.StoreInt32(&p.stateIdle, 0) 128 129 p.lacking = make(map[common.Hash]struct{}) 130 } 131 132 // FetchHeaders sends a header retrieval request to the remote peer. 133 func (p *peerConnection) FetchHeaders(from uint64, count int) error { 134 // Short circuit if the peer is already fetching 135 if !atomic.CompareAndSwapInt32(&p.headerIdle, 0, 1) { 136 return errAlreadyFetching 137 } 138 p.headerStarted = time.Now() 139 140 // Issue the header retrieval request (absolute upwards without gaps) 141 go p.peer.RequestHeadersByNumber(from, count, 0, false) 142 143 return nil 144 } 145 146 // FetchBodies sends a block body retrieval request to the remote peer. 147 func (p *peerConnection) FetchBodies(request *fetchRequest) error { 148 // Short circuit if the peer is already fetching 149 if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) { 150 return errAlreadyFetching 151 } 152 p.blockStarted = time.Now() 153 154 go func() { 155 // Convert the header set to a retrievable slice 156 hashes := make([]common.Hash, 0, len(request.Headers)) 157 for _, header := range request.Headers { 158 hashes = append(hashes, header.Hash()) 159 } 160 p.peer.RequestBodies(hashes) 161 }() 162 163 return nil 164 } 165 166 // FetchReceipts sends a receipt retrieval request to the remote peer. 167 func (p *peerConnection) FetchReceipts(request *fetchRequest) error { 168 // Short circuit if the peer is already fetching 169 if !atomic.CompareAndSwapInt32(&p.receiptIdle, 0, 1) { 170 return errAlreadyFetching 171 } 172 p.receiptStarted = time.Now() 173 174 go func() { 175 // Convert the header set to a retrievable slice 176 hashes := make([]common.Hash, 0, len(request.Headers)) 177 for _, header := range request.Headers { 178 hashes = append(hashes, header.Hash()) 179 } 180 p.peer.RequestReceipts(hashes) 181 }() 182 183 return nil 184 } 185 186 // FetchNodeData sends a node state data retrieval request to the remote peer. 187 func (p *peerConnection) FetchNodeData(hashes []common.Hash) error { 188 // Short circuit if the peer is already fetching 189 if !atomic.CompareAndSwapInt32(&p.stateIdle, 0, 1) { 190 return errAlreadyFetching 191 } 192 p.stateStarted = time.Now() 193 194 go p.peer.RequestNodeData(hashes) 195 196 return nil 197 } 198 199 // SetHeadersIdle sets the peer to idle, allowing it to execute new header retrieval 200 // requests. Its estimated header retrieval throughput is updated with that measured 201 // just now. 202 func (p *peerConnection) SetHeadersIdle(delivered int, deliveryTime time.Time) { 203 p.rates.Update(eth.BlockHeadersMsg, deliveryTime.Sub(p.headerStarted), delivered) 204 atomic.StoreInt32(&p.headerIdle, 0) 205 } 206 207 // SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval 208 // requests. Its estimated body retrieval throughput is updated with that measured 209 // just now. 210 func (p *peerConnection) SetBodiesIdle(delivered int, deliveryTime time.Time) { 211 p.rates.Update(eth.BlockBodiesMsg, deliveryTime.Sub(p.blockStarted), delivered) 212 atomic.StoreInt32(&p.blockIdle, 0) 213 } 214 215 // SetReceiptsIdle sets the peer to idle, allowing it to execute new receipt 216 // retrieval requests. Its estimated receipt retrieval throughput is updated 217 // with that measured just now. 218 func (p *peerConnection) SetReceiptsIdle(delivered int, deliveryTime time.Time) { 219 p.rates.Update(eth.ReceiptsMsg, deliveryTime.Sub(p.receiptStarted), delivered) 220 atomic.StoreInt32(&p.receiptIdle, 0) 221 } 222 223 // SetNodeDataIdle sets the peer to idle, allowing it to execute new state trie 224 // data retrieval requests. Its estimated state retrieval throughput is updated 225 // with that measured just now. 226 func (p *peerConnection) SetNodeDataIdle(delivered int, deliveryTime time.Time) { 227 p.rates.Update(eth.NodeDataMsg, deliveryTime.Sub(p.stateStarted), delivered) 228 atomic.StoreInt32(&p.stateIdle, 0) 229 } 230 231 // HeaderCapacity retrieves the peers header download allowance based on its 232 // previously discovered throughput. 233 func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int { 234 cap := p.rates.Capacity(eth.BlockHeadersMsg, targetRTT) 235 if cap > MaxHeaderFetch { 236 cap = MaxHeaderFetch 237 } 238 return cap 239 } 240 241 // BlockCapacity retrieves the peers block download allowance based on its 242 // previously discovered throughput. 243 func (p *peerConnection) BlockCapacity(targetRTT time.Duration) int { 244 cap := p.rates.Capacity(eth.BlockBodiesMsg, targetRTT) 245 if cap > MaxBlockFetch { 246 cap = MaxBlockFetch 247 } 248 return cap 249 } 250 251 // ReceiptCapacity retrieves the peers receipt download allowance based on its 252 // previously discovered throughput. 253 func (p *peerConnection) ReceiptCapacity(targetRTT time.Duration) int { 254 cap := p.rates.Capacity(eth.ReceiptsMsg, targetRTT) 255 if cap > MaxReceiptFetch { 256 cap = MaxReceiptFetch 257 } 258 return cap 259 } 260 261 // NodeDataCapacity retrieves the peers state download allowance based on its 262 // previously discovered throughput. 263 func (p *peerConnection) NodeDataCapacity(targetRTT time.Duration) int { 264 cap := p.rates.Capacity(eth.NodeDataMsg, targetRTT) 265 if cap > MaxStateFetch { 266 cap = MaxStateFetch 267 } 268 return cap 269 } 270 271 // MarkLacking appends a new entity to the set of items (blocks, receipts, states) 272 // that a peer is known not to have (i.e. have been requested before). If the 273 // set reaches its maximum allowed capacity, items are randomly dropped off. 274 func (p *peerConnection) MarkLacking(hash common.Hash) { 275 p.lock.Lock() 276 defer p.lock.Unlock() 277 278 for len(p.lacking) >= maxLackingHashes { 279 for drop := range p.lacking { 280 delete(p.lacking, drop) 281 break 282 } 283 } 284 p.lacking[hash] = struct{}{} 285 } 286 287 // Lacks retrieves whether the hash of a blockchain item is on the peers lacking 288 // list (i.e. whether we know that the peer does not have it). 289 func (p *peerConnection) Lacks(hash common.Hash) bool { 290 p.lock.RLock() 291 defer p.lock.RUnlock() 292 293 _, ok := p.lacking[hash] 294 return ok 295 } 296 297 // peerSet represents the collection of active peer participating in the chain 298 // download procedure. 299 type peerSet struct { 300 peers map[string]*peerConnection 301 rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat 302 303 newPeerFeed event.Feed 304 peerDropFeed event.Feed 305 306 lock sync.RWMutex 307 } 308 309 // newPeerSet creates a new peer set top track the active download sources. 310 func newPeerSet() *peerSet { 311 return &peerSet{ 312 peers: make(map[string]*peerConnection), 313 rates: msgrate.NewTrackers(log.New("proto", "eth")), 314 } 315 } 316 317 // SubscribeNewPeers subscribes to peer arrival events. 318 func (ps *peerSet) SubscribeNewPeers(ch chan<- *peerConnection) event.Subscription { 319 return ps.newPeerFeed.Subscribe(ch) 320 } 321 322 // SubscribePeerDrops subscribes to peer departure events. 323 func (ps *peerSet) SubscribePeerDrops(ch chan<- *peerConnection) event.Subscription { 324 return ps.peerDropFeed.Subscribe(ch) 325 } 326 327 // Reset iterates over the current peer set, and resets each of the known peers 328 // to prepare for a next batch of block retrieval. 329 func (ps *peerSet) Reset() { 330 ps.lock.RLock() 331 defer ps.lock.RUnlock() 332 333 for _, peer := range ps.peers { 334 peer.Reset() 335 } 336 } 337 338 // Register injects a new peer into the working set, or returns an error if the 339 // peer is already known. 340 // 341 // The method also sets the starting throughput values of the new peer to the 342 // average of all existing peers, to give it a realistic chance of being used 343 // for data retrievals. 344 func (ps *peerSet) Register(p *peerConnection) error { 345 // Register the new peer with some meaningful defaults 346 ps.lock.Lock() 347 if _, ok := ps.peers[p.id]; ok { 348 ps.lock.Unlock() 349 return errAlreadyRegistered 350 } 351 p.rates = msgrate.NewTracker(ps.rates.MeanCapacities(), ps.rates.MedianRoundTrip()) 352 if err := ps.rates.Track(p.id, p.rates); err != nil { 353 ps.lock.Unlock() 354 return err 355 } 356 ps.peers[p.id] = p 357 ps.lock.Unlock() 358 359 ps.newPeerFeed.Send(p) 360 return nil 361 } 362 363 // Unregister removes a remote peer from the active set, disabling any further 364 // actions to/from that particular entity. 365 func (ps *peerSet) Unregister(id string) error { 366 ps.lock.Lock() 367 p, ok := ps.peers[id] 368 if !ok { 369 ps.lock.Unlock() 370 return errNotRegistered 371 } 372 delete(ps.peers, id) 373 ps.rates.Untrack(id) 374 ps.lock.Unlock() 375 376 ps.peerDropFeed.Send(p) 377 return nil 378 } 379 380 // Peer retrieves the registered peer with the given id. 381 func (ps *peerSet) Peer(id string) *peerConnection { 382 ps.lock.RLock() 383 defer ps.lock.RUnlock() 384 385 return ps.peers[id] 386 } 387 388 // Len returns if the current number of peers in the set. 389 func (ps *peerSet) Len() int { 390 ps.lock.RLock() 391 defer ps.lock.RUnlock() 392 393 return len(ps.peers) 394 } 395 396 // AllPeers retrieves a flat list of all the peers within the set. 397 func (ps *peerSet) AllPeers() []*peerConnection { 398 ps.lock.RLock() 399 defer ps.lock.RUnlock() 400 401 list := make([]*peerConnection, 0, len(ps.peers)) 402 for _, p := range ps.peers { 403 list = append(list, p) 404 } 405 return list 406 } 407 408 // HeaderIdlePeers retrieves a flat list of all the currently header-idle peers 409 // within the active peer set, ordered by their reputation. 410 func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) { 411 idle := func(p *peerConnection) bool { 412 return atomic.LoadInt32(&p.headerIdle) == 0 413 } 414 throughput := func(p *peerConnection) int { 415 return p.rates.Capacity(eth.BlockHeadersMsg, time.Second) 416 } 417 return ps.idlePeers(eth.ETH66, eth.ETH67, idle, throughput) 418 } 419 420 // BodyIdlePeers retrieves a flat list of all the currently body-idle peers within 421 // the active peer set, ordered by their reputation. 422 func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) { 423 idle := func(p *peerConnection) bool { 424 return atomic.LoadInt32(&p.blockIdle) == 0 425 } 426 throughput := func(p *peerConnection) int { 427 return p.rates.Capacity(eth.BlockBodiesMsg, time.Second) 428 } 429 return ps.idlePeers(eth.ETH66, eth.ETH67, idle, throughput) 430 } 431 432 // ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers 433 // within the active peer set, ordered by their reputation. 434 func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) { 435 idle := func(p *peerConnection) bool { 436 return atomic.LoadInt32(&p.receiptIdle) == 0 437 } 438 throughput := func(p *peerConnection) int { 439 return p.rates.Capacity(eth.ReceiptsMsg, time.Second) 440 } 441 return ps.idlePeers(eth.ETH66, eth.ETH67, idle, throughput) 442 } 443 444 // NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle 445 // peers within the active peer set, ordered by their reputation. 446 func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) { 447 idle := func(p *peerConnection) bool { 448 return atomic.LoadInt32(&p.stateIdle) == 0 449 } 450 throughput := func(p *peerConnection) int { 451 return p.rates.Capacity(eth.NodeDataMsg, time.Second) 452 } 453 return ps.idlePeers(eth.ETH66, eth.ETH67, idle, throughput) 454 } 455 456 // idlePeers retrieves a flat list of all currently idle peers satisfying the 457 // protocol version constraints, using the provided function to check idleness. 458 // The resulting set of peers are sorted by their capacity. 459 func (ps *peerSet) idlePeers(minProtocol, maxProtocol uint, idleCheck func(*peerConnection) bool, capacity func(*peerConnection) int) ([]*peerConnection, int) { 460 ps.lock.RLock() 461 defer ps.lock.RUnlock() 462 463 var ( 464 total = 0 465 idle = make([]*peerConnection, 0, len(ps.peers)) 466 tps = make([]int, 0, len(ps.peers)) 467 ) 468 for _, p := range ps.peers { 469 if p.version >= minProtocol && p.version <= maxProtocol { 470 if idleCheck(p) { 471 idle = append(idle, p) 472 tps = append(tps, capacity(p)) 473 } 474 total++ 475 } 476 } 477 478 // And sort them 479 sortPeers := &peerCapacitySort{idle, tps} 480 sort.Sort(sortPeers) 481 return sortPeers.p, total 482 } 483 484 // peerCapacitySort implements sort.Interface. 485 // It sorts peer connections by capacity (descending). 486 type peerCapacitySort struct { 487 p []*peerConnection 488 tp []int 489 } 490 491 func (ps *peerCapacitySort) Len() int { 492 return len(ps.p) 493 } 494 495 func (ps *peerCapacitySort) Less(i, j int) bool { 496 return ps.tp[i] > ps.tp[j] 497 } 498 499 func (ps *peerCapacitySort) Swap(i, j int) { 500 ps.p[i], ps.p[j] = ps.p[j], ps.p[i] 501 ps.tp[i], ps.tp[j] = ps.tp[j], ps.tp[i] 502 }