records.go 3.9 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
	// Check locally. Will also try to extract the public key from the peer
	// ID itself if possible (if inlined).
31
	pk := dht.peerstore.PubKey(p)
32 33 34 35
	if pk != nil {
		return pk, nil
	}

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

46 47 48 49 50 51
	// 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.
52 53 54 55 56 57 58 59 60
	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++ {
61 62 63 64 65 66
		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)
67
			}
68
			return r.pubk, nil
Jeromy's avatar
Jeromy committed
69
		}
70
		err = r.err
Jeromy's avatar
Jeromy committed
71 72
	}

73 74 75
	// Both go routines failed to find a public key
	return nil, err
}
76

77 78 79 80 81
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
82 83 84 85
	if err != nil {
		return nil, err
	}

86 87 88 89 90
	if len(vals) == 0 || vals[0].Val == nil {
		log.Debugf("Could not find public key for %v in DHT", p)
		return nil, routing.ErrNotFound
	}

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

97 98 99 100
	// 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
101 102
}

103 104 105 106 107 108 109
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
	}

110
	// Get the key from the node itself
Jeromy's avatar
Jeromy committed
111
	pkkey := routing.KeyForPublicKey(p)
112 113 114 115 116 117 118 119
	pmes, err := dht.getValueSingle(ctx, p, pkkey)
	if err != nil {
		return nil, err
	}

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

123
	pubk, err := ci.UnmarshalPublicKey(record.GetValue())
124
	if err != nil {
125
		log.Errorf("Could not unmarshall public key for %v", p)
126 127 128
		return nil, err
	}

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

139
	log.Debugf("Got public key from node %v itself", p)
140
	return pubk, nil
141
}