@cryptotaxi247 / kubo / commits / 1089eda84

cleanup namesys a bit

Remove ~50 lines of code, some casting, and a superfluous map (when go starts looking like python, something's wrong). License: MIT Signed-off-by: Steven Allen <steven@stebalien.com>

Steven Allen committed May 9, 2018 at 13:55 UTC 1089eda84b9e1c81bed468587cc26cad99351d5e
14 files changed +175 -204
core/commands/ipns.go
+1 -1
@@ -90,7 +90,7 @@ Resolve the value of a dnslink:
90
91 if local {
92 offroute := offline.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
93 - resolver = namesys.NewRoutingResolver(offroute, 0)
93 + resolver = namesys.NewIpnsResolver(offroute)
94 }
95
96 if nocache {
core/coreapi/name.go
+1 -1
@@ -108,7 +108,7 @@ func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.Nam
108
109 if options.Local {
110 offroute := offline.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
111 - resolver = namesys.NewRoutingResolver(offroute, 0)
111 + resolver = namesys.NewIpnsResolver(offroute)
112 }
113
114 if !options.Cache {
fuse/ipns/common.go
+1 -1
@@ -28,7 +28,7 @@ func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error {
28 return err
29 }
30
31 - pub := nsys.NewRoutingPublisher(n.Routing, n.Repo.Datastore())
31 + pub := nsys.NewIpnsPublisher(n.Routing, n.Repo.Datastore())
32
33 return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid()))
34 }
namesys/base.go
+3 -2
@@ -2,6 +2,7 @@ package namesys
2
3 import (
4 "strings"
5 + "time"
6
7 context "context"
8
@@ -11,14 +12,14 @@ import (
12
13 type resolver interface {
14 // resolveOnce looks up a name once (without recursion).
14 - resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (value path.Path, err error)
15 + resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (value path.Path, ttl time.Duration, err error)
16 }
17
18 // resolve is a helper for implementing Resolver.ResolveN using resolveOnce.
19 func resolve(ctx context.Context, r resolver, name string, options *opts.ResolveOpts, prefixes ...string) (path.Path, error) {
20 depth := options.Depth
21 for {
21 - p, err := r.resolveOnce(ctx, name, options)
22 + p, _, err := r.resolveOnce(ctx, name, options)
23 if err != nil {
24 return "", err
25 }
namesys/cache.go new
+47
@@ -0,0 +1,47 @@
1 +package namesys
2 +
3 +import (
4 + "time"
5 +
6 + path "github.com/ipfs/go-ipfs/path"
7 +)
8 +
9 +func (ns *mpns) cacheGet(name string) (path.Path, bool) {
10 + if ns.cache == nil {
11 + return "", false
12 + }
13 +
14 + ientry, ok := ns.cache.Get(name)
15 + if !ok {
16 + return "", false
17 + }
18 +
19 + entry, ok := ientry.(cacheEntry)
20 + if !ok {
21 + // should never happen, purely for sanity
22 + log.Panicf("unexpected type %T in cache for %q.", ientry, name)
23 + }
24 +
25 + if time.Now().Before(entry.eol) {
26 + return entry.val, true
27 + }
28 +
29 + ns.cache.Remove(name)
30 +
31 + return "", false
32 +}
33 +
34 +func (ns *mpns) cacheSet(name string, val path.Path, ttl time.Duration) {
35 + if ns.cache == nil || ttl <= 0 {
36 + return
37 + }
38 + ns.cache.Add(name, cacheEntry{
39 + val: val,
40 + eol: time.Now().Add(ttl),
41 + })
42 +}
43 +
44 +type cacheEntry struct {
45 + val path.Path
46 + eol time.Time
47 +}
namesys/dns.go
+9 -8
@@ -5,6 +5,7 @@ import (
5 "errors"
6 "net"
7 "strings"
8 + "time"
9
10 opts "github.com/ipfs/go-ipfs/namesys/opts"
11 path "github.com/ipfs/go-ipfs/path"
@@ -38,12 +39,12 @@ type lookupRes struct {
39 // resolveOnce implements resolver.
40 // TXT records for a given domain name should contain a b58
41 // encoded multihash.
41 -func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) {
42 +func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) {
43 segments := strings.SplitN(name, "/", 2)
44 domain := segments[0]
45
46 if !isd.IsDomain(domain) {
46 - return "", errors.New("not a valid domain name")
47 + return "", 0, errors.New("not a valid domain name")
48 }
49 log.Debugf("DNSResolver resolving %s", domain)
50
@@ -57,7 +58,7 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opt
58 select {
59 case subRes = <-subChan:
60 case <-ctx.Done():
60 - return "", ctx.Err()
61 + return "", 0, ctx.Err()
62 }
63
64 var p path.Path
@@ -68,19 +69,19 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opt
69 select {
70 case rootRes = <-rootChan:
71 case <-ctx.Done():
71 - return "", ctx.Err()
72 + return "", 0, ctx.Err()
73 }
74 if rootRes.error == nil {
75 p = rootRes.path
76 } else {
76 - return "", ErrResolveFailed
77 + return "", 0, ErrResolveFailed
78 }
79 }
80 + var err error
81 if len(segments) > 1 {
80 - return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1])
81 - } else {
82 - return p, nil
82 + p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1])
83 }
84 + return p, 0, err
85 }
86
87 func workDomain(r *DNSResolver, name string, res chan lookupRes) {
namesys/ipns_validate_test.go
+6 -6
@@ -81,7 +81,7 @@ func TestResolverValidation(t *testing.T) {
81 peerstore := pstore.NewPeerstore()
82
83 vstore := newMockValueStore(rid, dstore, peerstore)
84 - resolver := NewRoutingResolver(vstore, 0)
84 + resolver := NewIpnsResolver(vstore)
85
86 // Create entry with expiry in one hour
87 priv, id, _, ipnsDHTPath := genKeys(t)
@@ -105,7 +105,7 @@ func TestResolverValidation(t *testing.T) {
105 }
106
107 // Resolve entry
108 - resp, err := resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts())
108 + resp, _, err := resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts())
109 if err != nil {
110 t.Fatal(err)
111 }
@@ -126,7 +126,7 @@ func TestResolverValidation(t *testing.T) {
126 }
127
128 // Record should fail validation because entry is expired
129 - _, err = resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts())
129 + _, _, err = resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts())
130 if err == nil {
131 t.Fatal("ValidateIpnsRecord should have returned error")
132 }
@@ -148,7 +148,7 @@ func TestResolverValidation(t *testing.T) {
148
149 // Record should fail validation because public key defined by
150 // ipns path doesn't match record signature
151 - _, err = resolver.resolveOnce(ctx, id2.Pretty(), opts.DefaultResolveOpts())
151 + _, _, err = resolver.resolveOnce(ctx, id2.Pretty(), opts.DefaultResolveOpts())
152 if err == nil {
153 t.Fatal("ValidateIpnsRecord should have failed signature verification")
154 }
@@ -166,7 +166,7 @@ func TestResolverValidation(t *testing.T) {
166
167 // Record should fail validation because public key is not available
168 // in peer store or on network
169 - _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts())
169 + _, _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts())
170 if err == nil {
171 t.Fatal("ValidateIpnsRecord should have failed because public key was not found")
172 }
@@ -181,7 +181,7 @@ func TestResolverValidation(t *testing.T) {
181 // public key is available in the peer store by looking it up in
182 // the DHT, which causes the DHT to fetch it and cache it in the
183 // peer store
184 - _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts())
184 + _, _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts())
185 if err != nil {
186 t.Fatal(err)
187 }
namesys/namesys.go
+49 -70
@@ -9,6 +9,7 @@ import (
9 path "github.com/ipfs/go-ipfs/path"
10
11 routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing"
12 + lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru"
13 isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain"
14 mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
15 peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer"
@@ -26,21 +27,25 @@ import (
27 // It can only publish to: (a) IPFS routing naming.
28 //
29 type mpns struct {
29 - resolvers map[string]resolver
30 - publishers map[string]Publisher
30 + dnsResolver, proquintResolver, ipnsResolver resolver
31 + ipnsPublisher Publisher
32 +
33 + cache *lru.Cache
34 }
35
36 // NewNameSystem will construct the IPFS naming system based on Routing
37 func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem {
38 + var cache *lru.Cache
39 + if cachesize > 0 {
40 + cache, _ = lru.New(cachesize)
41 + }
42 +
43 return &mpns{
36 - resolvers: map[string]resolver{
37 - "dns": NewDNSResolver(),
38 - "proquint": new(ProquintResolver),
39 - "ipns": NewRoutingResolver(r, cachesize),
40 - },
41 - publishers: map[string]Publisher{
42 - "ipns": NewRoutingPublisher(r, ds),
43 - },
44 + dnsResolver: NewDNSResolver(),
45 + proquintResolver: new(ProquintResolver),
46 + ipnsResolver: NewIpnsResolver(r),
47 + ipnsPublisher: NewIpnsPublisher(r, ds),
48 + cache: cache,
49 }
50 }
51
@@ -60,42 +65,46 @@ func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.Resolv
65 }
66
67 // resolveOnce implements resolver.
63 -func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) {
68 +func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) {
69 if !strings.HasPrefix(name, "/ipns/") {
70 name = "/ipns/" + name
71 }
72 segments := strings.SplitN(name, "/", 4)
73 if len(segments) < 3 || segments[0] != "" {
74 log.Debugf("invalid name syntax for %s", name)
70 - return "", ErrResolveFailed
75 + return "", 0, ErrResolveFailed
76 }
77
73 - // Resolver selection:
74 - // 1. if it is a multihash resolve through "ipns".
75 - // 2. if it is a domain name, resolve through "dns"
76 - // 3. otherwise resolve through the "proquint" resolver
78 key := segments[2]
78 - resName := "proquint"
79 - if _, err := mh.FromB58String(key); err == nil {
80 - resName = "ipns"
81 - } else if isd.IsDomain(key) {
82 - resName = "dns"
83 - }
79
85 - res, ok := ns.resolvers[resName]
80 + p, ok := ns.cacheGet(key)
81 + var err error
82 if !ok {
87 - log.Debugf("no resolver found for %s", name)
88 - return "", ErrResolveFailed
89 - }
90 - p, err := res.resolveOnce(ctx, key, options)
91 - if err != nil {
92 - return "", ErrResolveFailed
83 + // Resolver selection:
84 + // 1. if it is a multihash resolve through "ipns".
85 + // 2. if it is a domain name, resolve through "dns"
86 + // 3. otherwise resolve through the "proquint" resolver
87 + var res resolver
88 + if _, err := mh.FromB58String(key); err == nil {
89 + res = ns.ipnsResolver
90 + } else if isd.IsDomain(key) {
91 + res = ns.dnsResolver
92 + } else {
93 + res = ns.proquintResolver
94 + }
95 +
96 + var ttl time.Duration
97 + p, ttl, err = res.resolveOnce(ctx, key, options)
98 + if err != nil {
99 + return "", 0, ErrResolveFailed
100 + }
101 + ns.cacheSet(key, p, ttl)
102 }
103
104 if len(segments) > 3 {
96 - return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
105 + p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
106 }
98 - return p, nil
107 + return p, 0, err
108 }
109
110 // Publish implements Publisher
@@ -104,47 +113,17 @@ func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) e
113 }
114
115 func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error {
107 - pub, ok := ns.publishers["ipns"]
108 - if !ok {
109 - return ErrPublishFailed
110 - }
111 - if err := pub.PublishWithEOL(ctx, name, value, eol); err != nil {
112 - return err
113 - }
114 - ns.addToIpnsCache(name, value, eol)
115 - return nil
116 -
117 -}
118 -
119 -func (ns *mpns) addToIpnsCache(key ci.PrivKey, value path.Path, eol time.Time) {
120 - rr, ok := ns.resolvers["ipns"].(*routingResolver)
121 - if !ok {
122 - // should never happen, purely for sanity
123 - log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["ipns"])
124 - }
125 - if rr.cache == nil {
126 - // resolver has no caching
127 - return
128 - }
129 -
130 - var err error
131 - value, err = path.ParsePath(value.String())
116 + id, err := peer.IDFromPrivateKey(name)
117 if err != nil {
133 - log.Error("could not parse path")
134 - return
118 + return err
119 }
136 -
137 - name, err := peer.IDFromPrivateKey(key)
138 - if err != nil {
139 - log.Error("while adding to cache, could not get peerid from private key")
140 - return
120 + if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil {
121 + return err
122 }
142 -
143 - if time.Now().Add(DefaultResolverCacheTTL).Before(eol) {
144 - eol = time.Now().Add(DefaultResolverCacheTTL)
123 + ttl := DefaultResolverCacheTTL
124 + if ttEol := eol.Sub(time.Now()); ttEol < ttl {
125 + ttl = ttEol
126 }
146 - rr.cache.Add(name.Pretty(), cacheEntry{
147 - val: value,
148 - eol: eol,
149 - })
127 + ns.cacheSet(peer.IDB58Encode(id), value, ttl)
128 + return nil
129 }
namesys/namesys_test.go
+8 -8
@@ -1,10 +1,10 @@
1 package namesys
2
3 import (
4 + "context"
5 "fmt"
6 "testing"
6 -
7 - context "context"
7 + "time"
8
9 opts "github.com/ipfs/go-ipfs/namesys/opts"
10 path "github.com/ipfs/go-ipfs/path"
@@ -21,6 +21,7 @@ type mockResolver struct {
21 }
22
23 func testResolution(t *testing.T, resolver Resolver, name string, depth uint, expected string, expError error) {
24 + t.Helper()
25 p, err := resolver.Resolve(context.Background(), name, opts.Depth(depth))
26 if err != expError {
27 t.Fatal(fmt.Errorf(
@@ -34,8 +35,9 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth uint, ex
35 }
36 }
37
37 -func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *opts.ResolveOpts) (path.Path, error) {
38 - return path.ParsePath(r.entries[name])
38 +func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *opts.ResolveOpts) (path.Path, time.Duration, error) {
39 + p, err := path.ParsePath(r.entries[name])
40 + return p, 0, err
41 }
42
43 func mockResolverOne() *mockResolver {
@@ -58,10 +60,8 @@ func mockResolverTwo() *mockResolver {
60
61 func TestNamesysResolution(t *testing.T) {
62 r := &mpns{
61 - resolvers: map[string]resolver{
62 - "ipns": mockResolverOne(),
63 - "dns": mockResolverTwo(),
64 - },
63 + ipnsResolver: mockResolverOne(),
64 + dnsResolver: mockResolverTwo(),
65 }
66
67 testResolution(t, r, "Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", opts.DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil)
namesys/proquint.go
+5 -3
@@ -2,6 +2,7 @@ package namesys
2
3 import (
4 "errors"
5 + "time"
6
7 context "context"
8
@@ -18,10 +19,11 @@ func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ...
19 }
20
21 // resolveOnce implements resolver. Decodes the proquint string.
21 -func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) {
22 +func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) {
23 ok, err := proquint.IsProquint(name)
24 if err != nil || !ok {
24 - return "", errors.New("not a valid proquint string")
25 + return "", 0, errors.New("not a valid proquint string")
26 }
26 - return path.FromString(string(proquint.Decode(name))), nil
27 + // Return a 0 TTL as caching this result is pointless.
28 + return path.FromString(string(proquint.Decode(name))), 0, nil
29 }
namesys/publisher.go
+10 -10
@@ -28,9 +28,9 @@ const ipnsPrefix = "/ipns/"
28 const PublishPutValTimeout = time.Minute
29 const DefaultRecordTTL = 24 * time.Hour
30
31 -// ipnsPublisher is capable of publishing and resolving names to the IPFS
31 +// IpnsPublisher is capable of publishing and resolving names to the IPFS
32 // routing system.
33 -type ipnsPublisher struct {
33 +type IpnsPublisher struct {
34 routing routing.ValueStore
35 ds ds.Datastore
36
@@ -38,17 +38,17 @@ type ipnsPublisher struct {
38 mu sync.Mutex
39 }
40
41 -// NewRoutingPublisher constructs a publisher for the IPFS Routing name system.
42 -func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublisher {
41 +// NewIpnsPublisher constructs a publisher for the IPFS Routing name system.
42 +func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher {
43 if ds == nil {
44 panic("nil datastore")
45 }
46 - return &ipnsPublisher{routing: route, ds: ds}
46 + return &IpnsPublisher{routing: route, ds: ds}
47 }
48
49 // Publish implements Publisher. Accepts a keypair and a value,
50 // and publishes it out to the routing system
51 -func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
51 +func (p *IpnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
52 log.Debugf("Publish %s", value)
53 return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL))
54 }
@@ -62,7 +62,7 @@ func IpnsDsKey(id peer.ID) ds.Key {
62 //
63 // This method will not search the routing system for records published by other
64 // nodes.
65 -func (p *ipnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) {
65 +func (p *IpnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) {
66 query, err := p.ds.Query(dsquery.Query{
67 Prefix: ipnsPrefix,
68 })
@@ -114,7 +114,7 @@ func (p *ipnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.Ipns
114 //
115 // If `checkRouting` is true and we have no existing record, this method will
116 // check the routing system for any existing records.
117 -func (p *ipnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) {
117 +func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) {
118 ctx, cancel := context.WithTimeout(ctx, time.Second*30)
119 defer cancel()
120
@@ -148,7 +148,7 @@ func (p *ipnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti
148 return e, nil
149 }
150
151 -func (p *ipnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) {
151 +func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) {
152 id, err := peer.IDFromPrivateKey(k)
153 if err != nil {
154 return nil, err
@@ -197,7 +197,7 @@ func (p *ipnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value pa
197
198 // PublishWithEOL is a temporary stand in for the ipns records implementation
199 // see here for more details: https://github.com/ipfs/specs/tree/master/records
200 -func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {
200 +func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {
201 record, err := p.updateRecord(ctx, k, value, eol)
202 if err != nil {
203 return err
namesys/republisher/repub_test.go
+1 -1
@@ -58,7 +58,7 @@ func TestRepublish(t *testing.T) {
58 // have one node publish a record that is valid for 1 second
59 publisher := nodes[3]
60 p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid
61 - rp := namesys.NewRoutingPublisher(publisher.Routing, publisher.Repo.Datastore())
61 + rp := namesys.NewIpnsPublisher(publisher.Routing, publisher.Repo.Datastore())
62 err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second))
63 if err != nil {
64 t.Fatal(err)
namesys/resolve_test.go
+6 -6
@@ -21,8 +21,8 @@ func TestRoutingResolve(t *testing.T) {
21 id := testutil.RandIdentityOrFatal(t)
22 d := serv.ClientWithDatastore(context.Background(), id, dstore)
23
24 - resolver := NewRoutingResolver(d, 0)
25 - publisher := NewRoutingPublisher(d, dstore)
24 + resolver := NewIpnsResolver(d)
25 + publisher := NewIpnsPublisher(d, dstore)
26
27 privk, pubk, err := testutil.RandTestKeyPair(512)
28 if err != nil {
@@ -54,8 +54,8 @@ func TestPrexistingExpiredRecord(t *testing.T) {
54 dstore := dssync.MutexWrap(ds.NewMapDatastore())
55 d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore)
56
57 - resolver := NewRoutingResolver(d, 0)
58 - publisher := NewRoutingPublisher(d, dstore)
57 + resolver := NewIpnsResolver(d)
58 + publisher := NewIpnsPublisher(d, dstore)
59
60 privk, pubk, err := testutil.RandTestKeyPair(512)
61 if err != nil {
@@ -96,8 +96,8 @@ func TestPrexistingRecord(t *testing.T) {
96 dstore := dssync.MutexWrap(ds.NewMapDatastore())
97 d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore)
98
99 - resolver := NewRoutingResolver(d, 0)
100 - publisher := NewRoutingPublisher(d, dstore)
99 + resolver := NewIpnsResolver(d)
100 + publisher := NewIpnsPublisher(d, dstore)
101
102 privk, pubk, err := testutil.RandTestKeyPair(512)
103 if err != nil {
namesys/routing.go
+28 -87
@@ -12,7 +12,6 @@ import (
12 u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
13 logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log"
14 routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing"
15 - lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru"
15 proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
16 mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
17 peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer"
@@ -22,102 +21,31 @@ import (
21
22 var log = logging.Logger("namesys")
23
25 -// routingResolver implements NSResolver for the main IPFS SFS-like naming
26 -type routingResolver struct {
24 +// IpnsResolver implements NSResolver for the main IPFS SFS-like naming
25 +type IpnsResolver struct {
26 routing routing.ValueStore
28 -
29 - cache *lru.Cache
30 -}
31 -
32 -func (r *routingResolver) cacheGet(name string) (path.Path, bool) {
33 - if r.cache == nil {
34 - return "", false
35 - }
36 -
37 - ientry, ok := r.cache.Get(name)
38 - if !ok {
39 - return "", false
40 - }
41 -
42 - entry, ok := ientry.(cacheEntry)
43 - if !ok {
44 - // should never happen, purely for sanity
45 - log.Panicf("unexpected type %T in cache for %q.", ientry, name)
46 - }
47 -
48 - if time.Now().Before(entry.eol) {
49 - return entry.val, true
50 - }
51 -
52 - r.cache.Remove(name)
53 -
54 - return "", false
55 -}
56 -
57 -func (r *routingResolver) cacheSet(name string, val path.Path, rec *pb.IpnsEntry) {
58 - if r.cache == nil {
59 - return
60 - }
61 -
62 - // if completely unspecified, just use one minute
63 - ttl := DefaultResolverCacheTTL
64 - if rec.Ttl != nil {
65 - recttl := time.Duration(rec.GetTtl())
66 - if recttl >= 0 {
67 - ttl = recttl
68 - }
69 - }
70 -
71 - cacheTil := time.Now().Add(ttl)
72 - eol, ok := checkEOL(rec)
73 - if ok && eol.Before(cacheTil) {
74 - cacheTil = eol
75 - }
76 -
77 - r.cache.Add(name, cacheEntry{
78 - val: val,
79 - eol: cacheTil,
80 - })
81 -}
82 -
83 -type cacheEntry struct {
84 - val path.Path
85 - eol time.Time
27 }
28
88 -// NewRoutingResolver constructs a name resolver using the IPFS Routing system
29 +// NewIpnsResolver constructs a name resolver using the IPFS Routing system
30 // to implement SFS-like naming on top.
90 -// cachesize is the limit of the number of entries in the lru cache. Setting it
91 -// to '0' will disable caching.
92 -func NewRoutingResolver(route routing.ValueStore, cachesize int) *routingResolver {
31 +func NewIpnsResolver(route routing.ValueStore) *IpnsResolver {
32 if route == nil {
33 panic("attempt to create resolver with nil routing system")
34 }
96 -
97 - var cache *lru.Cache
98 - if cachesize > 0 {
99 - cache, _ = lru.New(cachesize)
100 - }
101 -
102 - return &routingResolver{
35 + return &IpnsResolver{
36 routing: route,
104 - cache: cache,
37 }
38 }
39
40 // Resolve implements Resolver.
109 -func (r *routingResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
41 +func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
42 return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/")
43 }
44
45 // resolveOnce implements resolver. Uses the IPFS routing system to
46 // resolve SFS-like names.
115 -func (r *routingResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) {
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)
117 - cached, ok := r.cacheGet(name)
118 - if ok {
119 - return cached, nil
120 - }
49
50 if options.DhtTimeout != 0 {
51 // Resolution must complete within the timeout
@@ -131,13 +59,13 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options
59 if err != nil {
60 // name should be a multihash. if it isn't, error out here.
61 log.Debugf("RoutingResolver: bad input hash: [%s]\n", name)
134 - return "", err
62 + return "", 0, err
63 }
64
65 pid, err := peer.IDFromBytes(hash)
66 if err != nil {
67 log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err)
140 - return "", err
68 + return "", 0, err
69 }
70
71 // Name should be the hash of a public key retrievable from ipfs.
@@ -148,7 +76,7 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options
76 _, err = routing.GetPublicKey(r.routing, ctx, pid)
77 if err != nil {
78 log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err)
151 - return "", err
79 + return "", 0, err
80 }
81
82 // Use the routing system to get the name.
@@ -158,14 +86,14 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options
86 val, err := r.routing.GetValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount)))
87 if err != nil {
88 log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err)
161 - return "", err
89 + return "", 0, err
90 }
91
92 entry := new(pb.IpnsEntry)
93 err = proto.Unmarshal(val, entry)
94 if err != nil {
95 log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", name, err)
168 - return "", err
96 + return "", 0, err
97 }
98
99 var p path.Path
@@ -178,12 +106,25 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options
106 // Not a multihash, probably a new record
107 p, err = path.ParsePath(string(entry.GetValue()))
108 if err != nil {
181 - return "", err
109 + return "", 0, err
110 + }
111 + }
112 +
113 + ttl := DefaultResolverCacheTTL
114 + if entry.Ttl != nil {
115 + ttl = time.Duration(*entry.Ttl)
116 + }
117 + if eol, ok := checkEOL(entry); ok {
118 + ttEol := eol.Sub(time.Now())
119 + if ttEol < 0 {
120 + // It *was* valid when we first resolved it.
121 + ttl = 0
122 + } else if ttEol < ttl {
123 + ttl = ttEol
124 }
125 }
126
185 - r.cacheSet(name, p, entry)
186 - return p, nil
127 + return p, ttl, nil
128 }
129
130 func checkEOL(e *pb.IpnsEntry) (time.Time, bool) {