@cryptotaxi247 / kubo / commits / 1abf8366e

Add options for record count and timeout for resolving DHT paths

License: MIT Signed-off-by: Dirk McCormick <dirkmdev@gmail.com>

Dirk McCormick committed Feb 23, 2018 at 11:47 UTC 1abf8366ee2a38c47b7fcc4a14654e24e9492159
20 files changed +165 -94
core/commands/dns.go
+4 -4
@@ -57,11 +57,11 @@ The resolver can recursively resolve:
57 name := req.Arguments()[0]
58 resolver := namesys.NewDNSResolver()
59
60 - depth := 1
61 - if recursive {
62 - depth = namesys.DefaultDepthLimit
60 + opts := namesys.DefaultResolveOpts()
61 + if !recursive {
62 + opts.Depth = 1
63 }
64 - output, err := resolver.ResolveN(req.Context(), name, depth)
64 + output, err := resolver.Resolve(req.Context(), name, opts)
65 if err == namesys.ErrResolveFailed {
66 res.SetError(err, cmdkit.ErrNotFound)
67 return
core/commands/ipns.go
+15 -5
@@ -4,6 +4,7 @@ import (
4 "errors"
5 "io"
6 "strings"
7 + "time"
8
9 cmds "github.com/ipfs/go-ipfs/commands"
10 e "github.com/ipfs/go-ipfs/core/commands/e"
@@ -57,9 +58,10 @@ Resolve the value of a dnslink:
58 Options: []cmdkit.Option{
59 cmdkit.BoolOption("recursive", "r", "Resolve until the result is not an IPNS name."),
60 cmdkit.BoolOption("nocache", "n", "Do not use cached entries."),
61 + cmdkit.UintOption("dht-record-count", "dhtrc", "Number of records to request for DHT resolution."),
62 + cmdkit.UintOption("dht-timeout", "dhtt", "Timeout in seconds for DHT resolution. Pass 0 for no timeout."),
63 },
64 Run: func(req cmds.Request, res cmds.Response) {
62 -
65 n, err := req.InvocContext().GetNode()
66 if err != nil {
67 res.SetError(err, cmdkit.ErrNormal)
@@ -107,16 +109,24 @@ Resolve the value of a dnslink:
109 }
110
111 recursive, _, _ := req.Option("recursive").Bool()
110 - depth := 1
111 - if recursive {
112 - depth = namesys.DefaultDepthLimit
112 + rc, rcok, _ := req.Option("dht-record-count").Int()
113 + dhtt, dhttok, _ := req.Option("dht-timeout").Int()
114 + opts := namesys.DefaultResolveOpts()
115 + if !recursive {
116 + opts.Depth = 1
117 + }
118 + if rcok {
119 + opts.DhtRecordCount = uint(rc)
120 + }
121 + if dhttok {
122 + opts.DhtTimeout = time.Duration(dhtt) * time.Second
123 }
124
125 if !strings.HasPrefix(name, "/ipns/") {
126 name = "/ipns/" + name
127 }
128
119 - output, err := resolver.ResolveN(req.Context(), name, depth)
129 + output, err := resolver.Resolve(req.Context(), name, opts)
130 if err != nil {
131 res.SetError(err, cmdkit.ErrNormal)
132 return
core/commands/resolve.go
+14 -1
@@ -3,6 +3,7 @@ package commands
3 import (
4 "io"
5 "strings"
6 + "time"
7
8 cmds "github.com/ipfs/go-ipfs/commands"
9 "github.com/ipfs/go-ipfs/core"
@@ -62,6 +63,8 @@ Resolve the value of an IPFS DAG path:
63 },
64 Options: []cmdkit.Option{
65 cmdkit.BoolOption("recursive", "r", "Resolve until the result is an IPFS name."),
66 + cmdkit.UintOption("dht-record-count", "dhtrc", "Number of records to request for DHT resolution."),
67 + cmdkit.UintOption("dht-timeout", "dhtt", "Timeout in seconds for DHT resolution. Pass 0 for no timeout."),
68 },
69 Run: func(req cmds.Request, res cmds.Response) {
70
@@ -84,7 +87,17 @@ Resolve the value of an IPFS DAG path:
87
88 // the case when ipns is resolved step by step
89 if strings.HasPrefix(name, "/ipns/") && !recursive {
87 - p, err := n.Namesys.ResolveN(req.Context(), name, 1)
90 + rc, rcok, _ := req.Option("dht-record-count").Int()
91 + dhtt, dhttok, _ := req.Option("dht-timeout").Int()
92 + opts := ns.DefaultResolveOpts()
93 + opts.Depth = 1
94 + if rcok {
95 + opts.DhtRecordCount = uint(rc)
96 + }
97 + if dhttok {
98 + opts.DhtTimeout = time.Duration(dhtt) * time.Second
99 + }
100 + p, err := n.Namesys.Resolve(req.Context(), name, opts)
101 // ErrResolveRecursion is fine
102 if err != nil && err != ns.ErrResolveRecursion {
103 res.SetError(err, cmdkit.ErrNormal)
core/coreapi/name.go
+4 -4
@@ -117,16 +117,16 @@ func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.Nam
117 resolver = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), 0)
118 }
119
120 - depth := 1
121 - if options.Recursive {
122 - depth = namesys.DefaultDepthLimit
120 + ropts := namesys.DefaultResolveOpts()
121 + if !options.Recursive {
122 + ropts.Depth = 1
123 }
124
125 if !strings.HasPrefix(name, "/ipns/") {
126 name = "/ipns/" + name
127 }
128
129 - output, err := resolver.ResolveN(ctx, name, depth)
129 + output, err := resolver.Resolve(ctx, name, ropts)
130 if err != nil {
131 return nil, err
132 }
core/corehttp/ipns_hostname.go
+2 -1
@@ -7,6 +7,7 @@ import (
7 "strings"
8
9 "github.com/ipfs/go-ipfs/core"
10 + namesys "github.com/ipfs/go-ipfs/namesys"
11
12 isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain"
13 )
@@ -24,7 +25,7 @@ func IPNSHostnameOption() ServeOption {
25 host := strings.SplitN(r.Host, ":", 2)[0]
26 if len(host) > 0 && isd.IsDomain(host) {
27 name := "/ipns/" + host
27 - if _, err := n.Namesys.Resolve(ctx, name); err == nil {
28 + if _, err := n.Namesys.Resolve(ctx, name, namesys.DefaultResolveOpts()); err == nil {
29 r.Header["X-Ipns-Original-Path"] = []string{r.URL.Path}
30 r.URL.Path = name + r.URL.Path
31 }
core/pathresolver.go
+1 -1
@@ -48,7 +48,7 @@ func Resolve(ctx context.Context, nsys namesys.NameSystem, r *resolver.Resolver,
48 return nil, err
49 }
50
51 - respath, err := nsys.Resolve(ctx, resolvable.String())
51 + respath, err := nsys.Resolve(ctx, resolvable.String(), namesys.DefaultResolveOpts())
52 if err != nil {
53 evt.Append(logging.LoggableMap{"error": err.Error()})
54 return nil, err
fuse/ipns/ipns_unix.go
+1 -1
@@ -203,7 +203,7 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
203 }
204
205 // other links go through ipns resolution and are symlinked into the ipfs mountpoint
206 - resolved, err := s.Ipfs.Namesys.Resolve(s.Ipfs.Context(), name)
206 + resolved, err := s.Ipfs.Namesys.Resolve(s.Ipfs.Context(), name, namesys.DefaultResolveOpts())
207 if err != nil {
208 log.Warningf("ipns: namesys resolve error: %s", err)
209 return nil, fuse.ENOENT
namesys/base.go
+4 -3
@@ -10,13 +10,14 @@ import (
10
11 type resolver interface {
12 // resolveOnce looks up a name once (without recursion).
13 - resolveOnce(ctx context.Context, name string) (value path.Path, err error)
13 + resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (value path.Path, err error)
14 }
15
16 // resolve is a helper for implementing Resolver.ResolveN using resolveOnce.
17 -func resolve(ctx context.Context, r resolver, name string, depth int, prefixes ...string) (path.Path, error) {
17 +func resolve(ctx context.Context, r resolver, name string, opts *ResolveOpts, prefixes ...string) (path.Path, error) {
18 + depth := opts.Depth
19 for {
19 - p, err := r.resolveOnce(ctx, name)
20 + p, err := r.resolveOnce(ctx, name, opts)
21 if err != nil {
22 return "", err
23 }
namesys/dns.go
+3 -8
@@ -31,13 +31,8 @@ func newDNSResolver() resolver {
31 }
32
33 // Resolve implements Resolver.
34 -func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
35 - return r.ResolveN(ctx, name, DefaultDepthLimit)
36 -}
37 -
38 -// ResolveN implements Resolver.
39 -func (r *DNSResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
40 - return resolve(ctx, r, name, depth, "/ipns/")
34 +func (r *DNSResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
35 + return resolve(ctx, r, name, opts, "/ipns/")
36 }
37
38 type lookupRes struct {
@@ -48,7 +43,7 @@ type lookupRes struct {
43 // resolveOnce implements resolver.
44 // TXT records for a given domain name should contain a b58
45 // encoded multihash.
51 -func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) {
46 +func (r *DNSResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
47 segments := strings.SplitN(name, "/", 2)
48 domain := segments[0]
49
namesys/interface.go
+2 -11
@@ -89,17 +89,8 @@ type Resolver interface {
89 //
90 // There is a default depth-limit to avoid infinite recursion. Most
91 // users will be fine with this default limit, but if you need to
92 - // adjust the limit you can use ResolveN.
93 - Resolve(ctx context.Context, name string) (value path.Path, err error)
94 -
95 - // ResolveN performs a recursive lookup, returning the dereferenced
96 - // path. The only difference from Resolve is that the depth limit
97 - // is configurable. You can use DefaultDepthLimit, UnlimitedDepth,
98 - // or a depth limit of your own choosing.
99 - //
100 - // Most users should use Resolve, since the default limit works well
101 - // in most real-world situations.
102 - ResolveN(ctx context.Context, name string, depth int) (value path.Path, err error)
92 + // adjust the limit you can specify it as an option.
93 + Resolve(ctx context.Context, name string, opts *ResolveOpts) (value path.Path, err error)
94 }
95
96 // Publisher is an object capable of publishing particular names.
namesys/ipns_validate_test.go
+22 -9
@@ -115,7 +115,7 @@ func TestResolverValidation(t *testing.T) {
115 }
116
117 // Resolve entry
118 - resp, err := resolver.resolveOnce(ctx, id.Pretty())
118 + resp, err := resolver.resolveOnce(ctx, id.Pretty(), DefaultResolveOpts())
119 if err != nil {
120 t.Fatal(err)
121 }
@@ -136,9 +136,9 @@ func TestResolverValidation(t *testing.T) {
136 }
137
138 // Record should fail validation because entry is expired
139 - _, err = resolver.resolveOnce(ctx, id.Pretty())
140 - if err != ErrExpiredRecord {
141 - t.Fatal("ValidateIpnsRecord should have returned ErrExpiredRecord")
139 + _, err = resolver.resolveOnce(ctx, id.Pretty(), DefaultResolveOpts())
140 + if err == nil {
141 + t.Fatal("ValidateIpnsRecord should have returned error")
142 }
143
144 // Create IPNS record path with a different private key
@@ -158,8 +158,8 @@ func TestResolverValidation(t *testing.T) {
158
159 // Record should fail validation because public key defined by
160 // ipns path doesn't match record signature
161 - _, err = resolver.resolveOnce(ctx, id2.Pretty())
162 - if err != ErrSignature {
161 + _, err = resolver.resolveOnce(ctx, id2.Pretty(), DefaultResolveOpts())
162 + if err == nil {
163 t.Fatal("ValidateIpnsRecord should have failed signature verification")
164 }
165
@@ -176,7 +176,7 @@ func TestResolverValidation(t *testing.T) {
176
177 // Record should fail validation because public key is not available
178 // in peer store or on network
179 - _, err = resolver.resolveOnce(ctx, id3.Pretty())
179 + _, err = resolver.resolveOnce(ctx, id3.Pretty(), DefaultResolveOpts())
180 if err == nil {
181 t.Fatal("ValidateIpnsRecord should have failed because public key was not found")
182 }
@@ -191,7 +191,7 @@ func TestResolverValidation(t *testing.T) {
191 // public key is available in the peer store by looking it up in
192 // the DHT, which causes the DHT to fetch it and cache it in the
193 // peer store
194 - _, err = resolver.resolveOnce(ctx, id3.Pretty())
194 + _, err = resolver.resolveOnce(ctx, id3.Pretty(), DefaultResolveOpts())
195 if err != nil {
196 t.Fatal(err)
197 }
@@ -263,7 +263,20 @@ func (m *mockValueStore) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey
263 }
264
265 func (m *mockValueStore) GetValues(ctx context.Context, k string, count int) ([]routing.RecvdVal, error) {
266 - return m.r.GetValues(ctx, k, count)
266 + vals, err := m.r.GetValues(ctx, k, count)
267 + if err != nil {
268 + return nil, err
269 + }
270 + valid := make([]routing.RecvdVal, 0, len(vals))
271 + for _, v := range vals {
272 + rec := new(recordpb.Record)
273 + rec.Key = proto.String(k)
274 + rec.Value = v.Val
275 + if err = m.Validator.VerifyRecord(rec); err == nil {
276 + valid = append(valid, v)
277 + }
278 + }
279 + return valid, nil
280 }
281
282 func (m *mockValueStore) PutValue(ctx context.Context, k string, d []byte) error {
namesys/namesys.go
+7 -12
@@ -67,12 +67,7 @@ func AddPubsubNameSystem(ctx context.Context, ns NameSystem, host p2phost.Host,
67 const DefaultResolverCacheTTL = time.Minute
68
69 // Resolve implements Resolver.
70 -func (ns *mpns) Resolve(ctx context.Context, name string) (path.Path, error) {
71 - return ns.ResolveN(ctx, name, DefaultDepthLimit)
72 -}
73 -
74 -// ResolveN implements Resolver.
75 -func (ns *mpns) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
70 +func (ns *mpns) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
71 if strings.HasPrefix(name, "/ipfs/") {
72 return path.ParsePath(name)
73 }
@@ -81,11 +76,11 @@ func (ns *mpns) ResolveN(ctx context.Context, name string, depth int) (path.Path
76 return path.ParsePath("/ipfs/" + name)
77 }
78
84 - return resolve(ctx, ns, name, depth, "/ipns/")
79 + return resolve(ctx, ns, name, opts, "/ipns/")
80 }
81
82 // resolveOnce implements resolver.
88 -func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) {
83 +func (ns *mpns) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
84 if !strings.HasPrefix(name, "/ipns/") {
85 name = "/ipns/" + name
86 }
@@ -114,7 +109,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error)
109 if err == nil {
110 res, ok := ns.resolvers["pubsub"]
111 if ok {
117 - p, err := res.resolveOnce(ctx, key)
112 + p, err := res.resolveOnce(ctx, key, opts)
113 if err == nil {
114 return makePath(p)
115 }
@@ -122,7 +117,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error)
117
118 res, ok = ns.resolvers["dht"]
119 if ok {
125 - p, err := res.resolveOnce(ctx, key)
120 + p, err := res.resolveOnce(ctx, key, opts)
121 if err == nil {
122 return makePath(p)
123 }
@@ -134,7 +129,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error)
129 if isd.IsDomain(key) {
130 res, ok := ns.resolvers["dns"]
131 if ok {
137 - p, err := res.resolveOnce(ctx, key)
132 + p, err := res.resolveOnce(ctx, key, opts)
133 if err == nil {
134 return makePath(p)
135 }
@@ -145,7 +140,7 @@ func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error)
140
141 res, ok := ns.resolvers["proquint"]
142 if ok {
148 - p, err := res.resolveOnce(ctx, key)
143 + p, err := res.resolveOnce(ctx, key, opts)
144 if err == nil {
145 return makePath(p)
146 }
namesys/namesys_test.go
+5 -3
@@ -19,8 +19,10 @@ type mockResolver struct {
19 entries map[string]string
20 }
21
22 -func testResolution(t *testing.T, resolver Resolver, name string, depth int, expected string, expError error) {
23 - p, err := resolver.ResolveN(context.Background(), name, depth)
22 +func testResolution(t *testing.T, resolver Resolver, name string, depth uint, expected string, expError error) {
23 + opts := DefaultResolveOpts()
24 + opts.Depth = depth
25 + p, err := resolver.Resolve(context.Background(), name, opts)
26 if err != expError {
27 t.Fatal(fmt.Errorf(
28 "Expected %s with a depth of %d to have a '%s' error, but got '%s'",
@@ -33,7 +35,7 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth int, exp
35 }
36 }
37
36 -func (r *mockResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) {
38 +func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
39 return path.ParsePath(r.entries[name])
40 }
41
namesys/opts.go new
+29
@@ -0,0 +1,29 @@
1 +package namesys
2 +
3 +import (
4 + "time"
5 +)
6 +
7 +// ResolveOpts specifies options for resolving an IPNS path
8 +type ResolveOpts struct {
9 + // Recursion depth limit
10 + Depth uint
11 + // The number of IPNS records to retrieve from the DHT
12 + // (the best record is selected from this set)
13 + DhtRecordCount uint
14 + // The amount of time to wait for DHT records to be fetched
15 + // and verified. A zero value indicates that there is no explicit
16 + // timeout (although there is an implicit timeout due to dial
17 + // timeouts within the DHT)
18 + DhtTimeout time.Duration
19 +}
20 +
21 +// DefaultResolveOpts returns the default options for resolving
22 +// an IPNS path
23 +func DefaultResolveOpts() *ResolveOpts {
24 + return &ResolveOpts{
25 + Depth: DefaultDepthLimit,
26 + DhtRecordCount: 16,
27 + DhtTimeout: time.Minute,
28 + }
29 +}
namesys/proquint.go
+3 -8
@@ -12,17 +12,12 @@ import (
12 type ProquintResolver struct{}
13
14 // Resolve implements Resolver.
15 -func (r *ProquintResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
16 - return r.ResolveN(ctx, name, DefaultDepthLimit)
17 -}
18 -
19 -// ResolveN implements Resolver.
20 -func (r *ProquintResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
21 - return resolve(ctx, r, name, depth, "/ipns/")
15 +func (r *ProquintResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
16 + return resolve(ctx, r, name, opts, "/ipns/")
17 }
18
19 // resolveOnce implements resolver. Decodes the proquint string.
25 -func (r *ProquintResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) {
20 +func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
21 ok, err := proquint.IsProquint(name)
22 if err != nil || !ok {
23 return "", errors.New("not a valid proquint string")
namesys/pubsub.go
+3 -8
@@ -185,16 +185,11 @@ func (p *PubsubPublisher) publishRecord(ctx context.Context, k ci.PrivKey, value
185 }
186
187 // Resolve resolves a name through pubsub and default depth limit
188 -func (r *PubsubResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
189 - return r.ResolveN(ctx, name, DefaultDepthLimit)
188 +func (r *PubsubResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
189 + return resolve(ctx, r, name, opts, "/ipns/")
190 }
191
192 -// ResolveN resolves a name through pubsub with the specified depth limit
193 -func (r *PubsubResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
194 - return resolve(ctx, r, name, depth, "/ipns/")
195 -}
196 -
197 -func (r *PubsubResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) {
192 +func (r *PubsubResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
193 log.Debugf("PubsubResolve: resolve '%s'", name)
194
195 // retrieve the public key once (for verifying messages)
namesys/pubsub_test.go
+2 -2
@@ -180,14 +180,14 @@ func TestPubsubPublishSubscribe(t *testing.T) {
180 }
181
182 func checkResolveNotFound(ctx context.Context, t *testing.T, i int, resolver Resolver, name string) {
183 - _, err := resolver.Resolve(ctx, name)
183 + _, err := resolver.Resolve(ctx, name, DefaultResolveOpts())
184 if err != ErrResolveFailed {
185 t.Fatalf("[resolver %d] unexpected error: %s", i, err.Error())
186 }
187 }
188
189 func checkResolve(ctx context.Context, t *testing.T, i int, resolver Resolver, name string, val path.Path) {
190 - xval, err := resolver.Resolve(ctx, name)
190 + xval, err := resolver.Resolve(ctx, name, DefaultResolveOpts())
191 if err != nil {
192 t.Fatalf("[resolver %d] resolve failed: %s", i, err.Error())
193 }
namesys/republisher/repub_test.go
+2 -2
@@ -98,7 +98,7 @@ func verifyResolution(nodes []*core.IpfsNode, key string, exp path.Path) error {
98 ctx, cancel := context.WithCancel(context.Background())
99 defer cancel()
100 for _, n := range nodes {
101 - val, err := n.Namesys.Resolve(ctx, key)
101 + val, err := n.Namesys.Resolve(ctx, key, namesys.DefaultResolveOpts())
102 if err != nil {
103 return err
104 }
@@ -114,7 +114,7 @@ func verifyResolutionFails(nodes []*core.IpfsNode, key string) error {
114 ctx, cancel := context.WithCancel(context.Background())
115 defer cancel()
116 for _, n := range nodes {
117 - _, err := n.Namesys.Resolve(ctx, key)
117 + _, err := n.Namesys.Resolve(ctx, key, namesys.DefaultResolveOpts())
118 if err == nil {
119 return errors.New("expected resolution to fail")
120 }
namesys/resolve_test.go
+2 -2
@@ -40,7 +40,7 @@ func TestRoutingResolve(t *testing.T) {
40 t.Fatal(err)
41 }
42
43 - res, err := resolver.Resolve(context.Background(), pid.Pretty())
43 + res, err := resolver.Resolve(context.Background(), pid.Pretty(), DefaultResolveOpts())
44 if err != nil {
45 t.Fatal(err)
46 }
@@ -125,7 +125,7 @@ func TestPrexistingRecord(t *testing.T) {
125 }
126
127 func verifyCanResolve(r Resolver, name string, exp path.Path) error {
128 - res, err := r.Resolve(context.Background(), name)
128 + res, err := r.Resolve(context.Background(), name, DefaultResolveOpts())
129 if err != nil {
130 return err
131 }
namesys/routing.go
+40 -9
@@ -104,24 +104,26 @@ func NewRoutingResolver(route routing.ValueStore, cachesize int) *routingResolve
104 }
105
106 // Resolve implements Resolver.
107 -func (r *routingResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
108 - return r.ResolveN(ctx, name, DefaultDepthLimit)
109 -}
110 -
111 -// ResolveN implements Resolver.
112 -func (r *routingResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
113 - return resolve(ctx, r, name, depth, "/ipns/")
107 +func (r *routingResolver) Resolve(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
108 + return resolve(ctx, r, name, opts, "/ipns/")
109 }
110
111 // resolveOnce implements resolver. Uses the IPFS routing system to
112 // resolve SFS-like names.
118 -func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) {
113 +func (r *routingResolver) resolveOnce(ctx context.Context, name string, opts *ResolveOpts) (path.Path, error) {
114 log.Debugf("RoutingResolver resolving %s", name)
115 cached, ok := r.cacheGet(name)
116 if ok {
117 return cached, nil
118 }
119
120 + if opts.DhtTimeout != 0 {
121 + // Resolution must complete within the timeout
122 + var cancel context.CancelFunc
123 + ctx, cancel = context.WithTimeout(ctx, opts.DhtTimeout)
124 + defer cancel()
125 + }
126 +
127 name = strings.TrimPrefix(name, "/ipns/")
128 hash, err := mh.FromB58String(name)
129 if err != nil {
@@ -151,7 +153,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa
153 // Note that the DHT will call the ipns validator when retrieving
154 // the value, which in turn verifies the ipns record signature
155 _, ipnsKey := IpnsKeysForID(pid)
154 - val, err := r.routing.GetValue(ctx, ipnsKey)
156 + val, err := r.getValue(ctx, ipnsKey, opts)
157 if err != nil {
158 log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err)
159 return "", err
@@ -184,6 +186,35 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Pa
186 }
187 }
188
189 +func (r *routingResolver) getValue(ctx context.Context, ipnsKey string, opts *ResolveOpts) ([]byte, error) {
190 + // Get specified number of values from the DHT
191 + vals, err := r.routing.GetValues(ctx, ipnsKey, int(opts.DhtRecordCount))
192 + if err != nil {
193 + return nil, err
194 + }
195 +
196 + // Select the best value
197 + recs := make([][]byte, 0, len(vals))
198 + for _, v := range vals {
199 + if v.Val != nil {
200 + recs = append(recs, v.Val)
201 + }
202 + }
203 +
204 + i, err := IpnsSelectorFunc(ipnsKey, recs)
205 + if err != nil {
206 + return nil, err
207 + }
208 +
209 + best := recs[i]
210 + if best == nil {
211 + log.Errorf("GetValues %s yielded record with nil value", ipnsKey)
212 + return nil, routing.ErrNotFound
213 + }
214 +
215 + return best, nil
216 +}
217 +
218 func checkEOL(e *pb.IpnsEntry) (time.Time, bool) {
219 if e.GetValidityType() == pb.IpnsEntry_EOL {
220 eol, err := u.ParseRFC3339(string(e.GetValidity()))