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