github.com/metacurrency/holochain@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, ¬if.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, ¬if.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 }