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