github.com/holochain/holochain-proto@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/kad_lookup.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/lookup.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. 8 9 package holochain 10 11 import ( 12 "context" 13 . "github.com/holochain/holochain-proto/hash" 14 peer "github.com/libp2p/go-libp2p-peer" 15 pstore "github.com/libp2p/go-libp2p-peerstore" 16 // notif "github.com/libp2p/go-libp2p-routing/notifications" 17 "errors" 18 ) 19 20 var ErrEmptyRoutingTable = errors.New("routing table empty") 21 22 func toPeerInfos(ps []peer.ID) []*pstore.PeerInfo { 23 out := make([]*pstore.PeerInfo, len(ps)) 24 for i, p := range ps { 25 out[i] = &pstore.PeerInfo{ID: p} 26 } 27 return out 28 } 29 30 // Kademlia 'node lookup' operation. Returns a channel of the K closest peers 31 // to the given key 32 func (node *Node) GetClosestPeers(ctx context.Context, key Hash) (<-chan peer.ID, error) { 33 node.log.Logf("Finding peers close to %v", key) 34 tablepeers := node.routingTable.NearestPeers(key, AlphaValue) 35 if len(tablepeers) == 0 { 36 return nil, ErrEmptyRoutingTable 37 } 38 39 out := make(chan peer.ID, KValue) 40 41 // since the query doesn't actually pass our context down 42 // we have to hack this here. whyrusleeping isnt a huge fan of goprocess 43 //parent := ctx 44 query := node.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) { 45 // For DHT query command 46 /*notif.PublishQueryEvent(parent, ¬if.QueryEvent{ 47 Type: notif.SendingQuery, 48 ID: p, 49 })*/ 50 51 closer, err := node.closerPeersSingle(ctx, key, p) 52 if err != nil { 53 node.log.Logf("error getting closer peers: %s", err) 54 return nil, err 55 } 56 node.log.Logf("closerPeers: %v\n", closer) 57 peerinfos := toPeerInfos(closer) 58 59 // For DHT query command 60 /*notif.PublishQueryEvent(parent, ¬if.QueryEvent{ 61 Type: notif.PeerResponse, 62 ID: p, 63 Responses: peerinfos, // todo: remove need for this pointerize thing 64 })*/ 65 66 return &dhtQueryResult{closerPeers: peerinfos}, nil 67 }) 68 69 go func() { 70 defer close(out) 71 // run it! 72 res, err := query.Run(ctx, tablepeers) 73 if err != nil { 74 node.log.Logf("closestPeers query run error: %v", err) 75 } 76 77 if res != nil && res.finalSet != nil { 78 node.log.Logf("closestPeers have %d in final set", res.finalSet.Size()) 79 // include self 80 sorted := SortClosestPeers(append(res.finalSet.Peers(), node.HashAddr), key) 81 if len(sorted) > KValue { 82 sorted = sorted[:KValue] 83 } 84 85 for _, p := range sorted { 86 out <- p 87 } 88 } 89 }() 90 91 return out, nil 92 } 93 94 func (node *Node) closerPeersSingle(ctx context.Context, key Hash, p peer.ID) ([]peer.ID, error) { 95 closerPeers, err := node.findPeerSingle(ctx, p, key) 96 if err != nil { 97 return nil, err 98 } 99 100 var out []peer.ID 101 for _, pinfo := range closerPeers { 102 if pinfo.ID != node.HashAddr { // dont add self 103 // TODO: what about the time to live of this peer??? 104 node.peerstore.AddAddrs(pinfo.ID, pinfo.Addrs, pstore.TempAddrTTL) 105 out = append(out, pinfo.ID) 106 } 107 } 108 return out, nil 109 }