From 425661772d59f2ba5a1f7548598e42d64b644d84 Mon Sep 17 00:00:00 2001 From: mx Date: Tue, 30 May 2023 14:33:35 +0800 Subject: [PATCH] style:Refactoring slashfilter --- chain/gen/slashfilter/slashfilter.go | 45 +++++----- cmd/lotus/daemon.go | 128 ++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 22 deletions(-) diff --git a/chain/gen/slashfilter/slashfilter.go b/chain/gen/slashfilter/slashfilter.go index 986586267..f5f5ec502 100644 --- a/chain/gen/slashfilter/slashfilter.go +++ b/chain/gen/slashfilter/slashfilter.go @@ -27,24 +27,24 @@ func New(dstore ds.Batching) *SlashFilter { } } -func (f *SlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) error { +func (f *SlashFilter) CheckBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) (cid.Cid, error) { if build.IsNearUpgrade(bh.Height, build.UpgradeOrangeHeight) { - return nil + return cid.Undef, nil } epochKey := ds.NewKey(fmt.Sprintf("/%s/%d", bh.Miner, bh.Height)) { // double-fork mining (2 blocks at one epoch) - if err := checkFault(ctx, f.byEpoch, epochKey, bh, "double-fork mining faults"); err != nil { - return err + if witness, err := checkFault(ctx, f.byEpoch, epochKey, bh, "double-fork mining faults"); err != nil { + return witness, err } } parentsKey := ds.NewKey(fmt.Sprintf("/%s/%x", bh.Miner, types.NewTipSetKey(bh.Parents...).Bytes())) { // time-offset mining faults (2 blocks with the same parents) - if err := checkFault(ctx, f.byParents, parentsKey, bh, "time-offset mining faults"); err != nil { - return err + if witness, err := checkFault(ctx, f.byParents, parentsKey, bh, "time-offset mining faults"); err != nil { + return witness, err } } @@ -55,19 +55,19 @@ func (f *SlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader, par parentEpochKey := ds.NewKey(fmt.Sprintf("/%s/%d", bh.Miner, parentEpoch)) have, err := f.byEpoch.Has(ctx, parentEpochKey) if err != nil { - return err + return cid.Undef, err } if have { // If we had, make sure it's in our parent tipset cidb, err := f.byEpoch.Get(ctx, parentEpochKey) if err != nil { - return xerrors.Errorf("getting other block cid: %w", err) + return cid.Undef, xerrors.Errorf("getting other block cid: %w", err) } _, parent, err := cid.CidFromBytes(cidb) if err != nil { - return err + return cid.Undef, err } var found bool @@ -78,45 +78,50 @@ func (f *SlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader, par } if !found { - return xerrors.Errorf("produced block would trigger 'parent-grinding fault' consensus fault; miner: %s; bh: %s, expected parent: %s", bh.Miner, bh.Cid(), parent) + return cid.Undef, xerrors.Errorf("produced block would trigger 'parent-grinding fault' consensus fault; miner: %s; bh: %s, expected parent: %s", bh.Miner, bh.Cid(), parent) } } } if err := f.byParents.Put(ctx, parentsKey, bh.Cid().Bytes()); err != nil { - return xerrors.Errorf("putting byEpoch entry: %w", err) + return cid.Undef, xerrors.Errorf("putting byEpoch entry: %w", err) } if err := f.byEpoch.Put(ctx, epochKey, bh.Cid().Bytes()); err != nil { - return xerrors.Errorf("putting byEpoch entry: %w", err) + return cid.Undef, xerrors.Errorf("putting byEpoch entry: %w", err) } - return nil + return cid.Undef, nil } -func checkFault(ctx context.Context, t ds.Datastore, key ds.Key, bh *types.BlockHeader, faultType string) error { +func (f *SlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) error { + _, err := f.CheckBlock(ctx, bh, parentEpoch) + return err +} + +func checkFault(ctx context.Context, t ds.Datastore, key ds.Key, bh *types.BlockHeader, faultType string) (cid.Cid, error) { fault, err := t.Has(ctx, key) if err != nil { - return err + return cid.Undef, err } if fault { cidb, err := t.Get(ctx, key) if err != nil { - return xerrors.Errorf("getting other block cid: %w", err) + return cid.Undef, xerrors.Errorf("getting other block cid: %w", err) } _, other, err := cid.CidFromBytes(cidb) if err != nil { - return err + return cid.Undef, err } if other == bh.Cid() { - return nil + return cid.Undef, nil } - return xerrors.Errorf("produced block would trigger '%s' consensus fault; miner: %s; bh: %s, other: %s", faultType, bh.Miner, bh.Cid(), other) + return other, xerrors.Errorf("produced block would trigger '%s' consensus fault; miner: %s; bh: %s, other: %s", faultType, bh.Miner, bh.Cid(), other) } - return nil + return cid.Undef, nil } diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index fbb9dfd9a..7af8e79a0 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -9,6 +9,15 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/filecoin-project/go-address" + cborutil "github.com/filecoin-project/go-cbor-util" + "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/gen/slashfilter" + "github.com/filecoin-project/specs-actors/actors/builtin/miner" + "github.com/ipfs/go-cid" + levelds "github.com/ipfs/go-ds-leveldb" + ldbopts "github.com/syndtr/goleveldb/leveldb/opt" "io" "os" "path" @@ -29,7 +38,6 @@ import ( "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-paramfetch" - lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/consensus" @@ -160,6 +168,19 @@ var DaemonCmd = &cli.Command{ Name: "restore-config", Usage: "config file to use when restoring from backup", }, + &cli.BoolFlag{ + Name: "slash-consensus", + Usage: "Report consensus fault", + Value: false, + }, + &cli.StringFlag{ + Name: "slasher-sender", + Usage: "optionally specify the account to report consensus from", + }, + &cli.StringFlag{ + Name: "slashdb-dir", + Value: "slash watch db dir path", + }, }, Action: func(cctx *cli.Context) error { isLite := cctx.Bool("lite") @@ -380,7 +401,14 @@ var DaemonCmd = &cli.Command{ if err != nil { return fmt.Errorf("failed to start json-rpc endpoint: %s", err) } - + if cctx.IsSet("slash-consensus") && cctx.IsSet("slashdb-dir") { + go func() { + err := slashConsensus(api, cctx.String("slashdb-dir"), cctx.String("slasher-sender")) + if err != nil { + panic("slashConsensus error") + } + }() + } // Monitor for shutdown. finishCh := node.MonitorShutdown(shutdownChan, node.ShutdownHandler{Component: "rpc server", StopFunc: rpcStopper}, @@ -574,3 +602,99 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) return nil } + +func slashConsensus(a lapi.FullNode, p string, from string) error { + ctx := context.Background() + var fromAddr address.Address + + ds, err := levelds.NewDatastore(p, &levelds.Options{ + Compression: ldbopts.NoCompression, + NoSync: false, + Strict: ldbopts.StrictAll, + ReadOnly: false, + }) + if err != nil { + return xerrors.Errorf("open leveldb: %w", err) + } + sf := slashfilter.New(ds) + if from == "" { + defaddr, err := a.WalletDefaultAddress(ctx) + if err != nil { + return err + } + fromAddr = defaddr + } else { + addr, err := address.NewFromString(from) + if err != nil { + return err + } + + fromAddr = addr + } + + blocks, err := a.SyncIncomingBlocks(ctx) + if err != nil { + return xerrors.Errorf("sync incoming blocks failed: %w", err) + } + for block := range blocks { + log.Infof("deal with block: %d, %v, %s", block.Height, block.Miner, block.Cid()) + if otherBlock, err := slashFilterMinedBlock(ctx, sf, a, block); err != nil { + if otherBlock == nil { + continue + } + log.Errorf(" SLASH FILTER ERROR: %s", err) + bh1, err := cborutil.Dump(otherBlock) + if err != nil { + log.Errorf("could not dump otherblock:%s, err:%s", otherBlock.Cid(), err) + continue + } + + bh2, err := cborutil.Dump(block) + if err != nil { + log.Errorf("could not dump block:%s, err:%s", block.Cid(), err) + continue + } + + params := miner.ReportConsensusFaultParams{ + BlockHeader1: bh1, + BlockHeader2: bh2, + } + + enc, err := actors.SerializeParams(¶ms) + if err != nil { + log.Errorf("could not serialize declare faults parameters: %s", err) + continue + } + message, err := a.MpoolPushMessage(ctx, &types.Message{ + To: block.Miner, + From: fromAddr, + Value: types.NewInt(0), + Method: builtin.MethodsMiner.ReportConsensusFault, + Params: enc, + }, nil) + if err != nil { + log.Errorf("ReportConsensusFault to messagepool error:%s", err) + continue + } + log.Infof("ReportConsensusFault message CID:%s", message.Cid()) + + } + } + return err +} + +func slashFilterMinedBlock(ctx context.Context, sf *slashfilter.SlashFilter, a lapi.FullNode, bh *types.BlockHeader) (*types.BlockHeader, error) { + parent, err := a.ChainGetBlock(ctx, bh.Parents[0]) + if err != nil { + return nil, xerrors.Errorf("chain get block error:%s", err) + } + otherCid, err := sf.CheckBlock(ctx, bh, parent.Height) + if err != nil { + return nil, xerrors.Errorf("slash filter check block error:%s", err) + } + if otherCid != cid.Undef { + otherHeader, err := a.ChainGetBlock(ctx, otherCid) + return otherHeader, xerrors.Errorf("chain get other block error:%s", err) + } + return nil, nil +}