github.com/theQRL/go-zond@v0.1.1/zond/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 "sync" 26 "time" 27 28 "github.com/theQRL/go-zond/common" 29 "github.com/theQRL/go-zond/event" 30 "github.com/theQRL/go-zond/log" 31 "github.com/theQRL/go-zond/p2p/msgrate" 32 "github.com/theQRL/go-zond/zond/protocols/zond" 33 ) 34 35 const ( 36 maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items 37 ) 38 39 var ( 40 errAlreadyRegistered = errors.New("peer is already registered") 41 errNotRegistered = errors.New("peer is not registered") 42 ) 43 44 // peerConnection represents an active peer from which hashes and blocks are retrieved. 45 type peerConnection struct { 46 id string // Unique identifier of the peer 47 48 rates *msgrate.Tracker // Tracker to hone in on the number of items retrievable per second 49 lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously) 50 51 peer Peer 52 53 version uint // Eth protocol version number to switch strategies 54 log log.Logger // Contextual logger to add extra infos to peer logs 55 lock sync.RWMutex 56 } 57 58 // LightPeer encapsulates the methods required to synchronise with a remote light peer. 59 type LightPeer interface { 60 Head() (common.Hash, *big.Int) 61 RequestHeadersByHash(common.Hash, int, int, bool, chan *zond.Response) (*zond.Request, error) 62 RequestHeadersByNumber(uint64, int, int, bool, chan *zond.Response) (*zond.Request, error) 63 } 64 65 // Peer encapsulates the methods required to synchronise with a remote full peer. 66 type Peer interface { 67 LightPeer 68 RequestBodies([]common.Hash, chan *zond.Response) (*zond.Request, error) 69 RequestReceipts([]common.Hash, chan *zond.Response) (*zond.Request, error) 70 } 71 72 // lightPeerWrapper wraps a LightPeer struct, stubbing out the Peer-only methods. 73 type lightPeerWrapper struct { 74 peer LightPeer 75 } 76 77 func (w *lightPeerWrapper) Head() (common.Hash, *big.Int) { return w.peer.Head() } 78 func (w *lightPeerWrapper) RequestHeadersByHash(h common.Hash, amount int, skip int, reverse bool, sink chan *zond.Response) (*zond.Request, error) { 79 return w.peer.RequestHeadersByHash(h, amount, skip, reverse, sink) 80 } 81 func (w *lightPeerWrapper) RequestHeadersByNumber(i uint64, amount int, skip int, reverse bool, sink chan *zond.Response) (*zond.Request, error) { 82 return w.peer.RequestHeadersByNumber(i, amount, skip, reverse, sink) 83 } 84 func (w *lightPeerWrapper) RequestBodies([]common.Hash, chan *zond.Response) (*zond.Request, error) { 85 panic("RequestBodies not supported in light client mode sync") 86 } 87 func (w *lightPeerWrapper) RequestReceipts([]common.Hash, chan *zond.Response) (*zond.Request, error) { 88 panic("RequestReceipts not supported in light client mode sync") 89 } 90 91 // newPeerConnection creates a new downloader peer. 92 func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection { 93 return &peerConnection{ 94 id: id, 95 lacking: make(map[common.Hash]struct{}), 96 peer: peer, 97 version: version, 98 log: logger, 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 p.lacking = make(map[common.Hash]struct{}) 108 } 109 110 // UpdateHeaderRate updates the peer's estimated header retrieval throughput with 111 // the current measurement. 112 func (p *peerConnection) UpdateHeaderRate(delivered int, elapsed time.Duration) { 113 p.rates.Update(zond.BlockHeadersMsg, elapsed, delivered) 114 } 115 116 // UpdateBodyRate updates the peer's estimated body retrieval throughput with the 117 // current measurement. 118 func (p *peerConnection) UpdateBodyRate(delivered int, elapsed time.Duration) { 119 p.rates.Update(zond.BlockBodiesMsg, elapsed, delivered) 120 } 121 122 // UpdateReceiptRate updates the peer's estimated receipt retrieval throughput 123 // with the current measurement. 124 func (p *peerConnection) UpdateReceiptRate(delivered int, elapsed time.Duration) { 125 p.rates.Update(zond.ReceiptsMsg, elapsed, delivered) 126 } 127 128 // HeaderCapacity retrieves the peer's header download allowance based on its 129 // previously discovered throughput. 130 func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int { 131 cap := p.rates.Capacity(zond.BlockHeadersMsg, targetRTT) 132 if cap > MaxHeaderFetch { 133 cap = MaxHeaderFetch 134 } 135 return cap 136 } 137 138 // BodyCapacity retrieves the peer's body download allowance based on its 139 // previously discovered throughput. 140 func (p *peerConnection) BodyCapacity(targetRTT time.Duration) int { 141 cap := p.rates.Capacity(zond.BlockBodiesMsg, targetRTT) 142 if cap > MaxBlockFetch { 143 cap = MaxBlockFetch 144 } 145 return cap 146 } 147 148 // ReceiptCapacity retrieves the peers receipt download allowance based on its 149 // previously discovered throughput. 150 func (p *peerConnection) ReceiptCapacity(targetRTT time.Duration) int { 151 cap := p.rates.Capacity(zond.ReceiptsMsg, targetRTT) 152 if cap > MaxReceiptFetch { 153 cap = MaxReceiptFetch 154 } 155 return cap 156 } 157 158 // MarkLacking appends a new entity to the set of items (blocks, receipts, states) 159 // that a peer is known not to have (i.e. have been requested before). If the 160 // set reaches its maximum allowed capacity, items are randomly dropped off. 161 func (p *peerConnection) MarkLacking(hash common.Hash) { 162 p.lock.Lock() 163 defer p.lock.Unlock() 164 165 for len(p.lacking) >= maxLackingHashes { 166 for drop := range p.lacking { 167 delete(p.lacking, drop) 168 break 169 } 170 } 171 p.lacking[hash] = struct{}{} 172 } 173 174 // Lacks retrieves whether the hash of a blockchain item is on the peers lacking 175 // list (i.e. whether we know that the peer does not have it). 176 func (p *peerConnection) Lacks(hash common.Hash) bool { 177 p.lock.RLock() 178 defer p.lock.RUnlock() 179 180 _, ok := p.lacking[hash] 181 return ok 182 } 183 184 // peeringEvent is sent on the peer event feed when a remote peer connects or 185 // disconnects. 186 type peeringEvent struct { 187 peer *peerConnection 188 join bool 189 } 190 191 // peerSet represents the collection of active peer participating in the chain 192 // download procedure. 193 type peerSet struct { 194 peers map[string]*peerConnection 195 rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat 196 events event.Feed // Feed to publish peer lifecycle events on 197 198 lock sync.RWMutex 199 } 200 201 // newPeerSet creates a new peer set top track the active download sources. 202 func newPeerSet() *peerSet { 203 return &peerSet{ 204 peers: make(map[string]*peerConnection), 205 rates: msgrate.NewTrackers(log.New("proto", "zond")), 206 } 207 } 208 209 // SubscribeEvents subscribes to peer arrival and departure events. 210 func (ps *peerSet) SubscribeEvents(ch chan<- *peeringEvent) event.Subscription { 211 return ps.events.Subscribe(ch) 212 } 213 214 // Reset iterates over the current peer set, and resets each of the known peers 215 // to prepare for a next batch of block retrieval. 216 func (ps *peerSet) Reset() { 217 ps.lock.RLock() 218 defer ps.lock.RUnlock() 219 220 for _, peer := range ps.peers { 221 peer.Reset() 222 } 223 } 224 225 // Register injects a new peer into the working set, or returns an error if the 226 // peer is already known. 227 // 228 // The method also sets the starting throughput values of the new peer to the 229 // average of all existing peers, to give it a realistic chance of being used 230 // for data retrievals. 231 func (ps *peerSet) Register(p *peerConnection) error { 232 // Register the new peer with some meaningful defaults 233 ps.lock.Lock() 234 if _, ok := ps.peers[p.id]; ok { 235 ps.lock.Unlock() 236 return errAlreadyRegistered 237 } 238 p.rates = msgrate.NewTracker(ps.rates.MeanCapacities(), ps.rates.MedianRoundTrip()) 239 if err := ps.rates.Track(p.id, p.rates); err != nil { 240 ps.lock.Unlock() 241 return err 242 } 243 ps.peers[p.id] = p 244 ps.lock.Unlock() 245 246 ps.events.Send(&peeringEvent{peer: p, join: true}) 247 return nil 248 } 249 250 // Unregister removes a remote peer from the active set, disabling any further 251 // actions to/from that particular entity. 252 func (ps *peerSet) Unregister(id string) error { 253 ps.lock.Lock() 254 p, ok := ps.peers[id] 255 if !ok { 256 ps.lock.Unlock() 257 return errNotRegistered 258 } 259 delete(ps.peers, id) 260 ps.rates.Untrack(id) 261 ps.lock.Unlock() 262 263 ps.events.Send(&peeringEvent{peer: p, join: false}) 264 return nil 265 } 266 267 // Peer retrieves the registered peer with the given id. 268 func (ps *peerSet) Peer(id string) *peerConnection { 269 ps.lock.RLock() 270 defer ps.lock.RUnlock() 271 272 return ps.peers[id] 273 } 274 275 // Len returns if the current number of peers in the set. 276 func (ps *peerSet) Len() int { 277 ps.lock.RLock() 278 defer ps.lock.RUnlock() 279 280 return len(ps.peers) 281 } 282 283 // AllPeers retrieves a flat list of all the peers within the set. 284 func (ps *peerSet) AllPeers() []*peerConnection { 285 ps.lock.RLock() 286 defer ps.lock.RUnlock() 287 288 list := make([]*peerConnection, 0, len(ps.peers)) 289 for _, p := range ps.peers { 290 list = append(list, p) 291 } 292 return list 293 } 294 295 // peerCapacitySort implements sort.Interface. 296 // It sorts peer connections by capacity (descending). 297 type peerCapacitySort struct { 298 peers []*peerConnection 299 caps []int 300 } 301 302 func (ps *peerCapacitySort) Len() int { 303 return len(ps.peers) 304 } 305 306 func (ps *peerCapacitySort) Less(i, j int) bool { 307 return ps.caps[i] > ps.caps[j] 308 } 309 310 func (ps *peerCapacitySort) Swap(i, j int) { 311 ps.peers[i], ps.peers[j] = ps.peers[j], ps.peers[i] 312 ps.caps[i], ps.caps[j] = ps.caps[j], ps.caps[i] 313 }