github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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 "fmt" 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 quitCh chan struct{} // Quit channel to signal termination 60 } 61 62 // newPeerSet creates a new peer set to track the active participants. 63 func newPeerSet() *peerSet { 64 return &peerSet{ 65 peers: make(map[string]*ethPeer), 66 snapWait: make(map[string]chan *snap.Peer), 67 snapPend: make(map[string]*snap.Peer), 68 quitCh: make(chan struct{}), 69 } 70 } 71 72 // registerSnapExtension unblocks an already connected `eth` peer waiting for its 73 // `snap` extension, or if no such peer exists, tracks the extension for the time 74 // being until the `eth` main protocol starts looking for it. 75 func (ps *peerSet) registerSnapExtension(peer *snap.Peer) error { 76 // Reject the peer if it advertises `snap` without `eth` as `snap` is only a 77 // satellite protocol meaningful with the chain selection of `eth` 78 if !peer.RunningCap(eth.ProtocolName, eth.ProtocolVersions) { 79 return fmt.Errorf("%w: have %v", errSnapWithoutEth, peer.Caps()) 80 } 81 // Ensure nobody can double connect 82 ps.lock.Lock() 83 defer ps.lock.Unlock() 84 85 id := peer.ID() 86 if _, ok := ps.peers[id]; ok { 87 return errPeerAlreadyRegistered // avoid connections with the same id as existing ones 88 } 89 if _, ok := ps.snapPend[id]; ok { 90 return errPeerAlreadyRegistered // avoid connections with the same id as pending ones 91 } 92 // Inject the peer into an `eth` counterpart is available, otherwise save for later 93 if wait, ok := ps.snapWait[id]; ok { 94 delete(ps.snapWait, id) 95 wait <- peer 96 return nil 97 } 98 ps.snapPend[id] = peer 99 return nil 100 } 101 102 // waitSnapExtension blocks until all satellite protocols are connected and tracked 103 // by the peerset. 104 func (ps *peerSet) waitSnapExtension(peer *eth.Peer) (*snap.Peer, error) { 105 // If the peer does not support a compatible `snap`, don't wait 106 if !peer.RunningCap(snap.ProtocolName, snap.ProtocolVersions) { 107 return nil, nil 108 } 109 // Ensure nobody can double connect 110 ps.lock.Lock() 111 112 id := peer.ID() 113 if _, ok := ps.peers[id]; ok { 114 ps.lock.Unlock() 115 return nil, errPeerAlreadyRegistered // avoid connections with the same id as existing ones 116 } 117 if _, ok := ps.snapWait[id]; ok { 118 ps.lock.Unlock() 119 return nil, errPeerAlreadyRegistered // avoid connections with the same id as pending ones 120 } 121 // If `snap` already connected, retrieve the peer from the pending set 122 if snap, ok := ps.snapPend[id]; ok { 123 delete(ps.snapPend, id) 124 125 ps.lock.Unlock() 126 return snap, nil 127 } 128 // Otherwise wait for `snap` to connect concurrently 129 wait := make(chan *snap.Peer) 130 ps.snapWait[id] = wait 131 ps.lock.Unlock() 132 133 select { 134 case p := <-wait: 135 return p, nil 136 case <-ps.quitCh: 137 ps.lock.Lock() 138 delete(ps.snapWait, id) 139 ps.lock.Unlock() 140 return nil, errPeerSetClosed 141 } 142 } 143 144 // registerPeer injects a new `eth` peer into the working set, or returns an error 145 // if the peer is already known. 146 func (ps *peerSet) registerPeer(peer *eth.Peer, ext *snap.Peer) error { 147 // Start tracking the new peer 148 ps.lock.Lock() 149 defer ps.lock.Unlock() 150 151 if ps.closed { 152 return errPeerSetClosed 153 } 154 id := peer.ID() 155 if _, ok := ps.peers[id]; ok { 156 return errPeerAlreadyRegistered 157 } 158 eth := ðPeer{ 159 Peer: peer, 160 } 161 if ext != nil { 162 eth.snapExt = &snapPeer{ext} 163 ps.snapPeers++ 164 } 165 ps.peers[id] = eth 166 return nil 167 } 168 169 // unregisterPeer removes a remote peer from the active set, disabling any further 170 // actions to/from that particular entity. 171 func (ps *peerSet) unregisterPeer(id string) error { 172 ps.lock.Lock() 173 defer ps.lock.Unlock() 174 175 peer, ok := ps.peers[id] 176 if !ok { 177 return errPeerNotRegistered 178 } 179 delete(ps.peers, id) 180 if peer.snapExt != nil { 181 ps.snapPeers-- 182 } 183 return nil 184 } 185 186 // peer retrieves the registered peer with the given id. 187 func (ps *peerSet) peer(id string) *ethPeer { 188 ps.lock.RLock() 189 defer ps.lock.RUnlock() 190 191 return ps.peers[id] 192 } 193 194 // peersWithoutTransaction retrieves a list of peers that do not have a given 195 // transaction in their set of known hashes. 196 func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer { 197 ps.lock.RLock() 198 defer ps.lock.RUnlock() 199 200 list := make([]*ethPeer, 0, len(ps.peers)) 201 for _, p := range ps.peers { 202 if !p.KnownTransaction(hash) { 203 list = append(list, p) 204 } 205 } 206 return list 207 } 208 209 // len returns if the current number of `eth` peers in the set. Since the `snap` 210 // peers are tied to the existence of an `eth` connection, that will always be a 211 // subset of `eth`. 212 func (ps *peerSet) len() int { 213 ps.lock.RLock() 214 defer ps.lock.RUnlock() 215 216 return len(ps.peers) 217 } 218 219 // snapLen returns if the current number of `snap` peers in the set. 220 func (ps *peerSet) snapLen() int { 221 ps.lock.RLock() 222 defer ps.lock.RUnlock() 223 224 return ps.snapPeers 225 } 226 227 // close disconnects all peers. 228 func (ps *peerSet) close() { 229 ps.lock.Lock() 230 defer ps.lock.Unlock() 231 232 for _, p := range ps.peers { 233 p.Disconnect(p2p.DiscQuitting) 234 } 235 if !ps.closed { 236 close(ps.quitCh) 237 } 238 ps.closed = true 239 }