github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/peerset.go (about) 1 // Copyright 2020 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 package gossip 18 19 import ( 20 "errors" 21 "sync" 22 23 "github.com/unicornultrafoundation/go-helios/hash" 24 "github.com/unicornultrafoundation/go-u2u/common" 25 "github.com/unicornultrafoundation/go-u2u/eth/protocols/snap" 26 "github.com/unicornultrafoundation/go-u2u/p2p" 27 ) 28 29 var ( 30 // errPeerSetClosed is returned if a peer is attempted to be added or removed 31 // from the peer set after it has been terminated. 32 errPeerSetClosed = errors.New("peerset closed") 33 34 // errPeerAlreadyRegistered is returned if a peer is attempted to be added 35 // to the peer set, but one with the same id already exists. 36 errPeerAlreadyRegistered = errors.New("peer already registered") 37 38 // errPeerNotRegistered is returned if a peer is attempted to be removed from 39 // a peer set, but no peer with the given id exists. 40 errPeerNotRegistered = errors.New("peer not registered") 41 42 // errSnapWithoutU2U is returned if a peer attempts to connect only on the 43 // snap protocol without advertizing the u2u main protocol. 44 errSnapWithoutU2U = errors.New("peer connected on snap without compatible u2u support") 45 ) 46 47 // peerSet represents the collection of active peers currently participating in 48 // the `eth` protocol, with or without the `snap` extension. 49 type peerSet struct { 50 peers map[string]*peer // Peers connected on the `eth` protocol 51 snapPeers int // Number of `snap` compatible peers for connection prioritization 52 53 snapWait map[string]chan *snap.Peer // Peers connected on `eth` waiting for their snap extension 54 snapPend map[string]*snap.Peer // Peers connected on the `snap` protocol, but not yet on `eth` 55 56 lock sync.RWMutex 57 closed bool 58 } 59 60 // newPeerSet creates a new peer set to track the active participants. 61 func newPeerSet() *peerSet { 62 return &peerSet{ 63 peers: make(map[string]*peer), 64 snapWait: make(map[string]chan *snap.Peer), 65 snapPend: make(map[string]*snap.Peer), 66 } 67 } 68 69 // RegisterSnapExtension unblocks an already connected `eth` peer waiting for its 70 // `snap` extension, or if no such peer exists, tracks the extension for the time 71 // being until the `eth` main protocol starts looking for it. 72 func (ps *peerSet) RegisterSnapExtension(peer *snap.Peer) error { 73 // Reject the peer if it is not eligible for a snap protocol 74 if !eligibleForSnap(peer.Peer) { 75 return errSnapWithoutU2U 76 } 77 // Ensure nobody can double connect 78 ps.lock.Lock() 79 defer ps.lock.Unlock() 80 81 id := peer.ID() 82 if _, ok := ps.peers[id]; ok { 83 return errPeerAlreadyRegistered // avoid connections with the same id as existing ones 84 } 85 if _, ok := ps.snapPend[id]; ok { 86 return errPeerAlreadyRegistered // avoid connections with the same id as pending ones 87 } 88 // Inject the peer into an `eth` counterpart is available, otherwise save for later 89 if wait, ok := ps.snapWait[id]; ok { 90 delete(ps.snapWait, id) 91 wait <- peer 92 return nil 93 } 94 ps.snapPend[id] = peer 95 return nil 96 } 97 98 // WaitSnapExtension blocks until all satellite protocols are connected and tracked 99 // by the peerset. 100 func (ps *peerSet) WaitSnapExtension(p *peer) (*snap.Peer, error) { 101 // If the peer is not eligible for a snap protocol`, don't wait 102 if !eligibleForSnap(p.Peer) { 103 return nil, nil 104 } 105 // Ensure nobody can double connect 106 ps.lock.Lock() 107 108 id := p.id 109 if _, ok := ps.peers[id]; ok { 110 ps.lock.Unlock() 111 return nil, errPeerAlreadyRegistered // avoid connections with the same id as existing ones 112 } 113 if _, ok := ps.snapWait[id]; ok { 114 ps.lock.Unlock() 115 return nil, errPeerAlreadyRegistered // avoid connections with the same id as pending ones 116 } 117 // If `snap` already connected, retrieve the peer from the pending set 118 if snap, ok := ps.snapPend[id]; ok { 119 delete(ps.snapPend, id) 120 121 ps.lock.Unlock() 122 return snap, nil 123 } 124 // Otherwise wait for `snap` to connect concurrently 125 wait := make(chan *snap.Peer) 126 ps.snapWait[id] = wait 127 ps.lock.Unlock() 128 129 return <-wait, nil 130 } 131 132 // RegisterPeer injects a new `eth` peer into the working set, or returns an error 133 // if the peer is already known. 134 func (ps *peerSet) RegisterPeer(p *peer, ext *snap.Peer) error { 135 // Start tracking the new peer 136 ps.lock.Lock() 137 defer ps.lock.Unlock() 138 139 if ps.closed { 140 return errPeerSetClosed 141 } 142 143 id := p.id 144 if _, ok := ps.peers[id]; ok { 145 return errPeerAlreadyRegistered 146 } 147 148 if ext != nil { 149 p.snapExt = &snapPeer{ext} 150 ps.snapPeers++ 151 } 152 153 ps.peers[id] = p 154 return nil 155 } 156 157 // UnregisterPeer removes a remote peer from the active set, disabling any further 158 // actions to/from that particular entity. 159 func (ps *peerSet) UnregisterPeer(id string) error { 160 ps.lock.Lock() 161 defer ps.lock.Unlock() 162 163 peer, ok := ps.peers[id] 164 if !ok { 165 return errPeerNotRegistered 166 } 167 delete(ps.peers, id) 168 if peer.snapExt != nil { 169 ps.snapPeers-- 170 } 171 return nil 172 } 173 174 // Peer retrieves the registered peer with the given id. 175 func (ps *peerSet) Peer(id string) *peer { 176 ps.lock.RLock() 177 defer ps.lock.RUnlock() 178 179 return ps.peers[id] 180 } 181 182 func (ps *peerSet) UselessNum() int { 183 ps.lock.RLock() 184 defer ps.lock.RUnlock() 185 186 num := 0 187 for _, p := range ps.peers { 188 if p.Useless() { 189 num++ 190 } 191 } 192 return num 193 } 194 195 // PeersWithoutEvent retrieves a list of peers that do not have a given event in 196 // their set of known hashes so it might be propagated to them. 197 func (ps *peerSet) PeersWithoutEvent(e hash.Event) []*peer { 198 ps.lock.RLock() 199 defer ps.lock.RUnlock() 200 201 list := make([]*peer, 0, len(ps.peers)) 202 for _, p := range ps.peers { 203 if p.InterestedIn(e) { 204 list = append(list, p) 205 } 206 } 207 return list 208 } 209 210 // PeersWithoutTx retrieves a list of peers that do not have a given 211 // transaction in their set of known hashes. 212 func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer { 213 ps.lock.RLock() 214 defer ps.lock.RUnlock() 215 216 list := make([]*peer, 0, len(ps.peers)) 217 for _, p := range ps.peers { 218 if !p.knownTxs.Contains(hash) { 219 list = append(list, p) 220 } 221 } 222 return list 223 } 224 225 // List returns array of peers in the set. 226 func (ps *peerSet) List() []*peer { 227 ps.lock.RLock() 228 defer ps.lock.RUnlock() 229 230 list := make([]*peer, 0, len(ps.peers)) 231 for _, p := range ps.peers { 232 list = append(list, p) 233 } 234 return list 235 } 236 237 // Len returns if the current number of `eth` peers in the set. Since the `snap` 238 // peers are tied to the existence of an `eth` connection, that will always be a 239 // subset of `eth`. 240 func (ps *peerSet) Len() int { 241 ps.lock.RLock() 242 defer ps.lock.RUnlock() 243 244 return len(ps.peers) 245 } 246 247 // SnapLen returns if the current number of `snap` peers in the set. 248 func (ps *peerSet) SnapLen() int { 249 ps.lock.RLock() 250 defer ps.lock.RUnlock() 251 252 return ps.snapPeers 253 } 254 255 // Close disconnects all peers. 256 func (ps *peerSet) Close() { 257 ps.lock.Lock() 258 defer ps.lock.Unlock() 259 260 for _, p := range ps.peers { 261 p.Disconnect(p2p.DiscQuitting) 262 } 263 ps.closed = true 264 }