github.com/pure-x-eth/consensus_tm@v0.0.0-20230502163723-e3c2ff987250/p2p/peer_set.go (about) 1 package p2p 2 3 import ( 4 "net" 5 6 tmsync "github.com/pure-x-eth/consensus_tm/libs/sync" 7 ) 8 9 // IPeerSet has a (immutable) subset of the methods of PeerSet. 10 type IPeerSet interface { 11 Has(key ID) bool 12 HasIP(ip net.IP) bool 13 Get(key ID) Peer 14 List() []Peer 15 Size() int 16 } 17 18 //----------------------------------------------------------------------------- 19 20 // PeerSet is a special structure for keeping a table of peers. 21 // Iteration over the peers is super fast and thread-safe. 22 type PeerSet struct { 23 mtx tmsync.Mutex 24 lookup map[ID]*peerSetItem 25 list []Peer 26 } 27 28 type peerSetItem struct { 29 peer Peer 30 index int 31 } 32 33 // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items. 34 func NewPeerSet() *PeerSet { 35 return &PeerSet{ 36 lookup: make(map[ID]*peerSetItem), 37 list: make([]Peer, 0, 256), 38 } 39 } 40 41 // Add adds the peer to the PeerSet. 42 // It returns an error carrying the reason, if the peer is already present. 43 func (ps *PeerSet) Add(peer Peer) error { 44 ps.mtx.Lock() 45 defer ps.mtx.Unlock() 46 47 if ps.lookup[peer.ID()] != nil { 48 return ErrSwitchDuplicatePeerID{peer.ID()} 49 } 50 if peer.GetRemovalFailed() { 51 return ErrPeerRemoval{} 52 } 53 54 index := len(ps.list) 55 // Appending is safe even with other goroutines 56 // iterating over the ps.list slice. 57 ps.list = append(ps.list, peer) 58 ps.lookup[peer.ID()] = &peerSetItem{peer, index} 59 return nil 60 } 61 62 // Has returns true if the set contains the peer referred to by this 63 // peerKey, otherwise false. 64 func (ps *PeerSet) Has(peerKey ID) bool { 65 ps.mtx.Lock() 66 _, ok := ps.lookup[peerKey] 67 ps.mtx.Unlock() 68 return ok 69 } 70 71 // HasIP returns true if the set contains the peer referred to by this IP 72 // address, otherwise false. 73 func (ps *PeerSet) HasIP(peerIP net.IP) bool { 74 ps.mtx.Lock() 75 defer ps.mtx.Unlock() 76 77 return ps.hasIP(peerIP) 78 } 79 80 // hasIP does not acquire a lock so it can be used in public methods which 81 // already lock. 82 func (ps *PeerSet) hasIP(peerIP net.IP) bool { 83 for _, item := range ps.lookup { 84 if item.peer.RemoteIP().Equal(peerIP) { 85 return true 86 } 87 } 88 89 return false 90 } 91 92 // Get looks up a peer by the provided peerKey. Returns nil if peer is not 93 // found. 94 func (ps *PeerSet) Get(peerKey ID) Peer { 95 ps.mtx.Lock() 96 defer ps.mtx.Unlock() 97 item, ok := ps.lookup[peerKey] 98 if ok { 99 return item.peer 100 } 101 return nil 102 } 103 104 // Remove discards peer by its Key, if the peer was previously memoized. 105 // Returns true if the peer was removed, and false if it was not found. 106 // in the set. 107 func (ps *PeerSet) Remove(peer Peer) bool { 108 ps.mtx.Lock() 109 defer ps.mtx.Unlock() 110 111 item := ps.lookup[peer.ID()] 112 if item == nil { 113 // Removing the peer has failed so we set a flag to mark that a removal was attempted. 114 // This can happen when the peer add routine from the switch is running in 115 // parallel to the receive routine of MConn. 116 // There is an error within MConn but the switch has not actually added the peer to the peer set yet. 117 // Setting this flag will prevent a peer from being added to a node's peer set afterwards. 118 peer.SetRemovalFailed() 119 return false 120 } 121 122 index := item.index 123 // Create a new copy of the list but with one less item. 124 // (we must copy because we'll be mutating the list). 125 newList := make([]Peer, len(ps.list)-1) 126 copy(newList, ps.list) 127 // If it's the last peer, that's an easy special case. 128 if index == len(ps.list)-1 { 129 ps.list = newList 130 delete(ps.lookup, peer.ID()) 131 return true 132 } 133 134 // Replace the popped item with the last item in the old list. 135 lastPeer := ps.list[len(ps.list)-1] 136 lastPeerKey := lastPeer.ID() 137 lastPeerItem := ps.lookup[lastPeerKey] 138 newList[index] = lastPeer 139 lastPeerItem.index = index 140 ps.list = newList 141 delete(ps.lookup, peer.ID()) 142 return true 143 } 144 145 // Size returns the number of unique items in the peerSet. 146 func (ps *PeerSet) Size() int { 147 ps.mtx.Lock() 148 defer ps.mtx.Unlock() 149 return len(ps.list) 150 } 151 152 // List returns the threadsafe list of peers. 153 func (ps *PeerSet) List() []Peer { 154 ps.mtx.Lock() 155 defer ps.mtx.Unlock() 156 return ps.list 157 }