Commit 19d8f624 authored by Steven Allen's avatar Steven Allen

base32: make GetEncoderFromPath more robust

Primarily, get rid of extractCidString and cidVer. Neither of these functions
did sane things when a path when a path didn't actually include a CID. For
example, "boo" would yield a base32 encoder.

Also:

* Avoid "optional" errors.
* Make it a pure function of the input path.
* Extract the multibase from *any* type of path of the form
  /namespace/cid-like-thing/... This is a DWIM function.

License: MIT
Signed-off-by: default avatarSteven Allen <steven@stebalien.com>
parent f31e6b61
package cmdenv package cmdenv
import ( import (
"fmt"
"strings" "strings"
cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds" cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc" cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit" cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
...@@ -59,32 +61,47 @@ func CidBaseDefined(req *cmds.Request) bool { ...@@ -59,32 +61,47 @@ func CidBaseDefined(req *cmds.Request) bool {
// CidEncoderFromPath creates a new encoder that is influenced from // CidEncoderFromPath creates a new encoder that is influenced from
// the encoded Cid in a Path. For CidV0 the multibase from the base // the encoded Cid in a Path. For CidV0 the multibase from the base
// encoder is used and automatic upgrades are disabled. For CidV1 the // encoder is used and automatic upgrades are disabled. For CidV1 the
// multibase from the CID is used and upgrades are eneabled. On error // multibase from the CID is used and upgrades are enabled.
// the base encoder is returned. If you don't care about the error //
// condition, it is safe to ignore the error returned. // This logic is intentionally fuzzy and will match anything of the form
func CidEncoderFromPath(enc cidenc.Encoder, p string) (cidenc.Encoder, error) { // `CidLike`, `CidLike/...`, or `/namespace/CidLike/...`.
v := extractCidString(p) //
if cidVer(v) == 0 { // For example:
return cidenc.Encoder{Base: enc.Base, Upgrade: false}, nil //
// * Qm...
// * Qm.../...
// * /ipfs/Qm...
// * /ipns/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
// * /bzz/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
func CidEncoderFromPath(p string) (cidenc.Encoder, error) {
components := strings.SplitN(p, "/", 4)
var maybeCid string
if components[0] != "" {
// No leading slash, first component is likely CID-like.
maybeCid = components[0]
} else if len(components) < 3 {
// Not enough components to include a CID.
return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
} else {
maybeCid = components[2]
} }
e, err := mbase.NewEncoder(mbase.Encoding(v[0])) c, err := cid.Decode(maybeCid)
if err != nil { if err != nil {
return enc, err // Ok, not a CID-like thing. Keep the current encoder.
return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
} }
return cidenc.Encoder{Base: e, Upgrade: true}, nil if c.Version() == 0 {
} // Version 0, use the base58 non-upgrading encoder.
return cidenc.Default(), nil
func extractCidString(str string) string {
parts := strings.Split(str, "/")
if len(parts) > 2 && (parts[1] == "ipfs" || parts[1] == "ipld") {
return parts[2]
} }
return str
}
func cidVer(v string) int { // Version 1+, extract multibase encoding.
if len(v) == 46 && v[:2] == "Qm" { encoding, _, err := mbase.Decode(maybeCid)
return 0 if err != nil {
// This should be impossible, we've already decoded the cid.
panic(fmt.Sprintf("BUG: failed to get multibase decoder for CID %s", maybeCid))
} }
return 1
return cidenc.Encoder{Base: mbase.MustNewEncoder(encoding), Upgrade: true}, nil
} }
...@@ -2,29 +2,72 @@ package cmdenv ...@@ -2,29 +2,72 @@ package cmdenv
import ( import (
"testing" "testing"
cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
mbase "gx/ipfs/QmekxXDhCxCJRNuzmHreuaT3BsuJcsjcXWNrtV9C8DRHtd/go-multibase"
) )
func TestExtractCidString(t *testing.T) { func TestEncoderFromPath(t *testing.T) {
test := func(path string, cid string) { test := func(path string, expected cidenc.Encoder) {
res := extractCidString(path) actual, err := CidEncoderFromPath(path)
if res != cid { if err != nil {
t.Errorf("extractCidString(%s) failed: expected '%s' but got '%s'", path, cid, res) t.Error(err)
}
if actual != expected {
t.Errorf("CidEncoderFromPath(%s) failed: expected %#v but got %#v", path, expected, actual)
} }
} }
p := "QmRqVG8VGdKZ7KARqR96MV7VNHgWvEQifk94br5HpURpfu" p := "QmRqVG8VGdKZ7KARqR96MV7VNHgWvEQifk94br5HpURpfu"
test(p, p) enc := cidenc.Default()
test("/ipfs/"+p, p) test(p, enc)
test(p+"/a", enc)
test(p+"/a/b", enc)
test(p+"/a/b/", enc)
test(p+"/a/b/c", enc)
test("/ipfs/"+p, enc)
test("/ipfs/"+p+"/b", enc)
p = "zb2rhfkM4FjkMLaUnygwhuqkETzbYXnUDf1P9MSmdNjW1w1Lk" p = "zb2rhfkM4FjkMLaUnygwhuqkETzbYXnUDf1P9MSmdNjW1w1Lk"
test(p, p) enc = cidenc.Encoder{
test("/ipfs/"+p, p) Base: mbase.MustNewEncoder(mbase.Base58BTC),
test("/ipld/"+p, p) Upgrade: true,
}
test(p, enc)
test(p+"/a", enc)
test(p+"/a/b", enc)
test(p+"/a/b/", enc)
test(p+"/a/b/c", enc)
test("/ipfs/"+p, enc)
test("/ipfs/"+p+"/b", enc)
test("/ipld/"+p, enc)
test("/ipns/"+p, enc) // even IPNS should work.
p = "bafyreifrcnyjokuw4i4ggkzg534tjlc25lqgt3ttznflmyv5fftdgu52hm" p = "bafyreifrcnyjokuw4i4ggkzg534tjlc25lqgt3ttznflmyv5fftdgu52hm"
test(p, p) enc = cidenc.Encoder{
test("/ipfs/"+p, p) Base: mbase.MustNewEncoder(mbase.Base32),
test("/ipld/"+p, p) Upgrade: true,
}
test(p, enc)
test("/ipfs/"+p, enc)
test("/ipld/"+p, enc)
// an error is also acceptable in future versions of extractCidString for _, badPath := range []string{
test("/ipfs", "/ipfs") "/ipld/",
"/ipld",
"/ipld//",
"ipld//",
"ipld",
"",
"ipns",
"/ipfs/asdf",
"/ipfs/...",
"...",
"abcdefg",
"boo",
} {
_, err := CidEncoderFromPath(badPath)
if err == nil {
t.Errorf("expected error extracting encoder from bad path: %s", badPath)
}
}
} }
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds" cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files" files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files"
ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format" ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format"
cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit" cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash" mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
) )
...@@ -231,12 +232,24 @@ var DagResolveCmd = &cmds.Command{ ...@@ -231,12 +232,24 @@ var DagResolveCmd = &cmds.Command{
}, },
Encoders: cmds.EncoderMap{ Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ResolveOutput) error { cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ResolveOutput) error {
enc, err := cmdenv.GetLowLevelCidEncoder(req) var (
if err != nil { enc cidenc.Encoder
return err err error
} )
if !cmdenv.CidBaseDefined(req) { switch {
enc, _ = cmdenv.CidEncoderFromPath(enc, req.Arguments[0]) case !cmdenv.CidBaseDefined(req):
// Not specified, check the path.
enc, err = cmdenv.CidEncoderFromPath(req.Arguments[0])
if err == nil {
break
}
// Nope, fallback on the default.
fallthrough
default:
enc, err = cmdenv.GetLowLevelCidEncoder(req)
if err != nil {
return err
}
} }
p := enc.Encode(out.Cid) p := enc.Encode(out.Cid)
if out.RemPath != "" { if out.RemPath != "" {
......
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path" path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path"
cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds" cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit" cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
) )
...@@ -82,12 +83,21 @@ Resolve the value of an IPFS DAG path: ...@@ -82,12 +83,21 @@ Resolve the value of an IPFS DAG path:
name := req.Arguments[0] name := req.Arguments[0]
recursive, _ := req.Options[resolveRecursiveOptionName].(bool) recursive, _ := req.Options[resolveRecursiveOptionName].(bool)
enc, err := cmdenv.GetCidEncoder(req) var enc cidenc.Encoder
if err != nil { switch {
return err case !cmdenv.CidBaseDefined(req):
} // Not specified, check the path.
if !cmdenv.CidBaseDefined(req) { enc, err = cmdenv.CidEncoderFromPath(name)
enc, _ = cmdenv.CidEncoderFromPath(enc, name) if err == nil {
break
}
// Nope, fallback on the default.
fallthrough
default:
enc, err = cmdenv.GetCidEncoder(req)
if err != nil {
return err
}
} }
// the case when ipns is resolved step by step // the case when ipns is resolved step by step
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment