github.com/igggame/nebulas-go@v2.1.0+incompatible/net/route_table.go (about)

     1  // Copyright (C) 2018 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package net
    20  
    21  import (
    22  	"bufio"
    23  	"errors"
    24  	"fmt"
    25  	"math/rand"
    26  	"os"
    27  	"path"
    28  	"reflect"
    29  	"strings"
    30  	"time"
    31  
    32  	"github.com/sirupsen/logrus"
    33  
    34  	"github.com/multiformats/go-multiaddr"
    35  	netpb "github.com/nebulasio/go-nebulas/net/pb"
    36  	"github.com/nebulasio/go-nebulas/util/logging"
    37  
    38  	kbucket "github.com/libp2p/go-libp2p-kbucket"
    39  	peer "github.com/libp2p/go-libp2p-peer"
    40  	peerstore "github.com/libp2p/go-libp2p-peerstore"
    41  	ma "github.com/multiformats/go-multiaddr"
    42  )
    43  
    44  // Route Table Errors
    45  var (
    46  	ErrExceedMaxSyncRouteResponse = errors.New("too many sync route table response")
    47  )
    48  
    49  // RouteTable route table struct.
    50  type RouteTable struct {
    51  	quitCh                   chan bool
    52  	peerStore                peerstore.Peerstore
    53  	routeTable               *kbucket.RoutingTable
    54  	maxPeersCountForSyncResp int
    55  	maxPeersCountToSync      int
    56  	cacheFilePath            string
    57  	seedNodes                []ma.Multiaddr
    58  	node                     *Node
    59  	streamManager            *StreamManager
    60  	latestUpdatedAt          int64
    61  	internalNodeList         []string
    62  }
    63  
    64  // NewRouteTable new route table.
    65  func NewRouteTable(config *Config, node *Node) *RouteTable {
    66  	table := &RouteTable{
    67  		quitCh:                   make(chan bool, 1),
    68  		peerStore:                peerstore.NewPeerstore(),
    69  		maxPeersCountForSyncResp: MaxPeersCountForSyncResp,
    70  		maxPeersCountToSync:      config.MaxSyncNodes,
    71  		cacheFilePath:            path.Join(config.RoutingTableDir, RouteTableCacheFileName),
    72  		seedNodes:                config.BootNodes,
    73  		node:                     node,
    74  		streamManager:            node.streamManager,
    75  		latestUpdatedAt:          0,
    76  	}
    77  
    78  	table.routeTable = kbucket.NewRoutingTable(
    79  		config.Bucketsize,
    80  		kbucket.ConvertPeerID(node.id),
    81  		config.Latency,
    82  		table.peerStore,
    83  	)
    84  
    85  	table.routeTable.Update(node.id)
    86  	table.peerStore.AddPubKey(node.id, node.networkKey.GetPublic())
    87  	table.peerStore.AddPrivKey(node.id, node.networkKey)
    88  
    89  	return table
    90  }
    91  
    92  // Start start route table syncLoop.
    93  func (table *RouteTable) Start() {
    94  	logging.CLog().Info("Starting NebService RouteTable Sync...")
    95  
    96  	go table.syncLoop()
    97  }
    98  
    99  // Stop quit route table syncLoop.
   100  func (table *RouteTable) Stop() {
   101  	logging.CLog().Info("Stopping NebService RouteTable Sync...")
   102  
   103  	table.quitCh <- true
   104  }
   105  
   106  // Peers return peers in route table.
   107  func (table *RouteTable) Peers() map[peer.ID][]ma.Multiaddr {
   108  	peers := make(map[peer.ID][]ma.Multiaddr)
   109  	for _, pid := range table.peerStore.Peers() {
   110  		peers[pid] = table.peerStore.Addrs(pid)
   111  	}
   112  	return peers
   113  }
   114  
   115  func (table *RouteTable) syncLoop() {
   116  	// Load Route Table.
   117  	table.LoadSeedNodes()
   118  	table.LoadRouteTableFromFile()
   119  	table.LoadInternalNodeList()
   120  
   121  	// trigger first sync.
   122  	table.SyncRouteTable()
   123  
   124  	logging.CLog().Info("Started NebService RouteTable Sync.")
   125  
   126  	syncLoopTicker := time.NewTicker(RouteTableSyncLoopInterval)
   127  	saveRouteTableToDiskTicker := time.NewTicker(RouteTableSaveToDiskInterval)
   128  	latestUpdatedAt := table.latestUpdatedAt
   129  
   130  	for {
   131  		select {
   132  		case <-table.quitCh:
   133  			logging.CLog().Info("Stopped NebService RouteTable Sync.")
   134  			return
   135  		case <-syncLoopTicker.C:
   136  			table.SyncRouteTable()
   137  		case <-saveRouteTableToDiskTicker.C:
   138  			if latestUpdatedAt < table.latestUpdatedAt {
   139  				table.SaveRouteTableToFile()
   140  				latestUpdatedAt = table.latestUpdatedAt
   141  			}
   142  		}
   143  	}
   144  }
   145  
   146  // AddPeerInfo add peer to route table.
   147  func (table *RouteTable) AddPeerInfo(prettyID string, addrStr []string) error {
   148  	pid, err := peer.IDB58Decode(prettyID)
   149  	if err != nil {
   150  		return nil
   151  	}
   152  
   153  	addrs := make([]ma.Multiaddr, len(addrStr))
   154  	for i, v := range addrStr {
   155  		addrs[i], err = multiaddr.NewMultiaddr(v)
   156  		if err != nil {
   157  			return err
   158  		}
   159  	}
   160  
   161  	if table.routeTable.Find(pid) != "" {
   162  		table.peerStore.SetAddrs(pid, addrs, peerstore.PermanentAddrTTL)
   163  	} else {
   164  		table.peerStore.AddAddrs(pid, addrs, peerstore.PermanentAddrTTL)
   165  	}
   166  	table.routeTable.Update(pid)
   167  	table.onRouteTableChange()
   168  
   169  	return nil
   170  }
   171  
   172  // AddPeer add peer to route table.
   173  func (table *RouteTable) AddPeer(pid peer.ID, addr ma.Multiaddr) {
   174  	logging.VLog().Debugf("Adding Peer: %s,%s", pid.Pretty(), addr.String())
   175  	table.peerStore.AddAddr(pid, addr, peerstore.PermanentAddrTTL)
   176  	table.routeTable.Update(pid)
   177  	table.onRouteTableChange()
   178  
   179  }
   180  
   181  // AddPeers add peers to route table
   182  func (table *RouteTable) AddPeers(pid string, peers *netpb.Peers) {
   183  	// recv too many peers info. say Bye.
   184  	if len(peers.Peers) > table.maxPeersCountForSyncResp {
   185  		table.streamManager.CloseStream(pid, ErrExceedMaxSyncRouteResponse)
   186  	}
   187  	for _, v := range peers.Peers {
   188  		table.AddPeerInfo(v.Id, v.Addrs)
   189  	}
   190  }
   191  
   192  // AddIPFSPeerAddr add a peer to route table with ipfs address.
   193  func (table *RouteTable) AddIPFSPeerAddr(addr ma.Multiaddr) {
   194  	id, addr, err := ParseFromIPFSAddr(addr)
   195  	if err != nil {
   196  		return
   197  	}
   198  	table.AddPeer(id, addr)
   199  }
   200  
   201  // AddPeerStream add peer stream to peerStore.
   202  func (table *RouteTable) AddPeerStream(s *Stream) {
   203  	table.peerStore.AddAddr(
   204  		s.pid,
   205  		s.addr,
   206  		peerstore.PermanentAddrTTL,
   207  	)
   208  	table.routeTable.Update(s.pid)
   209  	table.onRouteTableChange()
   210  }
   211  
   212  // RemovePeerStream remove peerStream from peerStore.
   213  func (table *RouteTable) RemovePeerStream(s *Stream) {
   214  	table.peerStore.AddAddr(s.pid, s.addr, 0)
   215  	table.routeTable.Remove(s.pid)
   216  	table.onRouteTableChange()
   217  }
   218  
   219  func (table *RouteTable) onRouteTableChange() {
   220  	table.latestUpdatedAt = time.Now().Unix()
   221  }
   222  
   223  // GetRandomPeers get random peers
   224  func (table *RouteTable) GetRandomPeers(pid peer.ID) []peerstore.PeerInfo {
   225  
   226  	// change sync route algorithm from `NearestPeers` to `randomPeers`
   227  	var peers []peer.ID
   228  	allPeers := table.routeTable.ListPeers()
   229  	// Do not accept internal node synchronization routing requests.
   230  	if inArray(pid.Pretty(), table.internalNodeList) {
   231  		return []peerstore.PeerInfo{}
   232  	}
   233  
   234  	for _, v := range allPeers {
   235  		if inArray(v.Pretty(), table.internalNodeList) == false {
   236  			peers = append(peers, v)
   237  		}
   238  	}
   239  	peers = shufflePeerID(peers)
   240  	if len(peers) > table.maxPeersCountForSyncResp {
   241  		peers = peers[:table.maxPeersCountForSyncResp]
   242  	}
   243  	ret := make([]peerstore.PeerInfo, len(peers))
   244  	for i, v := range peers {
   245  		ret[i] = table.peerStore.PeerInfo(v)
   246  	}
   247  	return ret
   248  }
   249  
   250  func inArray(obj interface{}, array interface{}) bool {
   251  	arrayValue := reflect.ValueOf(array)
   252  	if reflect.TypeOf(array).Kind() == reflect.Array || reflect.TypeOf(array).Kind() == reflect.Slice {
   253  		for i := 0; i < arrayValue.Len(); i++ {
   254  			if arrayValue.Index(i).Interface() == obj {
   255  				return true
   256  			}
   257  		}
   258  	}
   259  	return false
   260  }
   261  
   262  func shufflePeerID(pids []peer.ID) []peer.ID {
   263  
   264  	r := rand.New(rand.NewSource(time.Now().Unix()))
   265  	ret := make([]peer.ID, len(pids))
   266  	perm := r.Perm(len(pids))
   267  	for i, randIndex := range perm {
   268  		ret[i] = pids[randIndex]
   269  	}
   270  	return ret
   271  }
   272  
   273  // LoadSeedNodes load seed nodes.
   274  func (table *RouteTable) LoadSeedNodes() {
   275  	for _, ipfsAddr := range table.seedNodes {
   276  		table.AddIPFSPeerAddr(ipfsAddr)
   277  	}
   278  }
   279  
   280  // LoadRouteTableFromFile load route table from file.
   281  func (table *RouteTable) LoadRouteTableFromFile() {
   282  	file, err := os.Open(table.cacheFilePath)
   283  	if err != nil {
   284  		logging.VLog().WithFields(logrus.Fields{
   285  			"cacheFilePath": table.cacheFilePath,
   286  			"err":           err,
   287  		}).Warn("Failed to open Route Table Cache file.")
   288  		return
   289  	}
   290  	defer file.Close()
   291  
   292  	// read line by line.
   293  	scanner := bufio.NewScanner(file)
   294  	scanner.Split(bufio.ScanLines)
   295  
   296  	for scanner.Scan() {
   297  		line := strings.TrimSpace(scanner.Text())
   298  		if strings.HasPrefix(line, "#") {
   299  			continue
   300  		}
   301  
   302  		addr, err := ma.NewMultiaddr(line)
   303  		if err != nil {
   304  			// ignore.
   305  			logging.VLog().WithFields(logrus.Fields{
   306  				"err":  err,
   307  				"text": line,
   308  			}).Warn("Invalid address in Route Table Cache file.")
   309  			continue
   310  		}
   311  
   312  		table.AddIPFSPeerAddr(addr)
   313  	}
   314  }
   315  
   316  // SaveRouteTableToFile save route table to file.
   317  func (table *RouteTable) SaveRouteTableToFile() {
   318  	file, err := os.Create(table.cacheFilePath)
   319  	if err != nil {
   320  		logging.VLog().WithFields(logrus.Fields{
   321  			"cacheFilePath": table.cacheFilePath,
   322  			"err":           err,
   323  		}).Warn("Failed to open Route Table Cache file.")
   324  		return
   325  	}
   326  	defer file.Close()
   327  
   328  	// write header.
   329  	file.WriteString(fmt.Sprintf("# %s\n", time.Now().String()))
   330  
   331  	peers := table.routeTable.ListPeers()
   332  	for _, v := range peers {
   333  		for _, addr := range table.peerStore.Addrs(v) {
   334  			line := fmt.Sprintf("%s/ipfs/%s\n", addr, v.Pretty())
   335  			file.WriteString(line)
   336  		}
   337  	}
   338  }
   339  
   340  // SyncRouteTable sync route table.
   341  func (table *RouteTable) SyncRouteTable() {
   342  	syncedPeers := make(map[peer.ID]bool)
   343  
   344  	// sync with seed nodes.
   345  	for _, ipfsAddr := range table.seedNodes {
   346  		pid, _, err := ParseFromIPFSAddr(ipfsAddr)
   347  		if err != nil {
   348  			continue
   349  		}
   350  		table.SyncWithPeer(pid)
   351  		syncedPeers[pid] = true
   352  	}
   353  
   354  	// random peer selection.
   355  	peers := table.routeTable.ListPeers()
   356  	peersCount := len(peers)
   357  	if peersCount <= 1 {
   358  		return
   359  	}
   360  
   361  	peersCountToSync := table.maxPeersCountToSync
   362  
   363  	if peersCount < peersCountToSync {
   364  		peersCountToSync = peersCount
   365  	}
   366  	selectedPeersIdx := make(map[int]bool)
   367  	for i := 0; i < peersCountToSync/2; i++ {
   368  		ri := 0
   369  
   370  		for {
   371  			ri = rand.Intn(peersCountToSync)
   372  			if selectedPeersIdx[ri] == false {
   373  				break
   374  			}
   375  		}
   376  
   377  		selectedPeersIdx[ri] = true
   378  		pid := peers[ri]
   379  
   380  		if syncedPeers[pid] == false {
   381  			table.SyncWithPeer(pid)
   382  			syncedPeers[pid] = true
   383  		}
   384  	}
   385  }
   386  
   387  // SyncWithPeer sync route table with a peer.
   388  func (table *RouteTable) SyncWithPeer(pid peer.ID) {
   389  	if pid == table.node.id {
   390  		return
   391  	}
   392  
   393  	stream := table.streamManager.Find(pid)
   394  
   395  	if stream == nil {
   396  		stream = NewStreamFromPID(pid, table.node)
   397  		table.streamManager.AddStream(stream)
   398  	}
   399  
   400  	stream.SyncRoute()
   401  }
   402  
   403  //LoadInternalNodeList Load Internal Node list from file
   404  func (table *RouteTable) LoadInternalNodeList() {
   405  	file, err := os.Open(RouteTableInternalNodeFileName)
   406  	if err != nil {
   407  		logging.VLog().WithFields(logrus.Fields{
   408  			"err": err,
   409  		}).Warn("Failed to open internal list file.")
   410  		return
   411  	}
   412  	defer file.Close()
   413  
   414  	// read line by line.
   415  	scanner := bufio.NewScanner(file)
   416  	scanner.Split(bufio.ScanLines)
   417  
   418  	for scanner.Scan() {
   419  		line := strings.TrimSpace(scanner.Text())
   420  		if len(line) > 0 {
   421  			table.internalNodeList = append(table.internalNodeList, line)
   422  		}
   423  	}
   424  
   425  	logging.VLog().WithFields(logrus.Fields{
   426  		"internalNodeList": table.internalNodeList,
   427  	}).Info("Loaded internal node list.")
   428  }