diff --git a/core/commands/name/ipns.go b/core/commands/name/ipns.go index ff78948e7bb..388aa07069f 100644 --- a/core/commands/name/ipns.go +++ b/core/commands/name/ipns.go @@ -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" @@ -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] } @@ -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) @@ -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 { diff --git a/core/commands/name/publish.go b/core/commands/name/publish.go index f34bbbbc34e..1d068837305 100644 --- a/core/commands/name/publish.go +++ b/core/commands/name/publish.go @@ -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 ( @@ -90,71 +84,59 @@ Alternatively, publish an 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 { @@ -175,72 +157,3 @@ Alternatively, publish an 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") -} diff --git a/core/coreapi/interface/options/name.go b/core/coreapi/interface/options/name.go index ba3691b03cd..c614db3abc3 100644 --- a/core/coreapi/interface/options/name.go +++ b/core/coreapi/interface/options/name.go @@ -13,6 +13,10 @@ const ( type NamePublishSettings struct { ValidTime time.Duration Key string + + TTL *time.Duration + + AllowOffline bool } type NameResolveSettings struct { @@ -29,6 +33,8 @@ func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) options := &NamePublishSettings{ ValidTime: DefaultNameValidTime, Key: "self", + + AllowOffline: false, } for _, opt := range opts { @@ -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 { diff --git a/core/coreapi/key.go b/core/coreapi/key.go index 9fe207785ba..d7de4647685 100644 --- a/core/coreapi/key.go +++ b/core/coreapi/key.go @@ -3,6 +3,7 @@ package coreapi import ( "context" "crypto/rand" + "errors" "fmt" "sort" @@ -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 } diff --git a/core/coreapi/name.go b/core/coreapi/name.go index cb7fc9b7d18..ddd339c8ad7 100644 --- a/core/coreapi/name.go +++ b/core/coreapi/name.go @@ -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 @@ -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 {