Fix the rest of power slashing issues

This commit is contained in:
Łukasz Magiera 2019-11-14 17:14:52 +01:00
parent f4fc3bcc29
commit e9631601e3
6 changed files with 69 additions and 28 deletions

View File

@ -63,6 +63,9 @@ type StorageMinerActorState struct {
// Amount of power this miner has. // Amount of power this miner has.
Power types.BigInt Power types.BigInt
// Active is set to true after the miner has submitted their first PoSt
Active bool
// The height at which this miner was slashed at. // The height at which this miner was slashed at.
SlashedAt uint64 SlashedAt uint64
@ -126,7 +129,7 @@ type maMethods struct {
GetSectorSize uint64 GetSectorSize uint64
UpdatePeerID uint64 UpdatePeerID uint64
ChangeWorker uint64 ChangeWorker uint64
IsLate uint64 IsSlashed uint64
CheckMiner uint64 CheckMiner uint64
DeclareFaults uint64 DeclareFaults uint64
SlashConsensusFault uint64 SlashConsensusFault uint64
@ -151,7 +154,7 @@ func (sma StorageMinerActor) Exports() []interface{} {
13: sma.GetSectorSize, 13: sma.GetSectorSize,
14: sma.UpdatePeerID, 14: sma.UpdatePeerID,
//15: sma.ChangeWorker, //15: sma.ChangeWorker,
16: sma.IsLate, 16: sma.IsSlashed,
17: sma.CheckMiner, 17: sma.CheckMiner,
18: sma.DeclareFaults, 18: sma.DeclareFaults,
19: sma.SlashConsensusFault, 19: sma.SlashConsensusFault,
@ -530,10 +533,16 @@ func (sma StorageMinerActor) SubmitPoSt(act *types.Actor, vmctx types.VMContext,
delta = self.Power delta = self.Power
} }
prevPE := self.ProvingPeriodEnd
if !self.Active {
self.Active = true
prevPE = 0
}
enc, err := SerializeParams(&UpdateStorageParams{ enc, err := SerializeParams(&UpdateStorageParams{
Delta: delta, Delta: delta,
NextProvingPeriodEnd: currentProvingPeriodEnd + build.ProvingPeriodDuration, NextProvingPeriodEnd: currentProvingPeriodEnd + build.ProvingPeriodDuration,
PreviousProvingPeriodEnd: currentProvingPeriodEnd, PreviousProvingPeriodEnd: prevPE,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -740,16 +749,16 @@ func (sma StorageMinerActor) GetSectorSize(act *types.Actor, vmctx types.VMConte
} }
func isLate(height uint64, self *StorageMinerActorState) bool { func isLate(height uint64, self *StorageMinerActorState) bool {
return self.ProvingPeriodEnd == 0 || height >= self.ProvingPeriodEnd // TODO: review: maybe > ? return self.ProvingPeriodEnd > 0 && height >= self.ProvingPeriodEnd // TODO: review: maybe > ?
} }
func (sma StorageMinerActor) IsLate(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) { func (sma StorageMinerActor) IsSlashed(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
_, self, err := loadState(vmctx) _, self, err := loadState(vmctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return cbg.EncodeBool(isLate(vmctx.BlockHeight(), self)), nil return cbg.EncodeBool(self.SlashedAt != 0), nil
} }
type CheckMinerParams struct { type CheckMinerParams struct {

View File

@ -3,19 +3,19 @@ package actors
import ( import (
"bytes" "bytes"
"context" "context"
"github.com/filecoin-project/go-amt-ipld"
"io" "io"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/go-amt-ipld"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/lotus/chain/address"
"github.com/filecoin-project/lotus/chain/types"
cid "github.com/ipfs/go-cid" cid "github.com/ipfs/go-cid"
hamt "github.com/ipfs/go-hamt-ipld" hamt "github.com/ipfs/go-hamt-ipld"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors" xerrors "golang.org/x/xerrors"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/lotus/chain/address"
"github.com/filecoin-project/lotus/chain/types"
) )
type StoragePowerActor struct{} type StoragePowerActor struct{}
@ -444,11 +444,11 @@ func powerLookup(ctx context.Context, vmctx types.VMContext, self *StoragePowerS
return types.BigFromBytes(ret), nil return types.BigFromBytes(ret), nil
} }
type IsMinerParam struct { type IsValidMinerParam struct {
Addr address.Address Addr address.Address
} }
func (spa StoragePowerActor) IsValidMiner(act *types.Actor, vmctx types.VMContext, param *IsMinerParam) ([]byte, ActorError) { func (spa StoragePowerActor) IsValidMiner(act *types.Actor, vmctx types.VMContext, param *IsValidMinerParam) ([]byte, ActorError) {
var self StoragePowerState var self StoragePowerState
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil { if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
return nil, err return nil, err
@ -460,17 +460,23 @@ func (spa StoragePowerActor) IsValidMiner(act *types.Actor, vmctx types.VMContex
} }
if !has { if !has {
log.Warnf("Miner INVALID: not in set: %s", param.Addr)
return cbg.CborBoolFalse, nil return cbg.CborBoolFalse, nil
} }
ret, err := vmctx.Send(param.Addr, MAMethods.IsLate, types.NewInt(0), nil) ret, err := vmctx.Send(param.Addr, MAMethods.IsSlashed, types.NewInt(0), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
late := bytes.Equal(ret, cbg.CborBoolTrue) slashed := bytes.Equal(ret, cbg.CborBoolTrue)
return cbg.EncodeBool(!late), nil if slashed {
log.Warnf("Miner INVALID: /SLASHED/ : %s", param.Addr)
}
return cbg.EncodeBool(!slashed), nil
} }
type PledgeCollateralParams struct { type PledgeCollateralParams struct {
@ -602,7 +608,7 @@ func (spa StoragePowerActor) CheckProofSubmissions(act *types.Actor, vmctx types
} }
if power.GreaterThan(types.NewInt(0)) { if power.GreaterThan(types.NewInt(0)) {
log.Warnf("slashing miner %s for missed PoSt (%s B)", maddr, power) log.Warnf("slashing miner %s for missed PoSt (%s B, H: %d, Bucket: %d)", maddr, power, vmctx.BlockHeight(), bucketID)
self.TotalStorage = types.BigSub(self.TotalStorage, power) self.TotalStorage = types.BigSub(self.TotalStorage, power)
} }

View File

@ -53,7 +53,7 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
{ {
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner, ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner,
&IsMinerParam{Addr: minerAddr}) &IsValidMinerParam{Addr: minerAddr})
ApplyOK(t, ret) ApplyOK(t, ret)
var output bool var output bool
@ -108,7 +108,7 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
} }
{ {
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner, &IsMinerParam{minerAddr}) ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner, &IsValidMinerParam{minerAddr})
ApplyOK(t, ret) ApplyOK(t, ret)
assert.Equal(t, ret.Return, cbg.CborBoolFalse) assert.Equal(t, ret.Return, cbg.CborBoolFalse)
} }

View File

@ -198,7 +198,7 @@ func (t *StorageMinerActorState) MarshalCBOR(w io.Writer) error {
_, err := w.Write(cbg.CborNull) _, err := w.Write(cbg.CborNull)
return err return err
} }
if _, err := w.Write([]byte{138}); err != nil { if _, err := w.Write([]byte{139}); err != nil {
return err return err
} }
@ -268,6 +268,11 @@ func (t *StorageMinerActorState) MarshalCBOR(w io.Writer) error {
return err return err
} }
// t.t.Active (bool) (bool)
if err := cbg.WriteBool(w, t.Active); err != nil {
return err
}
// t.t.SlashedAt (uint64) (uint64) // t.t.SlashedAt (uint64) (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.SlashedAt))); err != nil { if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.SlashedAt))); err != nil {
return err return err
@ -291,7 +296,7 @@ func (t *StorageMinerActorState) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("cbor input should be of type array") return fmt.Errorf("cbor input should be of type array")
} }
if extra != 10 { if extra != 11 {
return fmt.Errorf("cbor input had wrong number of fields") return fmt.Errorf("cbor input had wrong number of fields")
} }
@ -420,6 +425,23 @@ func (t *StorageMinerActorState) UnmarshalCBOR(r io.Reader) error {
} }
} }
// t.t.Active (bool) (bool)
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajOther {
return fmt.Errorf("booleans must be major type 7")
}
switch extra {
case 20:
t.Active = false
case 21:
t.Active = true
default:
return fmt.Errorf("booleans are either major type 7, value 20 or 21 (got %d)", extra)
}
// t.t.SlashedAt (uint64) (uint64) // t.t.SlashedAt (uint64) (uint64)
maj, extra, err = cbg.CborReadHeader(br) maj, extra, err = cbg.CborReadHeader(br)
@ -2574,7 +2596,7 @@ func (t *CreateStorageMinerParams) UnmarshalCBOR(r io.Reader) error {
return nil return nil
} }
func (t *IsMinerParam) MarshalCBOR(w io.Writer) error { func (t *IsValidMinerParam) MarshalCBOR(w io.Writer) error {
if t == nil { if t == nil {
_, err := w.Write(cbg.CborNull) _, err := w.Write(cbg.CborNull)
return err return err
@ -2590,7 +2612,7 @@ func (t *IsMinerParam) MarshalCBOR(w io.Writer) error {
return nil return nil
} }
func (t *IsMinerParam) UnmarshalCBOR(r io.Reader) error { func (t *IsValidMinerParam) UnmarshalCBOR(r io.Reader) error {
br := cbg.GetPeeker(r) br := cbg.GetPeeker(r)
maj, extra, err := cbg.CborReadHeader(br) maj, extra, err := cbg.CborReadHeader(br)

View File

@ -1,6 +1,7 @@
package chain package chain
import ( import (
"bytes"
"context" "context"
"errors" "errors"
"fmt" "fmt"
@ -418,7 +419,7 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet)
func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, baseTs *types.TipSet) error { func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, baseTs *types.TipSet) error {
var err error var err error
enc, err := actors.SerializeParams(&actors.IsMinerParam{Addr: maddr}) enc, err := actors.SerializeParams(&actors.IsValidMinerParam{Addr: maddr})
if err != nil { if err != nil {
return err return err
} }
@ -437,7 +438,9 @@ func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, b
return xerrors.Errorf("StorageMarket.IsValidMiner check failed (exit code %d)", ret.ExitCode) return xerrors.Errorf("StorageMarket.IsValidMiner check failed (exit code %d)", ret.ExitCode)
} }
// TODO: ensure the miner is currently not late on their PoSt submission (this hasnt landed in the spec yet) if !bytes.Equal(ret.Return, cbg.CborBoolTrue) {
return xerrors.New("miner isn't valid")
}
return nil return nil
} }
@ -516,6 +519,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
minerCheck := async.Err(func() error { minerCheck := async.Err(func() error {
if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil { if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil {
log.Errorf("minerIsValid: %+v", err)
return xerrors.Errorf("minerIsValid failed: %w", err) return xerrors.Errorf("minerIsValid failed: %w", err)
} }
return nil return nil
@ -589,7 +593,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
var merr error var merr error
for _, fut := range await { for _, fut := range await {
if err := fut.AwaitContext(ctx); err != nil { if err := fut.AwaitContext(ctx); err != nil {
err = multierror.Append(merr, err) merr = multierror.Append(merr, err)
} }
} }

View File

@ -110,7 +110,7 @@ func main() {
actors.PaymentInfo{}, actors.PaymentInfo{},
actors.StoragePowerState{}, actors.StoragePowerState{},
actors.CreateStorageMinerParams{}, actors.CreateStorageMinerParams{},
actors.IsMinerParam{}, actors.IsValidMinerParam{},
actors.PowerLookupParams{}, actors.PowerLookupParams{},
actors.UpdateStorageParams{}, actors.UpdateStorageParams{},
actors.ArbitrateConsensusFaultParams{}, actors.ArbitrateConsensusFaultParams{},