forked from cerc-io/ipld-eth-server
Update vendor directory and make necessary code changes
Fixes for new geth version
This commit is contained in:
+120
@@ -0,0 +1,120 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
path "github.com/ipfs/go-path"
|
||||
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
|
||||
)
|
||||
|
||||
type onceResult struct {
|
||||
value path.Path
|
||||
ttl time.Duration
|
||||
err error
|
||||
}
|
||||
|
||||
type resolver interface {
|
||||
resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult
|
||||
}
|
||||
|
||||
// resolve is a helper for implementing Resolver.ResolveN using resolveOnce.
|
||||
func resolve(ctx context.Context, r resolver, name string, options opts.ResolveOpts) (path.Path, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
err := ErrResolveFailed
|
||||
var p path.Path
|
||||
|
||||
resCh := resolveAsync(ctx, r, name, options)
|
||||
|
||||
for res := range resCh {
|
||||
p, err = res.Path, res.Err
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return p, err
|
||||
}
|
||||
|
||||
func resolveAsync(ctx context.Context, r resolver, name string, options opts.ResolveOpts) <-chan Result {
|
||||
resCh := r.resolveOnceAsync(ctx, name, options)
|
||||
depth := options.Depth
|
||||
outCh := make(chan Result, 1)
|
||||
|
||||
go func() {
|
||||
defer close(outCh)
|
||||
var subCh <-chan Result
|
||||
var cancelSub context.CancelFunc
|
||||
defer func() {
|
||||
if cancelSub != nil {
|
||||
cancelSub()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case res, ok := <-resCh:
|
||||
if !ok {
|
||||
resCh = nil
|
||||
break
|
||||
}
|
||||
|
||||
if res.err != nil {
|
||||
emitResult(ctx, outCh, Result{Err: res.err})
|
||||
return
|
||||
}
|
||||
log.Debugf("resolved %s to %s", name, res.value.String())
|
||||
if !strings.HasPrefix(res.value.String(), ipnsPrefix) {
|
||||
emitResult(ctx, outCh, Result{Path: res.value})
|
||||
break
|
||||
}
|
||||
|
||||
if depth == 1 {
|
||||
emitResult(ctx, outCh, Result{Path: res.value, Err: ErrResolveRecursion})
|
||||
break
|
||||
}
|
||||
|
||||
subopts := options
|
||||
if subopts.Depth > 1 {
|
||||
subopts.Depth--
|
||||
}
|
||||
|
||||
var subCtx context.Context
|
||||
if cancelSub != nil {
|
||||
// Cancel previous recursive resolve since it won't be used anyways
|
||||
cancelSub()
|
||||
}
|
||||
subCtx, cancelSub = context.WithCancel(ctx)
|
||||
_ = cancelSub
|
||||
|
||||
p := strings.TrimPrefix(res.value.String(), ipnsPrefix)
|
||||
subCh = resolveAsync(subCtx, r, p, subopts)
|
||||
case res, ok := <-subCh:
|
||||
if !ok {
|
||||
subCh = nil
|
||||
break
|
||||
}
|
||||
|
||||
// We don't bother returning here in case of context timeout as there is
|
||||
// no good reason to do that, and we may still be able to emit a result
|
||||
emitResult(ctx, outCh, res)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
if resCh == nil && subCh == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return outCh
|
||||
}
|
||||
|
||||
func emitResult(ctx context.Context, outCh chan<- Result, r Result) {
|
||||
select {
|
||||
case outCh <- r:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
path "github.com/ipfs/go-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
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
path "github.com/ipfs/go-path"
|
||||
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
|
||||
isd "github.com/jbenet/go-is-domain"
|
||||
)
|
||||
|
||||
type LookupTXTFunc func(name string) (txt []string, err error)
|
||||
|
||||
// DNSResolver implements a Resolver on DNS domains
|
||||
type DNSResolver struct {
|
||||
lookupTXT LookupTXTFunc
|
||||
// TODO: maybe some sort of caching?
|
||||
// cache would need a timeout
|
||||
}
|
||||
|
||||
// NewDNSResolver constructs a name resolver using DNS TXT records.
|
||||
func NewDNSResolver() *DNSResolver {
|
||||
return &DNSResolver{lookupTXT: net.LookupTXT}
|
||||
}
|
||||
|
||||
// Resolve implements Resolver.
|
||||
func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
|
||||
return resolve(ctx, r, name, opts.ProcessOpts(options))
|
||||
}
|
||||
|
||||
// ResolveAsync implements Resolver.
|
||||
func (r *DNSResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result {
|
||||
return resolveAsync(ctx, r, name, opts.ProcessOpts(options))
|
||||
}
|
||||
|
||||
type lookupRes struct {
|
||||
path path.Path
|
||||
error error
|
||||
}
|
||||
|
||||
// resolveOnce implements resolver.
|
||||
// TXT records for a given domain name should contain a b58
|
||||
// encoded multihash.
|
||||
func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult {
|
||||
var fqdn string
|
||||
out := make(chan onceResult, 1)
|
||||
segments := strings.SplitN(name, "/", 2)
|
||||
domain := segments[0]
|
||||
|
||||
if !isd.IsDomain(domain) {
|
||||
out <- onceResult{err: errors.New("not a valid domain name")}
|
||||
close(out)
|
||||
return out
|
||||
}
|
||||
log.Debugf("DNSResolver resolving %s", domain)
|
||||
|
||||
if strings.HasSuffix(domain, ".") {
|
||||
fqdn = domain
|
||||
} else {
|
||||
fqdn = domain + "."
|
||||
}
|
||||
|
||||
rootChan := make(chan lookupRes, 1)
|
||||
go workDomain(r, fqdn, rootChan)
|
||||
|
||||
subChan := make(chan lookupRes, 1)
|
||||
go workDomain(r, "_dnslink."+fqdn, subChan)
|
||||
|
||||
appendPath := func(p path.Path) (path.Path, error) {
|
||||
if len(segments) > 1 {
|
||||
return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1])
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
for {
|
||||
select {
|
||||
case subRes, ok := <-subChan:
|
||||
if !ok {
|
||||
subChan = nil
|
||||
break
|
||||
}
|
||||
if subRes.error == nil {
|
||||
p, err := appendPath(subRes.path)
|
||||
emitOnceResult(ctx, out, onceResult{value: p, err: err})
|
||||
return
|
||||
}
|
||||
case rootRes, ok := <-rootChan:
|
||||
if !ok {
|
||||
rootChan = nil
|
||||
break
|
||||
}
|
||||
if rootRes.error == nil {
|
||||
p, err := appendPath(rootRes.path)
|
||||
emitOnceResult(ctx, out, onceResult{value: p, err: err})
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
if subChan == nil && rootChan == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func workDomain(r *DNSResolver, name string, res chan lookupRes) {
|
||||
defer close(res)
|
||||
|
||||
txt, err := r.lookupTXT(name)
|
||||
if err != nil {
|
||||
// Error is != nil
|
||||
res <- lookupRes{"", err}
|
||||
return
|
||||
}
|
||||
|
||||
for _, t := range txt {
|
||||
p, err := parseEntry(t)
|
||||
if err == nil {
|
||||
res <- lookupRes{p, nil}
|
||||
return
|
||||
}
|
||||
}
|
||||
res <- lookupRes{"", ErrResolveFailed}
|
||||
}
|
||||
|
||||
func parseEntry(txt string) (path.Path, error) {
|
||||
p, err := path.ParseCidToPath(txt) // bare IPFS multihashes
|
||||
if err == nil {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
return tryParseDnsLink(txt)
|
||||
}
|
||||
|
||||
func tryParseDnsLink(txt string) (path.Path, error) {
|
||||
parts := strings.SplitN(txt, "=", 2)
|
||||
if len(parts) == 2 && parts[0] == "dnslink" {
|
||||
return path.ParsePath(parts[1])
|
||||
}
|
||||
|
||||
return "", errors.New("not a valid dnslink entry")
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Package namesys implements resolvers and publishers for the IPFS
|
||||
naming system (IPNS).
|
||||
|
||||
The core of IPFS is an immutable, content-addressable Merkle graph.
|
||||
That works well for many use cases, but doesn't allow you to answer
|
||||
questions like "what is Alice's current homepage?". The mutable name
|
||||
system allows Alice to publish information like:
|
||||
|
||||
The current homepage for alice.example.com is
|
||||
/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
|
||||
|
||||
or:
|
||||
|
||||
The current homepage for node
|
||||
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
|
||||
is
|
||||
/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
|
||||
|
||||
The mutable name system also allows users to resolve those references
|
||||
to find the immutable IPFS object currently referenced by a given
|
||||
mutable name.
|
||||
|
||||
For command-line bindings to this functionality, see:
|
||||
|
||||
ipfs name
|
||||
ipfs dns
|
||||
ipfs resolve
|
||||
*/
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
context "context"
|
||||
|
||||
path "github.com/ipfs/go-path"
|
||||
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
|
||||
ci "github.com/libp2p/go-libp2p-core/crypto"
|
||||
)
|
||||
|
||||
// ErrResolveFailed signals an error when attempting to resolve.
|
||||
var ErrResolveFailed = errors.New("could not resolve name")
|
||||
|
||||
// ErrResolveRecursion signals a recursion-depth limit.
|
||||
var ErrResolveRecursion = errors.New(
|
||||
"could not resolve name (recursion limit exceeded)")
|
||||
|
||||
// ErrPublishFailed signals an error when attempting to publish.
|
||||
var ErrPublishFailed = errors.New("could not publish name")
|
||||
|
||||
// Namesys represents a cohesive name publishing and resolving system.
|
||||
//
|
||||
// Publishing a name is the process of establishing a mapping, a key-value
|
||||
// pair, according to naming rules and databases.
|
||||
//
|
||||
// Resolving a name is the process of looking up the value associated with the
|
||||
// key (name).
|
||||
type NameSystem interface {
|
||||
Resolver
|
||||
Publisher
|
||||
}
|
||||
|
||||
// Result is the return type for Resolver.ResolveAsync.
|
||||
type Result struct {
|
||||
Path path.Path
|
||||
Err error
|
||||
}
|
||||
|
||||
// Resolver is an object capable of resolving names.
|
||||
type Resolver interface {
|
||||
|
||||
// Resolve performs a recursive lookup, returning the dereferenced
|
||||
// path. For example, if ipfs.io has a DNS TXT record pointing to
|
||||
// /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
|
||||
// and there is a DHT IPNS entry for
|
||||
// QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
|
||||
// -> /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
|
||||
// then
|
||||
// Resolve(ctx, "/ipns/ipfs.io")
|
||||
// will resolve both names, returning
|
||||
// /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
|
||||
//
|
||||
// There is a default depth-limit to avoid infinite recursion. Most
|
||||
// users will be fine with this default limit, but if you need to
|
||||
// adjust the limit you can specify it as an option.
|
||||
Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (value path.Path, err error)
|
||||
|
||||
// ResolveAsync performs recursive name lookup, like Resolve, but it returns
|
||||
// entries as they are discovered in the DHT. Each returned result is guaranteed
|
||||
// to be "better" (which usually means newer) than the previous one.
|
||||
ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result
|
||||
}
|
||||
|
||||
// Publisher is an object capable of publishing particular names.
|
||||
type Publisher interface {
|
||||
|
||||
// Publish establishes a name-value mapping.
|
||||
// TODO make this not PrivKey specific.
|
||||
Publish(ctx context.Context, name ci.PrivKey, value path.Path) error
|
||||
|
||||
// TODO: to be replaced by a more generic 'PublishWithValidity' type
|
||||
// call once the records spec is implemented
|
||||
PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
ds "github.com/ipfs/go-datastore"
|
||||
path "github.com/ipfs/go-path"
|
||||
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
|
||||
isd "github.com/jbenet/go-is-domain"
|
||||
ci "github.com/libp2p/go-libp2p-core/crypto"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
routing "github.com/libp2p/go-libp2p-core/routing"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
// mpns (a multi-protocol NameSystem) implements generic IPFS naming.
|
||||
//
|
||||
// Uses several Resolvers:
|
||||
// (a) IPFS routing naming: SFS-like PKI names.
|
||||
// (b) dns domains: resolves using links in DNS TXT records
|
||||
// (c) proquints: interprets string as the raw byte data.
|
||||
//
|
||||
// It can only publish to: (a) IPFS routing naming.
|
||||
//
|
||||
type mpns struct {
|
||||
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{
|
||||
dnsResolver: NewDNSResolver(),
|
||||
proquintResolver: new(ProquintResolver),
|
||||
ipnsResolver: NewIpnsResolver(r),
|
||||
ipnsPublisher: NewIpnsPublisher(r, ds),
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
const DefaultResolverCacheTTL = time.Minute
|
||||
|
||||
// Resolve implements Resolver.
|
||||
func (ns *mpns) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
|
||||
if strings.HasPrefix(name, "/ipfs/") {
|
||||
return path.ParsePath(name)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(name, "/") {
|
||||
return path.ParsePath("/ipfs/" + name)
|
||||
}
|
||||
|
||||
return resolve(ctx, ns, name, opts.ProcessOpts(options))
|
||||
}
|
||||
|
||||
func (ns *mpns) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result {
|
||||
res := make(chan Result, 1)
|
||||
if strings.HasPrefix(name, "/ipfs/") {
|
||||
p, err := path.ParsePath(name)
|
||||
res <- Result{p, err}
|
||||
return res
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(name, "/") {
|
||||
p, err := path.ParsePath("/ipfs/" + name)
|
||||
res <- Result{p, err}
|
||||
return res
|
||||
}
|
||||
|
||||
return resolveAsync(ctx, ns, name, opts.ProcessOpts(options))
|
||||
}
|
||||
|
||||
// resolveOnce implements resolver.
|
||||
func (ns *mpns) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult {
|
||||
out := make(chan onceResult, 1)
|
||||
|
||||
if !strings.HasPrefix(name, ipnsPrefix) {
|
||||
name = ipnsPrefix + name
|
||||
}
|
||||
segments := strings.SplitN(name, "/", 4)
|
||||
if len(segments) < 3 || segments[0] != "" {
|
||||
log.Debugf("invalid name syntax for %s", name)
|
||||
out <- onceResult{err: ErrResolveFailed}
|
||||
close(out)
|
||||
return out
|
||||
}
|
||||
|
||||
key := segments[2]
|
||||
|
||||
if p, ok := ns.cacheGet(key); ok {
|
||||
if len(segments) > 3 {
|
||||
var err error
|
||||
p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
|
||||
if err != nil {
|
||||
emitOnceResult(ctx, out, onceResult{value: p, err: err})
|
||||
}
|
||||
}
|
||||
|
||||
out <- onceResult{value: p}
|
||||
close(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
resCh := res.resolveOnceAsync(ctx, key, options)
|
||||
var best onceResult
|
||||
go func() {
|
||||
defer close(out)
|
||||
for {
|
||||
select {
|
||||
case res, ok := <-resCh:
|
||||
if !ok {
|
||||
if best != (onceResult{}) {
|
||||
ns.cacheSet(key, best.value, best.ttl)
|
||||
}
|
||||
return
|
||||
}
|
||||
if res.err == nil {
|
||||
best = res
|
||||
}
|
||||
p := res.value
|
||||
|
||||
// Attach rest of the path
|
||||
if len(segments) > 3 {
|
||||
var err error
|
||||
p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
|
||||
if err != nil {
|
||||
emitOnceResult(ctx, out, onceResult{value: p, ttl: res.ttl, err: err})
|
||||
}
|
||||
}
|
||||
|
||||
emitOnceResult(ctx, out, onceResult{value: p, ttl: res.ttl, err: res.err})
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func emitOnceResult(ctx context.Context, outCh chan<- onceResult, r onceResult) {
|
||||
select {
|
||||
case outCh <- r:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
// Publish implements Publisher
|
||||
func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
|
||||
return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordEOL))
|
||||
}
|
||||
|
||||
func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error {
|
||||
id, err := peer.IDFromPrivateKey(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil {
|
||||
return err
|
||||
}
|
||||
ttl := DefaultResolverCacheTTL
|
||||
if ttEol := time.Until(eol); ttEol < ttl {
|
||||
ttl = ttEol
|
||||
}
|
||||
ns.cacheSet(peer.IDB58Encode(id), value, ttl)
|
||||
return nil
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
proquint "github.com/bren2010/proquint"
|
||||
path "github.com/ipfs/go-path"
|
||||
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
|
||||
)
|
||||
|
||||
type ProquintResolver struct{}
|
||||
|
||||
// Resolve implements Resolver.
|
||||
func (r *ProquintResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
|
||||
return resolve(ctx, r, name, opts.ProcessOpts(options))
|
||||
}
|
||||
|
||||
// resolveOnce implements resolver. Decodes the proquint string.
|
||||
func (r *ProquintResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult {
|
||||
out := make(chan onceResult, 1)
|
||||
defer close(out)
|
||||
|
||||
ok, err := proquint.IsProquint(name)
|
||||
if err != nil || !ok {
|
||||
out <- onceResult{err: errors.New("not a valid proquint string")}
|
||||
return out
|
||||
}
|
||||
// Return a 0 TTL as caching this result is pointless.
|
||||
out <- onceResult{value: path.FromString(string(proquint.Decode(name)))}
|
||||
return out
|
||||
}
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
pin "github.com/ipfs/go-ipfs/pin"
|
||||
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
ds "github.com/ipfs/go-datastore"
|
||||
dsquery "github.com/ipfs/go-datastore/query"
|
||||
ipns "github.com/ipfs/go-ipns"
|
||||
pb "github.com/ipfs/go-ipns/pb"
|
||||
path "github.com/ipfs/go-path"
|
||||
ft "github.com/ipfs/go-unixfs"
|
||||
ci "github.com/libp2p/go-libp2p-core/crypto"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
routing "github.com/libp2p/go-libp2p-core/routing"
|
||||
base32 "github.com/whyrusleeping/base32"
|
||||
)
|
||||
|
||||
const ipnsPrefix = "/ipns/"
|
||||
|
||||
const PublishPutValTimeout = time.Minute
|
||||
const DefaultRecordEOL = 24 * time.Hour
|
||||
|
||||
// IpnsPublisher is capable of publishing and resolving names to the IPFS
|
||||
// routing system.
|
||||
type IpnsPublisher struct {
|
||||
routing routing.ValueStore
|
||||
ds ds.Datastore
|
||||
|
||||
// Used to ensure we assign IPNS records *sequential* sequence numbers.
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// 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}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Debugf("Publish %s", value)
|
||||
return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordEOL))
|
||||
}
|
||||
|
||||
func IpnsDsKey(id peer.ID) ds.Key {
|
||||
return ds.NewKey("/ipns/" + base32.RawStdEncoding.EncodeToString([]byte(id)))
|
||||
}
|
||||
|
||||
// PublishedNames returns the latest IPNS records published by this node and
|
||||
// their expiration times.
|
||||
//
|
||||
// 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) {
|
||||
query, err := p.ds.Query(dsquery.Query{
|
||||
Prefix: ipnsPrefix,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer query.Close()
|
||||
|
||||
records := make(map[peer.ID]*pb.IpnsEntry)
|
||||
for {
|
||||
select {
|
||||
case result, ok := <-query.Next():
|
||||
if !ok {
|
||||
return records, nil
|
||||
}
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
e := new(pb.IpnsEntry)
|
||||
if err := proto.Unmarshal(result.Value, e); err != nil {
|
||||
// Might as well return what we can.
|
||||
log.Error("found an invalid IPNS entry:", err)
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(result.Key, ipnsPrefix) {
|
||||
log.Errorf("datastore query for keys with prefix %s returned a key: %s", ipnsPrefix, result.Key)
|
||||
continue
|
||||
}
|
||||
k := result.Key[len(ipnsPrefix):]
|
||||
pid, err := base32.RawStdEncoding.DecodeString(k)
|
||||
if err != nil {
|
||||
log.Errorf("ipns ds key invalid: %s", result.Key)
|
||||
continue
|
||||
}
|
||||
records[peer.ID(pid)] = e
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetPublished returns the record this node has published corresponding to the
|
||||
// given peer ID.
|
||||
//
|
||||
// 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) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
value, err := p.ds.Get(IpnsDsKey(id))
|
||||
switch err {
|
||||
case nil:
|
||||
case ds.ErrNotFound:
|
||||
if !checkRouting {
|
||||
return nil, nil
|
||||
}
|
||||
ipnskey := ipns.RecordKey(id)
|
||||
value, err = p.routing.GetValue(ctx, ipnskey)
|
||||
if err != nil {
|
||||
// Not found or other network issue. Can't really do
|
||||
// anything about this case.
|
||||
if err != routing.ErrNotFound {
|
||||
log.Debugf("error when determining the last published IPNS record for %s: %s", id, err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
e := new(pb.IpnsEntry)
|
||||
if err := proto.Unmarshal(value, e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// get previous records sequence number
|
||||
rec, err := p.GetPublished(ctx, id, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seqno := rec.GetSequence() // returns 0 if rec is nil
|
||||
if rec != nil && value != path.Path(rec.GetValue()) {
|
||||
// Don't bother incrementing the sequence number unless the
|
||||
// value changes.
|
||||
seqno++
|
||||
}
|
||||
|
||||
// Create record
|
||||
entry, err := ipns.Create(k, []byte(value), seqno, eol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the TTL
|
||||
// TODO: Make this less hacky.
|
||||
ttl, ok := checkCtxTTL(ctx)
|
||||
if ok {
|
||||
entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Put the new record.
|
||||
if err := p.ds.Put(IpnsDsKey(id), data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
record, err := p.updateRecord(ctx, k, value, eol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record)
|
||||
}
|
||||
|
||||
// setting the TTL on published records is an experimental feature.
|
||||
// as such, i'm using the context to wire it through to avoid changing too
|
||||
// much code along the way.
|
||||
func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
|
||||
v := ctx.Value("ipns-publish-ttl")
|
||||
if v == nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
d, ok := v.(time.Duration)
|
||||
return d, ok
|
||||
}
|
||||
|
||||
func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, entry *pb.IpnsEntry) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
errs := make(chan error, 2) // At most two errors (IPNS, and public key)
|
||||
|
||||
if err := ipns.EmbedPublicKey(k, entry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := peer.IDFromPublicKey(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
errs <- PublishEntry(ctx, r, ipns.RecordKey(id), entry)
|
||||
}()
|
||||
|
||||
// Publish the public key if a public key cannot be extracted from the ID
|
||||
// TODO: once v0.4.16 is widespread enough, we can stop doing this
|
||||
// and at that point we can even deprecate the /pk/ namespace in the dht
|
||||
//
|
||||
// NOTE: This check actually checks if the public key has been embedded
|
||||
// in the IPNS entry. This check is sufficient because we embed the
|
||||
// public key in the IPNS entry if it can't be extracted from the ID.
|
||||
if entry.PubKey != nil {
|
||||
go func() {
|
||||
errs <- PublishPublicKey(ctx, r, PkKeyForID(id), k)
|
||||
}()
|
||||
|
||||
if err := waitOnErrChan(ctx, errs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return waitOnErrChan(ctx, errs)
|
||||
}
|
||||
|
||||
func waitOnErrChan(ctx context.Context, errs chan error) error {
|
||||
select {
|
||||
case err := <-errs:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error {
|
||||
log.Debugf("Storing pubkey at: %s", k)
|
||||
pkbytes, err := pubk.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store associated public key
|
||||
timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
|
||||
defer cancel()
|
||||
return r.PutValue(timectx, k, pkbytes)
|
||||
}
|
||||
|
||||
func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error {
|
||||
timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
|
||||
defer cancel()
|
||||
|
||||
data, err := proto.Marshal(rec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Storing ipns entry at: %s", ipnskey)
|
||||
// Store ipns entry at "/ipns/"+h(pubkey)
|
||||
return r.PutValue(timectx, ipnskey, data)
|
||||
}
|
||||
|
||||
// InitializeKeyspace sets the ipns record for the given key to
|
||||
// point to an empty directory.
|
||||
// TODO: this doesnt feel like it belongs here
|
||||
func InitializeKeyspace(ctx context.Context, pub Publisher, pins pin.Pinner, key ci.PrivKey) error {
|
||||
emptyDir := ft.EmptyDirNode()
|
||||
|
||||
// pin recursively because this might already be pinned
|
||||
// and doing a direct pin would throw an error in that case
|
||||
err := pins.Pin(ctx, emptyDir, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pins.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid()))
|
||||
}
|
||||
|
||||
// PkKeyForID returns the public key routing key for the given peer ID.
|
||||
func PkKeyForID(id peer.ID) string {
|
||||
return "/pk/" + string(id)
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
package republisher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
keystore "github.com/ipfs/go-ipfs/keystore"
|
||||
namesys "github.com/ipfs/go-ipfs/namesys"
|
||||
path "github.com/ipfs/go-path"
|
||||
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
ds "github.com/ipfs/go-datastore"
|
||||
pb "github.com/ipfs/go-ipns/pb"
|
||||
logging "github.com/ipfs/go-log"
|
||||
goprocess "github.com/jbenet/goprocess"
|
||||
gpctx "github.com/jbenet/goprocess/context"
|
||||
ic "github.com/libp2p/go-libp2p-core/crypto"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
|
||||
var errNoEntry = errors.New("no previous entry")
|
||||
|
||||
var log = logging.Logger("ipns-repub")
|
||||
|
||||
// DefaultRebroadcastInterval is the default interval at which we rebroadcast IPNS records
|
||||
var DefaultRebroadcastInterval = time.Hour * 4
|
||||
|
||||
// InitialRebroadcastDelay is the delay before first broadcasting IPNS records on start
|
||||
var InitialRebroadcastDelay = time.Minute * 1
|
||||
|
||||
// FailureRetryInterval is the interval at which we retry IPNS records broadcasts (when they fail)
|
||||
var FailureRetryInterval = time.Minute * 5
|
||||
|
||||
// DefaultRecordLifetime is the default lifetime for IPNS records
|
||||
const DefaultRecordLifetime = time.Hour * 24
|
||||
|
||||
type Republisher struct {
|
||||
ns namesys.Publisher
|
||||
ds ds.Datastore
|
||||
self ic.PrivKey
|
||||
ks keystore.Keystore
|
||||
|
||||
Interval time.Duration
|
||||
|
||||
// how long records that are republished should be valid for
|
||||
RecordLifetime time.Duration
|
||||
}
|
||||
|
||||
// NewRepublisher creates a new Republisher
|
||||
func NewRepublisher(ns namesys.Publisher, ds ds.Datastore, self ic.PrivKey, ks keystore.Keystore) *Republisher {
|
||||
return &Republisher{
|
||||
ns: ns,
|
||||
ds: ds,
|
||||
self: self,
|
||||
ks: ks,
|
||||
Interval: DefaultRebroadcastInterval,
|
||||
RecordLifetime: DefaultRecordLifetime,
|
||||
}
|
||||
}
|
||||
|
||||
func (rp *Republisher) Run(proc goprocess.Process) {
|
||||
timer := time.NewTimer(InitialRebroadcastDelay)
|
||||
defer timer.Stop()
|
||||
if rp.Interval < InitialRebroadcastDelay {
|
||||
timer.Reset(rp.Interval)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
timer.Reset(rp.Interval)
|
||||
err := rp.republishEntries(proc)
|
||||
if err != nil {
|
||||
log.Info("republisher failed to republish: ", err)
|
||||
if FailureRetryInterval < rp.Interval {
|
||||
timer.Reset(FailureRetryInterval)
|
||||
}
|
||||
}
|
||||
case <-proc.Closing():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rp *Republisher) republishEntries(p goprocess.Process) error {
|
||||
ctx, cancel := context.WithCancel(gpctx.OnClosingContext(p))
|
||||
defer cancel()
|
||||
|
||||
// TODO: Use rp.ipns.ListPublished(). We can't currently *do* that
|
||||
// because:
|
||||
// 1. There's no way to get keys from the keystore by ID.
|
||||
// 2. We don't actually have access to the IPNS publisher.
|
||||
err := rp.republishEntry(ctx, rp.self)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rp.ks != nil {
|
||||
keyNames, err := rp.ks.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, name := range keyNames {
|
||||
priv, err := rp.ks.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rp.republishEntry(ctx, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rp *Republisher) republishEntry(ctx context.Context, priv ic.PrivKey) error {
|
||||
id, err := peer.IDFromPrivateKey(priv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("republishing ipns entry for %s", id)
|
||||
|
||||
// Look for it locally only
|
||||
p, err := rp.getLastVal(id)
|
||||
if err != nil {
|
||||
if err == errNoEntry {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// update record with same sequence number
|
||||
eol := time.Now().Add(rp.RecordLifetime)
|
||||
return rp.ns.PublishWithEOL(ctx, priv, p, eol)
|
||||
}
|
||||
|
||||
func (rp *Republisher) getLastVal(id peer.ID) (path.Path, error) {
|
||||
// Look for it locally only
|
||||
val, err := rp.ds.Get(namesys.IpnsDsKey(id))
|
||||
switch err {
|
||||
case nil:
|
||||
case ds.ErrNotFound:
|
||||
return "", errNoEntry
|
||||
default:
|
||||
return "", err
|
||||
}
|
||||
|
||||
e := new(pb.IpnsEntry)
|
||||
if err := proto.Unmarshal(val, e); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Path(e.Value), nil
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
ipns "github.com/ipfs/go-ipns"
|
||||
pb "github.com/ipfs/go-ipns/pb"
|
||||
logging "github.com/ipfs/go-log"
|
||||
path "github.com/ipfs/go-path"
|
||||
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
routing "github.com/libp2p/go-libp2p-core/routing"
|
||||
dht "github.com/libp2p/go-libp2p-kad-dht"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
var log = logging.Logger("namesys")
|
||||
|
||||
// IpnsResolver implements NSResolver for the main IPFS SFS-like naming
|
||||
type IpnsResolver struct {
|
||||
routing routing.ValueStore
|
||||
}
|
||||
|
||||
// NewIpnsResolver constructs a name resolver using the IPFS Routing system
|
||||
// to implement SFS-like naming on top.
|
||||
func NewIpnsResolver(route routing.ValueStore) *IpnsResolver {
|
||||
if route == nil {
|
||||
panic("attempt to create resolver with nil routing system")
|
||||
}
|
||||
return &IpnsResolver{
|
||||
routing: route,
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve implements Resolver.
|
||||
func (r *IpnsResolver) Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (path.Path, error) {
|
||||
return resolve(ctx, r, name, opts.ProcessOpts(options))
|
||||
}
|
||||
|
||||
// ResolveAsync implements Resolver.
|
||||
func (r *IpnsResolver) ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result {
|
||||
return resolveAsync(ctx, r, name, opts.ProcessOpts(options))
|
||||
}
|
||||
|
||||
// resolveOnce implements resolver. Uses the IPFS routing system to
|
||||
// resolve SFS-like names.
|
||||
func (r *IpnsResolver) resolveOnceAsync(ctx context.Context, name string, options opts.ResolveOpts) <-chan onceResult {
|
||||
out := make(chan onceResult, 1)
|
||||
log.Debugf("RoutingResolver resolving %s", name)
|
||||
cancel := func() {}
|
||||
|
||||
if options.DhtTimeout != 0 {
|
||||
// Resolution must complete within the timeout
|
||||
ctx, cancel = context.WithTimeout(ctx, options.DhtTimeout)
|
||||
}
|
||||
|
||||
name = strings.TrimPrefix(name, "/ipns/")
|
||||
pid, err := peer.IDB58Decode(name)
|
||||
if err != nil {
|
||||
log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err)
|
||||
out <- onceResult{err: err}
|
||||
close(out)
|
||||
cancel()
|
||||
return out
|
||||
}
|
||||
|
||||
// Name should be the hash of a public key retrievable from ipfs.
|
||||
// We retrieve the public key here to make certain that it's in the peer
|
||||
// store before calling GetValue() on the DHT - the DHT will call the
|
||||
// ipns validator, which in turn will get the public key from the peer
|
||||
// store to verify the record signature
|
||||
_, err = routing.GetPublicKey(r.routing, ctx, pid)
|
||||
if err != nil {
|
||||
log.Debugf("RoutingResolver: could not retrieve public key %s: %s\n", name, err)
|
||||
out <- onceResult{err: err}
|
||||
close(out)
|
||||
cancel()
|
||||
return out
|
||||
}
|
||||
|
||||
// Use the routing system to get the name.
|
||||
// Note that the DHT will call the ipns validator when retrieving
|
||||
// the value, which in turn verifies the ipns record signature
|
||||
ipnsKey := ipns.RecordKey(pid)
|
||||
|
||||
vals, err := r.routing.SearchValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount)))
|
||||
if err != nil {
|
||||
log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err)
|
||||
out <- onceResult{err: err}
|
||||
close(out)
|
||||
cancel()
|
||||
return out
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer cancel()
|
||||
defer close(out)
|
||||
for {
|
||||
select {
|
||||
case val, ok := <-vals:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
emitOnceResult(ctx, out, onceResult{err: err})
|
||||
return
|
||||
}
|
||||
|
||||
var p path.Path
|
||||
// check for old style record:
|
||||
if valh, err := mh.Cast(entry.GetValue()); err == nil {
|
||||
// Its an old style multihash record
|
||||
log.Debugf("encountered CIDv0 ipns entry: %s", valh)
|
||||
p = path.FromCid(cid.NewCidV0(valh))
|
||||
} else {
|
||||
// Not a multihash, probably a new style record
|
||||
p, err = path.ParsePath(string(entry.GetValue()))
|
||||
if err != nil {
|
||||
emitOnceResult(ctx, out, onceResult{err: err})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ttl := DefaultResolverCacheTTL
|
||||
if entry.Ttl != nil {
|
||||
ttl = time.Duration(*entry.Ttl)
|
||||
}
|
||||
switch eol, err := ipns.GetEOL(entry); err {
|
||||
case ipns.ErrUnrecognizedValidity:
|
||||
// No EOL.
|
||||
case nil:
|
||||
ttEol := time.Until(eol)
|
||||
if ttEol < 0 {
|
||||
// It *was* valid when we first resolved it.
|
||||
ttl = 0
|
||||
} else if ttEol < ttl {
|
||||
ttl = ttEol
|
||||
}
|
||||
default:
|
||||
log.Errorf("encountered error when parsing EOL: %s", err)
|
||||
emitOnceResult(ctx, out, onceResult{err: err})
|
||||
return
|
||||
}
|
||||
|
||||
emitOnceResult(ctx, out, onceResult{value: p, ttl: ttl})
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user