diff --git a/cmd/lotus-shed/invariants.go b/cmd/lotus-shed/invariants.go new file mode 100644 index 000000000..9fb67393d --- /dev/null +++ b/cmd/lotus-shed/invariants.go @@ -0,0 +1,142 @@ +package main + +import ( + "context" + "fmt" + "io" + "strconv" + "time" + + "github.com/ipfs/go-cid" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/builtin" + v8 "github.com/filecoin-project/go-state-types/builtin/v8" + v9 "github.com/filecoin-project/go-state-types/builtin/v9" + + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/node/repo" + "github.com/filecoin-project/lotus/storage/sealer/ffiwrapper" +) + +var invariantsCmd = &cli.Command{ + Name: "check-invariants", + Description: "Check state invariants", + ArgsUsage: "[StateRootCid, height]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + }, + }, + Action: func(cctx *cli.Context) error { + ctx := context.TODO() + + if cctx.NArg() != 2 { + return lcli.IncorrectNumArgs(cctx) + } + + stateRootCid, err := cid.Decode(cctx.Args().Get(0)) + if err != nil { + return fmt.Errorf("failed to parse state root cid: %w", err) + } + + epoch, err := strconv.ParseInt(cctx.Args().Get(1), 10, 64) + if err != nil { + return fmt.Errorf("failed to parse epoch: %w", err) + } + + fsrepo, err := repo.NewFS(cctx.String("repo")) + if err != nil { + return err + } + + lkrepo, err := fsrepo.Lock(repo.FullNode) + if err != nil { + return err + } + + defer lkrepo.Close() //nolint:errcheck + + bs, err := lkrepo.Blockstore(ctx, repo.UniversalBlockstore) + if err != nil { + return fmt.Errorf("failed to open blockstore: %w", err) + } + + defer func() { + if c, ok := bs.(io.Closer); ok { + if err := c.Close(); err != nil { + log.Warnf("failed to close blockstore: %s", err) + } + } + }() + + mds, err := lkrepo.Datastore(context.Background(), "/metadata") + if err != nil { + return err + } + + cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil) + defer cs.Close() //nolint:errcheck + + sm, err := stmgr.NewStateManager(cs, filcns.NewTipSetExecutor(), vm.Syscalls(ffiwrapper.ProofVerifier), filcns.DefaultUpgradeSchedule(), nil) + if err != nil { + return err + } + + nv := sm.GetNetworkVersion(ctx, abi.ChainEpoch(epoch)) + fmt.Println("Network Version ", nv) + + av, err := actorstypes.VersionForNetwork(nv) + fmt.Println("Actors Version ", av) + + actorCodeCids, err := actors.GetActorCodeIDs(av) + if err != nil { + return err + } + + actorStore := store.ActorStore(ctx, blockstore.NewTieredBstore(bs, blockstore.NewMemorySync())) + + // Load the state root. + var stateRoot types.StateRoot + if err := actorStore.Get(ctx, stateRootCid, &stateRoot); err != nil { + return xerrors.Errorf("failed to decode state root: %w", err) + } + + actorTree, err := builtin.LoadTree(actorStore, stateRoot.Actors) + + startTime := time.Now() + + var messages *builtin.MessageAccumulator + switch av { + case actorstypes.Version8: + messages, err = v8.CheckStateInvariants(actorTree, abi.ChainEpoch(epoch), actorCodeCids) + if err != nil { + return xerrors.Errorf("checking state invariants: %w", err) + } + case actorstypes.Version9: + messages, err = v9.CheckStateInvariants(actorTree, abi.ChainEpoch(epoch), actorCodeCids) + if err != nil { + return xerrors.Errorf("checking state invariants: %w", err) + } + } + + fmt.Println("completed, took ", time.Since(startTime)) + + for _, message := range messages.Messages() { + fmt.Println("got the following error: ", message) + } + + return nil + }, +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 0f10a617f..c661b04ef 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -73,6 +73,7 @@ func main() { migrationsCmd, diffCmd, itestdCmd, + invariantsCmd, } app := &cli.App{ diff --git a/cmd/lotus-shed/migrations.go b/cmd/lotus-shed/migrations.go index fd62da589..415faa16d 100644 --- a/cmd/lotus-shed/migrations.go +++ b/cmd/lotus-shed/migrations.go @@ -170,14 +170,15 @@ var migrationsCmd = &cli.Command{ newCid2) } - fmt.Println("new cid", newCid2) + fmt.Println("migration height ", blk.Height-1) + fmt.Println("new cid ", newCid2) fmt.Println("completed premigration 1, took ", preMigration1Time) fmt.Println("completed premigration 2, took ", preMigration2Time) fmt.Println("completed round actual (with cache), took ", cachedMigrationTime) fmt.Println("completed round actual (without cache), took ", uncachedMigrationTime) if cctx.Bool("check-invariants") { - err = checkMigrationInvariants(ctx, blk.ParentStateRoot, newCid1, bs, blk.Height-1) + err = checkMigrationInvariants(ctx, blk.ParentStateRoot, newCid2, bs, blk.Height-1) if err != nil { return err } @@ -187,16 +188,16 @@ var migrationsCmd = &cli.Command{ }, } -func checkMigrationInvariants(ctx context.Context, v8StateRoot cid.Cid, v9StateRoot cid.Cid, bs blockstore.Blockstore, epoch abi.ChainEpoch) error { +func checkMigrationInvariants(ctx context.Context, v8StateRootCid cid.Cid, v9StateRootCid cid.Cid, bs blockstore.Blockstore, epoch abi.ChainEpoch) error { actorStore := store.ActorStore(ctx, bs) startTime := time.Now() - stateTreeV8, err := state.LoadStateTree(actorStore, v8StateRoot) + stateTreeV8, err := state.LoadStateTree(actorStore, v8StateRootCid) if err != nil { return err } - stateTreeV9, err := state.LoadStateTree(actorStore, v9StateRoot) + stateTreeV9, err := state.LoadStateTree(actorStore, v9StateRootCid) if err != nil { return err } @@ -217,8 +218,8 @@ func checkMigrationInvariants(ctx context.Context, v8StateRoot cid.Cid, v9StateR } // Load the state root. - var stateRoot types.StateRoot - if err := actorStore.Get(ctx, v9StateRoot, &stateRoot); err != nil { + var v9stateRoot types.StateRoot + if err := actorStore.Get(ctx, v9StateRootCid, &v9stateRoot); err != nil { return xerrors.Errorf("failed to decode state root: %w", err) } @@ -227,8 +228,8 @@ func checkMigrationInvariants(ctx context.Context, v8StateRoot cid.Cid, v9StateR return err } - actorTree, err := builtin.LoadTree(actorStore, stateRoot.Actors) - messages, err := v9.CheckStateInvariants(actorTree, epoch, actorCodeCids) + v9actorTree, err := builtin.LoadTree(actorStore, v9stateRoot.Actors) + messages, err := v9.CheckStateInvariants(v9actorTree, epoch, actorCodeCids) if err != nil { return xerrors.Errorf("checking state invariants: %w", err) }