diff --git a/cmd/lotus-shed/diff.go b/cmd/lotus-shed/diff.go new file mode 100644 index 000000000..bcaa04122 --- /dev/null +++ b/cmd/lotus-shed/diff.go @@ -0,0 +1,104 @@ +package main + +import ( + "fmt" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" +) + +var diffCmd = &cli.Command{ + Name: "diff", + Usage: "diff state objects", + Subcommands: []*cli.Command{diffStateTrees}, +} + +var diffStateTrees = &cli.Command{ + Name: "state-trees", + Usage: "diff two state-trees", + ArgsUsage: " ", + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + + defer closer() + ctx := lcli.ReqContext(cctx) + + if cctx.NArg() != 2 { + return xerrors.Errorf("expected two state-tree roots") + } + + argA := cctx.Args().Get(1) + rootA, err := cid.Parse(argA) + if err != nil { + return xerrors.Errorf("first state-tree root (%q) is not a CID: %w", argA, err) + } + argB := cctx.Args().Get(1) + rootB, err := cid.Parse(argB) + if err != nil { + return xerrors.Errorf("second state-tree root (%q) is not a CID: %w", argB, err) + } + + if rootA == rootB { + fmt.Println("state trees do not differ") + return nil + } + + changedB, err := api.StateChangedActors(ctx, rootA, rootB) + if err != nil { + return err + } + changedA, err := api.StateChangedActors(ctx, rootB, rootA) + if err != nil { + return err + } + + diff := func(stateA, stateB types.Actor) { + if stateB.Code != stateA.Code { + fmt.Printf(" code: %s != %s\n", stateA.Code, stateB.Code) + } + if stateB.Head != stateA.Head { + fmt.Printf(" state: %s != %s\n", stateA.Head, stateB.Head) + } + if stateB.Nonce != stateA.Nonce { + fmt.Printf(" nonce: %d != %d\n", stateA.Nonce, stateB.Nonce) + } + if !stateB.Balance.Equals(stateA.Balance) { + fmt.Printf(" balance: %s != %s\n", stateA.Balance, stateB.Balance) + } + } + + fmt.Printf("state differences between %s (first) and %s (second):\n\n", rootA, rootB) + for addr, stateA := range changedA { + fmt.Println(addr) + stateB, ok := changedB[addr] + if ok { + diff(stateA, stateB) + continue + } else { + fmt.Printf(" actor does not exist in second state-tree (%s)\n", rootB) + } + fmt.Println() + delete(changedB, addr) + } + for addr, stateB := range changedB { + fmt.Println(addr) + stateA, ok := changedA[addr] + if ok { + diff(stateA, stateB) + continue + } else { + fmt.Printf(" actor does not exist in first state-tree (%s)\n", rootA) + } + fmt.Println() + } + return nil + }, +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 9bcea7224..45fd24e18 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -68,6 +68,7 @@ func main() { sendCsvCmd, terminationsCmd, migrationsCmd, + diffCmd, } app := &cli.App{