Skip to content

Commit

Permalink
Merge pull request #5563 from ipfs/feat/coreapi/name-cmd
Browse files Browse the repository at this point in the history
Switch name publish/resolve to coreapi
  • Loading branch information
Stebalien authored Oct 4, 2018
2 parents 181c399 + 58c9de9 commit 4409bad
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 150 deletions.
53 changes: 16 additions & 37 deletions core/commands/name/ipns.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ import (

cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
e "github.com/ipfs/go-ipfs/core/commands/e"
namesys "github.com/ipfs/go-ipfs/namesys"
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
nsopts "github.com/ipfs/go-ipfs/namesys/opts"

"gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
offline "gx/ipfs/QmScZySgru9jaoDa12sSfvh21sWbqF5eXkieTmJzAHJXkQ/go-ipfs-routing/offline"
cmds "gx/ipfs/QmXTmUCBtDUrzDYVzASogLiNph7EBuYqEgPL7QoHNMzUnz/go-ipfs-cmds"
logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log"
path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path"
Expand Down Expand Up @@ -80,44 +79,21 @@ Resolve the value of a dnslink:
cmdkit.StringOption(dhtTimeoutOptionName, "dhtt", "Max time to collect values during DHT resolution eg \"30s\". Pass 0 for no timeout."),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
n, err := cmdenv.GetNode(env)
api, err := cmdenv.GetApi(env)
if err != nil {
return err
}

if !n.OnlineMode() {
err := n.SetupOfflineRouting()
if err != nil {
return err
}
}

nocache, _ := req.Options["nocache"].(bool)
local, _ := req.Options["local"].(bool)

// default to nodes namesys resolver
var resolver namesys.Resolver = n.Namesys

if local && nocache {
return errors.New("cannot specify both local and nocache")
}

if local {
offroute := offline.NewOfflineRouter(n.Repo.Datastore(), n.RecordValidator)
resolver = namesys.NewIpnsResolver(offroute)
}

if nocache {
resolver = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), 0)
}

var name string
if len(req.Arguments) == 0 {
if n.Identity == "" {
return errors.New("identity not loaded")
self, err := api.Key().Self(req.Context)
if err != nil {
return err
}
name = n.Identity.Pretty()

name = self.ID().Pretty()
} else {
name = req.Arguments[0]
}
Expand All @@ -126,12 +102,16 @@ Resolve the value of a dnslink:
rc, rcok := req.Options[dhtRecordCountOptionName].(int)
dhtt, dhttok := req.Options[dhtTimeoutOptionName].(string)

var ropts []nsopts.ResolveOpt
opts := []options.NameResolveOption{
options.Name.Local(local),
options.Name.Cache(!nocache),
}

if !recursive {
ropts = append(ropts, nsopts.Depth(1))
opts = append(opts, options.Name.ResolveOption(nsopts.Depth(1)))
}
if rcok {
ropts = append(ropts, nsopts.DhtRecordCount(uint(rc)))
opts = append(opts, options.Name.ResolveOption(nsopts.DhtRecordCount(uint(rc))))
}
if dhttok {
d, err := time.ParseDuration(dhtt)
Expand All @@ -141,20 +121,19 @@ Resolve the value of a dnslink:
if d < 0 {
return errors.New("DHT timeout value must be >= 0")
}
ropts = append(ropts, nsopts.DhtTimeout(d))
opts = append(opts, options.Name.ResolveOption(nsopts.DhtTimeout(d)))
}

if !strings.HasPrefix(name, "/ipns/") {
name = "/ipns/" + name
}

output, err := resolver.Resolve(req.Context, name, ropts...)
output, err := api.Name().Resolve(req.Context, name, opts...)
if err != nil {
return err
}

// TODO: better errors (in the case of not finding the name, we get "failed to find any peer in table")
return cmds.EmitOnce(res, &ResolvedPath{output})
return cmds.EmitOnce(res, &ResolvedPath{path.FromString(output.String())})
},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
Expand Down
139 changes: 26 additions & 113 deletions core/commands/name/publish.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
package name

import (
"context"
"errors"
"fmt"
"io"
"time"

core "github.com/ipfs/go-ipfs/core"
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
e "github.com/ipfs/go-ipfs/core/commands/e"
keystore "github.com/ipfs/go-ipfs/keystore"
iface "github.com/ipfs/go-ipfs/core/coreapi/interface"
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"

crypto "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto"
"gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
cmds "gx/ipfs/QmXTmUCBtDUrzDYVzASogLiNph7EBuYqEgPL7QoHNMzUnz/go-ipfs-cmds"
peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer"
path "gx/ipfs/QmcjwUb36Z16NJkvDX6ccXPqsFswo6AsRXynyXcLLCphV2/go-path"
)

var (
errAllowOffline = errors.New("can't publish while offline: pass `--allow-offline` to override")
errIpnsMount = errors.New("cannot manually publish while IPNS is mounted")
errIdentityLoad = errors.New("identity not loaded")
)

const (
Expand Down Expand Up @@ -90,71 +84,59 @@ Alternatively, publish an <ipfs-path> using a valid PeerID (as listed by
cmdkit.BoolOption(quieterOptionName, "Q", "Write only final hash."),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
n, err := cmdenv.GetNode(env)
api, err := cmdenv.GetApi(env)
if err != nil {
return err
}

allowOffline, _ := req.Options[allowOfflineOptionName].(bool)
if !n.OnlineMode() {
if !allowOffline {
return errAllowOffline
}
err := n.SetupOfflineRouting()
if err != nil {
return err
}
}

if n.Mounts.Ipns != nil && n.Mounts.Ipns.IsActive() {
return errIpnsMount
}

pstr := req.Arguments[0]

if n.Identity == "" {
return errIdentityLoad
}

popts := new(publishOpts)

popts.verifyExists, _ = req.Options[resolveOptionName].(bool)
kname, _ := req.Options[keyOptionName].(string)

validtime, _ := req.Options[lifeTimeOptionName].(string)
d, err := time.ParseDuration(validtime)
validTimeOpt, _ := req.Options[lifeTimeOptionName].(string)
validTime, err := time.ParseDuration(validTimeOpt)
if err != nil {
return fmt.Errorf("error parsing lifetime option: %s", err)
}

popts.pubValidTime = d
opts := []options.NamePublishOption{
options.Name.AllowOffline(allowOffline),
options.Name.Key(kname),
options.Name.ValidTime(validTime),
}

ctx := req.Context
if ttl, found := req.Options[ttlOptionName].(string); found {
d, err := time.ParseDuration(ttl)
if err != nil {
return err
}

ctx = context.WithValue(ctx, "ipns-publish-ttl", d)
opts = append(opts, options.Name.TTL(d))
}

kname, _ := req.Options[keyOptionName].(string)
k, err := keylookup(n, kname)
p, err := iface.ParsePath(req.Arguments[0])
if err != nil {
return err
}

pth, err := path.ParsePath(pstr)
if err != nil {
return err
if verifyExists, _ := req.Options[resolveOptionName].(bool); verifyExists {
_, err := api.ResolveNode(req.Context, p)
if err != nil {
return err
}
}

output, err := publish(ctx, n, k, pth, popts)
out, err := api.Name().Publish(req.Context, p, opts...)
if err != nil {
if err == iface.ErrOffline {
err = errAllowOffline
}
return err
}

return cmds.EmitOnce(res, output)
return cmds.EmitOnce(res, &IpnsEntry{
Name: out.Name(),
Value: out.Value().String(),
})
},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
Expand All @@ -175,72 +157,3 @@ Alternatively, publish an <ipfs-path> using a valid PeerID (as listed by
},
Type: IpnsEntry{},
}

type publishOpts struct {
verifyExists bool
pubValidTime time.Duration
}

func publish(ctx context.Context, n *core.IpfsNode, k crypto.PrivKey, ref path.Path, opts *publishOpts) (*IpnsEntry, error) {

if opts.verifyExists {
// verify the path exists
_, err := core.Resolve(ctx, n.Namesys, n.Resolver, ref)
if err != nil {
return nil, err
}
}

eol := time.Now().Add(opts.pubValidTime)
err := n.Namesys.PublishWithEOL(ctx, k, ref, eol)
if err != nil {
return nil, err
}

pid, err := peer.IDFromPrivateKey(k)
if err != nil {
return nil, err
}

return &IpnsEntry{
Name: pid.Pretty(),
Value: ref.String(),
}, nil
}

func keylookup(n *core.IpfsNode, k string) (crypto.PrivKey, error) {

res, err := n.GetKey(k)
if res != nil {
return res, nil
}

if err != nil && err != keystore.ErrNoSuchKey {
return nil, err
}

keys, err := n.Repo.Keystore().List()
if err != nil {
return nil, err
}

for _, key := range keys {
privKey, err := n.Repo.Keystore().Get(key)
if err != nil {
return nil, err
}

pubKey := privKey.GetPublic()

pid, err := peer.IDFromPublicKey(pubKey)
if err != nil {
return nil, err
}

if pid.Pretty() == k {
return privKey, nil
}
}

return nil, fmt.Errorf("no key by the given name or PeerID was found")
}
24 changes: 24 additions & 0 deletions core/coreapi/interface/options/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ const (
type NamePublishSettings struct {
ValidTime time.Duration
Key string

TTL *time.Duration

AllowOffline bool
}

type NameResolveSettings struct {
Expand All @@ -29,6 +33,8 @@ func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error)
options := &NamePublishSettings{
ValidTime: DefaultNameValidTime,
Key: "self",

AllowOffline: false,
}

for _, opt := range opts {
Expand Down Expand Up @@ -82,6 +88,24 @@ func (nameOpts) Key(key string) NamePublishOption {
}
}

// AllowOffline is an option for Name.Publish which specifies whether to allow
// publishing when the node is offline. Default value is false
func (nameOpts) AllowOffline(allow bool) NamePublishOption {
return func(settings *NamePublishSettings) error {
settings.AllowOffline = allow
return nil
}
}

// TTL is an option for Name.Publish which specifies the time duration the
// published record should be cached for (caution: experimental).
func (nameOpts) TTL(ttl time.Duration) NamePublishOption {
return func(settings *NamePublishSettings) error {
settings.TTL = &ttl
return nil
}
}

// Local is an option for Name.Resolve which specifies if the lookup should be
// offline. Default value is false
func (nameOpts) Local(local bool) NameResolveOption {
Expand Down
5 changes: 5 additions & 0 deletions core/coreapi/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package coreapi
import (
"context"
"crypto/rand"
"errors"
"fmt"
"sort"

Expand Down Expand Up @@ -218,5 +219,9 @@ func (api *KeyAPI) Remove(ctx context.Context, name string) (coreiface.Key, erro
}

func (api *KeyAPI) Self(ctx context.Context) (coreiface.Key, error) {
if api.node.Identity == "" {
return nil, errors.New("identity not loaded")
}

return &key{"self", api.node.Identity}, nil
}
7 changes: 7 additions & 0 deletions core/coreapi/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func (api *NameAPI) Publish(ctx context.Context, p coreiface.Path, opts ...caopt
n := api.node

if !n.OnlineMode() {
if !options.AllowOffline {
return nil, coreiface.ErrOffline
}
err := n.SetupOfflineRouting()
if err != nil {
return nil, err
Expand All @@ -65,6 +68,10 @@ func (api *NameAPI) Publish(ctx context.Context, p coreiface.Path, opts ...caopt
return nil, err
}

if options.TTL != nil {
ctx = context.WithValue(ctx, "ipns-publish-ttl", *options.TTL)
}

eol := time.Now().Add(options.ValidTime)
err = n.Namesys.PublishWithEOL(ctx, k, pth, eol)
if err != nil {
Expand Down

0 comments on commit 4409bad

Please sign in to comment.