From ee29a92021dba9ce38b0be4d225c94205fa6cd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 1 Jun 2020 14:49:48 +0200 Subject: [PATCH] Add a test for Window Post --- api/api_storage.go | 3 +- api/test/test.go | 1 + api/test/window_post.go | 168 ++++++++++++++++++++++++++++++++++++++++ node/node_test.go | 48 ++++++++++-- storage/wdpost_run.go | 9 +++ 5 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 api/test/window_post.go diff --git a/api/api_storage.go b/api/api_storage.go index ab75e8058..d6004dcf3 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/sector-storage/stores" "github.com/filecoin-project/sector-storage/storiface" "github.com/filecoin-project/specs-actors/actors/abi" + sealing "github.com/filecoin-project/storage-fsm" "github.com/filecoin-project/lotus/chain/types" ) @@ -119,4 +120,4 @@ func (st *SealSeed) Equals(ost *SealSeed) bool { return bytes.Equal(st.Value, ost.Value) && st.Epoch == ost.Epoch } -type SectorState string +type SectorState = sealing.SectorState diff --git a/api/test/test.go b/api/test/test.go index 43dc7a25e..06f0584fd 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -20,6 +20,7 @@ type TestStorageNode struct { } var PresealGenesis = -1 +const GenesisPreseals = 2 type StorageMiner struct { Full int diff --git a/api/test/window_post.go b/api/test/window_post.go new file mode 100644 index 000000000..b67b3e498 --- /dev/null +++ b/api/test/window_post.go @@ -0,0 +1,168 @@ +package test + +import ( + "context" + "fmt" + "os" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/specs-actors/actors/abi" + miner2 "github.com/filecoin-project/specs-actors/actors/builtin/miner" + sealing "github.com/filecoin-project/storage-fsm" + + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/node/impl" +) + +func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { + os.Setenv("BELLMAN_NO_GPU", "1") + + ctx := context.Background() + n, sn := b(t, 1, oneMiner) + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + time.Sleep(time.Second) + + mine := true + done := make(chan struct{}) + go func() { + defer close(done) + for mine { + time.Sleep(blocktime) + if err := sn[0].MineOne(ctx, func(bool) {}); err != nil { + t.Error(err) + } + } + }() + + pledgeSectors(t, ctx, miner, nSectors) + + mine = false + <-done +} + +func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n int) { + for i := 0; i < n; i++ { + err := miner.PledgeSector(ctx) + require.NoError(t, err) + } + + for { + s, err := miner.SectorsList(ctx) // Note - the test builder doesn't import genesis sectors into FSM + require.NoError(t, err) + fmt.Printf("Sectors: %d\n", len(s)) + if len(s) >= n { + break + } + + time.Sleep(100 * time.Millisecond) + } + + fmt.Printf("All sectors is fsm\n") + + s, err := miner.SectorsList(ctx) + require.NoError(t, err) + + toCheck := map[abi.SectorNumber]struct{}{} + for _, number := range s { + toCheck[number] = struct{}{} + } + + for len(toCheck) > 0 { + for n := range toCheck { + st, err := miner.SectorsStatus(ctx, n) + require.NoError(t, err) + if st.State == sealing.Proving { + delete(toCheck, n) + } + if strings.Contains(string(st.State), "Fail") { + t.Fatal("sector in a failed state", st.State) + } + } + + time.Sleep(100 * time.Millisecond) + fmt.Printf("WaitSeal: %d\n", len(s)) + } +} + +func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { + os.Setenv("BELLMAN_NO_GPU", "1") + + ctx := context.Background() + n, sn := b(t, 1, oneMiner) + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + time.Sleep(time.Second) + + mine := true + done := make(chan struct{}) + go func() { + defer close(done) + for mine { + time.Sleep(blocktime) + if err := sn[0].MineOne(ctx, func(bool) {}); err != nil { + t.Error(err) + } + } + }() + + pledgeSectors(t, ctx, miner, nSectors) + + maddr, err := miner.ActorAddress(ctx) + require.NoError(t, err) + + di, err := client.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + + fmt.Printf("Running one proving periods\n") + + for { + head, err := client.ChainHead(ctx) + require.NoError(t, err) + + if head.Height() > di.PeriodStart + (miner2.WPoStProvingPeriod) + 2 { + break + } + + if head.Height() % 100 == 0 { + fmt.Printf("@%d\n", head.Height()) + } + time.Sleep(blocktime) + } + + p, err := client.StateMinerPower(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + + ssz, err := miner.ActorSectorSize(ctx, maddr) + require.NoError(t, err) + + require.Equal(t, p.MinerPower, p.TotalPower) + require.Equal(t, p.MinerPower.RawBytePower, types.NewInt(uint64(ssz) * uint64(nSectors + GenesisPreseals))) + + // TODO: Inject faults here + + mine = false + <-done +} diff --git a/node/node_test.go b/node/node_test.go index a31b4162b..9b543ee7a 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -83,7 +83,7 @@ func testStorageNode(ctx context.Context, t *testing.T, waddr address.Address, a require.NoError(t, err) nic := storedcounter.New(ds, datastore.NewKey(modules.StorageCounterDSPrefix)) - for i := 0; i < nGenesisPreseals; i++ { + for i := 0; i < test.GenesisPreseals; i++ { _, err := nic.Next() require.NoError(t, err) } @@ -188,7 +188,7 @@ func builder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestN if err != nil { t.Fatal(err) } - genm, k, err := seed.PreSeal(maddr, abi.RegisteredProof_StackedDRG2KiBPoSt, 0, nGenesisPreseals, tdir, []byte("make genesis mem random"), nil) + genm, k, err := seed.PreSeal(maddr, abi.RegisteredProof_StackedDRG2KiBPoSt, 0, test.GenesisPreseals, tdir, []byte("make genesis mem random"), nil) if err != nil { t.Fatal(err) } @@ -282,8 +282,6 @@ func builder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestN return fulls, storers } -const nGenesisPreseals = 2 - func mockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test.TestNode, []test.TestStorageNode) { ctx := context.Background() mn := mocknet.New(ctx) @@ -314,7 +312,7 @@ func mockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test preseals := storage[i].Preseal if preseals == test.PresealGenesis { - preseals = nGenesisPreseals + preseals = test.GenesisPreseals } genm, k, err := mockstorage.PreSeal(2048, maddr, preseals) @@ -350,7 +348,7 @@ func mockSbBuilder(t *testing.T, nFull int, storage []test.StorageMiner) ([]test templ := &genesis.Template{ Accounts: genaccs, Miners: genms, - Timestamp: uint64(time.Now().Unix() - 10000), + Timestamp: uint64(time.Now().Unix() - (build.BlockDelay * 20000)), } // END PRESEAL SECTION @@ -493,3 +491,41 @@ func TestDealMining(t *testing.T) { test.TestDealMining(t, mockSbBuilder, 50*time.Millisecond, false) } + +func TestPledgeSectors(t *testing.T) { + logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("chainstore", "ERROR") + logging.SetLogLevel("chain", "ERROR") + logging.SetLogLevel("sub", "ERROR") + logging.SetLogLevel("storageminer", "ERROR") + + t.Run("1", func(t *testing.T) { + test.TestPledgeSector(t, mockSbBuilder, 50*time.Millisecond, 1) + }) + + t.Run("100", func(t *testing.T) { + test.TestPledgeSector(t, mockSbBuilder, 50*time.Millisecond, 100) + }) + + t.Run("1000", func(t *testing.T) { + if testing.Short() { // takes ~16s + t.Skip("skipping test in short mode") + } + + test.TestPledgeSector(t, mockSbBuilder, 50*time.Millisecond, 1000) + }) +} + +func TestWindowedPost(t *testing.T) { + if testing.Short() { + t.Skip("this takes a while") + } + + logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("chainstore", "ERROR") + logging.SetLogLevel("chain", "ERROR") + logging.SetLogLevel("sub", "ERROR") + logging.SetLogLevel("storageminer", "ERROR") + + test.TestWindowPost(t, mockSbBuilder, 3*time.Millisecond, 10) +} diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index 08f5def30..fbdae5199 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -189,6 +189,15 @@ func (s *WindowPoStScheduler) checkNextRecoveries(ctx context.Context, deadline } func (s *WindowPoStScheduler) checkNextFaults(ctx context.Context, deadline uint64, deadlineSectors *abi.BitField, ts *types.TipSet) error { + dc, err := deadlineSectors.Count() + if err != nil { + return xerrors.Errorf("counting deadline sectors: %w", err) + } + if dc == 0 { + // nothing can become faulty + return nil + } + toCheck, err := s.getSectorsToProve(ctx, deadlineSectors, true, ts) if err != nil { return xerrors.Errorf("getting next sectors to prove: %w", err)