From 8578aaf8e7c8363cd0620a97f77cbe01fc9ff4b2 Mon Sep 17 00:00:00 2001 From: Devin Ivy Date: Fri, 20 Dec 2024 17:24:12 -0500 Subject: [PATCH] goat: add repo inspect --mst flag to display mst structure --- cmd/goat/repo.go | 102 ++++++++++++++++++++++++++++++++++++++++++++++- go.mod | 5 ++- go.sum | 2 + 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/cmd/goat/repo.go b/cmd/goat/repo.go index 97f2b81a2..f27dad99a 100644 --- a/cmd/goat/repo.go +++ b/cmd/goat/repo.go @@ -4,19 +4,26 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "os" "path/filepath" + "strings" "time" comatproto "github.com/bluesky-social/indigo/api/atproto" "github.com/bluesky-social/indigo/atproto/data" "github.com/bluesky-social/indigo/atproto/syntax" + "github.com/bluesky-social/indigo/mst" "github.com/bluesky-social/indigo/repo" + "github.com/bluesky-social/indigo/util" "github.com/bluesky-social/indigo/xrpc" "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" + ipld "github.com/ipfs/go-ipld-format" "github.com/urfave/cli/v2" + "github.com/xlab/treeprint" ) var cmdRepo = &cli.Command{ @@ -55,8 +62,14 @@ var cmdRepo = &cli.Command{ Name: "inspect", Usage: "show commit metadata from CAR file", ArgsUsage: ``, - Flags: []cli.Flag{}, - Action: runRepoInspect, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "mst", + Aliases: []string{"m"}, + Usage: "print the MST", + }, + }, + Action: runRepoInspect, }, &cli.Command{ Name: "unpack", @@ -169,6 +182,7 @@ func runRepoList(cctx *cli.Context) error { func runRepoInspect(cctx *cli.Context) error { ctx := context.Background() carPath := cctx.Args().First() + printMst := cctx.Bool("mst") if carPath == "" { return fmt.Errorf("need to provide path to CAR file as argument") } @@ -183,6 +197,23 @@ func runRepoInspect(cctx *cli.Context) error { return err } + if printMst { + dataCid := r.DataCid() + cst := util.CborStore(r.Blockstore()) + err, exists := nodeExists(ctx, cst, dataCid) + if err != nil { + return err + } + tree := treeprint.NewWithRoot(displayCID(&dataCid) + displayExists(exists)) + if exists { + if err := walkMST(ctx, cst, dataCid, tree); err != nil { + return err + } + } + fmt.Println(tree.String()) + return nil + } + sc := r.SignedCommit() fmt.Printf("ATProto Repo Spec Version: %d\n", sc.Version) fmt.Printf("DID: %s\n", sc.Did) @@ -194,6 +225,73 @@ func runRepoInspect(cctx *cli.Context) error { return nil } +func walkMST(ctx context.Context, cst *cbor.BasicIpldStore, cid cid.Cid, tree treeprint.Tree) error { + var node mst.NodeData + if err := cst.Get(ctx, cid, &node); err != nil { + return err + } + if node.Left != nil { + err, exists := nodeExists(ctx, cst, *node.Left) + if err != nil { + return err + } + subtree := tree.AddBranch(displayCID(node.Left) + displayExists(exists)) + if exists { + if err := walkMST(ctx, cst, *node.Left, subtree); err != nil { + return err + } + } + } + for _, entry := range node.Entries { + err, exists := nodeExists(ctx, cst, entry.Val) + if err != nil { + return err + } + tree.AddNode(displayEntryVal(&entry) + displayExists(exists)) + if entry.Tree != nil { + err, exists := nodeExists(ctx, cst, *entry.Tree) + if err != nil { + return err + } + subtree := tree.AddBranch(displayCID(entry.Tree) + displayExists(exists)) + if exists { + if err := walkMST(ctx, cst, *entry.Tree, subtree); err != nil { + return err + } + } + } + } + return nil +} + +func displayEntryVal(entry *mst.TreeEntry) string { + key := string(entry.KeySuffix) + return strings.Repeat(".", int(entry.PrefixLen)) + key + " " + displayCID(&entry.Val) +} + +func displayCID(cid *cid.Cid) string { + s := cid.String() + cut := string(s[len(s)-7:]) + return "(" + cut + ")" +} + +func displayExists(exists bool) string { + if exists { + return "" + } + return " ✗" +} + +func nodeExists(ctx context.Context, cst *cbor.BasicIpldStore, cid cid.Cid) (error, bool) { + if _, err := cst.Blocks.Get(ctx, cid); err != nil { + if errors.Is(err, ipld.ErrNotFound{}) { + return nil, false + } + return err, false + } + return nil, true +} + func runRepoUnpack(cctx *cli.Context) error { ctx := context.Background() carPath := cctx.Args().First() diff --git a/go.mod b/go.mod index ff1c8ee77..a3715007a 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/ipfs/go-ipld-cbor v0.1.0 github.com/ipfs/go-ipld-format v0.6.0 github.com/ipfs/go-libipfs v0.7.0 + github.com/ipfs/go-log/v2 v2.5.1 github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4 github.com/ipld/go-car/v2 v2.13.1 github.com/jackc/pgx/v5 v5.5.0 @@ -55,6 +56,7 @@ require ( github.com/urfave/cli/v2 v2.25.7 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e github.com/whyrusleeping/go-did v0.0.0-20230824162731-404d1707d5d6 + github.com/xlab/treeprint v1.2.0 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 go.opentelemetry.io/otel v1.21.0 @@ -64,6 +66,7 @@ require ( go.opentelemetry.io/otel/sdk v1.21.0 go.opentelemetry.io/otel/trace v1.21.0 go.uber.org/automaxprocs v1.5.3 + go.uber.org/zap v1.26.0 golang.org/x/crypto v0.21.0 golang.org/x/sync v0.7.0 golang.org/x/text v0.14.0 @@ -89,7 +92,6 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/ipfs/go-log v1.0.5 // indirect - github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/klauspost/compress v1.17.3 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -103,7 +105,6 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect - go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect ) diff --git a/go.sum b/go.sum index b72fdce5b..5f651b904 100644 --- a/go.sum +++ b/go.sum @@ -651,6 +651,8 @@ github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/whyrusleeping/go-did v0.0.0-20230824162731-404d1707d5d6 h1:yJ9/LwIGIk/c0CdoavpC9RNSGSruIspSZtxG3Nnldic= github.com/whyrusleeping/go-did v0.0.0-20230824162731-404d1707d5d6/go.mod h1:39U9RRVr4CKbXpXYopWn+FSH5s+vWu6+RmguSPWAq5s= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=