Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework mutable namespace resolution to handle recursion #1208

Merged
merged 11 commits into from
May 20, 2015
82 changes: 82 additions & 0 deletions core/commands/dns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package commands

import (
"io"
"strings"

cmds "github.com/ipfs/go-ipfs/commands"
namesys "github.com/ipfs/go-ipfs/namesys"
util "github.com/ipfs/go-ipfs/util"
)

var DNSCmd = &cmds.Command{
Copy link
Member

Choose a reason for hiding this comment

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

👍 to this command :)

Helptext: cmds.HelpText{
Tagline: "DNS link resolver",
ShortDescription: `
Multihashes are hard to remember, but domain names are usually easy to
remember. To create memorable aliases for multihashes, DNS TXT
records can point to other DNS links, IPFS objects, IPNS keys, etc.
This command resolves those links to the referenced object.
`,
LongDescription: `
Multihashes are hard to remember, but domain names are usually easy to
remember. To create memorable aliases for multihashes, DNS TXT
records can point to other DNS links, IPFS objects, IPNS keys, etc.
This command resolves those links to the referenced object.

For example, with this DNS TXT record:

ipfs.io. TXT "dnslink=/ipfs/QmRzTuh2Lpuz7Gr39stNr6mTFdqAghsZec1JoUnfySUzcy ..."

The resolver will give:

> ipfs dns ipfs.io
/ipfs/QmRzTuh2Lpuz7Gr39stNr6mTFdqAghsZec1JoUnfySUzcy

And with this DNS TXT record:

ipfs.ipfs.io. TXT "dnslink=/dns/ipfs.io ..."

The resolver will give:

> ipfs dns ipfs.io
Copy link
Member

Choose a reason for hiding this comment

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

i think this needs to be ipfs.ipfs.io

Copy link
Member

Choose a reason for hiding this comment

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

may want to use foo.ipfs.io

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On Thu, May 14, 2015 at 09:21:34PM -0700, Juan Batiz-Benet wrote:

  • ipfs dns ipfs.io

may want to use foo.ipfs.io

So two questions:

  1. Does it really matter if this example matches up with live DNS
    records?
  2. What live DNS links do we have? I don't see TXT records for
    ipfs.io, ipfs.ipfs.io, or foo.ipfs.io.

/dns/ipfs.io
> ipfs dns --recursive
/ipfs/QmRzTuh2Lpuz7Gr39stNr6mTFdqAghsZec1JoUnfySUzcy
`,
},

Arguments: []cmds.Argument{
cmds.StringArg("domain-name", true, false, "The domain-name name to resolve.").EnableStdin(),
},
Options: []cmds.Option{
cmds.BoolOption("recursive", "r", "Resolve until the result is not a DNS link"),
},
Run: func(req cmds.Request, res cmds.Response) {

recursive, _, _ := req.Option("recursive").Bool()
name := req.Arguments()[0]
resolver := namesys.NewDNSResolver()

depth := 1
if recursive {
depth = namesys.DefaultDepthLimit
}
output, err := resolver.ResolveN(req.Context().Context, name, depth)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
res.SetOutput(&ResolvedPath{output})
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
output, ok := res.Output().(*ResolvedPath)
if !ok {
return nil, util.ErrCast()
}
return strings.NewReader(output.Path.String()), nil
},
},
Type: ResolvedPath{},
}
104 changes: 104 additions & 0 deletions core/commands/ipns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package commands

import (
"errors"
"io"
"strings"

cmds "github.com/ipfs/go-ipfs/commands"
namesys "github.com/ipfs/go-ipfs/namesys"
u "github.com/ipfs/go-ipfs/util"
)

var ipnsCmd = &cmds.Command{
Copy link
Member

Choose a reason for hiding this comment

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

very minor thing-- i think we'll later want this to be something like ipnsResolve (later, doesnt matter), because i think ipfs name and ipns can be the same thing. (i.e. ipnsCmd would have both publish and resolve as children). thus an ipns independent tool could publish a record.

Helptext: cmds.HelpText{
Tagline: "Gets the value currently published at an IPNS name",
ShortDescription: `
IPNS is a PKI namespace, where names are the hashes of public keys, and
the private key enables publishing new (signed) values. In resolve, the
default value of <name> is your own identity public key.
`,
LongDescription: `
IPNS is a PKI namespace, where names are the hashes of public keys, and
the private key enables publishing new (signed) values. In resolve, the
default value of <name> is your own identity public key.


Examples:

Resolve the value of your identity:

> ipfs name resolve
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

Resolve the value of another name:

> ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

`,
},

Arguments: []cmds.Argument{
cmds.StringArg("name", false, false, "The IPNS name to resolve. Defaults to your node's peerID.").EnableStdin(),
},
Options: []cmds.Option{
cmds.BoolOption("recursive", "r", "Resolve until the result is not an IPNS name"),
},
Run: func(req cmds.Request, res cmds.Response) {

n, err := req.Context().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

if !n.OnlineMode() {
err := n.SetupOfflineRouting()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
}

var name string

if len(req.Arguments()) == 0 {
if n.Identity == "" {
res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal)
return
}
name = n.Identity.Pretty()

} else {
name = req.Arguments()[0]
}

recursive, _, _ := req.Option("recursive").Bool()
depth := 1
if recursive {
depth = namesys.DefaultDepthLimit
}

resolver := namesys.NewRoutingResolver(n.Routing)
output, err := resolver.ResolveN(n.Context(), name, depth)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

// TODO: better errors (in the case of not finding the name, we get "failed to find any peer in table")
Copy link
Member

Choose a reason for hiding this comment

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

failed to find any peers in table means that bootstrapping was not successful, not that a lookup necessarily resulted in 'not found'


res.SetOutput(&ResolvedPath{output})
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
output, ok := res.Output().(*ResolvedPath)
if !ok {
return nil, u.ErrCast()
}
return strings.NewReader(output.Path.String()), nil
},
},
Type: ResolvedPath{},
}
18 changes: 9 additions & 9 deletions core/commands/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,31 @@ and resolve, the default value of <name> is your own identity public key.

Examples:

Publish a <ref> to your identity name:
Publish an <ipfs-path> to your identity name:

> ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
> ipfs name publish /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
Published to QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

Publish a <ref> to another public key:
Publish an <ipfs-path> to another public key:

> ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
> ipfs name publish /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
Published to QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

Resolve the value of your identity:

> ipfs name resolve
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

Resolve the value of another name:

> ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

`,
},

Subcommands: map[string]*cmds.Command{
"publish": publishCmd,
"resolve": resolveCmd,
"resolve": ipnsCmd,
},
}
9 changes: 5 additions & 4 deletions core/commands/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ Examples:
Publish an <ipfs-path> to your identity name:

> ipfs name publish /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
Published to QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

Publish an <ipfs-path> to another public key (not implemented):

> ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
> ipfs name publish /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
Published to QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

`,
},

Expand Down Expand Up @@ -102,7 +103,7 @@ Publish an <ipfs-path> to another public key (not implemented):
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v := res.Output().(*IpnsEntry)
s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value)
s := fmt.Sprintf("Published to %s: %s\n", v.Name, v.Value)
return strings.NewReader(s), nil
},
},
Expand Down
61 changes: 31 additions & 30 deletions core/commands/resolve.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package commands

import (
"errors"
"io"
"strings"

cmds "github.com/ipfs/go-ipfs/commands"
namesys "github.com/ipfs/go-ipfs/namesys"
path "github.com/ipfs/go-ipfs/path"
u "github.com/ipfs/go-ipfs/util"
)
Expand All @@ -14,37 +14,46 @@ type ResolvedPath struct {
Path path.Path
}

var resolveCmd = &cmds.Command{
var ResolveCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Gets the value currently published at an IPNS name",
Tagline: "Resolve the value of names to IPFS",
ShortDescription: `
IPNS is a PKI namespace, where names are the hashes of public keys, and
the private key enables publishing new (signed) values. In resolve, the
default value of <name> is your own identity public key.
There are a number of mutable name protocols that can link among
themselves and into IPNS. This command accepts any of these
identifiers and resolves them to the referenced item.
`,
LongDescription: `
IPNS is a PKI namespace, where names are the hashes of public keys, and
the private key enables publishing new (signed) values. In resolve, the
default value of <name> is your own identity public key.

There are a number of mutable name protocols that can link among
themselves and into IPNS. For example IPNS references can (currently)
point at IPFS object, and DNS links can point at other DNS links, IPNS
entries, or IPFS objects. This command accepts any of these
identifiers and resolves them to the referenced item.

Examples:

Resolve the value of your identity:

> ipfs name resolve
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
> ipfs resolve /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj

Resolve the value of another name:

> ipfs resolve /ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy

Resolve te value of another name:
Resolve the value of another name recursively:

> ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
> ipfs resolve -r /ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj

`,
},

Arguments: []cmds.Argument{
cmds.StringArg("name", false, false, "The IPNS name to resolve. Defaults to your node's peerID.").EnableStdin(),
cmds.StringArg("name", true, false, "The name to resolve.").EnableStdin(),
},
Options: []cmds.Option{
cmds.BoolOption("recursive", "r", "Resolve until the result is an IPFS name"),
Copy link
Member

Choose a reason for hiding this comment

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

maybe this should default to true?

Copy link
Member

Choose a reason for hiding this comment

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

no, i think you're right. it should default to false.

},
Run: func(req cmds.Request, res cmds.Response) {

Expand All @@ -62,27 +71,19 @@ Resolve te value of another name:
}
}
Copy link
Member

Choose a reason for hiding this comment

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

no longer default to the local identity? this is probably ok for resolve, but may feel asymmetric for users of publish

Copy link
Member

Choose a reason for hiding this comment

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

ahhh nevermind, this is just ipfs resolve now. not ipfs name resolve


var name string

if len(req.Arguments()) == 0 {
if n.Identity == "" {
res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal)
return
}
name = n.Identity.Pretty()

} else {
name = req.Arguments()[0]
name := req.Arguments()[0]
recursive, _, _ := req.Option("recursive").Bool()
depth := 1
if recursive {
depth = namesys.DefaultDepthLimit
}

output, err := n.Namesys.Resolve(n.Context(), name)
output, err := n.Namesys.ResolveN(n.Context(), name, depth)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
Copy link
Member

Choose a reason for hiding this comment

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

do we return better errors now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On Thu, May 07, 2015 at 10:51:49PM -0700, Juan Batiz-Benet wrote:

  •   // TODO: better errors (in the case of not finding the name, we get "failed to find any peer in table")
    

do we return better errors now?

Without a daemon:

$ ipfs /ipfs/QmNonExistentKey
panic?
$ ipfs resolve /ipns/QmNonExistentKey
Error: datastore: key not found
$ ipfs resolve /dns/abc
Error: not a valid domain name
$ ipfs resolve /dns/example.com
Error: could not resolve name.

With a daemon running, the DNS errors are the same, and the IPFS/IPNS
errors are:

$ ipfs /ipfs/QmNonExistentKey
Error: context deadline exceeded
$ ipfs resolve /ipns/QmNonExistentKey
Error: routing: not found

What sort of better errors were you looking for?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm-- we probably should make the differing ones the same.

This is odd:

> ipfs /ipfs/QmNonExistentKey
Error: context deadline exceeded

cmdslib should just error out...

Copy link
Member

Choose a reason for hiding this comment

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

> ipfs resolve /ipns/QmNonExistentKey
Error: routing: not found

should probably be

> ipfs resolve /ipns/QmNonExistentKey
Error: could not resolve name.

in both cases.

}

// TODO: better errors (in the case of not finding the name, we get "failed to find any peer in table")

res.SetOutput(&ResolvedPath{output})
},
Marshalers: cmds.MarshalerMap{
Expand Down
4 changes: 4 additions & 0 deletions core/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ ADVANCED COMMANDS

daemon Start a long-running daemon process
mount Mount an ipfs read-only mountpoint
resolve Resolve any type of name
name Publish or resolve IPNS names
dns Resolve DNS links
pin Pin objects to local storage
repo gc Garbage collect unpinned objects

Expand Down Expand Up @@ -84,6 +86,7 @@ var rootSubcommands = map[string]*cmds.Command{
"config": ConfigCmd,
"dht": DhtCmd,
"diag": DiagCmd,
"dns": DNSCmd,
"get": GetCmd,
"id": IDCmd,
"log": LogCmd,
Expand All @@ -95,6 +98,7 @@ var rootSubcommands = map[string]*cmds.Command{
"ping": PingCmd,
"refs": RefsCmd,
"repo": RepoCmd,
"resolve": ResolveCmd,
"stats": StatsCmd,
"swarm": SwarmCmd,
"update": UpdateCmd,
Expand Down
Loading