package main import ( "context" "fmt" "os" "sort" "github.com/fatih/color" "github.com/ipfs/go-datastore" cbor "github.com/ipfs/go-ipld-cbor" "github.com/urfave/cli/v2" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/state" "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/lib/blockstore" "github.com/filecoin-project/specs-actors/actors/abi/big" "github.com/filecoin-project/specs-actors/actors/builtin" saacc "github.com/filecoin-project/specs-actors/actors/builtin/account" saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner" samsig "github.com/filecoin-project/specs-actors/actors/builtin/multisig" ) type addrInfo struct { Key address.Address Balance types.FIL } type msigInfo struct { Signers []address.Address Balance types.FIL Threshold uint64 } type minerInfo struct { } var genesisVerifyCmd = &cli.Command{ Name: "verify-genesis", Description: "verify some basic attributes of a genesis car file", Action: func(cctx *cli.Context) error { if !cctx.Args().Present() { return fmt.Errorf("must pass genesis car file") } bs := blockstore.NewBlockstore(datastore.NewMapDatastore()) cs := store.NewChainStore(bs, datastore.NewMapDatastore(), nil) cf := cctx.Args().Get(0) f, err := os.Open(cf) if err != nil { return xerrors.Errorf("opening the car file: %w", err) } ts, err := cs.Import(f) if err != nil { return err } fmt.Println("File loaded, now verifying state tree balances...") sm := stmgr.NewStateManager(cs) total, err := stmgr.CheckTotalFIL(context.TODO(), sm, ts) if err != nil { return err } expFIL := big.Mul(big.NewInt(int64(build.FilBase)), big.NewInt(int64(build.FilecoinPrecision))) fmt.Printf("Total FIL: %s", types.FIL(total)) if !expFIL.Equals(total) { color.Red(" INCORRECT!") } fmt.Println() cst := cbor.NewCborStore(bs) stree, err := state.LoadStateTree(cst, ts.ParentState()) if err != nil { return err } var accAddrs, msigAddrs []address.Address kaccounts := make(map[address.Address]addrInfo) kmultisigs := make(map[address.Address]msigInfo) kminers := make(map[address.Address]minerInfo) ctx := context.TODO() if err := stree.ForEach(func(addr address.Address, act *types.Actor) error { switch act.Code { case builtin.StorageMinerActorCodeID: var st saminer.State if err := cst.Get(ctx, act.Head, &st); err != nil { return err } kminers[addr] = minerInfo{} case builtin.MultisigActorCodeID: var st samsig.State if err := cst.Get(ctx, act.Head, &st); err != nil { return xerrors.Errorf("multisig actor: %w", err) } kmultisigs[addr] = msigInfo{ Balance: types.FIL(act.Balance), Signers: st.Signers, Threshold: st.NumApprovalsThreshold, } msigAddrs = append(msigAddrs, addr) case builtin.AccountActorCodeID: var st saacc.State if err := cst.Get(ctx, act.Head, &st); err != nil { log.Warn(xerrors.Errorf("account actor %s: %w", addr, err)) } kaccounts[addr] = addrInfo{ Key: st.Address, Balance: types.FIL(act.Balance.Copy()), } accAddrs = append(accAddrs, addr) } return nil }); err != nil { return err } sort.Slice(accAddrs, func(i, j int) bool { return accAddrs[i].String() < accAddrs[j].String() }) sort.Slice(msigAddrs, func(i, j int) bool { return msigAddrs[i].String() < msigAddrs[j].String() }) fmt.Println("Account Actors:") for _, acc := range accAddrs { a := kaccounts[acc] fmt.Printf("%s\t%s\t%s\n", acc, a.Key, a.Balance) } fmt.Println("Multisig Actors:") for _, acc := range msigAddrs { m := kmultisigs[acc] fmt.Printf("%s\t%s\t%d\t[", acc, m.Balance, m.Threshold) for i, s := range m.Signers { fmt.Print(s) if i != len(m.Signers)-1 { fmt.Print(",") } } fmt.Printf("]\n") } return nil }, }