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()))