Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 34 additions & 29 deletions core/commands/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ package commands

import (
"errors"
"fmt"
"io"
"strings"
"time"

cmds "github.com/ipfs/go-ipfs/commands"
"github.com/ipfs/go-ipfs/core"
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
e "github.com/ipfs/go-ipfs/core/commands/e"
ncmd "github.com/ipfs/go-ipfs/core/commands/name"
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
ns "github.com/ipfs/go-ipfs/namesys"
nsopts "github.com/ipfs/go-ipfs/namesys/opts"
path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path"

"gx/ipfs/QmPTfgFTo9PFr1PvPKyKoeMgBvYPh6cX3aDP7DHKVbnCbi/go-ipfs-cmds"
"gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
)

Expand Down Expand Up @@ -62,12 +64,17 @@ Resolve the value of an IPFS DAG path:
},
Options: []cmdkit.Option{
cmdkit.BoolOption("recursive", "r", "Resolve until the result is an IPFS name."),
cmdkit.UintOption("dht-record-count", "dhtrc", "Number of records to request for DHT resolution."),
cmdkit.IntOption("dht-record-count", "dhtrc", "Number of records to request for DHT resolution."),
cmdkit.StringOption("dht-timeout", "dhtt", "Max time to collect values during DHT resolution eg \"30s\". Pass 0 for no timeout."),
},
Run: func(req cmds.Request, res cmds.Response) {
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
api, err := cmdenv.GetApi(env)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}

n, err := req.InvocContext().GetNode()
n, err := cmdenv.GetNode(env)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
Expand All @@ -81,16 +88,17 @@ Resolve the value of an IPFS DAG path:
}
}

name := req.Arguments()[0]
recursive, _, _ := req.Option("recursive").Bool()
name := req.Arguments[0]
recursive, _ := req.Options["recursive"].(bool)

// the case when ipns is resolved step by step
if strings.HasPrefix(name, "/ipns/") && !recursive {
rc, rcok, _ := req.Option("dht-record-count").Int()
dhtt, dhttok, _ := req.Option("dht-timeout").String()
ropts := []nsopts.ResolveOpt{nsopts.Depth(1)}
rc, rcok := req.Options["dht-record-count"].(int)
dhtt, dhttok := req.Options["dht-timeout"].(string)
ropts := []options.NameResolveOption{options.Name.Depth(1)}

if rcok {
ropts = append(ropts, nsopts.DhtRecordCount(uint(rc)))
ropts = append(ropts, options.Name.DhtRecordCount(rc))
}
if dhttok {
d, err := time.ParseDuration(dhtt)
Expand All @@ -102,48 +110,45 @@ Resolve the value of an IPFS DAG path:
res.SetError(errors.New("DHT timeout value must be >= 0"), cmdkit.ErrNormal)
return
}
ropts = append(ropts, nsopts.DhtTimeout(d))
ropts = append(ropts, options.Name.DhtTimeout(d))
}
p, err := n.Namesys.Resolve(req.Context(), name, ropts...)
p, err := api.Name().Resolve(req.Context, name, ropts...)
// ErrResolveRecursion is fine
if err != nil && err != ns.ErrResolveRecursion {
res.SetError(err, cmdkit.ErrNormal)
return
}
res.SetOutput(&ncmd.ResolvedPath{Path: p})
cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: path.Path(p.String())})
return
}

// else, ipfs path or ipns with recursive flag
p, err := path.ParsePath(name)
p, err := coreiface.ParsePath(name)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}

node, err := core.Resolve(req.Context(), n.Namesys, n.Resolver, p)
rp, err := api.ResolvePath(req.Context, p)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}

c := node.Cid()
c := rp.Cid()

res.SetOutput(&ncmd.ResolvedPath{Path: path.FromCid(c)})
cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: path.FromCid(c)})
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v, err := unwrapOutput(res.Output())
if err != nil {
return nil, err
}

Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
output, ok := v.(*ncmd.ResolvedPath)
if !ok {
return nil, e.TypeErr(output, v)
return e.TypeErr(output, v)
}
return strings.NewReader(output.Path.String() + "\n"), nil
},

fmt.Fprintln(w, output.Path.String())
return nil
}),
},
Type: ncmd.ResolvedPath{},
}
4 changes: 2 additions & 2 deletions core/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ var rootSubcommands = map[string]*cmds.Command{
"ping": lgc.NewCommand(PingCmd),
"p2p": lgc.NewCommand(P2PCmd),
"refs": lgc.NewCommand(RefsCmd),
"resolve": lgc.NewCommand(ResolveCmd),
"resolve": ResolveCmd,
"swarm": lgc.NewCommand(SwarmCmd),
"tar": lgc.NewCommand(TarCmd),
"file": lgc.NewCommand(unixfs.UnixFSCmd),
Expand Down Expand Up @@ -183,7 +183,7 @@ var rootROSubcommands = map[string]*cmds.Command{
"resolve": dag.DagResolveCmd,
},
}),
"resolve": lgc.NewCommand(ResolveCmd),
"resolve": ResolveCmd,
"version": lgc.NewCommand(VersionCmd),
}

Expand Down
43 changes: 34 additions & 9 deletions core/coreapi/interface/options/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ type NamePublishSettings struct {
}

type NameResolveSettings struct {
Recursive bool
Local bool
Cache bool
Depth int
Local bool
Cache bool

DhtRecordCount int
DhtTimeout time.Duration
}

type NamePublishOption func(*NamePublishSettings) error
Expand All @@ -40,9 +43,12 @@ func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error)

func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) {
options := &NameResolveSettings{
Recursive: false,
Local: false,
Cache: true,
Depth: 1,
Local: false,
Cache: true,

DhtRecordCount: 16,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should hard-code this. Can we use the value from the DHT (unless we go with the "resolve options" solution in which case this point is moot).

DhtTimeout: time.Minute,
}

for _, opt := range opts {
Expand Down Expand Up @@ -80,11 +86,11 @@ func (nameOpts) Key(key string) NamePublishOption {
}
}

// Recursive is an option for Name.Resolve which specifies whether to perform a
// Depth is an option for Name.Resolve which specifies the maximum depth of a
// recursive lookup. Default value is false
func (nameOpts) Recursive(recursive bool) NameResolveOption {
func (nameOpts) Depth(depth int) NameResolveOption {
return func(settings *NameResolveSettings) error {
settings.Recursive = recursive
settings.Depth = depth
return nil
}
}
Expand All @@ -106,3 +112,22 @@ func (nameOpts) Cache(cache bool) NameResolveOption {
return nil
}
}

// DhtRecordCount is an option for Name.Resolve which specifies how many records
// we want to validate before selecting the best one (newest). Note that setting
// this value too low will have security implications
func (nameOpts) DhtRecordCount(rc int) NameResolveOption {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have DHT specific options like this or a more general purpose way to pass routing options (that is, go-libp2p-routing/options.Option). That is:

func RoutingOption(r ropts.Option) NameResolveOption ...

Or is this leaking too much into this API?

My worry here is adding a ton of special-purpose options for each router we may implement.

return func(settings *NameResolveSettings) error {
settings.DhtRecordCount = rc
return nil
}
}

// DhtTimeout is an option for Name.Resolve which specifies timeout for
// DHT lookup
func (nameOpts) DhtTimeout(timeout time.Duration) NameResolveOption {
return func(settings *NameResolveSettings) error {
settings.DhtTimeout = timeout
return nil
}
}
7 changes: 4 additions & 3 deletions core/coreapi/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,10 @@ func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.Nam
name = "/ipns/" + name
}

var ropts []nsopts.ResolveOpt
if !options.Recursive {
ropts = append(ropts, nsopts.Depth(1))
ropts := []nsopts.ResolveOpt{
nsopts.Depth(uint(options.Depth)),
nsopts.DhtRecordCount(uint(options.DhtRecordCount)),
nsopts.DhtTimeout(options.DhtTimeout),
}

output, err := resolver.Resolve(ctx, name, ropts...)
Expand Down
33 changes: 12 additions & 21 deletions core/coreapi/path.go
Original file line number Diff line number Diff line change
@@ -1,53 +1,44 @@
package coreapi

import (
context "context"
fmt "fmt"
"context"
"fmt"
gopath "path"

core "github.com/ipfs/go-ipfs/core"
"github.com/ipfs/go-ipfs/core"
coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
namesys "github.com/ipfs/go-ipfs/namesys"
uio "gx/ipfs/QmPL8bYtbACcSFFiSr4s2du7Na382NxRADR8hC7D9FkEA2/go-unixfs/io"
ipfspath "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path"
resolver "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path/resolver"
"gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path/resolver"

cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid"
"gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid"
ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format"
)

// ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the
// resolved Node.
func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (ipld.Node, error) {
return resolveNode(ctx, api.node.DAG, api.node.Namesys, p)
}

// ResolvePath resolves the path `p` using Unixfs resolver, returns the
// resolved path.
func (api *CoreAPI) ResolvePath(ctx context.Context, p coreiface.Path) (coreiface.ResolvedPath, error) {
return resolvePath(ctx, api.node.DAG, api.node.Namesys, p)
}

func resolveNode(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSystem, p coreiface.Path) (ipld.Node, error) {
rp, err := resolvePath(ctx, ng, nsys, p)
rp, err := api.ResolvePath(ctx, p)
if err != nil {
return nil, err
}

node, err := ng.Get(ctx, rp.Cid())
node, err := api.node.DAG.Get(ctx, rp.Cid())
if err != nil {
return nil, err
}
return node, nil
}

func resolvePath(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSystem, p coreiface.Path) (coreiface.ResolvedPath, error) {
// ResolvePath resolves the path `p` using Unixfs resolver, returns the
// resolved path.
func (api *CoreAPI) ResolvePath(ctx context.Context, p coreiface.Path) (coreiface.ResolvedPath, error) {
if _, ok := p.(coreiface.ResolvedPath); ok {
return p.(coreiface.ResolvedPath), nil
}

ipath := ipfspath.Path(p.String())
ipath, err := core.ResolveIPNS(ctx, nsys, ipath)
ipath, err := core.ResolveIPNS(ctx, api.node.Namesys, ipath)
if err == core.ErrNoNamesys {
return nil, coreiface.ErrOffline
} else if err != nil {
Expand All @@ -66,7 +57,7 @@ func resolvePath(ctx context.Context, ng ipld.NodeGetter, nsys namesys.NameSyste
}

r := &resolver.Resolver{
DAG: ng,
DAG: api.node.DAG,
ResolveOnce: resolveOnce,
}

Expand Down
2 changes: 1 addition & 1 deletion core/coreapi/unixfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (coreiface.ResolvedP
func (api *UnixfsAPI) Cat(ctx context.Context, p coreiface.Path) (coreiface.Reader, error) {
dget := api.node.DAG // TODO: use a session here once routing perf issues are resolved

dagnode, err := resolveNode(ctx, dget, api.node.Namesys, p)
dagnode, err := api.core().ResolveNode(ctx, p)
if err != nil {
return nil, err
}
Expand Down
6 changes: 6 additions & 0 deletions test/sharness/t0260-sharding.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ test_expect_success "gateway can resolve sharded dirs" '
test_cmp expected actual
'

test_expect_success "'ipfs resolve' can resolve sharded dirs" '
echo /ipfs/QmZ3RfWk1u5LEGYLHA633B5TNJy3Du27K6Fny9wcxpowGS > expected &&
ipfs resolve "/ipfs/$SHARDED/file100" > actual &&
test_cmp expected actual
'

test_kill_ipfs_daemon

test_add_large_dir_v1() {
Expand Down