提交 1089eda8 作者: Steven Allen

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: 's avatarSteven Allen <steven@stebalien.com>
上级 60708ea6
......@@ -90,7 +90,7 @@ Resolve the value of a dnslink:
if local {
offroute := offline.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
resolver = namesys.NewRoutingResolver(offroute, 0)
resolver = namesys.NewIpnsResolver(offroute)
}
if nocache {
......
......@@ -108,7 +108,7 @@ func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.Nam
if options.Local {
offroute := offline.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
resolver = namesys.NewRoutingResolver(offroute, 0)
resolver = namesys.NewIpnsResolver(offroute)
}
if !options.Cache {
......
......@@ -28,7 +28,7 @@ func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error {
return err
}
pub := nsys.NewRoutingPublisher(n.Routing, n.Repo.Datastore())
pub := nsys.NewIpnsPublisher(n.Routing, n.Repo.Datastore())
return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid()))
}
......@@ -2,6 +2,7 @@ package namesys
import (
"strings"
"time"
context "context"
......@@ -11,14 +12,14 @@ import (
type resolver interface {
// resolveOnce looks up a name once (without recursion).
resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (value path.Path, err error)
resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (value path.Path, ttl time.Duration, err error)
}
// resolve is a helper for implementing Resolver.ResolveN using resolveOnce.
func resolve(ctx context.Context, r resolver, name string, options *opts.ResolveOpts, prefixes ...string) (path.Path, error) {
depth := options.Depth
for {
p, err := r.resolveOnce(ctx, name, options)
p, _, err := r.resolveOnce(ctx, name, options)
if err != nil {
return "", err
}
......
package namesys
import (
"time"
path "github.com/ipfs/go-ipfs/path"
)
func (ns *mpns) cacheGet(name string) (path.Path, bool) {
if ns.cache == nil {
return "", false
}
ientry, ok := ns.cache.Get(name)
if !ok {
return "", false
}
entry, ok := ientry.(cacheEntry)
if !ok {
// should never happen, purely for sanity
log.Panicf("unexpected type %T in cache for %q.", ientry, name)
}
if time.Now().Before(entry.eol) {
return entry.val, true
}
ns.cache.Remove(name)
return "", false
}
func (ns *mpns) cacheSet(name string, val path.Path, ttl time.Duration) {
if ns.cache == nil || ttl <= 0 {
return
}
ns.cache.Add(name, cacheEntry{
val: val,
eol: time.Now().Add(ttl),
})
}
type cacheEntry struct {
val path.Path
eol time.Time
}
......@@ -5,6 +5,7 @@ import (
"errors"
"net"
"strings"
"time"
opts "github.com/ipfs/go-ipfs/namesys/opts"
path "github.com/ipfs/go-ipfs/path"
......@@ -38,12 +39,12 @@ type lookupRes struct {
// resolveOnce implements resolver.
// TXT records for a given domain name should contain a b58
// encoded multihash.
func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) {
func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) {
segments := strings.SplitN(name, "/", 2)
domain := segments[0]
if !isd.IsDomain(domain) {
return "", errors.New("not a valid domain name")
return "", 0, errors.New("not a valid domain name")
}
log.Debugf("DNSResolver resolving %s", domain)
......@@ -57,7 +58,7 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opt
select {
case subRes = <-subChan:
case <-ctx.Done():
return "", ctx.Err()
return "", 0, ctx.Err()
}
var p path.Path
......@@ -68,19 +69,19 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string, options *opt
select {
case rootRes = <-rootChan:
case <-ctx.Done():
return "", ctx.Err()
return "", 0, ctx.Err()
}
if rootRes.error == nil {
p = rootRes.path
} else {
return "", ErrResolveFailed
return "", 0, ErrResolveFailed
}
}
var err error
if len(segments) > 1 {
return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1])
} else {
return p, nil
p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1])
}
return p, 0, err
}
func workDomain(r *DNSResolver, name string, res chan lookupRes) {
......
......@@ -81,7 +81,7 @@ func TestResolverValidation(t *testing.T) {
peerstore := pstore.NewPeerstore()
vstore := newMockValueStore(rid, dstore, peerstore)
resolver := NewRoutingResolver(vstore, 0)
resolver := NewIpnsResolver(vstore)
// Create entry with expiry in one hour
priv, id, _, ipnsDHTPath := genKeys(t)
......@@ -105,7 +105,7 @@ func TestResolverValidation(t *testing.T) {
}
// Resolve entry
resp, err := resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts())
resp, _, err := resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts())
if err != nil {
t.Fatal(err)
}
......@@ -126,7 +126,7 @@ func TestResolverValidation(t *testing.T) {
}
// Record should fail validation because entry is expired
_, err = resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts())
_, _, err = resolver.resolveOnce(ctx, id.Pretty(), opts.DefaultResolveOpts())
if err == nil {
t.Fatal("ValidateIpnsRecord should have returned error")
}
......@@ -148,7 +148,7 @@ func TestResolverValidation(t *testing.T) {
// Record should fail validation because public key defined by
// ipns path doesn't match record signature
_, err = resolver.resolveOnce(ctx, id2.Pretty(), opts.DefaultResolveOpts())
_, _, err = resolver.resolveOnce(ctx, id2.Pretty(), opts.DefaultResolveOpts())
if err == nil {
t.Fatal("ValidateIpnsRecord should have failed signature verification")
}
......@@ -166,7 +166,7 @@ func TestResolverValidation(t *testing.T) {
// Record should fail validation because public key is not available
// in peer store or on network
_, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts())
_, _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts())
if err == nil {
t.Fatal("ValidateIpnsRecord should have failed because public key was not found")
}
......@@ -181,7 +181,7 @@ func TestResolverValidation(t *testing.T) {
// public key is available in the peer store by looking it up in
// the DHT, which causes the DHT to fetch it and cache it in the
// peer store
_, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts())
_, _, err = resolver.resolveOnce(ctx, id3.Pretty(), opts.DefaultResolveOpts())
if err != nil {
t.Fatal(err)
}
......
......@@ -9,6 +9,7 @@ import (
path "github.com/ipfs/go-ipfs/path"
routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing"
lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru"
isd "gx/ipfs/QmZmmuAXgX73UQmX1jRKjTGmjzq24Jinqkq8vzkBtno4uX/go-is-domain"
mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer"
......@@ -26,21 +27,25 @@ import (
// It can only publish to: (a) IPFS routing naming.
//
type mpns struct {
resolvers map[string]resolver
publishers map[string]Publisher
dnsResolver, proquintResolver, ipnsResolver resolver
ipnsPublisher Publisher
cache *lru.Cache
}
// NewNameSystem will construct the IPFS naming system based on Routing
func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem {
var cache *lru.Cache
if cachesize > 0 {
cache, _ = lru.New(cachesize)
}
return &mpns{
resolvers: map[string]resolver{
"dns": NewDNSResolver(),
"proquint": new(ProquintResolver),
"ipns": NewRoutingResolver(r, cachesize),
},
publishers: map[string]Publisher{
"ipns": NewRoutingPublisher(r, ds),
},
dnsResolver: NewDNSResolver(),
proquintResolver: new(ProquintResolver),
ipnsResolver: NewIpnsResolver(r),
ipnsPublisher: NewIpnsPublisher(r, ds),
cache: cache,
}
}
......@@ -60,42 +65,46 @@ func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.Resolv
}
// resolveOnce implements resolver.
func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) {
func (ns *mpns) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) {
if !strings.HasPrefix(name, "/ipns/") {
name = "/ipns/" + name
}
segments := strings.SplitN(name, "/", 4)
if len(segments) < 3 || segments[0] != "" {
log.Debugf("invalid name syntax for %s", name)
return "", ErrResolveFailed
return "", 0, ErrResolveFailed
}
// Resolver selection:
// 1. if it is a multihash resolve through "ipns".
// 2. if it is a domain name, resolve through "dns"
// 3. otherwise resolve through the "proquint" resolver
key := segments[2]
resName := "proquint"
if _, err := mh.FromB58String(key); err == nil {
resName = "ipns"
} else if isd.IsDomain(key) {
resName = "dns"
}
res, ok := ns.resolvers[resName]
p, ok := ns.cacheGet(key)
var err error
if !ok {
log.Debugf("no resolver found for %s", name)
return "", ErrResolveFailed
}
p, err := res.resolveOnce(ctx, key, options)
if err != nil {
return "", ErrResolveFailed
// Resolver selection:
// 1. if it is a multihash resolve through "ipns".
// 2. if it is a domain name, resolve through "dns"
// 3. otherwise resolve through the "proquint" resolver
var res resolver
if _, err := mh.FromB58String(key); err == nil {
res = ns.ipnsResolver
} else if isd.IsDomain(key) {
res = ns.dnsResolver
} else {
res = ns.proquintResolver
}
var ttl time.Duration
p, ttl, err = res.resolveOnce(ctx, key, options)
if err != nil {
return "", 0, ErrResolveFailed
}
ns.cacheSet(key, p, ttl)
}
if len(segments) > 3 {
return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
}
return p, nil
return p, 0, err
}
// Publish implements Publisher
......@@ -104,47 +113,17 @@ func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) e
}
func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error {
pub, ok := ns.publishers["ipns"]
if !ok {
return ErrPublishFailed
}
if err := pub.PublishWithEOL(ctx, name, value, eol); err != nil {
return err
}
ns.addToIpnsCache(name, value, eol)
return nil
}
func (ns *mpns) addToIpnsCache(key ci.PrivKey, value path.Path, eol time.Time) {
rr, ok := ns.resolvers["ipns"].(*routingResolver)
if !ok {
// should never happen, purely for sanity
log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["ipns"])
}
if rr.cache == nil {
// resolver has no caching
return
}
var err error
value, err = path.ParsePath(value.String())
id, err := peer.IDFromPrivateKey(name)
if err != nil {
log.Error("could not parse path")
return
return err
}
name, err := peer.IDFromPrivateKey(key)
if err != nil {
log.Error("while adding to cache, could not get peerid from private key")
return
if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil {
return err
}
if time.Now().Add(DefaultResolverCacheTTL).Before(eol) {
eol = time.Now().Add(DefaultResolverCacheTTL)
ttl := DefaultResolverCacheTTL
if ttEol := eol.Sub(time.Now()); ttEol < ttl {
ttl = ttEol
}
rr.cache.Add(name.Pretty(), cacheEntry{
val: value,
eol: eol,
})
ns.cacheSet(peer.IDB58Encode(id), value, ttl)
return nil
}
package namesys
import (
"context"
"fmt"
"testing"
context "context"
"time"
opts "github.com/ipfs/go-ipfs/namesys/opts"
path "github.com/ipfs/go-ipfs/path"
......@@ -21,6 +21,7 @@ type mockResolver struct {
}
func testResolution(t *testing.T, resolver Resolver, name string, depth uint, expected string, expError error) {
t.Helper()
p, err := resolver.Resolve(context.Background(), name, opts.Depth(depth))
if err != expError {
t.Fatal(fmt.Errorf(
......@@ -34,8 +35,9 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth uint, ex
}
}
func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *opts.ResolveOpts) (path.Path, error) {
return path.ParsePath(r.entries[name])
func (r *mockResolver) resolveOnce(ctx context.Context, name string, opts *opts.ResolveOpts) (path.Path, time.Duration, error) {
p, err := path.ParsePath(r.entries[name])
return p, 0, err
}
func mockResolverOne() *mockResolver {
......@@ -58,10 +60,8 @@ func mockResolverTwo() *mockResolver {
func TestNamesysResolution(t *testing.T) {
r := &mpns{
resolvers: map[string]resolver{
"ipns": mockResolverOne(),
"dns": mockResolverTwo(),
},
ipnsResolver: mockResolverOne(),
dnsResolver: mockResolverTwo(),
}
testResolution(t, r, "Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", opts.DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", nil)
......
......@@ -2,6 +2,7 @@ package namesys
import (
"errors"
"time"
context "context"
......@@ -18,10 +19,11 @@ func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ...
}
// resolveOnce implements resolver. Decodes the proquint string.
func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) {
func (r *ProquintResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) {
ok, err := proquint.IsProquint(name)
if err != nil || !ok {
return "", errors.New("not a valid proquint string")
return "", 0, errors.New("not a valid proquint string")
}
return path.FromString(string(proquint.Decode(name))), nil
// Return a 0 TTL as caching this result is pointless.
return path.FromString(string(proquint.Decode(name))), 0, nil
}
......@@ -28,9 +28,9 @@ const ipnsPrefix = "/ipns/"
const PublishPutValTimeout = time.Minute
const DefaultRecordTTL = 24 * time.Hour
// ipnsPublisher is capable of publishing and resolving names to the IPFS
// IpnsPublisher is capable of publishing and resolving names to the IPFS
// routing system.
type ipnsPublisher struct {
type IpnsPublisher struct {
routing routing.ValueStore
ds ds.Datastore
......@@ -38,17 +38,17 @@ type ipnsPublisher struct {
mu sync.Mutex
}
// NewRoutingPublisher constructs a publisher for the IPFS Routing name system.
func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublisher {
// NewIpnsPublisher constructs a publisher for the IPFS Routing name system.
func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher {
if ds == nil {
panic("nil datastore")
}
return &ipnsPublisher{routing: route, ds: ds}
return &IpnsPublisher{routing: route, ds: ds}
}
// Publish implements Publisher. Accepts a keypair and a value,
// and publishes it out to the routing system
func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
func (p *IpnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
log.Debugf("Publish %s", value)
return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL))
}
......@@ -62,7 +62,7 @@ func IpnsDsKey(id peer.ID) ds.Key {
//
// This method will not search the routing system for records published by other
// nodes.
func (p *ipnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) {
func (p *IpnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) {
query, err := p.ds.Query(dsquery.Query{
Prefix: ipnsPrefix,
})
......@@ -114,7 +114,7 @@ func (p *ipnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.Ipns
//
// If `checkRouting` is true and we have no existing record, this method will
// check the routing system for any existing records.
func (p *ipnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) {
func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()
......@@ -148,7 +148,7 @@ func (p *ipnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti
return e, nil
}
func (p *ipnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) {
func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) {
id, err := peer.IDFromPrivateKey(k)
if err != nil {
return nil, err
......@@ -197,7 +197,7 @@ func (p *ipnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value pa
// PublishWithEOL is a temporary stand in for the ipns records implementation
// see here for more details: https://github.com/ipfs/specs/tree/master/records
func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {
func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {
record, err := p.updateRecord(ctx, k, value, eol)
if err != nil {
return err
......
......@@ -58,7 +58,7 @@ func TestRepublish(t *testing.T) {
// have one node publish a record that is valid for 1 second
publisher := nodes[3]
p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid
rp := namesys.NewRoutingPublisher(publisher.Routing, publisher.Repo.Datastore())
rp := namesys.NewIpnsPublisher(publisher.Routing, publisher.Repo.Datastore())
err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second))
if err != nil {
t.Fatal(err)
......
......@@ -21,8 +21,8 @@ func TestRoutingResolve(t *testing.T) {
id := testutil.RandIdentityOrFatal(t)
d := serv.ClientWithDatastore(context.Background(), id, dstore)
resolver := NewRoutingResolver(d, 0)
publisher := NewRoutingPublisher(d, dstore)
resolver := NewIpnsResolver(d)
publisher := NewIpnsPublisher(d, dstore)
privk, pubk, err := testutil.RandTestKeyPair(512)
if err != nil {
......@@ -54,8 +54,8 @@ func TestPrexistingExpiredRecord(t *testing.T) {
dstore := dssync.MutexWrap(ds.NewMapDatastore())
d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore)
resolver := NewRoutingResolver(d, 0)
publisher := NewRoutingPublisher(d, dstore)
resolver := NewIpnsResolver(d)
publisher := NewIpnsPublisher(d, dstore)
privk, pubk, err := testutil.RandTestKeyPair(512)
if err != nil {
......@@ -96,8 +96,8 @@ func TestPrexistingRecord(t *testing.T) {
dstore := dssync.MutexWrap(ds.NewMapDatastore())
d := mockrouting.NewServer().ClientWithDatastore(context.Background(), testutil.RandIdentityOrFatal(t), dstore)
resolver := NewRoutingResolver(d, 0)
publisher := NewRoutingPublisher(d, dstore)
resolver := NewIpnsResolver(d)
publisher := NewIpnsPublisher(d, dstore)
privk, pubk, err := testutil.RandTestKeyPair(512)
if err != nil {
......
......@@ -12,7 +12,6 @@ import (
u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
logging "gx/ipfs/QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7/go-log"
routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing"
lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru"
proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
mh "gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer"
......@@ -22,102 +21,31 @@ import (
var log = logging.Logger("namesys")
// routingResolver implements NSResolver for the main IPFS SFS-like naming
type routingResolver struct {
// IpnsResolver implements NSResolver for the main IPFS SFS-like naming
type IpnsResolver struct {
routing routing.ValueStore
cache *lru.Cache
}
func (r *routingResolver) cacheGet(name string) (path.Path, bool) {
if r.cache == nil {
return "", false
}
ientry, ok := r.cache.Get(name)
if !ok {
return "", false
}
entry, ok := ientry.(cacheEntry)
if !ok {
// should never happen, purely for sanity
log.Panicf("unexpected type %T in cache for %q.", ientry, name)
}
if time.Now().Before(entry.eol) {
return entry.val, true
}
r.cache.Remove(name)
return "", false
}
func (r *routingResolver) cacheSet(name string, val path.Path, rec *pb.IpnsEntry) {
if r.cache == nil {
return
}
// if completely unspecified, just use one minute
ttl := DefaultResolverCacheTTL
if rec.Ttl != nil {
recttl := time.Duration(rec.GetTtl())
if recttl >= 0 {
ttl = recttl
}
}
cacheTil := time.Now().Add(ttl)
eol, ok := checkEOL(rec)
if ok && eol.Before(cacheTil) {
cacheTil = eol
}
r.cache.Add(name, cacheEntry{
val: val,
eol: cacheTil,
})
}
type cacheEntry struct {
val path.Path
eol time.Time
}
// NewRoutingResolver constructs a name resolver using the IPFS Routing system
// NewIpnsResolver constructs a name resolver using the IPFS Routing system
// to implement SFS-like naming on top.
// cachesize is the limit of the number of entries in the lru cache. Setting it
// to '0' will disable caching.
func NewRoutingResolver(route routing.ValueStore, cachesize int) *routingResolver {
func NewIpnsResolver(route routing.ValueStore) *IpnsResolver {
if route == nil {
panic("attempt to create resolver with nil routing system")
}
var cache *lru.Cache
if cachesize > 0 {
cache, _ = lru.New(cachesize)
}
return &routingResolver{
return &IpnsResolver{
routing: route,
cache: cache,
}
}
// Resolve implements Resolver.
func (r *routingResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
return resolve(ctx, r, name, opts.ProcessOpts(options), "/ipns/")
}
// resolveOnce implements resolver. Uses the IPFS routing system to
// resolve SFS-like names.
func (r *routingResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, error) {
func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *opts.ResolveOpts) (path.Path, time.Duration, error) {
log.Debugf("RoutingResolver resolving %s", name)
cached, ok := r.cacheGet(name)
if ok {
return cached, nil
}
if options.DhtTimeout != 0 {
// Resolution must complete within the timeout
......@@ -131,13 +59,13 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options
if err != nil {
// name should be a multihash. if it isn't, error out here.
log.Debugf("RoutingResolver: bad input hash: [%s]\n", name)
return "", err
return "", 0, err
}
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)
return "", err
return "", 0, err
}
// 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
_, err = routing.GetPublicKey(r.routing, ctx, pid)
if err != nil {
log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err)
return "", err
return "", 0, err
}
// Use the routing system to get the name.
......@@ -158,14 +86,14 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options
val, err := r.routing.GetValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount)))
if err != nil {
log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err)
return "", err
return "", 0, err
}
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)
return "", err
return "", 0, err
}
var p path.Path
......@@ -178,12 +106,25 @@ func (r *routingResolver) resolveOnce(ctx context.Context, name string, options
// Not a multihash, probably a new record
p, err = path.ParsePath(string(entry.GetValue()))
if err != nil {
return "", err
return "", 0, err
}
}
ttl := DefaultResolverCacheTTL
if entry.Ttl != nil {
ttl = time.Duration(*entry.Ttl)
}
if eol, ok := checkEOL(entry); ok {
ttEol := eol.Sub(time.Now())
if ttEol < 0 {
// It *was* valid when we first resolved it.
ttl = 0
} else if ttEol < ttl {
ttl = ttEol
}
}
r.cacheSet(name, p, entry)
return p, nil
return p, ttl, nil
}
func checkEOL(e *pb.IpnsEntry) (time.Time, bool) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论