2022-02-11 18:09:40 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2022-10-20 17:10:45 +00:00
|
|
|
"context"
|
2022-02-11 18:09:40 +00:00
|
|
|
"fmt"
|
2022-10-20 17:10:45 +00:00
|
|
|
"io"
|
2022-02-11 18:09:40 +00:00
|
|
|
|
2022-06-14 15:00:51 +00:00
|
|
|
"github.com/ipfs/go-cid"
|
2022-02-11 18:09:40 +00:00
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
2022-10-20 17:10:45 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
|
|
miner9 "github.com/filecoin-project/go-state-types/builtin/v9/miner"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/chain/store"
|
2022-02-11 18:09:40 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
lcli "github.com/filecoin-project/lotus/cli"
|
2022-10-20 17:10:45 +00:00
|
|
|
"github.com/filecoin-project/lotus/node/repo"
|
2022-02-11 18:09:40 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var diffCmd = &cli.Command{
|
2022-10-20 17:10:45 +00:00
|
|
|
Name: "diff",
|
|
|
|
Usage: "diff state objects",
|
|
|
|
Subcommands: []*cli.Command{
|
|
|
|
diffStateTrees,
|
|
|
|
diffMinerStates,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var diffMinerStates = &cli.Command{
|
|
|
|
Name: "miner-states",
|
|
|
|
Usage: "diff two miner-states",
|
|
|
|
ArgsUsage: "<stateCidA> <stateCidB>",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
2023-01-27 16:20:01 +00:00
|
|
|
Name: "repo",
|
|
|
|
Value: "~/.lotus",
|
|
|
|
EnvVars: []string{"LOTUS_PATH"},
|
2022-10-20 17:10:45 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
ctx := context.TODO()
|
|
|
|
|
|
|
|
if cctx.NArg() != 2 {
|
|
|
|
return lcli.IncorrectNumArgs(cctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
stCidA, err := cid.Decode(cctx.Args().First())
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to parse input: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
stCidB, err := cid.Decode(cctx.Args().Get(1))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to parse input: %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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
actorStore := store.ActorStore(ctx, bs)
|
|
|
|
|
|
|
|
var minerStA miner9.State
|
|
|
|
if err = actorStore.Get(ctx, stCidA, &minerStA); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var minerStB miner9.State
|
|
|
|
if err = actorStore.Get(ctx, stCidB, &minerStB); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(minerStA.Deadlines)
|
|
|
|
fmt.Println(minerStB.Deadlines)
|
|
|
|
|
|
|
|
minerDeadlinesA, err := minerStA.LoadDeadlines(actorStore)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
minerDeadlinesB, err := minerStB.LoadDeadlines(actorStore)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, dACid := range minerDeadlinesA.Due {
|
|
|
|
dBCid := minerDeadlinesB.Due[i]
|
|
|
|
if dACid != dBCid {
|
|
|
|
fmt.Println("Difference at index ", i, dACid, " != ", dBCid)
|
|
|
|
dA, err := minerDeadlinesA.LoadDeadline(actorStore, uint64(i))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
dB, err := minerDeadlinesB.LoadDeadline(actorStore, uint64(i))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if dA.SectorsSnapshot != dB.SectorsSnapshot {
|
|
|
|
fmt.Println("They differ at Sectors snapshot ", dA.SectorsSnapshot, " != ", dB.SectorsSnapshot)
|
|
|
|
|
|
|
|
sectorsSnapshotA, err := miner9.LoadSectors(actorStore, dA.SectorsSnapshot)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sectorsSnapshotB, err := miner9.LoadSectors(actorStore, dB.SectorsSnapshot)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if sectorsSnapshotA.Length() != sectorsSnapshotB.Length() {
|
|
|
|
fmt.Println("sector snapshots have different lengts!")
|
|
|
|
}
|
|
|
|
|
|
|
|
var infoA miner9.SectorOnChainInfo
|
|
|
|
err = sectorsSnapshotA.ForEach(&infoA, func(i int64) error {
|
|
|
|
infoB, ok, err := sectorsSnapshotB.Get(abi.SectorNumber(i))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
fmt.Println(i, "isn't found in infoB!!")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !infoA.DealWeight.Equals(infoB.DealWeight) {
|
|
|
|
fmt.Println("Deal Weights differ! ", infoA.DealWeight, infoB.DealWeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !infoA.VerifiedDealWeight.Equals(infoB.VerifiedDealWeight) {
|
|
|
|
fmt.Println("Verified Deal Weights differ! ", infoA.VerifiedDealWeight, infoB.VerifiedDealWeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
infoStrA := fmt.Sprint(infoA)
|
|
|
|
infoStrB := fmt.Sprint(*infoB)
|
|
|
|
if infoStrA != infoStrB {
|
|
|
|
fmt.Println(infoStrA)
|
|
|
|
fmt.Println(infoStrB)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
2022-02-11 18:09:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var diffStateTrees = &cli.Command{
|
|
|
|
Name: "state-trees",
|
|
|
|
Usage: "diff two state-trees",
|
|
|
|
ArgsUsage: "<state-tree-a> <state-tree-b>",
|
|
|
|
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 {
|
2022-09-14 19:38:15 +00:00
|
|
|
return lcli.IncorrectNumArgs(cctx)
|
2022-02-11 18:09:40 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 01:03:45 +00:00
|
|
|
argA := cctx.Args().Get(0)
|
2022-02-11 18:09:40 +00:00
|
|
|
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
|
|
|
|
},
|
|
|
|
}
|