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