lotus/chain/actors/actor_miner_test.go
2020-02-10 20:16:36 +01:00

286 lines
8.6 KiB
Go

package actors_test
import (
"bytes"
"context"
"math"
"math/rand"
"testing"
"github.com/filecoin-project/go-address"
commcid "github.com/filecoin-project/go-fil-commcid"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/specs-actors/actors/crypto"
blockstore "github.com/ipfs/go-ipfs-blockstore"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/stretchr/testify/assert"
cbg "github.com/whyrusleeping/cbor-gen"
"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"
)
func TestMinerCommitSectors(t *testing.T) {
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",
}
}),
}
h := NewHarness(t, opts...)
h.vm.Syscalls.ValidatePoRep = func(ctx context.Context, maddr address.Address, ssize abi.SectorSize, commD, commR, ticket, proof, seed []byte, sectorID abi.SectorNumber) (bool, aerrors.ActorError) {
// all proofs are valid
return true, nil
}
ret, _ := h.SendFunds(t, worker, minerAddr, types.NewInt(100000))
ApplyOK(t, ret)
ret, _ = h.InvokeWithValue(t, client, actors.StorageMarketAddress, actors.SMAMethods.AddBalance, types.NewInt(2000), nil)
ApplyOK(t, ret)
addSectorToMiner(h, t, minerAddr, worker, client, 1)
assertSectorIDs(h, t, minerAddr, []abi.SectorNumber{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 = []abi.SectorSize{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),
HarnessAddMiner(&minerAddr, &worker),
}
h := NewHarness(t, opts...)
h.vm.Syscalls.ValidatePoRep = func(ctx context.Context, maddr address.Address, ssize abi.SectorSize, commD, commR, ticket, proof, seed []byte, sectorID abi.SectorNumber) (bool, aerrors.ActorError) {
// all proofs are valid
return true, nil
}
ret, _ := h.SendFunds(t, worker, minerAddr, types.NewInt(100000))
ApplyOK(t, ret)
ret, _ = h.InvokeWithValue(t, client, actors.StorageMarketAddress, actors.SMAMethods.AddBalance, types.NewInt(2000), nil)
ApplyOK(t, ret)
addSectorToMiner(h, t, minerAddr, worker, client, 1)
assertSectorIDs(h, t, minerAddr, []abi.SectorNumber{1})
bf := types.NewBitField()
bf.Set(6)
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, []abi.SectorNumber{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, []abi.SectorNumber{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, []abi.SectorNumber{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, []abi.SectorNumber{1})
bf = types.NewBitField()
bf.Set(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, []abi.SectorNumber{})
}
func addSectorToMiner(h *Harness, t *testing.T, minerAddr, worker, client address.Address, sid abi.SectorNumber) {
t.Helper()
s := abi.PaddedPieceSize(1024).Unpadded()
deal := h.makeFakeDeal(t, minerAddr, worker, client, s)
ret, _ := h.Invoke(t, worker, actors.StorageMarketAddress, actors.SMAMethods.PublishStorageDeals,
&actors.PublishStorageDealsParams{
Deals: []market.ClientDealProposal{*deal},
})
ApplyOK(t, ret)
var dealIds actors.PublishStorageDealResponse
if err := dealIds.UnmarshalCBOR(bytes.NewReader(ret.Return)); err != nil {
t.Fatal(err)
}
dealid := dealIds.IDs[0]
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.PreCommitSector,
&miner.SectorPreCommitInfo{
SectorNumber: sid,
SealedCID: commcid.ReplicaCommitmentV1ToCID([]byte("cats")),
SealEpoch: 10,
DealIDs: []abi.DealID{dealid},
})
ApplyOK(t, ret)
h.BlockHeight += 100
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.ProveCommitSector,
&miner.ProveCommitSectorParams{
SectorNumber: sid,
Proof: abi.SealProof{ProofBytes: []byte("prooofy")},
})
ApplyOK(t, ret)
}
func assertSectorIDs(h *Harness, t *testing.T, maddr address.Address, ids []abi.SectorNumber) {
t.Helper()
sectors, err := getMinerSectorSet(context.TODO(), h.vm.StateTree(), h.bs, maddr)
if err != nil {
t.Fatal(err)
}
if len(sectors) != len(ids) {
t.Fatal("miner has wrong number of sectors in their sector set")
}
all := make(map[abi.SectorNumber]bool)
for _, s := range sectors {
all[s.SectorID] = true
}
for _, id := range ids {
if !all[id] {
t.Fatal("expected to find sector ID: ", id)
}
}
}
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
}
cst := cbor.NewCborStore(bs)
var mstate actors.StorageMinerActorState
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)
}
func (h *Harness) makeFakeDeal(t *testing.T, miner, worker, client address.Address, size abi.UnpaddedPieceSize) *market.ClientDealProposal {
data := make([]byte, size)
rand.Read(data)
commP, err := sectorbuilder.GeneratePieceCommitment(bytes.NewReader(data), size)
if err != nil {
t.Fatal(err)
}
prop := market.DealProposal{
PieceCID: commcid.PieceCommitmentV1ToCID(commP[:]),
PieceSize: size.Padded(),
Client: client,
Provider: miner,
StartEpoch: 10000,
EndEpoch: 10150,
StoragePricePerEpoch: types.NewInt(1),
ProviderCollateral: types.NewInt(0),
}
return &market.ClientDealProposal{
Proposal: prop,
ClientSignature: crypto.Signature{}, // TODO: not quite correct
}
}