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