github.com/holochain/holochain-proto@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/kad_rpc.go (about)

     1  // Copyright (C) 2013-2018, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.)
     2  // Use of this source code is governed by GPLv3 found in the LICENSE file
     3  //
     4  // This code is adapted from the libp2p project, specifically:
     5  // https://github.com/libp2p/go-libp2p-kad-dht/routing.go
     6  // The ipfs use of kademlia is substantially different than that needed by holochain so we remove
     7  // parts we don't need and add others, also we have do our message wire-formats and encoding
     8  // differently, so our RPC handlers are need to be different.
     9  
    10  package holochain
    11  
    12  import (
    13  	"context"
    14  	"errors"
    15  	"fmt"
    16  	. "github.com/holochain/holochain-proto/hash"
    17  	peer "github.com/libp2p/go-libp2p-peer"
    18  	pstore "github.com/libp2p/go-libp2p-peerstore"
    19  	ma "github.com/multiformats/go-multiaddr"
    20  	_ "sync"
    21  	_ "time"
    22  )
    23  
    24  var ErrDHTUnexpectedTypeInBody error = errors.New("unexpected type in message body")
    25  
    26  type FindNodeReq struct {
    27  	H Hash
    28  }
    29  
    30  // an encodable version of pstore.PeerInfo which gob doesn't like
    31  // also libp2p encodes other stuff like connection type into this
    32  // which we may have to do too.
    33  type PeerInfo struct {
    34  	ID    []byte   // byte version peer.ID
    35  	Addrs [][]byte // byte version of multiaddrs
    36  }
    37  
    38  type CloserPeersResp struct {
    39  	CloserPeers []PeerInfo // note this is not a pstore.PeerInfo which can't be serialized by gob.
    40  }
    41  
    42  // The number of closer peers to send on requests.
    43  var CloserPeerCount = KValue
    44  
    45  // FindLocal looks for a peer with a given ID connected to this node and returns its peer info
    46  func (node *Node) FindLocal(id peer.ID) pstore.PeerInfo {
    47  	p := node.routingTable.Find(id)
    48  	if p != "" {
    49  		return node.peerstore.PeerInfo(p)
    50  	}
    51  	return pstore.PeerInfo{}
    52  }
    53  
    54  // findPeerSingle asks peer 'p' if they know where the peer with id 'id' is and respond with
    55  // any closer peers if not.
    56  func (node *Node) findPeerSingle(ctx context.Context, p peer.ID, hash Hash) (closerPeers []*pstore.PeerInfo, err error) {
    57  	node.log.Logf("Sending FIND_NODE_REQUEST to %v for hash: %v\n", p, hash)
    58  	pmes := node.NewMessage(FIND_NODE_REQUEST, FindNodeReq{H: hash})
    59  	var resp Message
    60  	resp, err = node.Send(ctx, KademliaProtocol, p, pmes)
    61  	if err != nil {
    62  		return
    63  	}
    64  
    65  	response, ok := resp.Body.(CloserPeersResp)
    66  	if !ok {
    67  		err = ErrDHTUnexpectedTypeInBody
    68  		return
    69  	}
    70  
    71  	closerPeers = peerInfos2Pis(response.CloserPeers)
    72  
    73  	return
    74  }
    75  
    76  // nearestPeersToHash returns the routing tables closest peers to a given hash
    77  func (node *Node) nearestPeersToHash(hash *Hash, count int) []peer.ID {
    78  	//	fmt.Printf("%v NearestPeers to %s: ", node.HashAddr.Pretty()[2:4], hash.String())
    79  	closer := node.routingTable.NearestPeers(*hash, count)
    80  	return closer
    81  }
    82  
    83  // betterPeersForHash returns nearestPeersToHash, but iff closer than self.
    84  func (node *Node) betterPeersForHash(hash *Hash, p peer.ID, excludeSelf bool, count int) []peer.ID {
    85  	closer := node.nearestPeersToHash(hash, count)
    86  
    87  	// no node? nil
    88  	if closer == nil {
    89  		node.log.Logf("no closer peers to send to %v", p)
    90  		return nil
    91  	}
    92  
    93  	if excludeSelf {
    94  		two := []peer.ID{node.HashAddr, closer[0]}
    95  		two = SortClosestPeers(two, *hash)
    96  		if two[0] == node.HashAddr {
    97  			return nil
    98  		}
    99  	}
   100  
   101  	var filtered []peer.ID
   102  	for _, clp := range closer {
   103  
   104  		// == to self? thats bad
   105  		if clp == node.HashAddr {
   106  			Debug("attempted to return self! this shouldn't happen...")
   107  			return nil
   108  		}
   109  		// Dont send a peer back themselves
   110  		if clp == p {
   111  			continue
   112  		}
   113  
   114  		filtered = append(filtered, clp)
   115  	}
   116  
   117  	// ok seems like closer nodes
   118  	return filtered
   119  }
   120  
   121  // FindPeer searches for a peer with given ID.
   122  // it is also an implementation the FindPeer() method of the RoutedHost interface in go-libp2p/p2p/host/routed
   123  // and makes the Node object the "Router"
   124  func (node *Node) FindPeer(ctx context.Context, id peer.ID) (pstore.PeerInfo, error) {
   125  	// Check if were already connected to them
   126  	if pi := node.FindLocal(id); pi.ID != "" {
   127  		return pi, nil
   128  	}
   129  
   130  	hashID := HashFromPeerID(id)
   131  	peers := node.routingTable.NearestPeers(hashID, AlphaValue)
   132  	if len(peers) == 0 {
   133  		return pstore.PeerInfo{}, ErrEmptyRoutingTable
   134  	}
   135  
   136  	// Sanity...
   137  	for _, p := range peers {
   138  		if p == id {
   139  			node.log.Logf("found target peer in list of closest peers...")
   140  			return node.peerstore.PeerInfo(p), nil
   141  		}
   142  	}
   143  
   144  	// setup the Query
   145  	query := node.newQuery(hashID, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) {
   146  		/*	notif.PublishQueryEvent(parent, &notif.QueryEvent{
   147  			Type: notif.SendingQuery,
   148  			ID:   p,
   149  		})*/
   150  
   151  		closerPeers, err := node.findPeerSingle(ctx, p, hashID)
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  
   156  		// see if we got the peer here
   157  		for _, npi := range closerPeers {
   158  			if npi.ID == id {
   159  				return &dhtQueryResult{
   160  					peer:    npi,
   161  					success: true,
   162  				}, nil
   163  			}
   164  		}
   165  
   166  		/*		notif.PublishQueryEvent(parent, &notif.QueryEvent{
   167  				Type:      notif.PeerResponse,
   168  				ID:        p,
   169  				Responses: clpeerInfos,
   170  			})*/
   171  
   172  		return &dhtQueryResult{closerPeers: closerPeers}, nil
   173  	})
   174  
   175  	// run it!
   176  	result, err := query.Run(ctx, peers)
   177  	if err != nil {
   178  		return pstore.PeerInfo{}, err
   179  	}
   180  
   181  	node.log.Logf("FindPeer %v %v", id, result.success)
   182  	if result.peer.ID == "" {
   183  		return pstore.PeerInfo{}, ErrHashNotFound
   184  	}
   185  
   186  	return *result.peer, nil
   187  }
   188  
   189  // KademliaReceiver implements the handler for the kademlia RPC protocol messages
   190  func KademliaReceiver(h *Holochain, m *Message) (response interface{}, err error) {
   191  	dht := h.dht
   192  	node := h.node
   193  	switch m.Type {
   194  	case FIND_NODE_REQUEST:
   195  		dht.dlog.Logf("KademliaReceiver got: %v", m)
   196  		switch t := m.Body.(type) {
   197  		case FindNodeReq:
   198  
   199  			p := m.From
   200  			var closest []peer.ID
   201  			resp := CloserPeersResp{}
   202  			// if looking for self... special case where we send it on CloserPeers.
   203  			x := HashFromPeerID(node.HashAddr)
   204  			if x.Equal(t.H) {
   205  				closest = []peer.ID{node.HashAddr}
   206  			} else {
   207  				closest = node.betterPeersForHash(&t.H, p, false, CloserPeerCount)
   208  			}
   209  			if closest == nil {
   210  				dht.dlog.Logf("could not find any peers")
   211  				return &resp, nil
   212  			}
   213  
   214  			resp.CloserPeers = node.peers2PeerInfos(closest)
   215  			response = &resp
   216  		default:
   217  			err = ErrDHTUnexpectedTypeInBody
   218  		}
   219  	default:
   220  		err = fmt.Errorf("message type %d not in holochain-kademlia protocol", int(m.Type))
   221  	}
   222  	return
   223  }
   224  
   225  // PI2PeerInfos convert the closest PeerInfos to a serializable type and gets their addrs from the peerstore
   226  func (node *Node) peers2PeerInfos(peers []peer.ID) []PeerInfo {
   227  	var pis []PeerInfo
   228  	infos := pstore.PeerInfos(node.peerstore, peers)
   229  	for _, pi := range infos {
   230  		if len(pi.Addrs) > 0 {
   231  			addrs := make([][]byte, len(pi.Addrs))
   232  			for i, a := range pi.Addrs {
   233  				addrs[i] = a.Bytes()
   234  			}
   235  			pis = append(pis, PeerInfo{ID: []byte(pi.ID), Addrs: addrs})
   236  		}
   237  	}
   238  	return pis
   239  }
   240  
   241  // PeerInfos2Pis convert a list of PeerInfo structs to a list of pstore.PeerInfo
   242  func peerInfos2Pis(peerInfos []PeerInfo) []*pstore.PeerInfo {
   243  	pis := make([]*pstore.PeerInfo, 0, len(peerInfos))
   244  	for _, pi := range peerInfos {
   245  		peerInfo := pstore.PeerInfo{ID: peer.ID(pi.ID)}
   246  		if len(pi.Addrs) > 0 {
   247  			maddrs := make([]ma.Multiaddr, 0, len(pi.Addrs))
   248  			for _, addr := range pi.Addrs {
   249  				maddr, err := ma.NewMultiaddrBytes(addr)
   250  				if err != nil {
   251  					Infof("error decoding Multiaddr for peer: %s", peerInfo.ID)
   252  					continue
   253  				}
   254  				maddrs = append(maddrs, maddr)
   255  			}
   256  			peerInfo.Addrs = maddrs
   257  		}
   258  		pis = append(pis, &peerInfo)
   259  	}
   260  	return pis
   261  }