github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/protocols/snap/snapstream/snapleecher/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 snapleecher 21 22 import ( 23 "errors" 24 "sort" 25 "sync" 26 "sync/atomic" 27 "time" 28 29 "github.com/unicornultrafoundation/go-u2u/common" 30 "github.com/unicornultrafoundation/go-u2u/event" 31 "github.com/unicornultrafoundation/go-u2u/log" 32 "github.com/unicornultrafoundation/go-u2u/p2p/msgrate" 33 ) 34 35 const ( 36 maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items 37 38 // ETH protocol constants 39 eth65 = 65 40 eth66 = 66 41 nodeDataMsg = 0x0e 42 ) 43 44 var ( 45 errAlreadyFetching = errors.New("already fetching blocks from peer") 46 errAlreadyRegistered = errors.New("peer is already registered") 47 errNotRegistered = errors.New("peer is not registered") 48 ) 49 50 // peerConnection represents an active peer from which hashes and blocks are retrieved. 51 type peerConnection struct { 52 id string // Unique identifier of the peer 53 54 stateIdle int32 // Current node data activity state of the peer (idle = 0, active = 1) 55 56 stateStarted time.Time // Time instance when the last node data fetch was started 57 58 rates *msgrate.Tracker // Tracker to hone in on the number of items retrievable per second 59 lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously) 60 61 peer Peer 62 63 version uint // Eth protocol version number to switch strategies 64 log log.Logger // Contextual logger to add extra infos to peer logs 65 lock sync.RWMutex 66 } 67 68 // Peer encapsulates the methods required to synchronise with a remote full peer. 69 type Peer interface { 70 RequestNodeData([]common.Hash) error 71 } 72 73 // newPeerConnection creates a new downloader peer. 74 func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection { 75 return &peerConnection{ 76 id: id, 77 lacking: make(map[common.Hash]struct{}), 78 peer: peer, 79 version: version, 80 log: logger, 81 } 82 } 83 84 // Reset clears the internal state of a peer entity. 85 func (p *peerConnection) Reset() { 86 p.lock.Lock() 87 defer p.lock.Unlock() 88 89 atomic.StoreInt32(&p.stateIdle, 0) 90 91 p.lacking = make(map[common.Hash]struct{}) 92 } 93 94 // FetchNodeData sends a node state data retrieval request to the remote peer. 95 func (p *peerConnection) FetchNodeData(hashes []common.Hash) error { 96 // Short circuit if the peer is already fetching 97 if !atomic.CompareAndSwapInt32(&p.stateIdle, 0, 1) { 98 return errAlreadyFetching 99 } 100 p.stateStarted = time.Now() 101 102 go p.peer.RequestNodeData(hashes) 103 104 return nil 105 } 106 107 // SetNodeDataIdle sets the peer to idle, allowing it to execute new state trie 108 // data retrieval requests. Its estimated state retrieval throughput is updated 109 // with that measured just now. 110 func (p *peerConnection) SetNodeDataIdle(delivered int, deliveryTime time.Time) { 111 p.rates.Update(nodeDataMsg, deliveryTime.Sub(p.stateStarted), delivered) 112 atomic.StoreInt32(&p.stateIdle, 0) 113 } 114 115 // NodeDataCapacity retrieves the peers state download allowance based on its 116 // previously discovered throughput. 117 func (p *peerConnection) NodeDataCapacity(targetRTT time.Duration) int { 118 cap := p.rates.Capacity(nodeDataMsg, targetRTT) 119 if cap > MaxStateFetch { 120 cap = MaxStateFetch 121 } 122 return cap 123 } 124 125 // MarkLacking appends a new entity to the set of items (blocks, receipts, states) 126 // that a peer is known not to have (i.e. have been requested before). If the 127 // set reaches its maximum allowed capacity, items are randomly dropped off. 128 func (p *peerConnection) MarkLacking(hash common.Hash) { 129 p.lock.Lock() 130 defer p.lock.Unlock() 131 132 for len(p.lacking) >= maxLackingHashes { 133 for drop := range p.lacking { 134 delete(p.lacking, drop) 135 break 136 } 137 } 138 p.lacking[hash] = struct{}{} 139 } 140 141 // Lacks retrieves whether the hash of a blockchain item is on the peers lacking 142 // list (i.e. whether we know that the peer does not have it). 143 func (p *peerConnection) Lacks(hash common.Hash) bool { 144 p.lock.RLock() 145 defer p.lock.RUnlock() 146 147 _, ok := p.lacking[hash] 148 return ok 149 } 150 151 // peerSet represents the collection of active peer participating in the chain 152 // download procedure. 153 type peerSet struct { 154 peers map[string]*peerConnection 155 rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat 156 157 newPeerFeed event.Feed 158 peerDropFeed event.Feed 159 160 lock sync.RWMutex 161 } 162 163 // newPeerSet creates a new peer set top track the active download sources. 164 func newPeerSet() *peerSet { 165 return &peerSet{ 166 peers: make(map[string]*peerConnection), 167 rates: msgrate.NewTrackers(log.New("proto", "eth")), 168 } 169 } 170 171 // SubscribeNewPeers subscribes to peer arrival events. 172 func (ps *peerSet) SubscribeNewPeers(ch chan<- *peerConnection) event.Subscription { 173 return ps.newPeerFeed.Subscribe(ch) 174 } 175 176 // SubscribePeerDrops subscribes to peer departure events. 177 func (ps *peerSet) SubscribePeerDrops(ch chan<- *peerConnection) event.Subscription { 178 return ps.peerDropFeed.Subscribe(ch) 179 } 180 181 // Reset iterates over the current peer set, and resets each of the known peers 182 // to prepare for a next batch of block retrieval. 183 func (ps *peerSet) Reset() { 184 ps.lock.RLock() 185 defer ps.lock.RUnlock() 186 187 for _, peer := range ps.peers { 188 peer.Reset() 189 } 190 } 191 192 // Register injects a new peer into the working set, or returns an error if the 193 // peer is already known. 194 // 195 // The method also sets the starting throughput values of the new peer to the 196 // average of all existing peers, to give it a realistic chance of being used 197 // for data retrievals. 198 func (ps *peerSet) Register(p *peerConnection) error { 199 // Register the new peer with some meaningful defaults 200 ps.lock.Lock() 201 if _, ok := ps.peers[p.id]; ok { 202 ps.lock.Unlock() 203 return errAlreadyRegistered 204 } 205 p.rates = msgrate.NewTracker(ps.rates.MeanCapacities(), ps.rates.MedianRoundTrip()) 206 if err := ps.rates.Track(p.id, p.rates); err != nil { 207 return err 208 } 209 ps.peers[p.id] = p 210 ps.lock.Unlock() 211 212 ps.newPeerFeed.Send(p) 213 return nil 214 } 215 216 // Unregister removes a remote peer from the active set, disabling any further 217 // actions to/from that particular entity. 218 func (ps *peerSet) Unregister(id string) error { 219 ps.lock.Lock() 220 p, ok := ps.peers[id] 221 if !ok { 222 ps.lock.Unlock() 223 return errNotRegistered 224 } 225 delete(ps.peers, id) 226 ps.rates.Untrack(id) 227 ps.lock.Unlock() 228 229 ps.peerDropFeed.Send(p) 230 return nil 231 } 232 233 // Peer retrieves the registered peer with the given id. 234 func (ps *peerSet) Peer(id string) *peerConnection { 235 ps.lock.RLock() 236 defer ps.lock.RUnlock() 237 238 return ps.peers[id] 239 } 240 241 // Len returns if the current number of peers in the set. 242 func (ps *peerSet) Len() int { 243 ps.lock.RLock() 244 defer ps.lock.RUnlock() 245 246 return len(ps.peers) 247 } 248 249 // AllPeers retrieves a flat list of all the peers within the set. 250 func (ps *peerSet) AllPeers() []*peerConnection { 251 ps.lock.RLock() 252 defer ps.lock.RUnlock() 253 254 list := make([]*peerConnection, 0, len(ps.peers)) 255 for _, p := range ps.peers { 256 list = append(list, p) 257 } 258 return list 259 } 260 261 // NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle 262 // peers within the active peer set, ordered by their reputation. 263 func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) { 264 idle := func(p *peerConnection) bool { 265 return atomic.LoadInt32(&p.stateIdle) == 0 266 } 267 throughput := func(p *peerConnection) int { 268 return p.rates.Capacity(nodeDataMsg, time.Second) 269 } 270 return ps.idlePeers(eth65, eth66, idle, throughput) 271 } 272 273 // idlePeers retrieves a flat list of all currently idle peers satisfying the 274 // protocol version constraints, using the provided function to check idleness. 275 // The resulting set of peers are sorted by their capacity. 276 func (ps *peerSet) idlePeers(minProtocol, maxProtocol uint, idleCheck func(*peerConnection) bool, capacity func(*peerConnection) int) ([]*peerConnection, int) { 277 ps.lock.RLock() 278 defer ps.lock.RUnlock() 279 280 var ( 281 total = 0 282 idle = make([]*peerConnection, 0, len(ps.peers)) 283 tps = make([]int, 0, len(ps.peers)) 284 ) 285 for _, p := range ps.peers { 286 if p.version >= minProtocol && p.version <= maxProtocol { 287 if idleCheck(p) { 288 idle = append(idle, p) 289 tps = append(tps, capacity(p)) 290 } 291 total++ 292 } 293 } 294 295 // And sort them 296 sortPeers := &peerCapacitySort{idle, tps} 297 sort.Sort(sortPeers) 298 return sortPeers.p, total 299 } 300 301 // peerCapacitySort implements sort.Interface. 302 // It sorts peer connections by capacity (descending). 303 type peerCapacitySort struct { 304 p []*peerConnection 305 tp []int 306 } 307 308 func (ps *peerCapacitySort) Len() int { 309 return len(ps.p) 310 } 311 312 func (ps *peerCapacitySort) Less(i, j int) bool { 313 return ps.tp[i] > ps.tp[j] 314 } 315 316 func (ps *peerCapacitySort) Swap(i, j int) { 317 ps.p[i], ps.p[j] = ps.p[j], ps.p[i] 318 ps.tp[i], ps.tp[j] = ps.tp[j], ps.tp[i] 319 }