Merge pull request #4065 from filecoin-project/asr/sync-validate
Add lotus shed util to validate a tipset
This commit is contained in:
commit
76b1ec1de0
@ -176,6 +176,9 @@ type FullNode interface {
|
|||||||
// the reason.
|
// the reason.
|
||||||
SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error)
|
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
|
// MethodGroup: Mpool
|
||||||
// The Mpool methods are for interacting with the message pool. The message pool
|
// The Mpool methods are for interacting with the message pool. The message pool
|
||||||
// manages all incoming and outgoing 'messages' going over the network.
|
// manages all incoming and outgoing 'messages' going over the network.
|
||||||
|
@ -112,6 +112,7 @@ type FullNodeStruct struct {
|
|||||||
SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
|
SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
|
||||||
SyncUnmarkBad 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"`
|
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"`
|
MpoolGetConfig func(context.Context) (*types.MpoolConfig, error) `perm:"read"`
|
||||||
MpoolSetConfig func(context.Context, *types.MpoolConfig) error `perm:"write"`
|
MpoolSetConfig func(context.Context, *types.MpoolConfig) error `perm:"write"`
|
||||||
@ -740,6 +741,10 @@ func (c *FullNodeStruct) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string
|
|||||||
return c.Internal.SyncCheckBad(ctx, bcid)
|
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) {
|
func (c *FullNodeStruct) StateNetworkName(ctx context.Context) (dtypes.NetworkName, error) {
|
||||||
return c.Internal.StateNetworkName(ctx)
|
return c.Internal.StateNetworkName(ctx)
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,16 @@ func (cs *ChainStore) MarkBlockAsValidated(ctx context.Context, blkid cid.Cid) e
|
|||||||
return nil
|
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 {
|
func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error {
|
||||||
ts, err := types.NewTipSet([]*types.BlockHeader{b})
|
ts, err := types.NewTipSet([]*types.BlockHeader{b})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -597,7 +597,7 @@ func isPermanent(err error) bool {
|
|||||||
return !errors.Is(err, ErrTemporal)
|
return !errors.Is(err, ErrTemporal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet) error {
|
func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet, useCache bool) error {
|
||||||
ctx, span := trace.StartSpan(ctx, "validateTipSet")
|
ctx, span := trace.StartSpan(ctx, "validateTipSet")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
@ -613,7 +613,7 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet)
|
|||||||
b := b // rebind to a scoped variable
|
b := b // rebind to a scoped variable
|
||||||
|
|
||||||
futures = append(futures, async.Err(func() error {
|
futures = append(futures, async.Err(func() error {
|
||||||
if err := syncer.ValidateBlock(ctx, b); err != nil {
|
if err := syncer.ValidateBlock(ctx, b, useCache); err != nil {
|
||||||
if isPermanent(err) {
|
if isPermanent(err) {
|
||||||
syncer.bad.Add(b.Cid(), NewBadBlockReason([]cid.Cid{b.Cid()}, err.Error()))
|
syncer.bad.Add(b.Cid(), NewBadBlockReason([]cid.Cid{b.Cid()}, err.Error()))
|
||||||
}
|
}
|
||||||
@ -680,7 +680,7 @@ func blockSanityChecks(h *types.BlockHeader) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateBlock should match up with 'Semantical Validation' in validation.md in the spec
|
// ValidateBlock should match up with 'Semantical Validation' in validation.md in the spec
|
||||||
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (err error) {
|
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock, useCache bool) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
// b.Cid() could panic for empty blocks that are used in tests.
|
// b.Cid() could panic for empty blocks that are used in tests.
|
||||||
if rerr := recover(); rerr != nil {
|
if rerr := recover(); rerr != nil {
|
||||||
@ -689,6 +689,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if useCache {
|
||||||
isValidated, err := syncer.store.IsBlockValidated(ctx, b.Cid())
|
isValidated, err := syncer.store.IsBlockValidated(ctx, b.Cid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("check block validation cache %s: %w", b.Cid(), err)
|
return xerrors.Errorf("check block validation cache %s: %w", b.Cid(), err)
|
||||||
@ -697,6 +698,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
|
|||||||
if isValidated {
|
if isValidated {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
validationStart := build.Clock.Now()
|
validationStart := build.Clock.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -959,9 +961,11 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
|
|||||||
return mulErr
|
return mulErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if useCache {
|
||||||
if err := syncer.store.MarkBlockAsValidated(ctx, b.Cid()); err != nil {
|
if err := syncer.store.MarkBlockAsValidated(ctx, b.Cid()); err != nil {
|
||||||
return xerrors.Errorf("caching block validation %s: %w", b.Cid(), err)
|
return xerrors.Errorf("caching block validation %s: %w", b.Cid(), err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1462,7 +1466,7 @@ func (syncer *Syncer) syncMessagesAndCheckState(ctx context.Context, headers []*
|
|||||||
|
|
||||||
return syncer.iterFullTipsets(ctx, headers, func(ctx context.Context, fts *store.FullTipSet) error {
|
return syncer.iterFullTipsets(ctx, headers, func(ctx context.Context, fts *store.FullTipSet) error {
|
||||||
log.Debugw("validating tipset", "height", fts.TipSet().Height(), "size", len(fts.TipSet().Cids()))
|
log.Debugw("validating tipset", "height", fts.TipSet().Height(), "size", len(fts.TipSet().Cids()))
|
||||||
if err := syncer.ValidateTipSet(ctx, fts); err != nil {
|
if err := syncer.ValidateTipSet(ctx, fts, true); err != nil {
|
||||||
log.Errorf("failed to validate tipset: %+v", err)
|
log.Errorf("failed to validate tipset: %+v", err)
|
||||||
return xerrors.Errorf("message processing failed: %w", err)
|
return xerrors.Errorf("message processing failed: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -732,7 +732,7 @@ func TestSyncInputs(t *testing.T) {
|
|||||||
|
|
||||||
err := s.ValidateBlock(context.TODO(), &types.FullBlock{
|
err := s.ValidateBlock(context.TODO(), &types.FullBlock{
|
||||||
Header: &types.BlockHeader{},
|
Header: &types.BlockHeader{},
|
||||||
})
|
}, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("should error on empty block")
|
t.Fatal("should error on empty block")
|
||||||
}
|
}
|
||||||
@ -741,7 +741,7 @@ func TestSyncInputs(t *testing.T) {
|
|||||||
|
|
||||||
h.ElectionProof = nil
|
h.ElectionProof = nil
|
||||||
|
|
||||||
err = s.ValidateBlock(context.TODO(), &types.FullBlock{Header: h})
|
err = s.ValidateBlock(context.TODO(), &types.FullBlock{Header: h}, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("should error on block with nil election proof")
|
t.Fatal("should error on block with nil election proof")
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ func main() {
|
|||||||
exportChainCmd,
|
exportChainCmd,
|
||||||
consensusCmd,
|
consensusCmd,
|
||||||
serveDealStatsCmd,
|
serveDealStatsCmd,
|
||||||
|
syncCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
app := &cli.App{
|
app := &cli.App{
|
||||||
|
64
cmd/lotus-shed/sync.go
Normal file
64
cmd/lotus-shed/sync.go
Normal file
@ -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: <blockCid1> <blockCid2>...")
|
||||||
|
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
|
||||||
|
},
|
||||||
|
}
|
@ -169,6 +169,7 @@
|
|||||||
* [SyncState](#SyncState)
|
* [SyncState](#SyncState)
|
||||||
* [SyncSubmitBlock](#SyncSubmitBlock)
|
* [SyncSubmitBlock](#SyncSubmitBlock)
|
||||||
* [SyncUnmarkBad](#SyncUnmarkBad)
|
* [SyncUnmarkBad](#SyncUnmarkBad)
|
||||||
|
* [SyncValidateTipset](#SyncValidateTipset)
|
||||||
* [Wallet](#Wallet)
|
* [Wallet](#Wallet)
|
||||||
* [WalletBalance](#WalletBalance)
|
* [WalletBalance](#WalletBalance)
|
||||||
* [WalletDefaultAddress](#WalletDefaultAddress)
|
* [WalletDefaultAddress](#WalletDefaultAddress)
|
||||||
@ -4380,6 +4381,28 @@ Inputs:
|
|||||||
|
|
||||||
Response: `{}`
|
Response: `{}`
|
||||||
|
|
||||||
|
### SyncValidateTipset
|
||||||
|
SyncValidateTipset indicates whether the provided tipset is valid or not
|
||||||
|
|
||||||
|
|
||||||
|
Perms: read
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Response: `true`
|
||||||
|
|
||||||
## Wallet
|
## Wallet
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,3 +126,22 @@ func (a *SyncAPI) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error
|
|||||||
|
|
||||||
return reason, nil
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Syncer.ValidateTipSet(ctx, fts, false)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user