Skip to content

Commit

Permalink
feat: Implement a tooling for slasher
Browse files Browse the repository at this point in the history
  • Loading branch information
simlecode committed Aug 22, 2023
1 parent eb6fe63 commit a1cb32e
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 43 deletions.
4 changes: 4 additions & 0 deletions app/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ func (node *Node) ConfigModule() *configModule.ConfigModule {
return node.configModule
}

func (node *Node) Sync() *syncer2.SyncerSubmodule {
return node.syncer
}

func (node *Node) Repo() repo.Repo {
return node.repo
}
Expand Down
13 changes: 10 additions & 3 deletions app/submodule/syncer/syncer_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,16 @@ func (sa *syncerAPI) SyncSubmitBlock(ctx context.Context, blk *types.BlockMsg) e
}

if !constants.NoSlashFilter {
if err := sa.syncer.SlashFilter.MinedBlock(ctx, blk.Header, parent.Height); err != nil {
log.Errorf("<!!> SLASH FILTER ERROR: %s", err)
return fmt.Errorf("<!!> SLASH FILTER ERROR: %v", err)
witness, fault, err := sa.syncer.SlashFilter.MinedBlock(ctx, blk.Header, parent.Height)
if err != nil {
log.Errorf("<!!> SLASH FILTER ERRORED: %s", err)
// Return an error here, because it's _probably_ wiser to not submit this block
return fmt.Errorf("<!!> SLASH FILTER ERRORED: %w", err)
}

if fault {
log.Errorf("<!!> SLASH FILTER DETECTED FAULT due to witness %s", witness)
return fmt.Errorf("<!!> SLASH FILTER DETECTED FAULT due to witness %s", witness)
}
}

Expand Down
8 changes: 8 additions & 0 deletions cmd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
types2 "github.com/filecoin-project/venus/venus-shared/actors/types"
"github.com/filecoin-project/venus/venus-shared/utils"

"github.com/filecoin-project/venus/pkg/chainsync/slashfilter"
"github.com/filecoin-project/venus/pkg/util/ulimit"

paramfetch "github.com/filecoin-project/go-paramfetch"
Expand Down Expand Up @@ -295,6 +296,13 @@ func daemonRun(req *cmds.Request, re cmds.ResponseEmitter) error {
_ = re.Emit("--" + ELStdout + " option is deprecated\n")
}

if config.FaultReporter.EnableConsensusFaultReporter {
if err := slashfilter.SlashConsensus(req.Context, config.FaultReporter, fcn.Wallet().API(),
fcn.Chain().API(), fcn.Mpool().API(), fcn.Sync().API()); err != nil {
return fmt.Errorf("run consensus fault reporter failed: %v", err)
}
}

// Start the node.
if err := fcn.Start(req.Context); err != nil {
return err
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ require (
github.com/ipfs/go-cid v0.4.1
github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-ds-badger2 v0.1.3
github.com/ipfs/go-ds-leveldb v0.5.0
github.com/ipfs/go-fs-lock v0.0.7
github.com/ipfs/go-graphsync v0.14.6
github.com/ipfs/go-ipfs-blockstore v1.3.0
Expand Down Expand Up @@ -98,6 +99,7 @@ require (
github.com/raulk/clock v1.1.0
github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e
github.com/stretchr/testify v1.8.3
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa
github.com/whyrusleeping/go-logging v0.0.1
github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,7 @@ github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIyk
github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8=
github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=
github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=
github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo=
github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q=
github.com/ipfs/go-ds-measure v0.2.0/go.mod h1:SEUD/rE2PwRa4IQEC5FuNAmjJCyYObZr9UvVh8V3JxE=
github.com/ipfs/go-fetcher v1.5.0/go.mod h1:5pDZ0393oRF/fHiLmtFZtpMNBQfHOYNPtryWedVuSWE=
Expand Down Expand Up @@ -2289,6 +2290,7 @@ github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t
github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs=
github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e h1:T5PdfK/M1xyrHwynxMIVMWLS7f/qHwfslZphxtGnw7s=
Expand Down
51 changes: 31 additions & 20 deletions pkg/chainsync/slashfilter/mysqldb.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"github.com/filecoin-project/venus/venus-shared/types"
)

var log = logging.Logger("mysql")
var log = logging.Logger("slashfilter")

type MysqlSlashFilter struct {
_db *gorm.DB
Expand Down Expand Up @@ -66,53 +66,63 @@ func NewMysqlSlashFilter(cfg config.MySQLConfig) (ISlashFilter, error) {
}

// checkSameHeightFault check whether the miner mined multi block on the same height
func (f *MysqlSlashFilter) checkSameHeightFault(bh *types.BlockHeader) error {
func (f *MysqlSlashFilter) checkSameHeightFault(bh *types.BlockHeader) (cid.Cid, bool, error) {
var bk MinedBlock
err := f._db.Model(&MinedBlock{}).Take(&bk, "miner=? and epoch=?", bh.Miner.String(), bh.Height).Error
if err == gorm.ErrRecordNotFound {
return nil
return cid.Undef, false, nil
}

other, err := cid.Decode(bk.Cid)
if err != nil {
return err
return cid.Undef, false, err
}

if other == bh.Cid() {
return nil
return cid.Undef, false, nil
}
log.Infof("produced block would trigger double-fork mining faults consensus fault; miner: %s; bh: %s, other: %s", bh.Miner, bh.Cid(), other)

return fmt.Errorf("produced block would trigger double-fork mining faults consensus fault; miner: %s; bh: %s, other: %s", bh.Miner, bh.Cid(), other)
return other, true, nil
}

// checkSameParentFault check whether the miner mined block on the same parent
func (f *MysqlSlashFilter) checkSameParentFault(bh *types.BlockHeader) error {
func (f *MysqlSlashFilter) checkSameParentFault(bh *types.BlockHeader) (cid.Cid, bool, error) {
var bk MinedBlock
err := f._db.Model(&MinedBlock{}).Take(&bk, "miner=? and parent_key=?", bh.Miner.String(), types.NewTipSetKey(bh.Parents...).String()).Error
if err == gorm.ErrRecordNotFound {
return nil
return cid.Undef, false, nil
}

other, err := cid.Decode(bk.Cid)
if err != nil {
return err
return cid.Undef, false, err
}

if other == bh.Cid() {
return nil
return cid.Undef, false, nil
}

return fmt.Errorf("produced block would trigger time-offset mining faults consensus fault; miner: %s; bh: %s, other: %s", bh.Miner, bh.Cid(), other)
log.Infof("produced block would trigger time-offset mining faults consensus fault; miner: %s; bh: %s, other: %s", bh.Miner, bh.Cid(), other)
return other, true, nil
}

// MinedBlock check whether the block mined is slash
func (f *MysqlSlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) error {
if err := f.checkSameHeightFault(bh); err != nil {
return err
func (f *MysqlSlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) (cid.Cid, bool, error) {
doubleForkWitness, doubleForkFault, err := f.checkSameHeightFault(bh)
if err != nil {
return cid.Undef, false, fmt.Errorf("check double-fork mining faults: %w", err)
}
if doubleForkFault {
return doubleForkWitness, doubleForkFault, nil
}

if err := f.checkSameParentFault(bh); err != nil {
return err
timeOffsetWitness, timeOffsetFault, err := f.checkSameParentFault(bh)
if err != nil {
return cid.Undef, false, fmt.Errorf("check time-offset mining faults: %w", err)
}
if timeOffsetFault {
return timeOffsetWitness, timeOffsetFault, nil
}

{
Expand All @@ -125,7 +135,7 @@ func (f *MysqlSlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader
// if exit
parent, err := cid.Decode(bk.Cid)
if err != nil {
return err
return cid.Undef, false, err
}

var found bool
Expand All @@ -136,16 +146,17 @@ func (f *MysqlSlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader
}

if !found {
return fmt.Errorf("produced block would trigger 'parent-grinding fault' consensus fault; miner: %s; bh: %s, expected parent: %s", bh.Miner, bh.Cid(), parent)
log.Infof("produced block would trigger 'parent-grinding fault' consensus fault; miner: %s; bh: %s, expected parent: %s", bh.Miner, bh.Cid(), parent)
return parent, true, nil
}
} else if err != gorm.ErrRecordNotFound {
// other error except not found
return err
return cid.Undef, false, err
}
// if not exit good block
}

return f._db.Save(&MinedBlock{
return cid.Undef, false, f._db.Save(&MinedBlock{
ParentEpoch: int64(parentEpoch),
ParentKey: types.NewTipSetKey(bh.Parents...).String(),
Epoch: int64(bh.Height),
Expand Down
52 changes: 32 additions & 20 deletions pkg/chainsync/slashfilter/slashfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

// ISlashFilter used to detect whether the miner mined a invalidated block , support local db and mysql storage
type ISlashFilter interface {
MinedBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) error
MinedBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) (cid.Cid, bool, error)
}

// LocalSlashFilter use badger db to save mined block for detect slash consensus block
Expand All @@ -32,20 +32,30 @@ func NewLocalSlashFilter(dstore ds.Batching) ISlashFilter {
}

// MinedBlock check whether the block mined is slash
func (f *LocalSlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) error {
func (f *LocalSlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader, parentEpoch abi.ChainEpoch) (cid.Cid, bool, error) {
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
doubleForkWitness, doubleForkFault, err := checkFault(ctx, f.byEpoch, epochKey, bh, "double-fork mining faults")
if err != nil {
return cid.Undef, false, fmt.Errorf("check double-fork mining faults: %w", err)
}

if doubleForkFault {
return doubleForkWitness, doubleForkFault, nil
}
}

parentsKey := ds.NewKey(fmt.Sprintf("/%s/%s", bh.Miner, types.NewTipSetKey(bh.Parents...).String()))
{
// 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
timeOffsetWitness, timeOffsetFault, err := checkFault(ctx, f.byParents, parentsKey, bh, "time-offset mining faults")
if err != nil {
return cid.Undef, false, fmt.Errorf("check time-offset mining faults: %w", err)
}

if timeOffsetFault {
return timeOffsetWitness, timeOffsetFault, nil
}
}

Expand All @@ -56,19 +66,19 @@ func (f *LocalSlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader
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, false, 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 fmt.Errorf("getting other block cid: %w", err)
return cid.Undef, false, fmt.Errorf("getting other block cid: %w", err)
}

_, parent, err := cid.CidFromBytes(cidb)
if err != nil {
return err
return cid.Undef, false, err
}

var found bool
Expand All @@ -79,45 +89,47 @@ func (f *LocalSlashFilter) MinedBlock(ctx context.Context, bh *types.BlockHeader
}

if !found {
return fmt.Errorf("produced block would trigger 'parent-grinding fault' consensus fault; miner: %s; bh: %s, expected parent: %s", bh.Miner, bh.Cid(), parent)
log.Infof("produced block would trigger 'parent-grinding fault' consensus fault; miner: %s; bh: %s, expected parent: %s", bh.Miner, bh.Cid(), parent)
return parent, true, nil
}
}
}

if err := f.byParents.Put(ctx, parentsKey, bh.Cid().Bytes()); err != nil {
return fmt.Errorf("putting byParents entry: %w", err)
return cid.Undef, false, fmt.Errorf("putting byParents entry: %w", err)
}

if err := f.byEpoch.Put(ctx, epochKey, bh.Cid().Bytes()); err != nil {
return fmt.Errorf("putting byEpoch entry: %w", err)
return cid.Undef, false, fmt.Errorf("putting byEpoch entry: %w", err)
}

return nil
return cid.Undef, false, nil
}

func checkFault(ctx context.Context, t ds.Datastore, key ds.Key, bh *types.BlockHeader, faultType string) error {
func checkFault(ctx context.Context, t ds.Datastore, key ds.Key, bh *types.BlockHeader, faultType string) (cid.Cid, bool, error) {
fault, err := t.Has(ctx, key)
if err != nil {
return err
return cid.Undef, false, fmt.Errorf("failed to read from datastore: %w", err)
}

if fault {
cidb, err := t.Get(ctx, key)
if err != nil {
return fmt.Errorf("getting other block cid: %w", err)
return cid.Undef, false, fmt.Errorf("getting other block cid: %w", err)
}

_, other, err := cid.CidFromBytes(cidb)
if err != nil {
return err
return cid.Undef, false, err
}

if other == bh.Cid() {
return nil
return cid.Undef, false, nil
}

return fmt.Errorf("produced block would trigger '%s' consensus fault; miner: %s; bh: %s, other: %s", faultType, bh.Miner, bh.Cid(), other)
log.Infof("produced block would trigger '%s' consensus fault; miner: %s; bh: %s, other: %s", faultType, bh.Miner, bh.Cid(), other)
return other, true, nil
}

return nil
return cid.Undef, false, nil
}
Loading

0 comments on commit a1cb32e

Please sign in to comment.