github.com/dominant-strategies/go-quai@v0.28.2/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/dominant-strategies/go-quai/common" 25 "github.com/dominant-strategies/go-quai/eth/protocols/eth" 26 "github.com/dominant-strategies/go-quai/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 // errSnapWithoutEth is returned if a peer attempts to connect only on the 43 // snap protocol without advertizing the eth main protocol. 44 errSnapWithoutEth = errors.New("peer connected on snap without compatible eth 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]*ethPeer // Peers connected on the `eth` protocol 51 lock sync.RWMutex 52 closed bool 53 } 54 55 // newPeerSet creates a new peer set to track the active participants. 56 func newPeerSet() *peerSet { 57 return &peerSet{ 58 peers: make(map[string]*ethPeer), 59 } 60 } 61 62 // registerPeer injects a new `eth` peer into the working set, or returns an error 63 // if the peer is already known. 64 func (ps *peerSet) registerPeer(peer *eth.Peer) error { 65 // Start tracking the new peer 66 ps.lock.Lock() 67 defer ps.lock.Unlock() 68 69 if ps.closed { 70 return errPeerSetClosed 71 } 72 id := peer.ID() 73 if _, ok := ps.peers[id]; ok { 74 return errPeerAlreadyRegistered 75 } 76 eth := ðPeer{ 77 Peer: peer, 78 } 79 ps.peers[id] = eth 80 return nil 81 } 82 83 // unregisterPeer removes a remote peer from the active set, disabling any further 84 // actions to/from that particular entity. 85 func (ps *peerSet) unregisterPeer(id string) error { 86 ps.lock.Lock() 87 defer ps.lock.Unlock() 88 _, ok := ps.peers[id] 89 if !ok { 90 return errPeerNotRegistered 91 } 92 delete(ps.peers, id) 93 return nil 94 } 95 96 // peer retrieves the registered peer with the given id. 97 func (ps *peerSet) peer(id string) *ethPeer { 98 ps.lock.RLock() 99 defer ps.lock.RUnlock() 100 101 return ps.peers[id] 102 } 103 104 // peersWithoutBlock retrieves a list of peers that do not have a given block in 105 // their set of known hashes so it might be propagated to them. 106 func (ps *peerSet) peersWithoutBlock(hash common.Hash) []*ethPeer { 107 ps.lock.RLock() 108 defer ps.lock.RUnlock() 109 110 list := make([]*ethPeer, 0, len(ps.peers)) 111 for _, p := range ps.peers { 112 if !p.KnownBlock(hash) { 113 list = append(list, p) 114 } 115 } 116 return list 117 } 118 119 // peersWithoutTransaction retrieves a list of peers that do not have a given 120 // transaction in their set of known hashes. 121 func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer { 122 ps.lock.RLock() 123 defer ps.lock.RUnlock() 124 125 list := make([]*ethPeer, 0, len(ps.peers)) 126 for _, p := range ps.peers { 127 if !p.KnownTransaction(hash) { 128 list = append(list, p) 129 } 130 } 131 return list 132 } 133 134 // len returns if the current number of `eth` peers in the set. Since the `snap` 135 // peers are tied to the existence of an `eth` connection, that will always be a 136 // subset of `eth`. 137 func (ps *peerSet) len() int { 138 ps.lock.RLock() 139 defer ps.lock.RUnlock() 140 141 return len(ps.peers) 142 } 143 144 // peerWithHighestEntropy retrieves the known peer with the currently highest Entropy 145 func (ps *peerSet) peerWithHighestEntropy() *eth.Peer { 146 ps.lock.RLock() 147 defer ps.lock.RUnlock() 148 149 var ( 150 bestPeer *eth.Peer 151 bestEntropy *big.Int 152 ) 153 for _, p := range ps.peers { 154 if _, _, entropy, _ := p.Head(); bestPeer == nil || entropy.Cmp(bestEntropy) > 0 { 155 bestPeer, bestEntropy = p.Peer, entropy 156 } 157 } 158 return bestPeer 159 } 160 161 func (ps *peerSet) peerRunningSlice(location common.Location) []*eth.Peer { 162 ps.lock.RLock() 163 defer ps.lock.RUnlock() 164 165 containsLocation := func(s []common.Location, e common.Location) bool { 166 for _, a := range s { 167 if common.Location.Equal(a, e) { 168 return true 169 } 170 } 171 return false 172 } 173 174 var peersRunningSlice []*eth.Peer 175 for _, p := range ps.peers { 176 if containsLocation(p.Peer.SlicesRunning(), location) { 177 peersRunningSlice = append(peersRunningSlice, p.Peer) 178 } 179 } 180 return peersRunningSlice 181 } 182 183 func (ps *peerSet) allPeers() []*eth.Peer { 184 ps.lock.RLock() 185 defer ps.lock.RUnlock() 186 187 var allPeers []*eth.Peer 188 for _, p := range ps.peers { 189 allPeers = append(allPeers, p.Peer) 190 } 191 return allPeers 192 } 193 194 // close disconnects all peers. 195 func (ps *peerSet) close() { 196 ps.lock.Lock() 197 defer ps.lock.Unlock() 198 199 for _, p := range ps.peers { 200 p.Disconnect(p2p.DiscQuitting) 201 } 202 ps.closed = true 203 }