Skip to content

Commit 9ad0c3b

Browse files
committed
feat(dag): add --local-only to dag export and import
- Export: only export blocks present locally; skip missing (partial CAR). --local-only with --offline. Support both binary and base58 link keys. - Import: support partial CARs; --local-only with -- pin-roots=false (error if both --pin-roots and --local-only set). - Fix cidFromBinString to accept base58 key format from link implementations. Signed-off-by: Chayan Das <01chayandas@gmail.com>
1 parent 9c894a7 commit 9ad0c3b

File tree

5 files changed

+41
-9
lines changed

5 files changed

+41
-9
lines changed

core/commands/dag/dag.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
statsOptionName = "stats"
2424
fastProvideRootOptionName = "fast-provide-root"
2525
fastProvideWaitOptionName = "fast-provide-wait"
26+
localOnlyOptionName = "local-only"
2627
)
2728

2829
// DagCmd provides a subset of commands for interacting with ipld dag objects
@@ -192,6 +193,9 @@ Note:
192193
currently present in the blockstore does not represent a complete DAG,
193194
pinning of that individual root will fail.
194195
196+
Use --local-only with --pin-roots=false when importing a partial CAR (e.g. from
197+
'dag export --local-only').
198+
195199
FAST PROVIDE OPTIMIZATION:
196200
197201
Root CIDs from CAR headers are immediately provided to the DHT in addition
@@ -213,6 +217,7 @@ Specification of CAR formats: https://ipld.io/specs/transport/car/
213217
},
214218
Options: []cmds.Option{
215219
cmds.BoolOption(pinRootsOptionName, "Pin optional roots listed in the .car headers after importing.").WithDefault(true),
220+
cmds.BoolOption(localOnlyOptionName, "Import a partial CAR without pinning roots (for CARs from dag export --local-only)."),
216221
cmds.BoolOption(silentOptionName, "No output."),
217222
cmds.BoolOption(statsOptionName, "Output stats."),
218223
cmds.BoolOption(fastProvideRootOptionName, "Immediately provide root CIDs to DHT in addition to regular queue, for faster discovery. Default: Import.FastProvideRoot"),
@@ -285,6 +290,7 @@ CAR file follows the CARv1 format: https://ipld.io/specs/transport/car/carv1/
285290
},
286291
Options: []cmds.Option{
287292
cmds.BoolOption(progressOptionName, "p", "Display progress on CLI. Defaults to true when STDERR is a TTY."),
293+
cmds.BoolOption(localOnlyOptionName, "If set, only blocks present locally are exported; missing blocks are skipped (partial CAR). Use with --offline for a local-only DAG walk."),
288294
},
289295
Run: dagExport,
290296
PostRun: cmds.PostRunMap{

core/commands/dag/export.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dagcmd
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
67
"fmt"
@@ -16,7 +17,10 @@ import (
1617
"github.com/ipfs/kubo/core/commands/cmdutils"
1718
iface "github.com/ipfs/kubo/core/coreiface"
1819
gocar "github.com/ipld/go-car/v2"
20+
"github.com/ipld/go-ipld-prime/datamodel"
21+
"github.com/ipld/go-ipld-prime/linking"
1922
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
23+
"github.com/ipld/go-ipld-prime/traversal"
2024
selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse"
2125
)
2226

@@ -27,6 +31,7 @@ func dagExport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
2731
return err
2832
}
2933

34+
localOnly, _ := req.Options[localOnlyOptionName].(bool)
3035
api, err := cmdenv.GetApi(env, req)
3136
if err != nil {
3237
return err
@@ -51,7 +56,25 @@ func dagExport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
5156
}()
5257

5358
lsys := cidlink.DefaultLinkSystem()
54-
lsys.SetReadStorage(&dagStore{dag: api.Dag(), ctx: req.Context})
59+
ds := &dagStore{dag: api.Dag(), ctx: req.Context}
60+
if localOnly {
61+
lsys.StorageReadOpener = func(lctx linking.LinkContext, lnk datamodel.Link) (io.Reader, error) {
62+
cl, ok := lnk.(cidlink.Link)
63+
if !ok {
64+
return nil, fmt.Errorf("unsupported link type: %T", lnk)
65+
}
66+
data, err := ds.Get(lctx.Ctx, cl.Cid.String())
67+
if err != nil {
68+
if ipld.IsNotFound(err) {
69+
return nil, traversal.SkipMe{}
70+
}
71+
return nil, fmt.Errorf("local block read failed: %w", err)
72+
}
73+
return bytes.NewReader(data), nil
74+
}
75+
} else {
76+
lsys.SetReadStorage(ds)
77+
}
5578

5679
// Uncomment the following to support CARv2 output.
5780
/*
@@ -189,12 +212,11 @@ func (ds *dagStore) Has(ctx context.Context, key string) (bool, error) {
189212
}
190213

191214
func cidFromBinString(key string) (cid.Cid, error) {
192-
l, k, err := cid.CidFromBytes([]byte(key))
193-
if err != nil {
194-
return cid.Undef, fmt.Errorf("dagStore: key was not a cid: %w", err)
215+
if l, k, err := cid.CidFromBytes([]byte(key)); err == nil && l == len(key) {
216+
return k, nil
195217
}
196-
if l != len(key) {
197-
return cid.Undef, fmt.Errorf("dagSore: key was not a cid: had %d bytes leftover", len(key)-l)
218+
if c, decodeErr := cid.Decode(key); decodeErr == nil {
219+
return c, nil
198220
}
199-
return k, nil
221+
return cid.Undef, fmt.Errorf("dagStore: key was not a cid (binary or base58)")
200222
}

core/commands/dag/import.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
4949
}
5050

5151
doPinRoots, _ := req.Options[pinRootsOptionName].(bool)
52+
localOnly, _ := req.Options[localOnlyOptionName].(bool)
5253

54+
if doPinRoots && localOnly {
55+
return fmt.Errorf("cannot pass both --%s and --%s", pinRootsOptionName, localOnlyOptionName)
56+
}
5357
fastProvideRoot, fastProvideRootSet := req.Options[fastProvideRootOptionName].(bool)
5458
fastProvideWait, fastProvideWaitSet := req.Options[fastProvideWaitOptionName].(bool)
5559

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,5 @@ exclude (
280280
github.com/ipfs/go-ipfs-cmds v2.0.1+incompatible
281281
github.com/libp2p/go-libp2p v6.0.23+incompatible
282282
)
283+
284+
replace github.com/ipld/go-car/v2 => ../go-car/v2

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,6 @@ github.com/ipfs/go-test v0.2.3 h1:Z/jXNAReQFtCYyn7bsv/ZqUwS6E7iIcSpJ2CuzCvnrc=
458458
github.com/ipfs/go-test v0.2.3/go.mod h1:QW8vSKkwYvWFwIZQLGQXdkt9Ud76eQXRQ9Ao2H+cA1o=
459459
github.com/ipfs/go-unixfsnode v1.10.3 h1:c8sJjuGNkxXAQH75P+f5ngPda/9T+DrboVA0TcDGvGI=
460460
github.com/ipfs/go-unixfsnode v1.10.3/go.mod h1:2Jlc7DoEwr12W+7l8Hr6C7XF4NHST3gIkqSArLhGSxU=
461-
github.com/ipld/go-car/v2 v2.16.0 h1:LWe0vmN/QcQmUU4tr34W5Nv5mNraW+G6jfN2s+ndBco=
462-
github.com/ipld/go-car/v2 v2.16.0/go.mod h1:RqFGWN9ifcXVmCrTAVnfnxiWZk1+jIx67SYhenlmL34=
463461
github.com/ipld/go-codec-dagpb v1.7.0 h1:hpuvQjCSVSLnTnHXn+QAMR0mLmb1gA6wl10LExo2Ts0=
464462
github.com/ipld/go-codec-dagpb v1.7.0/go.mod h1:rD3Zg+zub9ZnxcLwfol/OTQRVjaLzXypgy4UqHQvilM=
465463
github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8=

0 commit comments

Comments
 (0)