routing.go 4.3 KB
Newer Older
1 2 3
package namesys

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

Dirk McCormick's avatar
Dirk McCormick committed
8
	opts "github.com/ipfs/go-ipfs/namesys/opts"
9
	path "gx/ipfs/QmdMPBephdLYNESkruDX2hcDTgFYhoCt4LimWhgnomSdV2/go-path"
Jeromy's avatar
Jeromy committed
10

11 12
	ipns "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns"
	pb "gx/ipfs/QmNqBhXpBKa5jcjoUZHfxDgAFxtqK3rDA5jtW811GBvVob/go-ipns/pb"
13
	mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash"
14
	peer "gx/ipfs/QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W/go-libp2p-peer"
Steven Allen's avatar
Steven Allen committed
15
	logging "gx/ipfs/QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr/go-log"
16 17 18
	routing "gx/ipfs/QmS4niovD1U6pRjUBXivr1zvvLBqiTKbERjFo994JU7oQS/go-libp2p-routing"
	dht "gx/ipfs/QmTRj8mj6X5LtjVochPPSNX6MTbJ6iVojcfakWJKG13re7/go-libp2p-kad-dht"
	cid "gx/ipfs/QmZFbDTY9jfSBms2MchvYM9oYRbAF19K7Pby47yDBfpPrb/go-cid"
Steven Allen's avatar
Steven Allen committed
19
	proto "gx/ipfs/QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8/gogo-protobuf/proto"
20 21
)

Jeromy's avatar
Jeromy committed
22
var log = logging.Logger("namesys")
23

Steven Allen's avatar
Steven Allen committed
24 25
// IpnsResolver implements NSResolver for the main IPFS SFS-like naming
type IpnsResolver struct {
26
	routing routing.ValueStore
27 28
}

Steven Allen's avatar
Steven Allen committed
29
// NewIpnsResolver constructs a name resolver using the IPFS Routing system
30
// to implement SFS-like naming on top.
Steven Allen's avatar
Steven Allen committed
31
func NewIpnsResolver(route routing.ValueStore) *IpnsResolver {
32 33 34
	if route == nil {
		panic("attempt to create resolver with nil routing system")
	}
Steven Allen's avatar
Steven Allen committed
35
	return &IpnsResolver{
36 37
		routing: route,
	}
Jeromy's avatar
Jeromy committed
38 39
}

40
// Resolve implements Resolver.
Steven Allen's avatar
Steven Allen committed
41
func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
Dirk McCormick's avatar
Dirk McCormick committed
42
	return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/")
43 44 45 46
}

// resolveOnce implements resolver. Uses the IPFS routing system to
// resolve SFS-like names.
Steven Allen's avatar
Steven Allen committed
47
func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) {
48
	log.Debugf("RoutingResolver resolving %s", name)
49

Dirk McCormick's avatar
Dirk McCormick committed
50
	if options.DhtTimeout != 0 {
51 52
		// Resolution must complete within the timeout
		var cancel context.CancelFunc
Dirk McCormick's avatar
Dirk McCormick committed
53
		ctx, cancel = context.WithTimeout(ctx, options.DhtTimeout)
54 55 56
		defer cancel()
	}

57
	name = strings.TrimPrefix(name, "/ipns/")
58 59
	hash, err := mh.FromB58String(name)
	if err != nil {
Jeromy's avatar
Jeromy committed
60
		// name should be a multihash. if it isn't, error out here.
61
		log.Debugf("RoutingResolver: bad input hash: [%s]\n", name)
Steven Allen's avatar
Steven Allen committed
62
		return "", 0, err
63 64
	}

65 66 67
	pid, err := peer.IDFromBytes(hash)
	if err != nil {
		log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err)
Steven Allen's avatar
Steven Allen committed
68
		return "", 0, err
69 70
	}

71 72 73 74 75
	// Name should be the hash of a public key retrievable from ipfs.
	// We retrieve the public key here to make certain that it's in the peer
	// store before calling GetValue() on the DHT - the DHT will call the
	// ipns validator, which in turn will get the public key from the peer
	// store to verify the record signature
76
	_, err = routing.GetPublicKey(r.routing, ctx, pid)
77 78
	if err != nil {
		log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err)
Steven Allen's avatar
Steven Allen committed
79
		return "", 0, err
80
	}
Jeromy's avatar
Jeromy committed
81

82 83 84
	// Use the routing system to get the name.
	// Note that the DHT will call the ipns validator when retrieving
	// the value, which in turn verifies the ipns record signature
85
	ipnsKey := ipns.RecordKey(pid)
86
	val, err := r.routing.GetValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount)))
87 88
	if err != nil {
		log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err)
Steven Allen's avatar
Steven Allen committed
89
		return "", 0, err
90 91
	}

92 93 94 95
	entry := new(pb.IpnsEntry)
	err = proto.Unmarshal(val, entry)
	if err != nil {
		log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", name, err)
Steven Allen's avatar
Steven Allen committed
96
		return "", 0, err
97
	}
98

99
	var p path.Path
100
	// check for old style record:
101 102 103 104 105
	if valh, err := mh.Cast(entry.GetValue()); err == nil {
		// Its an old style multihash record
		log.Debugf("encountered CIDv0 ipns entry: %s", valh)
		p = path.FromCid(cid.NewCidV0(valh))
	} else {
106
		// Not a multihash, probably a new record
107
		p, err = path.ParsePath(string(entry.GetValue()))
108
		if err != nil {
Steven Allen's avatar
Steven Allen committed
109 110 111 112 113 114 115 116
			return "", 0, err
		}
	}

	ttl := DefaultResolverCacheTTL
	if entry.Ttl != nil {
		ttl = time.Duration(*entry.Ttl)
	}
117 118 119 120
	switch eol, err := ipns.GetEOL(entry); err {
	case ipns.ErrUnrecognizedValidity:
		// No EOL.
	case nil:
Steven Allen's avatar
Steven Allen committed
121 122 123 124 125 126
		ttEol := eol.Sub(time.Now())
		if ttEol < 0 {
			// It *was* valid when we first resolved it.
			ttl = 0
		} else if ttEol < ttl {
			ttl = ttEol
127
		}
128 129 130
	default:
		log.Errorf("encountered error when parsing EOL: %s", err)
		return "", 0, err
131
	}
132

Steven Allen's avatar
Steven Allen committed
133
	return p, ttl, nil
134
}