github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/vntp2p/discovery.go (about)

     1  // Copyright 2019 The go-vnt Authors
     2  // This file is part of the go-vnt library.
     3  //
     4  // The go-vnt 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-vnt 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-vnt library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package vntp2p
    18  
    19  import (
    20  	"context"
    21  	"crypto/rand"
    22  	"sync"
    23  	"time"
    24  
    25  	util "github.com/ipfs/go-ipfs-util"
    26  	dht "github.com/libp2p/go-libp2p-kad-dht"
    27  	peer "github.com/libp2p/go-libp2p-peer"
    28  	routing "github.com/libp2p/go-libp2p-routing"
    29  	"github.com/vntchain/go-vnt/log"
    30  )
    31  
    32  const (
    33  	refreshInterval = 30 * time.Second
    34  	searchTimeOut   = 1 * time.Minute
    35  )
    36  
    37  type DhtTable interface {
    38  	Start(ctx context.Context) error
    39  	Lookup(ctx context.Context, targetID NodeID) []*NodeID
    40  	Update(ctx context.Context, id peer.ID) error
    41  	RandomPeer() []peer.ID
    42  	GetDhtTable() *dht.IpfsDHT
    43  }
    44  
    45  type VNTDht struct {
    46  	mutex sync.Mutex
    47  	table *dht.IpfsDHT
    48  	self  peer.ID
    49  }
    50  
    51  func NewDHTTable(dht *dht.IpfsDHT, id peer.ID) *VNTDht {
    52  	return &VNTDht{
    53  		table: dht,
    54  		self:  id,
    55  	}
    56  }
    57  
    58  func (vdht *VNTDht) Start(ctx context.Context) error {
    59  	var bootStrapConfig = dht.DefaultBootstrapConfig
    60  	bootStrapConfig.Period = time.Duration(refreshInterval)
    61  	bootStrapConfig.Timeout = time.Duration(searchTimeOut)
    62  	proc, err := vdht.table.BootstrapWithConfig(bootStrapConfig)
    63  	if err != nil {
    64  		log.Debug("Start refresh k-bucket error", "error", err)
    65  		return err
    66  	}
    67  
    68  	// wait till ctx or dht.Context exits.
    69  	// we have to do it this way to satisfy the Routing interface (contexts)
    70  	go func() {
    71  		defer proc.Close()
    72  		select {
    73  		case <-ctx.Done():
    74  		case <-vdht.table.Context().Done():
    75  		}
    76  	}()
    77  
    78  	return nil
    79  }
    80  
    81  func (vdht *VNTDht) Update(ctx context.Context, id peer.ID) error {
    82  	vdht.table.Update(ctx, id)
    83  	return nil
    84  }
    85  
    86  func randomID() peer.ID {
    87  	id := make([]byte, 16)
    88  	rand.Read(id)
    89  	id = util.Hash(id)
    90  
    91  	// var aid NodeID
    92  	// copy(aid, id)
    93  	return peer.ID(id)
    94  }
    95  
    96  func (vdht *VNTDht) Lookup(ctx context.Context, targetID NodeID) []*NodeID {
    97  	// vdht.table.GetClosestPeers(vdht.Context, )
    98  
    99  	results := vdht.lookup(ctx, targetID.PeerID())
   100  
   101  	nodeids := []*NodeID{}
   102  
   103  	for _, result := range results {
   104  		nodeid := PeerIDtoNodeID(result)
   105  		nodeids = append(nodeids, &nodeid)
   106  	}
   107  
   108  	return nodeids
   109  }
   110  
   111  func (vdht *VNTDht) lookup(ctx context.Context, targetid peer.ID) []peer.ID {
   112  	// 开始搜寻
   113  
   114  	// fmt.Println("Begin Lookup")
   115  	cctx, cancel := context.WithTimeout(ctx, searchTimeOut)
   116  	defer cancel()
   117  
   118  	runQuery := func(ctxs context.Context, id peer.ID) {
   119  		p, err := vdht.table.FindPeer(ctxs, id)
   120  		if err == routing.ErrNotFound {
   121  		} else if err != nil {
   122  			log.Debug("lookup peer occurs error", "error", err)
   123  		} else {
   124  			log.Debug("lookup peer find peer", "id", id.ToString(), "peer", p.ID.ToString())
   125  		}
   126  	}
   127  
   128  	runQuery(cctx, targetid)
   129  
   130  	return nil
   131  }
   132  
   133  func (vdht *VNTDht) doRefresh(ctx context.Context, done chan struct{}) {
   134  	// defer close(done)
   135  
   136  	// Load nodes from the database and insert
   137  	// them. This should yield a few previously seen nodes that are
   138  	// (hopefully) still alive.
   139  	// tab.loadSeedNodes(true)
   140  
   141  	// Run self lookup to discover new neighbor nodes.
   142  	// fmt.Println("@@@CCCC find my self", vdht.self)
   143  	vdht.lookup(ctx, vdht.self)
   144  
   145  	// The Kademlia paper specifies that the bucket refresh should
   146  	// perform a lookup in the least recently used bucket. We cannot
   147  	// adhere to this because the findnode target is a 512bit value
   148  	// (not hash-sized) and it is not easily possible to generate a
   149  	// sha3 preimage that falls into a chosen bucket.
   150  	// We perform a few lookups with a random target instead.
   151  	for i := 0; i < 3; i++ {
   152  		target := randomID()
   153  		// fmt.Println("random id: ", target)
   154  		vdht.lookup(ctx, target)
   155  	}
   156  }
   157  
   158  func (vdht *VNTDht) RandomPeer() []peer.ID {
   159  	return vdht.table.GetRandomPeers()
   160  }
   161  
   162  func (vdht *VNTDht) GetDhtTable() *dht.IpfsDHT {
   163  	return vdht.table
   164  }