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