Skip to content

Commit

Permalink
chore: apply feedback from @lidel
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Sep 6, 2023
1 parent 77f1f58 commit bb397e5
Show file tree
Hide file tree
Showing 7 changed files with 469 additions and 471 deletions.
4 changes: 1 addition & 3 deletions namesys/dns_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ type LookupTXTFunc func(ctx context.Context, name string) (txt []string, err err
// DNSResolver implements [Resolver] on DNS domains.
type DNSResolver struct {
lookupTXT LookupTXTFunc
// TODO: maybe some sort of caching?
// cache would need a timeout
}

var _ Resolver = &DNSResolver{}
Expand Down Expand Up @@ -57,7 +55,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options
domain := segments[0]

if _, ok := dns.IsDomainName(domain); !ok {
out <- ResolveResult{Err: fmt.Errorf("not a valid domain name: %s", domain)}
out <- ResolveResult{Err: fmt.Errorf("not a valid domain name: %q", domain)}
close(out)
return out
}
Expand Down
204 changes: 204 additions & 0 deletions namesys/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package namesys

import (
"context"
"errors"
"time"

"github.com/ipfs/boxo/path"
logging "github.com/ipfs/go-log/v2"
ci "github.com/libp2p/go-libp2p/core/crypto"
)

var log = logging.Logger("namesys")

var (
// ErrResolveFailed signals an error when attempting to resolve.
ErrResolveFailed = errors.New("could not resolve name")

// ErrResolveRecursion signals a recursion-depth limit.
ErrResolveRecursion = errors.New("could not resolve name (recursion limit exceeded)")
)

const (
// DefaultDepthLimit is the default depth limit used by [Resolver].
DefaultDepthLimit = 32

// UnlimitedDepth allows infinite recursion in [Resolver]. You probably don't want
// to use this, but it's here if you absolutely trust resolution to eventually
// complete and can't put an upper limit on how many steps it will take.
UnlimitedDepth = 0

// DefaultIPNSRecordTTL specifies the time that the record can be cached before
// checking if its validity again.
DefaultIPNSRecordTTL = time.Minute

// DefaultIPNSRecordEOL specifies the time that the network will cache IPNS
// records after being published. Records should be re-published before this
// interval expires. We use the same default expiration as the DHT.
DefaultIPNSRecordEOL = 48 * time.Hour

// DefaultResolverCacheTTL defines max TTL of a record placed in [NameSystem] cache.
DefaultResolverCacheTTL = time.Minute
)

// NameSystem 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
}

// ResolveResult is the return type for [Resolver.ResolveAsync].
type ResolveResult struct {
Path path.Path
TTL time.Duration
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 example.com 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 ...ResolveOption) (value path.Path, ttl time.Duration, 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 ...ResolveOption) <-chan ResolveResult
}

// ResolveOptions specifies options for resolving an IPNS Path.
type ResolveOptions struct {
// Depth is the recursion depth limit.
Depth uint

// DhtRecordCount is the number of IPNS Records to retrieve from the DHT
// (the best record is selected from this set).
DhtRecordCount uint

// DhtTimeout is the amount of time to wait for DHT records to be fetched
// and verified. A zero value indicates that there is no explicit timeout
// (although there is an implicit timeout due to dial timeouts within the DHT).
DhtTimeout time.Duration
}

// DefaultResolveOptions returns the default options for resolving an IPNS Path.
func DefaultResolveOptions() ResolveOptions {
return ResolveOptions{
Depth: DefaultDepthLimit,
DhtRecordCount: 16,
DhtTimeout: time.Minute,
}
}

// ResolveOption is used to set a resolve option.
type ResolveOption func(*ResolveOptions)

// ResolveWithDepth sets [ResolveOptions.Depth].
func ResolveWithDepth(depth uint) ResolveOption {
return func(o *ResolveOptions) {
o.Depth = depth
}
}

// ResolveWithDhtRecordCount sets [ResolveOptions.DhtRecordCount].
func ResolveWithDhtRecordCount(count uint) ResolveOption {
return func(o *ResolveOptions) {
o.DhtRecordCount = count
}
}

// ResolveWithDhtTimeout sets [ResolveOptions.ResolveWithDhtTimeout].
func ResolveWithDhtTimeout(timeout time.Duration) ResolveOption {
return func(o *ResolveOptions) {
o.DhtTimeout = timeout
}
}

// ProcessResolveOptions converts an array of [ResolveOption] into a [ResolveOptions] object.
func ProcessResolveOptions(opts []ResolveOption) ResolveOptions {
resolveOptions := DefaultResolveOptions()
for _, option := range opts {
option(&resolveOptions)
}
return resolveOptions
}

// 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, options ...PublishOption) error
}

// PublishOptions specifies options for publishing an IPNS Record.
type PublishOptions struct {
EOL time.Time
TTL time.Duration
CompatibleWithV1 bool
}

// DefaultPublishOptions returns the default options for publishing an IPNS Record.
func DefaultPublishOptions() PublishOptions {
return PublishOptions{
EOL: time.Now().Add(DefaultIPNSRecordEOL),
TTL: DefaultIPNSRecordTTL,
}
}

// PublishOption is used to set an option for [PublishOptions].
type PublishOption func(*PublishOptions)

// PublishWithEOL sets [PublishOptions.EOL].
func PublishWithEOL(eol time.Time) PublishOption {
return func(o *PublishOptions) {
o.EOL = eol
}
}

// PublishWithEOL sets [PublishOptions.TTL].
func PublishWithTTL(ttl time.Duration) PublishOption {
return func(o *PublishOptions) {
o.TTL = ttl
}
}

// PublishCompatibleWithV1 sets [PublishOptions.CompatibleWithV1].
func PublishCompatibleWithV1(compatible bool) PublishOption {
return func(o *PublishOptions) {
o.CompatibleWithV1 = compatible
}
}

// ProcessPublishOptions converts an array of [PublishOption] into a [PublishOptions] object.
func ProcessPublishOptions(opts []PublishOption) PublishOptions {
publishOptions := DefaultPublishOptions()
for _, option := range opts {
option(&publishOptions)
}
return publishOptions
}
2 changes: 1 addition & 1 deletion namesys/ipns_publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func PublishPublicKey(ctx context.Context, r routing.ValueStore, key string, pub
ctx, span := startSpan(ctx, "PublishPublicKey", trace.WithAttributes(attribute.String("Key", key)))
defer span.End()

log.Debugf("Storing pubkey at: %s", key)
log.Debugf("Storing pubkey at: %q", key)
bytes, err := crypto.MarshalPublicKey(pubKey)
if err != nil {
return err
Expand Down
Loading

0 comments on commit bb397e5

Please sign in to comment.