records.go 3.32 KB
Newer Older
1 2 3
package dht

import (
Jeromy's avatar
Jeromy committed
4
	"context"
5
	"fmt"
6

7 8 9 10
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/routing"

	ci "github.com/libp2p/go-libp2p-core/crypto"
11 12
)

13 14 15 16 17
type pubkrs struct {
	pubk ci.PubKey
	err  error
}

18
func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) {
Matt Joiner's avatar
Matt Joiner committed
19
	logger.Debugf("getPublicKey for: %s", p)
20

21 22
	// Check locally. Will also try to extract the public key from the peer
	// ID itself if possible (if inlined).
23
	pk := dht.peerstore.PubKey(p)
24 25 26 27
	if pk != nil {
		return pk, nil
	}

28 29
	// Try getting the public key both directly from the node it identifies
	// and from the DHT, in parallel
30 31
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
32 33 34 35 36 37
	resp := make(chan pubkrs, 2)
	go func() {
		pubk, err := dht.getPublicKeyFromNode(ctx, p)
		resp <- pubkrs{pubk, err}
	}()

38 39 40 41 42 43
	// Note that the number of open connections is capped by the dial
	// limiter, so there is a chance that getPublicKeyFromDHT(), which
	// potentially opens a lot of connections, will block
	// getPublicKeyFromNode() from getting a connection.
	// Currently this doesn't seem to cause an issue so leaving as is
	// for now.
44 45 46 47 48 49 50 51 52
	go func() {
		pubk, err := dht.getPublicKeyFromDHT(ctx, p)
		resp <- pubkrs{pubk, err}
	}()

	// Wait for one of the two go routines to return
	// a public key (or for both to error out)
	var err error
	for i := 0; i < 2; i++ {
53 54 55 56 57
		r := <-resp
		if r.err == nil {
			// Found the public key
			err := dht.peerstore.AddPubKey(p, r.pubk)
			if err != nil {
Matt Joiner's avatar
Matt Joiner committed
58
				logger.Warningf("Failed to add public key to peerstore for %v", p)
59
			}
60
			return r.pubk, nil
Jeromy's avatar
Jeromy committed
61
		}
62
		err = r.err
Jeromy's avatar
Jeromy committed
63 64
	}

65 66 67
	// Both go routines failed to find a public key
	return nil, err
}
68

69 70 71 72
func (dht *IpfsDHT) getPublicKeyFromDHT(ctx context.Context, p peer.ID) (ci.PubKey, error) {
	// Only retrieve one value, because the public key is immutable
	// so there's no need to retrieve multiple versions
	pkkey := routing.KeyForPublicKey(p)
Steven Allen's avatar
Steven Allen committed
73
	val, err := dht.GetValue(ctx, pkkey, Quorum(1))
Jeromy's avatar
Jeromy committed
74 75 76 77
	if err != nil {
		return nil, err
	}

Steven Allen's avatar
Steven Allen committed
78
	pubk, err := ci.UnmarshalPublicKey(val)
Jeromy's avatar
Jeromy committed
79
	if err != nil {
Matt Joiner's avatar
Matt Joiner committed
80
		logger.Errorf("Could not unmarshall public key retrieved from DHT for %v", p)
Jeromy's avatar
Jeromy committed
81 82
		return nil, err
	}
Jeromy's avatar
Jeromy committed
83

84 85
	// Note: No need to check that public key hash matches peer ID
	// because this is done by GetValues()
Matt Joiner's avatar
Matt Joiner committed
86
	logger.Debugf("Got public key for %s from DHT", p)
87
	return pubk, nil
Jeromy's avatar
Jeromy committed
88 89
}

90 91 92 93 94 95 96
func (dht *IpfsDHT) getPublicKeyFromNode(ctx context.Context, p peer.ID) (ci.PubKey, error) {
	// check locally, just in case...
	pk := dht.peerstore.PubKey(p)
	if pk != nil {
		return pk, nil
	}

97
	// Get the key from the node itself
Jeromy's avatar
Jeromy committed
98
	pkkey := routing.KeyForPublicKey(p)
99 100 101 102 103 104 105 106
	pmes, err := dht.getValueSingle(ctx, p, pkkey)
	if err != nil {
		return nil, err
	}

	// node doesn't have key :(
	record := pmes.GetRecord()
	if record == nil {
107
		return nil, fmt.Errorf("node %v not responding with its public key", p)
108 109
	}

110
	pubk, err := ci.UnmarshalPublicKey(record.GetValue())
111
	if err != nil {
Matt Joiner's avatar
Matt Joiner committed
112
		logger.Errorf("Could not unmarshall public key for %v", p)
113 114 115
		return nil, err
	}

116 117
	// Make sure the public key matches the peer ID
	id, err := peer.IDFromPublicKey(pubk)
118
	if err != nil {
Matt Joiner's avatar
Matt Joiner committed
119
		logger.Errorf("Could not extract peer id from public key for %v", p)
120 121 122
		return nil, err
	}
	if id != p {
123
		return nil, fmt.Errorf("public key %v does not match peer %v", id, p)
124 125
	}

Matt Joiner's avatar
Matt Joiner committed
126
	logger.Debugf("Got public key from node %v itself", p)
127
	return pubk, nil
128
}