Skip to content

Commit f96bd5f

Browse files
committed
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 <[email protected]>
1 parent f31e6b6 commit f96bd5f

File tree

4 files changed

+121
-49
lines changed

4 files changed

+121
-49
lines changed

core/commands/cmdenv/cidbase.go

+36-22
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package cmdenv
22

33
import (
4+
"fmt"
45
"strings"
56

7+
cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
68
cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
79
cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
810
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
@@ -59,32 +61,44 @@ func CidBaseDefined(req *cmds.Request) bool {
5961
// CidEncoderFromPath creates a new encoder that is influenced from
6062
// the encoded Cid in a Path. For CidV0 the multibase from the base
6163
// 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` or `/namespace/CidLike/...`.
68+
//
69+
// For example:
70+
//
71+
// * Qm...
72+
// * /ipfs/Qm...
73+
// * /ipns/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
74+
// * /bzz/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
75+
func CidEncoderFromPath(p string) (cidenc.Encoder, error) {
76+
components := strings.SplitN(p, "/", 4)
77+
78+
var maybeCid string
79+
if len(components) == 1 {
80+
maybeCid = components[0]
81+
} else if len(components) < 3 {
82+
return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
83+
} else {
84+
maybeCid = components[2]
6985
}
70-
e, err := mbase.NewEncoder(mbase.Encoding(v[0]))
86+
c, err := cid.Decode(maybeCid)
7187
if err != nil {
72-
return enc, err
88+
// Ok, not a CID-like thing. Keep the current encoder.
89+
return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
7390
}
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]
91+
if c.Version() == 0 {
92+
// Version 0, use the base58 non-upgrading encoder.
93+
return cidenc.Default(), nil
8194
}
82-
return str
83-
}
8495

85-
func cidVer(v string) int {
86-
if len(v) == 46 && v[:2] == "Qm" {
87-
return 0
96+
// Version 1+, extract multibase encoding.
97+
encoding, _, err := mbase.Decode(maybeCid)
98+
if err != nil {
99+
// This should be impossible, we've already decoded the cid.
100+
panic(fmt.Sprintf("BUG: failed to get multibase decoder for CID %s", maybeCid))
88101
}
89-
return 1
102+
103+
return cidenc.Encoder{Base: mbase.MustNewEncoder(encoding), Upgrade: true}, nil
90104
}

core/commands/cmdenv/cidbase_test.go

+50-15
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,64 @@ package cmdenv
22

33
import (
44
"testing"
5+
6+
cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
7+
mbase "gx/ipfs/QmekxXDhCxCJRNuzmHreuaT3BsuJcsjcXWNrtV9C8DRHtd/go-multibase"
58
)
69

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)
1218
}
1319
}
1420
p := "QmRqVG8VGdKZ7KARqR96MV7VNHgWvEQifk94br5HpURpfu"
15-
test(p, p)
16-
test("/ipfs/"+p, p)
21+
enc := cidenc.Default()
22+
test(p, enc)
23+
test("/ipfs/"+p, enc)
24+
test("/ipfs/"+p+"/b", enc)
1725

1826
p = "zb2rhfkM4FjkMLaUnygwhuqkETzbYXnUDf1P9MSmdNjW1w1Lk"
19-
test(p, p)
20-
test("/ipfs/"+p, p)
21-
test("/ipld/"+p, p)
27+
enc = cidenc.Encoder{
28+
Base: mbase.MustNewEncoder(mbase.Base58BTC),
29+
Upgrade: true,
30+
}
31+
test(p, enc)
32+
test("/ipfs/"+p, enc)
33+
test("/ipfs/"+p+"/b", enc)
34+
test("/ipld/"+p, enc)
35+
test("/ipns/"+p, enc) // even IPNS should work.
2236

2337
p = "bafyreifrcnyjokuw4i4ggkzg534tjlc25lqgt3ttznflmyv5fftdgu52hm"
24-
test(p, p)
25-
test("/ipfs/"+p, p)
26-
test("/ipld/"+p, p)
38+
enc = cidenc.Encoder{
39+
Base: mbase.MustNewEncoder(mbase.Base32),
40+
Upgrade: true,
41+
}
42+
test(p, enc)
43+
test("/ipfs/"+p, enc)
44+
test("/ipld/"+p, enc)
2745

28-
// an error is also acceptable in future versions of extractCidString
29-
test("/ipfs", "/ipfs")
46+
for _, badPath := range []string{
47+
"/ipld/",
48+
"/ipld",
49+
"/ipld//",
50+
"ipld//",
51+
"ipld",
52+
"",
53+
"ipns",
54+
"/ipfs/asdf",
55+
"/ipfs/...",
56+
"...",
57+
"abcdefg",
58+
"boo",
59+
} {
60+
_, err := CidEncoderFromPath(badPath)
61+
if err == nil {
62+
t.Errorf("expected error extracting encoder from bad path: %s", badPath)
63+
}
64+
}
3065
}

core/commands/dag/dag.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
1515
files "gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files"
1616
ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format"
17+
cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
1718
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
1819
mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"
1920
)
@@ -231,12 +232,24 @@ var DagResolveCmd = &cmds.Command{
231232
},
232233
Encoders: cmds.EncoderMap{
233234
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+
}
240253
}
241254
p := enc.Encode(out.Cid)
242255
if out.RemPath != "" {

core/commands/resolve.go

+16-6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
path "gx/ipfs/QmNYPETsdAu2uQ1k9q9S1jYEGURaLHV6cbYRSVFVRftpF8/go-path"
1717

1818
cmds "gx/ipfs/QmWGm4AbZEbnmdgVTza52MSNpEmBdFVqzmAysRbjrRyGbH/go-ipfs-cmds"
19+
cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
1920
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
2021
)
2122

@@ -82,12 +83,21 @@ Resolve the value of an IPFS DAG path:
8283
name := req.Arguments[0]
8384
recursive, _ := req.Options[resolveRecursiveOptionName].(bool)
8485

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+
}
91101
}
92102

93103
// the case when ipns is resolved step by step

0 commit comments

Comments
 (0)