@cryptotaxi247 / kubo / commits / 19d8f624e

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: Steven Allen <steven@stebalien.com>

Steven Allen committed Jan 21, 2019 at 08:59 UTC 19d8f624ed180e229b57b560764eb3a2875bfaed
4 files changed +132 -49
core/commands/cmdenv/cidbase.go
+39 -22
@@ -1,8 +1,10 @@
1 package cmdenv
2
3 import (
4 + "fmt"
5 "strings"
6
7 + cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
8 cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
9 cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
10 cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
@@ -59,32 +61,47 @@ func CidBaseDefined(req *cmds.Request) bool {
61 // CidEncoderFromPath creates a new encoder that is influenced from
62 // the encoded Cid in a Path. For CidV0 the multibase from the base
63 // encoder is used and automatic upgrades are disabled. For CidV1 the
62 -// multibase from the CID is used and upgrades are eneabled. On error
63 -// the base encoder is returned. If you don't care about the error
64 -// condition, it is safe to ignore the error returned.
65 -func CidEncoderFromPath(enc cidenc.Encoder, p string) (cidenc.Encoder, error) {
66 - v := extractCidString(p)
67 - if cidVer(v) == 0 {
68 - return cidenc.Encoder{Base: enc.Base, Upgrade: false}, nil
64 +// multibase from the CID is used and upgrades are enabled.
65 +//
66 +// This logic is intentionally fuzzy and will match anything of the form
67 +// `CidLike`, `CidLike/...`, or `/namespace/CidLike/...`.
68 +//
69 +// For example:
70 +//
71 +// * Qm...
72 +// * Qm.../...
73 +// * /ipfs/Qm...
74 +// * /ipns/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
75 +// * /bzz/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
76 +func CidEncoderFromPath(p string) (cidenc.Encoder, error) {
77 + components := strings.SplitN(p, "/", 4)
78 +
79 + var maybeCid string
80 + if components[0] != "" {
81 + // No leading slash, first component is likely CID-like.
82 + maybeCid = components[0]
83 + } else if len(components) < 3 {
84 + // Not enough components to include a CID.
85 + return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
86 + } else {
87 + maybeCid = components[2]
88 }
70 - e, err := mbase.NewEncoder(mbase.Encoding(v[0]))
89 + c, err := cid.Decode(maybeCid)
90 if err != nil {
72 - return enc, err
91 + // Ok, not a CID-like thing. Keep the current encoder.
92 + return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
93 }
74 - return cidenc.Encoder{Base: e, Upgrade: true}, nil
75 -}
76 -
77 -func extractCidString(str string) string {
78 - parts := strings.Split(str, "/")
79 - if len(parts) > 2 && (parts[1] == "ipfs" || parts[1] == "ipld") {
80 - return parts[2]
94 + if c.Version() == 0 {
95 + // Version 0, use the base58 non-upgrading encoder.
96 + return cidenc.Default(), nil
97 }
82 - return str
83 -}
98
85 -func cidVer(v string) int {
86 - if len(v) == 46 && v[:2] == "Qm" {
87 - return 0
99 + // Version 1+, extract multibase encoding.
100 + encoding, _, err := mbase.Decode(maybeCid)
101 + if err != nil {
102 + // This should be impossible, we've already decoded the cid.
103 + panic(fmt.Sprintf("BUG: failed to get multibase decoder for CID %s", maybeCid))
104 }
89 - return 1
105 +
106 + return cidenc.Encoder{Base: mbase.MustNewEncoder(encoding), Upgrade: true}, nil
107 }
core/commands/cmdenv/cidbase_test.go
+58 -15
@@ -2,29 +2,72 @@ package cmdenv
2
3 import (
4 "testing"
5 +
6 + cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
7 + mbase "gx/ipfs/QmekxXDhCxCJRNuzmHreuaT3BsuJcsjcXWNrtV9C8DRHtd/go-multibase"
8 )
9
7 -func TestExtractCidString(t *testing.T) {
8 - test := func(path string, cid string) {
9 - res := extractCidString(path)
10 - if res != cid {
11 - t.Errorf("extractCidString(%s) failed: expected '%s' but got '%s'", path, cid, res)
10 +func TestEncoderFromPath(t *testing.T) {
11 + test := func(path string, expected cidenc.Encoder) {
12 + actual, err := CidEncoderFromPath(path)
13 + if err != nil {
14 + t.Error(err)
15 + }
16 + if actual != expected {
17 + t.Errorf("CidEncoderFromPath(%s) failed: expected %#v but got %#v", path, expected, actual)
18 }
19 }
20 p := "QmRqVG8VGdKZ7KARqR96MV7VNHgWvEQifk94br5HpURpfu"
15 - test(p, p)
16 - test("/ipfs/"+p, p)
21 + enc := cidenc.Default()
22 + test(p, enc)
23 + test(p+"/a", enc)
24 + test(p+"/a/b", enc)
25 + test(p+"/a/b/", enc)
26 + test(p+"/a/b/c", enc)
27 + test("/ipfs/"+p, enc)
28 + test("/ipfs/"+p+"/b", enc)
29
30 p = "zb2rhfkM4FjkMLaUnygwhuqkETzbYXnUDf1P9MSmdNjW1w1Lk"
19 - test(p, p)
20 - test("/ipfs/"+p, p)
21 - test("/ipld/"+p, p)
31 + enc = cidenc.Encoder{
32 + Base: mbase.MustNewEncoder(mbase.Base58BTC),
33 + Upgrade: true,
34 + }
35 + test(p, enc)
36 + test(p+"/a", enc)
37 + test(p+"/a/b", enc)
38 + test(p+"/a/b/", enc)
39 + test(p+"/a/b/c", enc)
40 + test("/ipfs/"+p, enc)
41 + test("/ipfs/"+p+"/b", enc)
42 + test("/ipld/"+p, enc)
43 + test("/ipns/"+p, enc) // even IPNS should work.
44
45 p = "bafyreifrcnyjokuw4i4ggkzg534tjlc25lqgt3ttznflmyv5fftdgu52hm"
24 - test(p, p)
25 - test("/ipfs/"+p, p)
26 - test("/ipld/"+p, p)
46 + enc = cidenc.Encoder{
47 + Base: mbase.MustNewEncoder(mbase.Base32),
48 + Upgrade: true,
49 + }
50 + test(p, enc)
51 + test("/ipfs/"+p, enc)
52 + test("/ipld/"+p, enc)
53
28 - // an error is also acceptable in future versions of extractCidString
29 - test("/ipfs", "/ipfs")
54 + for _, badPath := range []string{
55 + "/ipld/",
56 + "/ipld",
57 + "/ipld//",
58 + "ipld//",
59 + "ipld",
60 + "",
61 + "ipns",
62 + "/ipfs/asdf",
63 + "/ipfs/...",
64 + "...",
65 + "abcdefg",
66 + "boo",
67 + } {
68 + _, err := CidEncoderFromPath(badPath)
69 + if err == nil {
70 + t.Errorf("expected error extracting encoder from bad path: %s", badPath)
71 + }
72 + }
73 }
core/commands/dag/dag.go
+19 -6
@@ -14,6 +14,7 @@ import (
14 cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
15 files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files"
16 ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format"
17 + cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
18 cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
19 mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
20 )
@@ -231,12 +232,24 @@ var DagResolveCmd = &cmds.Command{
232 },
233 Encoders: cmds.EncoderMap{
234 cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ResolveOutput) error {
234 - enc, err := cmdenv.GetLowLevelCidEncoder(req)
235 - if err != nil {
236 - return err
237 - }
238 - if !cmdenv.CidBaseDefined(req) {
239 - enc, _ = cmdenv.CidEncoderFromPath(enc, req.Arguments[0])
235 + var (
236 + enc cidenc.Encoder
237 + err error
238 + )
239 + switch {
240 + case !cmdenv.CidBaseDefined(req):
241 + // Not specified, check the path.
242 + enc, err = cmdenv.CidEncoderFromPath(req.Arguments[0])
243 + if err == nil {
244 + break
245 + }
246 + // Nope, fallback on the default.
247 + fallthrough
248 + default:
249 + enc, err = cmdenv.GetLowLevelCidEncoder(req)
250 + if err != nil {
251 + return err
252 + }
253 }
254 p := enc.Encode(out.Cid)
255 if out.RemPath != "" {
core/commands/resolve.go
+16 -6
@@ -16,6 +16,7 @@ import (
16 path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path"
17
18 cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
19 + cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
20 cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
21 )
22
@@ -82,12 +83,21 @@ Resolve the value of an IPFS DAG path:
83 name := req.Arguments[0]
84 recursive, _ := req.Options[resolveRecursiveOptionName].(bool)
85
85 - enc, err := cmdenv.GetCidEncoder(req)
86 - if err != nil {
87 - return err
88 - }
89 - if !cmdenv.CidBaseDefined(req) {
90 - enc, _ = cmdenv.CidEncoderFromPath(enc, name)
86 + var enc cidenc.Encoder
87 + switch {
88 + case !cmdenv.CidBaseDefined(req):
89 + // Not specified, check the path.
90 + enc, err = cmdenv.CidEncoderFromPath(name)
91 + if err == nil {
92 + break
93 + }
94 + // Nope, fallback on the default.
95 + fallthrough
96 + default:
97 + enc, err = cmdenv.GetCidEncoder(req)
98 + if err != nil {
99 + return err
100 + }
101 }
102
103 // the case when ipns is resolved step by step