github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/vnt/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 "fmt" 25 "math" 26 "math/big" 27 "sort" 28 "sync" 29 "sync/atomic" 30 "time" 31 32 libp2p "github.com/libp2p/go-libp2p-peer" 33 "github.com/vntchain/go-vnt/common" 34 "github.com/vntchain/go-vnt/event" 35 "github.com/vntchain/go-vnt/log" 36 ) 37 38 const ( 39 maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items 40 measurementImpact = 0.1 // The impact a single measurement has on a peer's final throughput value. 41 ) 42 43 var ( 44 errAlreadyFetching = errors.New("already fetching blocks from peer") 45 errAlreadyRegistered = errors.New("peer is already registered") 46 errNotRegistered = errors.New("peer is not registered") 47 ) 48 49 // peerConnection represents an active peer from which hashes and blocks are retrieved. 50 type peerConnection struct { 51 id libp2p.ID // Unique identifier of the peer 52 53 headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1) 54 blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1) 55 receiptIdle int32 // Current receipt activity state of the peer (idle = 0, active = 1) 56 stateIdle int32 // Current node data activity state of the peer (idle = 0, active = 1) 57 58 headerThroughput float64 // Number of headers measured to be retrievable per second 59 blockThroughput float64 // Number of blocks (bodies) measured to be retrievable per second 60 receiptThroughput float64 // Number of receipts measured to be retrievable per second 61 stateThroughput float64 // Number of node data pieces measured to be retrievable per second 62 63 rtt time.Duration // Request round trip time to track responsiveness (QoS) 64 65 headerStarted time.Time // Time instance when the last header fetch was started 66 blockStarted time.Time // Time instance when the last block (body) fetch was started 67 receiptStarted time.Time // Time instance when the last receipt fetch was started 68 stateStarted time.Time // Time instance when the last node data fetch was started 69 70 lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously) 71 72 peer Peer 73 74 version int // Vnt protocol version number to switch strategies 75 log log.Logger // Contextual logger to add extra infos to peer logs 76 lock sync.RWMutex 77 } 78 79 // LightPeer encapsulates the methods required to synchronise with a remote light peer. 80 type LightPeer interface { 81 Head() (common.Hash, *big.Int) 82 RequestHeadersByHash(common.Hash, int, int, bool) error 83 RequestHeadersByNumber(uint64, int, int, bool) error 84 } 85 86 // Peer encapsulates the methods required to synchronise with a remote full peer. 87 type Peer interface { 88 LightPeer 89 RequestBodies([]common.Hash) error 90 RequestReceipts([]common.Hash) error 91 RequestNodeData([]common.Hash) error 92 } 93 94 // lightPeerWrapper wraps a LightPeer struct, stubbing out the Peer-only methods. 95 type lightPeerWrapper struct { 96 peer LightPeer 97 } 98 99 func (w *lightPeerWrapper) Head() (common.Hash, *big.Int) { return w.peer.Head() } 100 func (w *lightPeerWrapper) RequestHeadersByHash(h common.Hash, amount int, skip int, reverse bool) error { 101 return w.peer.RequestHeadersByHash(h, amount, skip, reverse) 102 } 103 func (w *lightPeerWrapper) RequestHeadersByNumber(i uint64, amount int, skip int, reverse bool) error { 104 return w.peer.RequestHeadersByNumber(i, amount, skip, reverse) 105 } 106 func (w *lightPeerWrapper) RequestBodies([]common.Hash) error { 107 panic("RequestBodies not supported in light client mode sync") 108 } 109 func (w *lightPeerWrapper) RequestReceipts([]common.Hash) error { 110 panic("RequestReceipts not supported in light client mode sync") 111 } 112 func (w *lightPeerWrapper) RequestNodeData([]common.Hash) error { 113 panic("RequestNodeData not supported in light client mode sync") 114 } 115 116 // newPeerConnection creates a new downloader peer. 117 func newPeerConnection(id libp2p.ID, version int, peer Peer, logger log.Logger) *peerConnection { 118 return &peerConnection{ 119 id: id, 120 lacking: make(map[common.Hash]struct{}), 121 122 peer: peer, 123 124 version: version, 125 log: logger, 126 } 127 } 128 129 // Reset clears the internal state of a peer entity. 130 func (p *peerConnection) Reset() { 131 p.lock.Lock() 132 defer p.lock.Unlock() 133 134 atomic.StoreInt32(&p.headerIdle, 0) 135 atomic.StoreInt32(&p.blockIdle, 0) 136 atomic.StoreInt32(&p.receiptIdle, 0) 137 atomic.StoreInt32(&p.stateIdle, 0) 138 139 p.headerThroughput = 0 140 p.blockThroughput = 0 141 p.receiptThroughput = 0 142 p.stateThroughput = 0 143 144 p.lacking = make(map[common.Hash]struct{}) 145 } 146 147 // FetchHeaders sends a header retrieval request to the remote peer. 148 func (p *peerConnection) FetchHeaders(from uint64, count int) error { 149 // Sanity check the protocol version 150 if p.version < 62 { 151 panic(fmt.Sprintf("header fetch [vnt/62+] requested on vnt/%d", p.version)) 152 } 153 // Short circuit if the peer is already fetching 154 if !atomic.CompareAndSwapInt32(&p.headerIdle, 0, 1) { 155 return errAlreadyFetching 156 } 157 p.headerStarted = time.Now() 158 159 // Issue the header retrieval request (absolut upwards without gaps) 160 go p.peer.RequestHeadersByNumber(from, count, 0, false) 161 162 return nil 163 } 164 165 // FetchBodies sends a block body retrieval request to the remote peer. 166 func (p *peerConnection) FetchBodies(request *fetchRequest) error { 167 // Sanity check the protocol version 168 if p.version < 62 { 169 panic(fmt.Sprintf("body fetch [vnt/62+] requested on vnt/%d", p.version)) 170 } 171 // Short circuit if the peer is already fetching 172 if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) { 173 return errAlreadyFetching 174 } 175 p.blockStarted = time.Now() 176 177 // Convert the header set to a retrievable slice 178 hashes := make([]common.Hash, 0, len(request.Headers)) 179 for _, header := range request.Headers { 180 hashes = append(hashes, header.Hash()) 181 } 182 go p.peer.RequestBodies(hashes) 183 184 return nil 185 } 186 187 // FetchReceipts sends a receipt retrieval request to the remote peer. 188 func (p *peerConnection) FetchReceipts(request *fetchRequest) error { 189 // Sanity check the protocol version 190 if p.version < 63 { 191 panic(fmt.Sprintf("body fetch [vnt/63+] requested on vnt/%d", p.version)) 192 } 193 // Short circuit if the peer is already fetching 194 if !atomic.CompareAndSwapInt32(&p.receiptIdle, 0, 1) { 195 return errAlreadyFetching 196 } 197 p.receiptStarted = time.Now() 198 199 // Convert the header set to a retrievable slice 200 hashes := make([]common.Hash, 0, len(request.Headers)) 201 for _, header := range request.Headers { 202 hashes = append(hashes, header.Hash()) 203 } 204 go p.peer.RequestReceipts(hashes) 205 206 return nil 207 } 208 209 // FetchNodeData sends a node state data retrieval request to the remote peer. 210 func (p *peerConnection) FetchNodeData(hashes []common.Hash) error { 211 // Sanity check the protocol version 212 if p.version < 63 { 213 panic(fmt.Sprintf("node data fetch [vnt/63+] requested on vnt/%d", p.version)) 214 } 215 // Short circuit if the peer is already fetching 216 if !atomic.CompareAndSwapInt32(&p.stateIdle, 0, 1) { 217 return errAlreadyFetching 218 } 219 p.stateStarted = time.Now() 220 221 go p.peer.RequestNodeData(hashes) 222 223 return nil 224 } 225 226 // SetHeadersIdle sets the peer to idle, allowing it to execute new header retrieval 227 // requests. Its estimated header retrieval throughput is updated with that measured 228 // just now. 229 func (p *peerConnection) SetHeadersIdle(delivered int) { 230 p.setIdle(p.headerStarted, delivered, &p.headerThroughput, &p.headerIdle) 231 } 232 233 // SetBlocksIdle sets the peer to idle, allowing it to execute new block retrieval 234 // requests. Its estimated block retrieval throughput is updated with that measured 235 // just now. 236 func (p *peerConnection) SetBlocksIdle(delivered int) { 237 p.setIdle(p.blockStarted, delivered, &p.blockThroughput, &p.blockIdle) 238 } 239 240 // SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval 241 // requests. Its estimated body retrieval throughput is updated with that measured 242 // just now. 243 func (p *peerConnection) SetBodiesIdle(delivered int) { 244 p.setIdle(p.blockStarted, delivered, &p.blockThroughput, &p.blockIdle) 245 } 246 247 // SetReceiptsIdle sets the peer to idle, allowing it to execute new receipt 248 // retrieval requests. Its estimated receipt retrieval throughput is updated 249 // with that measured just now. 250 func (p *peerConnection) SetReceiptsIdle(delivered int) { 251 p.setIdle(p.receiptStarted, delivered, &p.receiptThroughput, &p.receiptIdle) 252 } 253 254 // SetNodeDataIdle sets the peer to idle, allowing it to execute new state trie 255 // data retrieval requests. Its estimated state retrieval throughput is updated 256 // with that measured just now. 257 func (p *peerConnection) SetNodeDataIdle(delivered int) { 258 p.setIdle(p.stateStarted, delivered, &p.stateThroughput, &p.stateIdle) 259 } 260 261 // setIdle sets the peer to idle, allowing it to execute new retrieval requests. 262 // Its estimated retrieval throughput is updated with that measured just now. 263 func (p *peerConnection) setIdle(started time.Time, delivered int, throughput *float64, idle *int32) { 264 // Irrelevant of the scaling, make sure the peer ends up idle 265 defer atomic.StoreInt32(idle, 0) 266 267 p.lock.Lock() 268 defer p.lock.Unlock() 269 270 // If nothing was delivered (hard timeout / unavailable data), reduce throughput to minimum 271 if delivered == 0 { 272 *throughput = 0 273 return 274 } 275 // Otherwise update the throughput with a new measurement 276 elapsed := time.Since(started) + 1 // +1 (ns) to ensure non-zero divisor 277 measured := float64(delivered) / (float64(elapsed) / float64(time.Second)) 278 279 *throughput = (1-measurementImpact)*(*throughput) + measurementImpact*measured 280 p.rtt = time.Duration((1-measurementImpact)*float64(p.rtt) + measurementImpact*float64(elapsed)) 281 282 p.log.Trace("Peer throughput measurements updated", 283 "hps", p.headerThroughput, "bps", p.blockThroughput, 284 "rps", p.receiptThroughput, "sps", p.stateThroughput, 285 "miss", len(p.lacking), "rtt", p.rtt) 286 } 287 288 // HeaderCapacity retrieves the peers header download allowance based on its 289 // previously discovered throughput. 290 func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int { 291 p.lock.RLock() 292 defer p.lock.RUnlock() 293 294 return int(math.Min(1+math.Max(1, p.headerThroughput*float64(targetRTT)/float64(time.Second)), float64(MaxHeaderFetch))) 295 } 296 297 // BlockCapacity retrieves the peers block download allowance based on its 298 // previously discovered throughput. 299 func (p *peerConnection) BlockCapacity(targetRTT time.Duration) int { 300 p.lock.RLock() 301 defer p.lock.RUnlock() 302 303 return int(math.Min(1+math.Max(1, p.blockThroughput*float64(targetRTT)/float64(time.Second)), float64(MaxBlockFetch))) 304 } 305 306 // ReceiptCapacity retrieves the peers receipt download allowance based on its 307 // previously discovered throughput. 308 func (p *peerConnection) ReceiptCapacity(targetRTT time.Duration) int { 309 p.lock.RLock() 310 defer p.lock.RUnlock() 311 312 return int(math.Min(1+math.Max(1, p.receiptThroughput*float64(targetRTT)/float64(time.Second)), float64(MaxReceiptFetch))) 313 } 314 315 // NodeDataCapacity retrieves the peers state download allowance based on its 316 // previously discovered throughput. 317 func (p *peerConnection) NodeDataCapacity(targetRTT time.Duration) int { 318 p.lock.RLock() 319 defer p.lock.RUnlock() 320 321 return int(math.Min(1+math.Max(1, p.stateThroughput*float64(targetRTT)/float64(time.Second)), float64(MaxStateFetch))) 322 } 323 324 // MarkLacking appends a new entity to the set of items (blocks, receipts, states) 325 // that a peer is known not to have (i.e. have been requested before). If the 326 // set reaches its maximum allowed capacity, items are randomly dropped off. 327 func (p *peerConnection) MarkLacking(hash common.Hash) { 328 p.lock.Lock() 329 defer p.lock.Unlock() 330 331 for len(p.lacking) >= maxLackingHashes { 332 for drop := range p.lacking { 333 delete(p.lacking, drop) 334 break 335 } 336 } 337 p.lacking[hash] = struct{}{} 338 } 339 340 // Lacks retrieves whether the hash of a blockchain item is on the peers lacking 341 // list (i.e. whether we know that the peer does not have it). 342 func (p *peerConnection) Lacks(hash common.Hash) bool { 343 p.lock.RLock() 344 defer p.lock.RUnlock() 345 346 _, ok := p.lacking[hash] 347 return ok 348 } 349 350 // peerSet represents the collection of active peer participating in the chain 351 // download procedure. 352 type peerSet struct { 353 peers map[libp2p.ID]*peerConnection 354 newPeerFeed event.Feed 355 peerDropFeed event.Feed 356 lock sync.RWMutex 357 } 358 359 // newPeerSet creates a new peer set top track the active download sources. 360 func newPeerSet() *peerSet { 361 return &peerSet{ 362 peers: make(map[libp2p.ID]*peerConnection), 363 } 364 } 365 366 // SubscribeNewPeers subscribes to peer arrival events. 367 func (ps *peerSet) SubscribeNewPeers(ch chan<- *peerConnection) event.Subscription { 368 return ps.newPeerFeed.Subscribe(ch) 369 } 370 371 // SubscribePeerDrops subscribes to peer departure events. 372 func (ps *peerSet) SubscribePeerDrops(ch chan<- *peerConnection) event.Subscription { 373 return ps.peerDropFeed.Subscribe(ch) 374 } 375 376 // Reset iterates over the current peer set, and resets each of the known peers 377 // to prepare for a next batch of block retrieval. 378 func (ps *peerSet) Reset() { 379 ps.lock.RLock() 380 defer ps.lock.RUnlock() 381 382 for _, peer := range ps.peers { 383 peer.Reset() 384 } 385 } 386 387 // Register injects a new peer into the working set, or returns an error if the 388 // peer is already known. 389 // 390 // The method also sets the starting throughput values of the new peer to the 391 // average of all existing peers, to give it a realistic chance of being used 392 // for data retrievals. 393 func (ps *peerSet) Register(p *peerConnection) error { 394 // Retrieve the current median RTT as a sane default 395 p.rtt = ps.medianRTT() 396 397 // Register the new peer with some meaningful defaults 398 ps.lock.Lock() 399 if _, ok := ps.peers[p.id]; ok { 400 ps.lock.Unlock() 401 return errAlreadyRegistered 402 } 403 if len(ps.peers) > 0 { 404 p.headerThroughput, p.blockThroughput, p.receiptThroughput, p.stateThroughput = 0, 0, 0, 0 405 406 for _, peer := range ps.peers { 407 peer.lock.RLock() 408 p.headerThroughput += peer.headerThroughput 409 p.blockThroughput += peer.blockThroughput 410 p.receiptThroughput += peer.receiptThroughput 411 p.stateThroughput += peer.stateThroughput 412 peer.lock.RUnlock() 413 } 414 p.headerThroughput /= float64(len(ps.peers)) 415 p.blockThroughput /= float64(len(ps.peers)) 416 p.receiptThroughput /= float64(len(ps.peers)) 417 p.stateThroughput /= float64(len(ps.peers)) 418 } 419 ps.peers[p.id] = p 420 ps.lock.Unlock() 421 422 ps.newPeerFeed.Send(p) 423 return nil 424 } 425 426 // Unregister removes a remote peer from the active set, disabling any further 427 // actions to/from that particular entity. 428 func (ps *peerSet) Unregister(id libp2p.ID) error { 429 ps.lock.Lock() 430 p, ok := ps.peers[id] 431 if !ok { 432 defer ps.lock.Unlock() 433 return errNotRegistered 434 } 435 delete(ps.peers, id) 436 ps.lock.Unlock() 437 438 ps.peerDropFeed.Send(p) 439 return nil 440 } 441 442 // Peer retrieves the registered peer with the given id. 443 func (ps *peerSet) Peer(id libp2p.ID) *peerConnection { 444 ps.lock.RLock() 445 defer ps.lock.RUnlock() 446 447 return ps.peers[id] 448 } 449 450 // Len returns if the current number of peers in the set. 451 func (ps *peerSet) Len() int { 452 ps.lock.RLock() 453 defer ps.lock.RUnlock() 454 455 return len(ps.peers) 456 } 457 458 // AllPeers retrieves a flat list of all the peers within the set. 459 func (ps *peerSet) AllPeers() []*peerConnection { 460 ps.lock.RLock() 461 defer ps.lock.RUnlock() 462 463 list := make([]*peerConnection, 0, len(ps.peers)) 464 for _, p := range ps.peers { 465 list = append(list, p) 466 } 467 return list 468 } 469 470 // HeaderIdlePeers retrieves a flat list of all the currently header-idle peers 471 // within the active peer set, ordered by their reputation. 472 func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) { 473 idle := func(p *peerConnection) bool { 474 return atomic.LoadInt32(&p.headerIdle) == 0 475 } 476 throughput := func(p *peerConnection) float64 { 477 p.lock.RLock() 478 defer p.lock.RUnlock() 479 return p.headerThroughput 480 } 481 return ps.idlePeers(62, 64, idle, throughput) 482 } 483 484 // BodyIdlePeers retrieves a flat list of all the currently body-idle peers within 485 // the active peer set, ordered by their reputation. 486 func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) { 487 idle := func(p *peerConnection) bool { 488 return atomic.LoadInt32(&p.blockIdle) == 0 489 } 490 throughput := func(p *peerConnection) float64 { 491 p.lock.RLock() 492 defer p.lock.RUnlock() 493 return p.blockThroughput 494 } 495 return ps.idlePeers(62, 64, idle, throughput) 496 } 497 498 // ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers 499 // within the active peer set, ordered by their reputation. 500 func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) { 501 idle := func(p *peerConnection) bool { 502 return atomic.LoadInt32(&p.receiptIdle) == 0 503 } 504 throughput := func(p *peerConnection) float64 { 505 p.lock.RLock() 506 defer p.lock.RUnlock() 507 return p.receiptThroughput 508 } 509 return ps.idlePeers(63, 64, idle, throughput) 510 } 511 512 // NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle 513 // peers within the active peer set, ordered by their reputation. 514 func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) { 515 idle := func(p *peerConnection) bool { 516 return atomic.LoadInt32(&p.stateIdle) == 0 517 } 518 throughput := func(p *peerConnection) float64 { 519 p.lock.RLock() 520 defer p.lock.RUnlock() 521 return p.stateThroughput 522 } 523 return ps.idlePeers(63, 64, idle, throughput) 524 } 525 526 // idlePeers retrieves a flat list of all currently idle peers satisfying the 527 // protocol version constraints, using the provided function to check idleness. 528 // The resulting set of peers are sorted by their measure throughput. 529 func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peerConnection) bool, throughput func(*peerConnection) float64) ([]*peerConnection, int) { 530 ps.lock.RLock() 531 defer ps.lock.RUnlock() 532 533 idle, total := make([]*peerConnection, 0, len(ps.peers)), 0 534 for _, p := range ps.peers { 535 if p.version >= minProtocol && p.version <= maxProtocol { 536 if idleCheck(p) { 537 idle = append(idle, p) 538 } 539 total++ 540 } 541 } 542 for i := 0; i < len(idle); i++ { 543 for j := i + 1; j < len(idle); j++ { 544 if throughput(idle[i]) < throughput(idle[j]) { 545 idle[i], idle[j] = idle[j], idle[i] 546 } 547 } 548 } 549 return idle, total 550 } 551 552 // medianRTT returns the median RTT of the peerset, considering only the tuning 553 // peers if there are more peers available. 554 func (ps *peerSet) medianRTT() time.Duration { 555 // Gather all the currently measured round trip times 556 ps.lock.RLock() 557 defer ps.lock.RUnlock() 558 559 rtts := make([]float64, 0, len(ps.peers)) 560 for _, p := range ps.peers { 561 p.lock.RLock() 562 rtts = append(rtts, float64(p.rtt)) 563 p.lock.RUnlock() 564 } 565 sort.Float64s(rtts) 566 567 median := rttMaxEstimate 568 if qosTuningPeers <= len(rtts) { 569 median = time.Duration(rtts[qosTuningPeers/2]) // Median of our tuning peers 570 } else if len(rtts) > 0 { 571 median = time.Duration(rtts[len(rtts)/2]) // Median of our connected peers (maintain even like this some baseline qos) 572 } 573 // Restrict the RTT into some QoS defaults, irrelevant of true RTT 574 if median < rttMinEstimate { 575 median = rttMinEstimate 576 } 577 if median > rttMaxEstimate { 578 median = rttMaxEstimate 579 } 580 return median 581 }