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

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

8 9
	ci "github.com/libp2p/go-libp2p-crypto"
	peer "github.com/libp2p/go-libp2p-peer"
George Antoniadis's avatar
George Antoniadis committed
10
	routing "github.com/libp2p/go-libp2p-routing"
11 12
)

13 14 15 16 17 18 19 20
// MaxRecordAge specifies the maximum time that any node will hold onto a record
// from the time its received. This does not apply to any other forms of validity that
// the record may contain.
// For example, a record may contain an ipns entry with an EOL saying its valid
// until the year 2020 (a great time in the future). For that record to stick around
// it must be rebroadcasted more frequently than once every 'MaxRecordAge'
const MaxRecordAge = time.Hour * 36

21 22 23 24 25
type pubkrs struct {
	pubk ci.PubKey
	err  error
}

26
func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) {
27 28
	log.Debugf("getPublicKey for: %s", p)

29 30 31 32 33 34
	// try extracting from identity.
	pk := p.ExtractPublicKey()
	if pk != nil {
		return pk, nil
	}

35
	// check locally.
36
	pk = dht.peerstore.PubKey(p)
37 38 39 40
	if pk != nil {
		return pk, nil
	}

41 42
	// Try getting the public key both directly from the node it identifies
	// and from the DHT, in parallel
43 44
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
45 46 47 48 49 50
	resp := make(chan pubkrs, 2)
	go func() {
		pubk, err := dht.getPublicKeyFromNode(ctx, p)
		resp <- pubkrs{pubk, err}
	}()

51 52 53 54 55 56
	// 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.
57 58 59 60 61 62 63 64 65
	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++ {
66 67 68 69 70 71
		r := <-resp
		if r.err == nil {
			// Found the public key
			err := dht.peerstore.AddPubKey(p, r.pubk)
			if err != nil {
				log.Warningf("Failed to add public key to peerstore for %v", p)
72
			}
73
			return r.pubk, nil
Jeromy's avatar
Jeromy committed
74
		}
75
		err = r.err
Jeromy's avatar
Jeromy committed
76 77
	}

78 79 80
	// Both go routines failed to find a public key
	return nil, err
}
81

82 83 84 85 86
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)
	vals, err := dht.GetValues(ctx, pkkey, 1)
Jeromy's avatar
Jeromy committed
87 88 89 90
	if err != nil {
		return nil, err
	}

91 92 93 94 95
	if len(vals) == 0 || vals[0].Val == nil {
		log.Debugf("Could not find public key for %v in DHT", p)
		return nil, routing.ErrNotFound
	}

96
	pubk, err := ci.UnmarshalPublicKey(vals[0].Val)
Jeromy's avatar
Jeromy committed
97
	if err != nil {
98
		log.Errorf("Could not unmarshall public key retrieved from DHT for %v", p)
Jeromy's avatar
Jeromy committed
99 100
		return nil, err
	}
Jeromy's avatar
Jeromy committed
101

102 103 104 105
	// Note: No need to check that public key hash matches peer ID
	// because this is done by GetValues()
	log.Debugf("Got public key for %s from DHT", p)
	return pubk, nil
Jeromy's avatar
Jeromy committed
106 107
}

108 109 110 111 112 113 114
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
	}

115
	// Get the key from the node itself
Jeromy's avatar
Jeromy committed
116
	pkkey := routing.KeyForPublicKey(p)
117 118 119 120 121 122 123 124
	pmes, err := dht.getValueSingle(ctx, p, pkkey)
	if err != nil {
		return nil, err
	}

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

128
	pubk, err := ci.UnmarshalPublicKey(record.GetValue())
129
	if err != nil {
130
		log.Errorf("Could not unmarshall public key for %v", p)
131 132 133
		return nil, err
	}

134 135
	// Make sure the public key matches the peer ID
	id, err := peer.IDFromPublicKey(pubk)
136
	if err != nil {
137
		log.Errorf("Could not extract peer id from public key for %v", p)
138 139 140
		return nil, err
	}
	if id != p {
141
		return nil, fmt.Errorf("public key %v does not match peer %v", id, p)
142 143
	}

144
	log.Debugf("Got public key from node %v itself", p)
145
	return pubk, nil
146
}