github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/p2p/peer_pool.go (about) 1 // Copyright 2017-2018 DERO Project. All rights reserved. 2 // Use of this source code in any form is governed by RESEARCH license. 3 // license can be found in the LICENSE file. 4 // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8 5 // 6 // 7 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 8 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 9 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 10 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 11 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 12 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 13 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 14 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 15 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 17 package p2p 18 19 /* this file implements the peer manager, keeping a list of peers which can be tried for connection etc 20 * 21 */ 22 import "os" 23 import "fmt" 24 25 //import "net" 26 import "sync" 27 import "time" 28 import "sort" 29 import "path/filepath" 30 import "encoding/json" 31 32 //import "encoding/binary" 33 //import "container/list" 34 35 //import log "github.com/sirupsen/logrus" 36 37 import "github.com/deroproject/derosuite/globals" 38 39 //import "github.com/deroproject/derosuite/crypto" 40 41 // This structure is used to do book keeping for the peer list and keeps other DATA related to peer 42 // all peers are servers, means they have exposed a port for connections 43 // all peers are identified by their endpoint tcp address 44 // all clients are identified by ther peer id ( however ip-address is used to control amount ) 45 // the following daemon commands interact with the list 46 // peer_list := print the peer list 47 // ban address time // ban this address for spcific time 48 // unban address 49 // enableban address // by default all addresses are bannable 50 // disableban address // this address will never be banned 51 type Peer struct { 52 Address string `json:"address"` // pairs in the ip:port or dns:port, basically endpoint 53 ID uint64 `json:"peerid"` // peer id 54 Miner bool `json:"miner"` // miner 55 //NeverBlacklist bool // this address will never be blacklisted 56 LastConnected uint64 `json:"lastconnected"` // epoch time when it was connected , 0 if never connected 57 FailCount uint64 `json:"failcount"` // how many times have we failed (tcp errors) 58 ConnectAfter uint64 `json:"connectafter"` // we should connect when the following timestamp passes 59 BlacklistBefore uint64 `json:"blacklistbefore"` // peer blacklisted till epoch , priority nodes are never blacklisted, 0 if not blacklist 60 GoodCount uint64 `json:"goodcount"` // how many times peer has been shared with us 61 Version int `json:"version"` // version 1 is original C daemon peer, version 2 is golang p2p version 62 Whitelist bool `json:"whitelist"` 63 sync.Mutex 64 } 65 66 var peer_map = map[string]*Peer{} 67 var peer_mutex sync.Mutex 68 69 // loads peers list from disk 70 func load_peer_list() { 71 defer clean_up() 72 peer_mutex.Lock() 73 defer peer_mutex.Unlock() 74 75 peer_file := filepath.Join(globals.GetDataDirectory(), "peers.json") 76 file, err := os.Open(peer_file) 77 if err != nil { 78 logger.Warnf("Error opening peer data file %s err %s", peer_file, err) 79 } else { 80 defer file.Close() 81 decoder := json.NewDecoder(file) 82 err = decoder.Decode(&peer_map) 83 if err != nil { 84 logger.Warnf("Error unmarshalling p2p data err %s", err) 85 } else { // successfully unmarshalled data 86 logger.Debugf("Successfully loaded %d peers from file", (len(peer_map))) 87 } 88 } 89 90 } 91 92 //save peer list to disk 93 func save_peer_list() { 94 95 clean_up() 96 peer_mutex.Lock() 97 defer peer_mutex.Unlock() 98 99 peer_file := filepath.Join(globals.GetDataDirectory(), "peers.json") 100 file, err := os.Create(peer_file) 101 if err != nil { 102 logger.Warnf("Error creating peer data file %s err %s", peer_file, err) 103 } else { 104 defer file.Close() 105 encoder := json.NewEncoder(file) 106 encoder.SetIndent("", "\t") 107 err = encoder.Encode(&peer_map) 108 if err != nil { 109 logger.Warnf("Error marshalling p2p data err %s", err) 110 } else { // successfully unmarshalled data 111 logger.Debugf("Successfully saved %d peers to file", (len(peer_map))) 112 } 113 } 114 } 115 116 // clean up by discarding entries which are too much into future 117 func clean_up() { 118 peer_mutex.Lock() 119 defer peer_mutex.Unlock() 120 for k, v := range peer_map { 121 if v.FailCount >= 16 { // roughly 16 tries, 18 hrs before we discard the peer 122 delete(peer_map, k) 123 } 124 } 125 126 } 127 128 // check whether an IP is in the map already 129 func IsPeerInList(address string) bool { 130 peer_mutex.Lock() 131 defer peer_mutex.Unlock() 132 133 if _, ok := peer_map[address]; ok { 134 return true 135 } 136 return false 137 } 138 func GetPeerInList(address string) *Peer { 139 peer_mutex.Lock() 140 defer peer_mutex.Unlock() 141 142 if v, ok := peer_map[address]; ok { 143 return v 144 } 145 return nil 146 } 147 148 // add connection to map 149 func Peer_Add(p *Peer) { 150 peer_mutex.Lock() 151 defer peer_mutex.Unlock() 152 153 if p.ID == GetPeerID() { // if peer is self do not connect 154 // logger.Infof("Peer is ourselves, discard") 155 return 156 157 } 158 159 if v, ok := peer_map[p.Address]; ok { 160 v.Lock() 161 // logger.Infof("Peer already in list adding good count") 162 v.GoodCount++ 163 v.Unlock() 164 } else { 165 // logger.Infof("Peer adding to list") 166 peer_map[p.Address] = p 167 } 168 } 169 170 // a peer marked as fail, will only be connected based on exponential back-off based on powers of 2 171 func Peer_SetFail(address string) { 172 p := GetPeerInList(address) 173 if p == nil { 174 return 175 } 176 peer_mutex.Lock() 177 defer peer_mutex.Unlock() 178 p.FailCount++ // increase fail count, and mark for delayed connect 179 p.ConnectAfter = uint64(time.Now().UTC().Unix()) + 1<<(p.FailCount-1) 180 } 181 182 // set peer as successfully connected 183 // we will only distribute peers which have been successfully connected by us 184 func Peer_SetSuccess(address string) { 185 //logger.Infof("Setting peer as success") 186 p := GetPeerInList(address) 187 if p == nil { 188 return 189 } 190 peer_mutex.Lock() 191 defer peer_mutex.Unlock() 192 p.FailCount = 0 // fail count is zero again 193 p.ConnectAfter = 0 194 p.Whitelist = true 195 p.LastConnected = uint64(time.Now().UTC().Unix()) // set time when last connected 196 // logger.Infof("Setting peer as white listed") 197 } 198 199 /* 200 //TODO do we need a functionality so some peers are never banned 201 func Peer_DisableBan(address string) (err error){ 202 p := GetPeerInList(address) 203 if p == nil { 204 return fmt.Errorf("Peer \"%s\" not found in list") 205 } 206 p.Lock() 207 defer p.Unlock() 208 p.NeverBlacklist = true 209 } 210 211 func Peer_EnableBan(address string) (err error){ 212 p := GetPeerInList(address) 213 if p == nil { 214 return fmt.Errorf("Peer \"%s\" not found in list") 215 } 216 p.Lock() 217 defer p.Unlock() 218 p.NeverBlacklist = false 219 } 220 */ 221 222 // add connection to map 223 func Peer_Delete(p *Peer) { 224 peer_mutex.Lock() 225 defer peer_mutex.Unlock() 226 delete(peer_map, p.Address) 227 } 228 229 // prints all the connection info to screen 230 func PeerList_Print() { 231 peer_mutex.Lock() 232 defer peer_mutex.Unlock() 233 fmt.Printf("Peer List\n") 234 fmt.Printf("%-22s %-6s %-4s %-5s %-7s %9s %3s\n", "Remote Addr", "Active", "Good", "Fail", " State", "Height", "DIR") 235 236 var list []*Peer 237 greycount := 0 238 for _, v := range peer_map { 239 if v.Whitelist { // only display white listed peer 240 list = append(list, v) 241 } else { 242 greycount++ 243 } 244 } 245 246 // sort the list 247 sort.Slice(list, func(i, j int) bool { return list[i].Address < list[j].Address }) 248 249 for i := range list { 250 connected := "" 251 if IsAddressConnected(list[i].Address) { 252 connected = "ACTIVE" 253 } 254 fmt.Printf("%-22s %-6s %4d %5d \n", list[i].Address, connected, list[i].GoodCount, list[i].FailCount) 255 } 256 257 fmt.Printf("\nWhitelist size %d\n", len(peer_map)-greycount) 258 fmt.Printf("Greylist size %d\n", greycount) 259 260 } 261 262 // this function return peer count which have successful handshake 263 func Peer_Counts() (Count uint64) { 264 peer_mutex.Lock() 265 defer peer_mutex.Unlock() 266 return uint64(len(peer_map)) 267 } 268 269 // this function finds a possible peer to connect to keeping blacklist and already existing connections into picture 270 // it must not be already connected using outgoing connection 271 // we do allow loops such as both incoming/outgoing simultaneously 272 // this will return atmost 1 address, empty address if peer list is empty 273 func find_peer_to_connect(version int) *Peer { 274 defer clean_up() 275 peer_mutex.Lock() 276 defer peer_mutex.Unlock() 277 278 // first search the whitelisted ones 279 for _, v := range peer_map { 280 if uint64(time.Now().Unix()) > v.BlacklistBefore && // if ip is blacklisted skip it 281 uint64(time.Now().Unix()) > v.ConnectAfter && 282 !IsAddressConnected(v.Address) && v.Whitelist && !IsAddressInBanList(v.Address) { 283 v.ConnectAfter = uint64(time.Now().UTC().Unix()) + 10 // minimum 10 secs gap 284 return v 285 } 286 } 287 // if we donot have any white listed, choose from the greylist 288 for _, v := range peer_map { 289 if uint64(time.Now().Unix()) > v.BlacklistBefore && // if ip is blacklisted skip it 290 uint64(time.Now().Unix()) > v.ConnectAfter && 291 !IsAddressConnected(v.Address) && !v.Whitelist && !IsAddressInBanList(v.Address) { 292 v.ConnectAfter = uint64(time.Now().UTC().Unix()) + 10 // minimum 10 secs gap 293 return v 294 } 295 } 296 297 return nil // if no peer found, return nil 298 } 299 300 // return white listed peer list 301 // for use in handshake 302 func get_peer_list() (peers []Peer_Info) { 303 peer_mutex.Lock() 304 defer peer_mutex.Unlock() 305 306 for _, v := range peer_map { 307 if v.Whitelist { 308 peers = append(peers, Peer_Info{Addr: v.Address}) 309 } 310 } 311 return 312 }