diff --git a/api/api_full.go b/api/api_full.go index 6d2d0c7b5..0e5622f4c 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -176,6 +176,9 @@ type FullNode interface { // the reason. SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error) + // SyncValidateTipset indicates whether the provided tipset is valid or not + SyncValidateTipset(ctx context.Context, tsk types.TipSetKey) (bool, error) + // MethodGroup: Mpool // The Mpool methods are for interacting with the message pool. The message pool // manages all incoming and outgoing 'messages' going over the network. diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index d5b6950ad..cc2b8b5b5 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -112,6 +112,7 @@ type FullNodeStruct struct { SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"` SyncUnmarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"` SyncCheckBad func(ctx context.Context, bcid cid.Cid) (string, error) `perm:"read"` + SyncValidateTipset func(ctx context.Context, tsk types.TipSetKey) (bool, error) `perm:"read"` MpoolGetConfig func(context.Context) (*types.MpoolConfig, error) `perm:"read"` MpoolSetConfig func(context.Context, *types.MpoolConfig) error `perm:"write"` @@ -735,6 +736,10 @@ func (c *FullNodeStruct) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string return c.Internal.SyncCheckBad(ctx, bcid) } +func (c *FullNodeStruct) SyncValidateTipset(ctx context.Context, tsk types.TipSetKey) (bool, error) { + return c.Internal.SyncValidateTipset(ctx, tsk) +} + func (c *FullNodeStruct) StateNetworkName(ctx context.Context) (dtypes.NetworkName, error) { return c.Internal.StateNetworkName(ctx) } diff --git a/chain/store/store.go b/chain/store/store.go index 6c93db7a0..0806fb921 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -286,6 +286,16 @@ func (cs *ChainStore) MarkBlockAsValidated(ctx context.Context, blkid cid.Cid) e return nil } +func (cs *ChainStore) UnmarkBlockAsValidated(ctx context.Context, blkid cid.Cid) error { + key := blockValidationCacheKeyPrefix.Instance(blkid.String()) + + if err := cs.ds.Delete(key); err != nil { + return xerrors.Errorf("removing from valid block cache: %w", err) + } + + return nil +} + func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error { ts, err := types.NewTipSet([]*types.BlockHeader{b}) if err != nil { diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index c7ded7a25..3864d3014 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -38,6 +38,7 @@ func main() { exportChainCmd, consensusCmd, serveDealStatsCmd, + syncCmd, } app := &cli.App{ diff --git a/cmd/lotus-shed/sync.go b/cmd/lotus-shed/sync.go new file mode 100644 index 000000000..bfe7cc8b7 --- /dev/null +++ b/cmd/lotus-shed/sync.go @@ -0,0 +1,64 @@ +package main + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/urfave/cli/v2" +) + +var syncCmd = &cli.Command{ + Name: "sync", + Usage: "tools for diagnosing sync issues", + Flags: []cli.Flag{}, + Subcommands: []*cli.Command{ + syncValidateCmd, + }, +} + +var syncValidateCmd = &cli.Command{ + Name: "validate", + Usage: "checks whether a provided tipset is valid", + 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.Args().Len() < 1 { + fmt.Println("usage: ...") + fmt.Println("At least one block cid must be provided") + return nil + } + + args := cctx.Args().Slice() + + var tscids []cid.Cid + for _, s := range args { + c, err := cid.Decode(s) + if err != nil { + return fmt.Errorf("block cid was invalid: %s", err) + } + tscids = append(tscids, c) + } + + tsk := types.NewTipSetKey(tscids...) + + valid, err := api.SyncValidateTipset(ctx, tsk) + if err != nil { + fmt.Println("Tipset is invalid: ", err) + } + + if valid { + fmt.Println("Tipset is valid") + } + + return nil + }, +} diff --git a/node/impl/full/sync.go b/node/impl/full/sync.go index dc3bfe230..221942673 100644 --- a/node/impl/full/sync.go +++ b/node/impl/full/sync.go @@ -126,3 +126,29 @@ func (a *SyncAPI) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error return reason, nil } + +func (a *SyncAPI) SyncValidateTipset(ctx context.Context, tsk types.TipSetKey) (bool, error) { + ts, err := a.Syncer.ChainStore().LoadTipSet(tsk) + if err != nil { + return false, err + } + + fts, err := a.Syncer.ChainStore().TryFillTipSet(ts) + if err != nil { + return false, err + } + + for _, blk := range tsk.Cids() { + err = a.Syncer.ChainStore().UnmarkBlockAsValidated(ctx, blk) + if err != nil { + return false, err + } + } + + err = a.Syncer.ValidateTipSet(ctx, fts) + if err != nil { + return false, err + } + + return true, nil +}