Merge pull request #1025 from filecoin-project/fix/faults-counting

Fix faults handling
This commit is contained in:
Jakub Sztandera 2020-01-08 11:03:14 +01:00 committed by GitHub
commit d213bd1fee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 203 additions and 31 deletions

View File

@ -467,14 +467,21 @@ func (sma StorageMinerActor) SubmitFallbackPoSt(act *types.Actor, vmctx types.VM
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
}
faults, nerr := self.FaultSet.AllMap()
ss, lerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
if lerr != nil {
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
}
faults, nerr := self.FaultSet.AllMap(2 * ss.Count)
if nerr != nil {
return nil, aerrors.Absorb(err, 5, "RLE+ invalid")
}
activeFaults := uint64(0)
var sectorInfos []ffi.PublicSectorInfo
if err := pss.ForEach(func(id uint64, v *cbg.Deferred) error {
if faults[id] {
activeFaults++
return nil
}
@ -512,7 +519,7 @@ func (sma StorageMinerActor) SubmitFallbackPoSt(act *types.Actor, vmctx types.VM
}
if ok, lerr := sectorbuilder.VerifyFallbackPost(vmctx.Context(), mi.SectorSize,
sectorbuilder.NewSortedPublicSectorInfo(sectorInfos), seed[:], params.Proof, candidates, proverID, len(faults)); !ok || lerr != nil {
sectorbuilder.NewSortedPublicSectorInfo(sectorInfos), seed[:], params.Proof, candidates, proverID, activeFaults); !ok || lerr != nil {
if lerr != nil {
// TODO: study PoST errors
return nil, aerrors.Absorb(lerr, 4, "PoST error")
@ -523,7 +530,7 @@ func (sma StorageMinerActor) SubmitFallbackPoSt(act *types.Actor, vmctx types.VM
}
// Post submission is successful!
if err := onSuccessfulPoSt(self, vmctx); err != nil {
if err := onSuccessfulPoSt(self, vmctx, activeFaults); err != nil {
return nil, err
}
@ -834,6 +841,20 @@ func (sma StorageMinerActor) DeclareFaults(act *types.Actor, vmctx types.VMConte
return nil, aerrors.Absorb(err, 1, "failed to merge bitfields")
}
ss, nerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
if nerr != nil {
return nil, aerrors.HandleExternalError(nerr, "failed to load sector set")
}
cf, nerr := nfaults.Count()
if nerr != nil {
return nil, aerrors.Absorb(nerr, 2, "could not decode RLE+")
}
if cf > 2*ss.Count {
return nil, aerrors.Newf(3, "too many declared faults: %d > %d", cf, 2*ss.Count)
}
self.FaultSet = nfaults
self.LastFaultSubmission = vmctx.BlockHeight()
@ -909,7 +930,41 @@ func (sma StorageMinerActor) SubmitElectionPoSt(act *types.Actor, vmctx types.VM
return nil, aerrors.New(1, "slashed miners can't perform election PoSt")
}
if err := onSuccessfulPoSt(self, vmctx); err != nil {
pss, nerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
if nerr != nil {
return nil, aerrors.HandleExternalError(nerr, "failed to load proving set")
}
ss, nerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
if nerr != nil {
return nil, aerrors.HandleExternalError(nerr, "failed to load proving set")
}
faults, nerr := self.FaultSet.AllMap(2 * ss.Count)
if nerr != nil {
return nil, aerrors.Absorb(nerr, 1, "invalid bitfield (fatal?)")
}
activeFaults := uint64(0)
for f := range faults {
if f > amt.MaxIndex {
continue
}
var comms [][]byte
err := pss.Get(f, &comms)
if err != nil {
var notfound *amt.ErrNotFound
if !xerrors.As(err, &notfound) {
return nil, aerrors.HandleExternalError(err, "failed to find sector in sector set")
}
continue
}
activeFaults++
}
if err := onSuccessfulPoSt(self, vmctx, activeFaults); err != nil { // TODO
return nil, err
}
@ -924,7 +979,7 @@ func (sma StorageMinerActor) SubmitElectionPoSt(act *types.Actor, vmctx types.VM
return nil, nil
}
func onSuccessfulPoSt(self *StorageMinerActorState, vmctx types.VMContext) aerrors.ActorError {
func onSuccessfulPoSt(self *StorageMinerActorState, vmctx types.VMContext, activeFaults uint64) aerrors.ActorError {
// TODO: some sector upkeep stuff that is very haphazard and unclear in the spec
var mi MinerInfo
@ -937,7 +992,12 @@ func onSuccessfulPoSt(self *StorageMinerActorState, vmctx types.VMContext) aerro
return aerrors.HandleExternalError(nerr, "failed to load proving set")
}
faults, nerr := self.FaultSet.All()
ss, nerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
if nerr != nil {
return aerrors.HandleExternalError(nerr, "failed to load sector set")
}
faults, nerr := self.FaultSet.All(2 * ss.Count)
if nerr != nil {
return aerrors.Absorb(nerr, 1, "invalid bitfield (fatal?)")
}
@ -945,7 +1005,7 @@ func onSuccessfulPoSt(self *StorageMinerActorState, vmctx types.VMContext) aerro
self.FaultSet = types.NewBitField()
oldPower := self.Power
newPower := types.BigMul(types.NewInt(pss.Count-uint64(len(faults))), types.NewInt(mi.SectorSize))
newPower := types.BigMul(types.NewInt(pss.Count-activeFaults), types.NewInt(mi.SectorSize))
// If below the minimum size requirement, miners have zero power
if newPower.LessThan(types.NewInt(build.MinimumMinerPower)) {

View File

@ -3,18 +3,22 @@ package actors_test
import (
"bytes"
"context"
"math"
"math/rand"
"testing"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/rlepluslazy"
hamt "github.com/ipfs/go-hamt-ipld"
blockstore "github.com/ipfs/go-ipfs-blockstore"
"github.com/stretchr/testify/assert"
cbg "github.com/whyrusleeping/cbor-gen"
)
@ -50,23 +54,37 @@ func TestMinerCommitSectors(t *testing.T) {
addSectorToMiner(h, t, minerAddr, worker, client, 1)
assertSectorIDs(h, t, minerAddr, []uint64{1})
}
type badRuns struct {
done bool
}
func (br *badRuns) HasNext() bool {
return !br.done
}
func (br *badRuns) NextRun() (rlepluslazy.Run, error) {
br.done = true
return rlepluslazy.Run{true, math.MaxInt64}, nil
}
var _ rlepluslazy.RunIterator = (*badRuns)(nil)
func TestMinerSubmitBadFault(t *testing.T) {
oldSS, oldMin := build.SectorSizes, build.MinimumMinerPower
build.SectorSizes, build.MinimumMinerPower = []uint64{1024}, 1024
defer func() {
build.SectorSizes, build.MinimumMinerPower = oldSS, oldMin
}()
var worker, client address.Address
var minerAddr address.Address
opts := []HarnessOpt{
HarnessAddr(&worker, 1000000),
HarnessAddr(&client, 1000000),
HarnessActor(&minerAddr, &worker, actors.StorageMinerCodeCid,
func() cbg.CBORMarshaler {
return &actors.StorageMinerConstructorParams{
Owner: worker,
Worker: worker,
SectorSize: 1024,
PeerID: "fakepeerid",
}
}),
HarnessAddMiner(&minerAddr, &worker),
}
h := NewHarness(t, opts...)
@ -92,18 +110,52 @@ func TestMinerSubmitBadFault(t *testing.T) {
ret, _ = h.Invoke(t, actors.NetworkAddress, minerAddr, actors.MAMethods.SubmitElectionPoSt, nil)
ApplyOK(t, ret)
assertSectorIDs(h, t, minerAddr, []uint64{1})
st, err := getMinerState(context.TODO(), h.vm.StateTree(), h.bs, minerAddr)
assert.NoError(t, err)
expectedPower := st.Power
if types.BigCmp(expectedPower, types.NewInt(1024)) != 0 {
t.Errorf("Expected power of 1024, got %s", expectedPower)
}
badnum := uint64(0)
badnum--
bf = types.NewBitField()
bf.Set(badnum)
bf.Set(badnum - 1)
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.DeclareFaults, &actors.DeclareFaultsParams{bf})
ApplyOK(t, ret)
ret, _ = h.Invoke(t, actors.NetworkAddress, minerAddr, actors.MAMethods.SubmitElectionPoSt, nil)
ApplyOK(t, ret)
assertSectorIDs(h, t, minerAddr, []uint64{1})
st, err = getMinerState(context.TODO(), h.vm.StateTree(), h.bs, minerAddr)
assert.NoError(t, err)
currentPower := st.Power
if types.BigCmp(expectedPower, currentPower) != 0 {
t.Errorf("power changed and shouldn't have: %s != %s", expectedPower, currentPower)
}
bf.Set(badnum - 2)
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.DeclareFaults, &actors.DeclareFaultsParams{bf})
if ret.ExitCode != 3 {
t.Errorf("expected exit code 3, got %d: %+v", ret.ExitCode, ret.ActorErr)
}
assertSectorIDs(h, t, minerAddr, []uint64{1})
rle, err := rlepluslazy.EncodeRuns(&badRuns{}, []byte{})
assert.NoError(t, err)
bf, err = types.NewBitFieldFromBytes(rle)
assert.NoError(t, err)
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.DeclareFaults, &actors.DeclareFaultsParams{bf})
if ret.ExitCode != 3 {
t.Errorf("expected exit code 3, got %d: %+v", ret.ExitCode, ret.ActorErr)
}
assertSectorIDs(h, t, minerAddr, []uint64{1})
bf = types.NewBitField()
bf.Set(1)
@ -175,7 +227,7 @@ func assertSectorIDs(h *Harness, t *testing.T, maddr address.Address, ids []uint
}
}
func getMinerSectorSet(ctx context.Context, st types.StateTree, bs blockstore.Blockstore, maddr address.Address) ([]*api.ChainSectorInfo, error) {
func getMinerState(ctx context.Context, st types.StateTree, bs blockstore.Blockstore, maddr address.Address) (*actors.StorageMinerActorState, error) {
mact, err := st.GetActor(maddr)
if err != nil {
return nil, err
@ -187,6 +239,14 @@ func getMinerSectorSet(ctx context.Context, st types.StateTree, bs blockstore.Bl
if err := cst.Get(ctx, mact.Head, &mstate); err != nil {
return nil, err
}
return &mstate, nil
}
func getMinerSectorSet(ctx context.Context, st types.StateTree, bs blockstore.Blockstore, maddr address.Address) ([]*api.ChainSectorInfo, error) {
mstate, err := getMinerState(ctx, st, bs, maddr)
if err != nil {
return nil, err
}
return stmgr.LoadSectorsFromSet(ctx, bs, mstate.Sectors)
}

View File

@ -67,7 +67,7 @@ type CreateStorageMinerParams struct {
func (spa StoragePowerActor) CreateStorageMiner(act *types.Actor, vmctx types.VMContext, params *CreateStorageMinerParams) ([]byte, ActorError) {
if !build.SupportedSectorSize(params.SectorSize) {
return nil, aerrors.New(1, "Unsupported sector size")
return nil, aerrors.Newf(1, "Unsupported sector size: %d", params.SectorSize)
}
var self StoragePowerState

View File

@ -115,6 +115,32 @@ func HarnessActor(actor *address.Address, creator *address.Address, code cid.Cid
}
func HarnessAddMiner(addr *address.Address, creator *address.Address) HarnessOpt {
return func(t testing.TB, h *Harness) error {
if h.Stage != HarnessPostInit {
return nil
}
if !addr.Empty() {
return xerrors.New("actor address should be empty")
}
ret, _ := h.InvokeWithValue(t, *creator, actors.StoragePowerAddress,
actors.SPAMethods.CreateStorageMiner, types.NewInt(3000), &actors.StorageMinerConstructorParams{
Owner: *creator,
Worker: *creator,
SectorSize: 1024,
PeerID: "fakepeerid",
})
if ret.ExitCode != 0 {
return xerrors.Errorf("creating actor: %w", ret.ActorErr)
}
var err error
*addr, err = address.NewFromBytes(ret.Return)
return err
}
}
func HarnessCtx(ctx context.Context) HarnessOpt {
return func(t testing.TB, h *Harness) error {
h.ctx = ctx
@ -177,7 +203,7 @@ func NewHarness(t *testing.T, options ...HarnessOpt) *Harness {
for _, opt := range options {
err := opt(t, h)
if err != nil {
t.Fatalf("Applying options: %v", err)
t.Fatalf("Applying options: %+v", err)
}
}

View File

@ -1,6 +1,7 @@
package types
import (
"errors"
"fmt"
"io"
@ -9,6 +10,8 @@ import (
"golang.org/x/xerrors"
)
var ErrBitFieldTooMany = errors.New("to many items in RLE")
type BitField struct {
rle rlepluslazy.RLE
@ -16,14 +19,23 @@ type BitField struct {
}
func NewBitField() BitField {
rle, err := rlepluslazy.FromBuf([]byte{})
bf, err := NewBitFieldFromBytes([]byte{})
if err != nil {
panic(err)
panic(fmt.Sprintf("creating empty rle: %+v", err))
}
return BitField{
rle: rle,
bits: make(map[uint64]struct{}),
return bf
}
func NewBitFieldFromBytes(rle []byte) (BitField, error) {
bf := BitField{}
rlep, err := rlepluslazy.FromBuf(rle)
if err != nil {
return BitField{}, xerrors.Errorf("could not decode rle+: %w", err)
}
bf.rle = rlep
bf.bits = make(map[uint64]struct{})
return bf, nil
}
func BitFieldFromSet(setBits []uint64) BitField {
@ -106,7 +118,14 @@ func (bf BitField) Count() (uint64, error) {
}
// All returns all set bits
func (bf BitField) All() ([]uint64, error) {
func (bf BitField) All(max uint64) ([]uint64, error) {
c, err := bf.Count()
if err != nil {
return nil, xerrors.Errorf("count errror: %w", err)
}
if c > max {
return nil, xerrors.Errorf("expected %d, got %d: %w", max, c, ErrBitFieldTooMany)
}
runs, err := bf.sum()
if err != nil {
@ -121,7 +140,14 @@ func (bf BitField) All() ([]uint64, error) {
return res, nil
}
func (bf BitField) AllMap() (map[uint64]bool, error) {
func (bf BitField) AllMap(max uint64) (map[uint64]bool, error) {
c, err := bf.Count()
if err != nil {
return nil, xerrors.Errorf("count errror: %w", err)
}
if c > max {
return nil, xerrors.Errorf("expected %d, got %d: %w", max, c, ErrBitFieldTooMany)
}
runs, err := bf.sum()
if err != nil {

View File

@ -216,7 +216,7 @@ func IsTicketWinner(partialTicket []byte, ssizeI uint64, snum uint64, totpow Big
return lhs.Cmp(rhs) < 0
}
func ElectionPostChallengeCount(sectors uint64, faults int) uint64 {
func ElectionPostChallengeCount(sectors uint64, faults uint64) uint64 {
return sectorbuilder.ElectionPostChallengeCount(sectors, faults)
}

2
go.mod
View File

@ -16,7 +16,7 @@ require (
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878
github.com/filecoin-project/go-sectorbuilder v0.0.0-20200107152336-0cbb2c483013
github.com/filecoin-project/go-sectorbuilder v0.0.0-20200107220006-3361d30ea5ab
github.com/filecoin-project/go-statestore v0.0.0-20200102200712-1f63c701c1e5
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
github.com/go-ole/go-ole v1.2.4 // indirect

4
go.sum
View File

@ -93,8 +93,8 @@ github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMX
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
github.com/filecoin-project/go-sectorbuilder v0.0.0-20200107152336-0cbb2c483013 h1:OGpRq3HRxyrxZJtbNKCOsb5YTmc+RBLLwdAgwZfkRnY=
github.com/filecoin-project/go-sectorbuilder v0.0.0-20200107152336-0cbb2c483013/go.mod h1:3OZ4E3B2OuwhJjtxR4r7hPU9bCfB+A+hm4alLEsaeDc=
github.com/filecoin-project/go-sectorbuilder v0.0.0-20200107220006-3361d30ea5ab h1:bsrBNO1LwnhOLxPEXlSPal/WuY61mLJUCHYyD0NayHg=
github.com/filecoin-project/go-sectorbuilder v0.0.0-20200107220006-3361d30ea5ab/go.mod h1:3OZ4E3B2OuwhJjtxR4r7hPU9bCfB+A+hm4alLEsaeDc=
github.com/filecoin-project/go-statestore v0.0.0-20200102200712-1f63c701c1e5 h1:NZXq90YlfakSmB2/84dGr0AVmKYFA97+yyViBIgTFbk=
github.com/filecoin-project/go-statestore v0.0.0-20200102200712-1f63c701c1e5/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=