From de63fbca8b3e670c0777862ade4a244c139d28cb Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Thu, 13 Sep 2018 22:57:22 -0400 Subject: [PATCH] Attempt to preserve base of Cid's provided on the command line. License: MIT Signed-off-by: Kevin Atkinson --- core/apicid.go | 86 +------------------------- core/cidenc/encoder.go | 130 +++++++++++++++++++++++++++++++++++++++ core/commands/add.go | 2 +- core/commands/ls.go | 2 +- core/commands/pin.go | 10 +-- core/commands/resolve.go | 13 +++- core/commands/root.go | 41 ++++++++---- core/commands/tar.go | 2 +- 8 files changed, 181 insertions(+), 105 deletions(-) create mode 100644 core/cidenc/encoder.go diff --git a/core/apicid.go b/core/apicid.go index 7ff82313494..8eb2ab97337 100644 --- a/core/apicid.go +++ b/core/apicid.go @@ -3,30 +3,11 @@ package core import ( "encoding/json" + cidenc "github.com/ipfs/go-ipfs/core/cidenc" cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" mbase "gx/ipfs/QmekxXDhCxCJRNuzmHreuaT3BsuJcsjcXWNrtV9C8DRHtd/go-multibase" - //path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" ) -// CidEncoder is a type used to encode or recode Cid as the user -// specifies -type CidEncoder interface { - Encode(c cid.Cid) string - Recode(v string) (string, error) -} - -// BasicCidEncoder is a basic CidEncoder that will encode Cid's using -// a specifed base, optionally upgrading a Cid if is Version 0 -type BasicCidEncoder struct { - Base mbase.Encoder - Upgrade bool -} - -var DefaultCidEncoder = BasicCidEncoder{ - Base: mbase.MustNewEncoder(mbase.Base58BTC), - Upgrade: false, -} - // CidJSONBase is the base to use when Encoding into JSON. //var CidJSONBase mbase.Encoder = mbase.MustNewEncoder(mbase.Base58BTC) var CidJSONBase mbase.Encoder = mbase.MustNewEncoder(mbase.Base32) @@ -47,10 +28,10 @@ func (c APICid) Cid() (cid.Cid, error) { } func (c APICid) String() string { - return c.Encode(DefaultCidEncoder) + return c.Encode(cidenc.Default) } -func (c APICid) Encode(enc CidEncoder) string { +func (c APICid) Encode(enc cidenc.Interface) string { if c.str == "" { return "" } @@ -68,64 +49,3 @@ func (c *APICid) UnmarshalJSON(b []byte) error { func (c APICid) MarshalJSON() ([]byte, error) { return json.Marshal(c.str) } - -func (enc BasicCidEncoder) Encode(c cid.Cid) string { - if c.Version() == 0 { - c = cid.NewCidV1(c.Type(), c.Hash()) - } - return c.Encode(enc.Base) -} - -func (enc BasicCidEncoder) Recode(v string) (string, error) { - skip, err := enc.NoopRecode(v) - if skip || err != nil { - return v, err - } - - c, err := cid.Decode(v) - if err != nil { - return v, err - } - - return enc.Encode(c), nil -} - -func (enc BasicCidEncoder) NoopRecode(v string) (bool, error) { - if len(v) < 2 { - return false, cid.ErrCidTooShort - } - ver := cidVer(v) - skip := ver == 0 && !enc.Upgrade || ver == 1 && v[0] == byte(enc.Base.Encoding()) - return skip, nil -} - -func cidVer(v string) int { - if len(v) == 46 && v[:2] == "Qm" { - return 0 - } else { - return 1 - } -} - -// func (enc *CidEncoder) Scan(cids ...string) { -// if enc.Override == nil { -// enc.Override = map[cid.Cid]string{} -// } -// for _, p := range cids { -// //segs := path.FromString(p).Segments() -// //v := segs[0] -// //if v == "ipfs" && len(segs) > 0 { -// // v = segs[1] -// //} -// v := p -// skip, err := enc.noopRecode(v) -// if skip || err != nil { -// continue -// } -// c, err := cid.Decode(v) -// if err != nil { -// continue -// } -// enc.Override[c] = v -// } -// } diff --git a/core/cidenc/encoder.go b/core/cidenc/encoder.go new file mode 100644 index 00000000000..45267256056 --- /dev/null +++ b/core/cidenc/encoder.go @@ -0,0 +1,130 @@ +package cidenc + +import ( + cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid" + path "gx/ipfs/QmX7uSbkNz76yNwBhuwYwRbhihLnJqM73VTCjS3UMJud9A/go-path" + mbase "gx/ipfs/QmekxXDhCxCJRNuzmHreuaT3BsuJcsjcXWNrtV9C8DRHtd/go-multibase" +) + +// Encoder is a type used to encode or recode Cid as the user +// specifies +type Interface interface { + Encode(c cid.Cid) string + Recode(v string) (string, error) +} + +// Basic is a basic Encoder that will encode Cid's using +// a specifed base, optionally upgrading a Cid if is Version 0 +type Encoder struct { + Base mbase.Encoder + Upgrade bool +} + +var Default = Encoder{ + Base: mbase.MustNewEncoder(mbase.Base58BTC), + Upgrade: false, +} + +func (enc Encoder) Encode(c cid.Cid) string { + if enc.Upgrade && c.Version() == 0 { + c = cid.NewCidV1(c.Type(), c.Hash()) + } + return c.Encode(enc.Base) +} + +func (enc Encoder) Recode(v string) (string, error) { + skip, err := enc.NoopRecode(v) + if skip || err != nil { + return v, err + } + + c, err := cid.Decode(v) + if err != nil { + return v, err + } + + return enc.Encode(c), nil +} + +func (enc Encoder) NoopRecode(v string) (bool, error) { + if len(v) < 2 { + return false, cid.ErrCidTooShort + } + ver := cidVer(v) + skip := ver == 0 && !enc.Upgrade || ver == 1 && v[0] == byte(enc.Base.Encoding()) + return skip, nil +} + +func cidVer(v string) int { + if len(v) == 46 && v[:2] == "Qm" { + return 0 + } else { + return 1 + } +} + +// On error an unmodified encoder will be returned so it is safe to +// ignore the error +func (enc Encoder) FromPath(p string) (Encoder, error) { + v := extractCidString(p) + if cidVer(v) == 0 { + return Encoder{enc.Base, false}, nil + } + e, err := mbase.NewEncoder(mbase.Encoding(v[0])) + if err != nil { + return enc, err + } + return Encoder{e, true}, nil +} + +func extractCidString(p string) string { + segs := path.FromString(p).Segments() + v := segs[0] + if v == "ipfs" && len(segs) > 0 { + v = segs[1] + } + return v +} + +type WithOverride struct { + base Encoder + override map[cid.Cid]string +} + +func (enc Encoder) WithOverride(cids ...string) Interface { + override := map[cid.Cid]string{} + for _, p := range cids { + v := p + skip, err := enc.NoopRecode(v) + if skip || err != nil { + continue + } + c, err := cid.Decode(v) + if err != nil { + continue + } + println(v) + override[c] = v + } + if len(override) == 0 { + return enc + } + return WithOverride{enc, override} +} + +func (enc WithOverride) Encode(c cid.Cid) string { + v, ok := enc.override[c] + if ok { + return v + } + return enc.base.Encode(c) +} + +func (enc WithOverride) Recode(v string) (string, error) { + c, err := cid.Decode(v) + if err != nil { + return v, err + } + + return enc.Encode(c), nil +} diff --git a/core/commands/add.go b/core/commands/add.go index 17b76ea81c4..5c3bec384cb 100644 --- a/core/commands/add.go +++ b/core/commands/add.go @@ -390,7 +390,7 @@ You can now check what blocks have been created by: log.Warning("cannot determine size of input file") } - err := HandleCidBase(req) + _, err := HandleCidBase(nil, req) if err != nil { re.SetError(err, cmdkit.ErrNormal) return re diff --git a/core/commands/ls.go b/core/commands/ls.go index 201253546a2..27c3da5188e 100644 --- a/core/commands/ls.go +++ b/core/commands/ls.go @@ -172,7 +172,7 @@ The JSON output contains type information. }, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) (io.Reader, error) { - err := HandleCidBaseLegacy(res.Request()) + _, err := HandleCidBaseLegacy(nil, res.Request()) if err != nil { return nil, err } diff --git a/core/commands/pin.go b/core/commands/pin.go index 5449c7c9d78..9e698446cc9 100644 --- a/core/commands/pin.go +++ b/core/commands/pin.go @@ -130,7 +130,7 @@ var addPinCmd = &cmds.Command{ }, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) (io.Reader, error) { - err := HandleCidBaseLegacy(res.Request()) + enc, err := HandleCidBaseWithOverrideLegacy(res.Request()) if err != nil { return nil, err } @@ -167,7 +167,7 @@ var addPinCmd = &cmds.Command{ buf := new(bytes.Buffer) for _, k := range added { - fmt.Fprintf(buf, "pinned %s %s\n", k, pintype) + fmt.Fprintf(buf, "pinned %s %s\n", k.Encode(enc), pintype) } return buf, nil }, @@ -214,7 +214,7 @@ collected if needed. (By default, recursively. Use -r=false for direct pins.) }, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) (io.Reader, error) { - err := HandleCidBaseLegacy(res.Request()) + enc, err := HandleCidBaseWithOverrideLegacy(res.Request()) if err != nil { return nil, err } @@ -230,7 +230,7 @@ collected if needed. (By default, recursively. Use -r=false for direct pins.) buf := new(bytes.Buffer) for _, k := range added.Pins { - fmt.Fprintf(buf, "unpinned %s\n", k) + fmt.Fprintf(buf, "unpinned %s\n", k.Encode(enc)) } return buf, nil }, @@ -326,7 +326,7 @@ Example: Type: RefKeyList{}, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) (io.Reader, error) { - err := HandleCidBaseLegacy(res.Request()) + _, err := HandleCidBaseLegacy(nil, res.Request()) if err != nil { return nil, err } diff --git a/core/commands/resolve.go b/core/commands/resolve.go index 75258464f84..a325e45133d 100644 --- a/core/commands/resolve.go +++ b/core/commands/resolve.go @@ -8,6 +8,7 @@ import ( cmds "github.com/ipfs/go-ipfs/commands" "github.com/ipfs/go-ipfs/core" + cidenc "github.com/ipfs/go-ipfs/core/cidenc" e "github.com/ipfs/go-ipfs/core/commands/e" ncmd "github.com/ipfs/go-ipfs/core/commands/name" ns "github.com/ipfs/go-ipfs/namesys" @@ -84,6 +85,16 @@ Resolve the value of an IPFS DAG path: name := req.Arguments()[0] recursive, _, _ := req.Option("recursive").Bool() + enc := cidenc.Default // make local copy + defined, err := HandleCidBaseLegacy(&enc, req) + if err != nil { + res.SetError(err, cmdkit.ErrNormal) + return + } + if !defined { + enc, _ = enc.FromPath(name) + } + // the case when ipns is resolved step by step if strings.HasPrefix(name, "/ipns/") && !recursive { rc, rcok, _ := req.Option("dht-record-count").Int() @@ -129,7 +140,7 @@ Resolve the value of an IPFS DAG path: c := node.Cid() - res.SetOutput(&ncmd.ResolvedPath{Path: path.FromCid(c)}) + res.SetOutput(&ncmd.ResolvedPath{Path: path.FromString("/ipfs/" + enc.Encode(c))}) }, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) (io.Reader, error) { diff --git a/core/commands/root.go b/core/commands/root.go index 55aebe25367..08a26ae1ff5 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -7,7 +7,7 @@ import ( oldcmds "github.com/ipfs/go-ipfs/commands" lgc "github.com/ipfs/go-ipfs/commands/legacy" - core "github.com/ipfs/go-ipfs/core" + cidenc "github.com/ipfs/go-ipfs/core/cidenc" dag "github.com/ipfs/go-ipfs/core/commands/dag" e "github.com/ipfs/go-ipfs/core/commands/e" name "github.com/ipfs/go-ipfs/core/commands/name" @@ -229,34 +229,49 @@ func MessageTextMarshaler(res oldcmds.Response) (io.Reader, error) { return strings.NewReader(out.Message), nil } -func handleCidBase(base string, upgrade bool, upgradeDefined bool) error { - enc := core.DefaultCidEncoder +func handleCidBase(enc *cidenc.Encoder, base string, upgrade bool, upgradeDefined bool) (bool, error) { + if enc == nil { + enc = &cidenc.Default + } + e := *enc if base != "" { var err error - enc.Base, err = mbase.EncoderByName(base) + e.Base, err = mbase.EncoderByName(base) if err != nil { - return err + return false, err } } - enc.Upgrade = upgrade + e.Upgrade = upgrade if base != "" && !upgradeDefined { - enc.Upgrade = true + e.Upgrade = true } - core.DefaultCidEncoder = enc - return nil + *enc = e + return base != "" || upgradeDefined, nil } -func HandleCidBase(req *cmds.Request) error { +func HandleCidBase(enc *cidenc.Encoder, req *cmds.Request) (bool, error) { base, _ := req.Options[CidBaseOption].(string) upgrade, defined := req.Options[UpgradeCidV0Option].(bool) - return handleCidBase(base, upgrade, defined) + return handleCidBase(enc, base, upgrade, defined) } -func HandleCidBaseLegacy(req oldcmds.Request) error { +func HandleCidBaseLegacy(enc *cidenc.Encoder, req oldcmds.Request) (bool, error) { base, _, _ := req.Option(CidBaseOption).String() upgrade, defined, _ := req.Option(UpgradeCidV0Option).Bool() - return handleCidBase(base, upgrade, defined) + return handleCidBase(enc, base, upgrade, defined) +} + +func HandleCidBaseWithOverrideLegacy(req oldcmds.Request) (cidenc.Interface, error) { + defined, err := HandleCidBaseLegacy(nil, req) + if err != nil { + return nil, err + } + var enc cidenc.Interface = cidenc.Default + if !defined { + enc = cidenc.Default.WithOverride(req.Arguments()...) + } + return enc, nil } diff --git a/core/commands/tar.go b/core/commands/tar.go index 40b979e0cf0..8d308b41b01 100644 --- a/core/commands/tar.go +++ b/core/commands/tar.go @@ -68,7 +68,7 @@ represent it. Type: coreunix.AddedObject{}, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) (io.Reader, error) { - err := HandleCidBaseLegacy(res.Request()) + _, err := HandleCidBaseLegacy(nil, res.Request()) if err != nil { return nil, err }