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  }