diff --git a/api/test/window_post.go b/api/test/window_post.go index f8ab9412d..99d480836 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -27,7 +27,7 @@ import ( "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/builtin/miner" + minerActor "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" bminer "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/node/impl" @@ -747,7 +747,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) // OBJECTION! The good miner files a DISPUTE!!!! { - params := &miner.DisputeWindowedPoStParams{ + params := &minerActor.DisputeWindowedPoStParams{ Deadline: evilSectorLoc.Deadline, PoStIndex: 0, } @@ -757,7 +757,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) msg := &types.Message{ To: evilMinerAddr, - Method: miner.Methods.DisputeWindowedPoSt, + Method: minerActor.Methods.DisputeWindowedPoSt, Params: enc, Value: types.NewInt(0), From: defaultFrom, @@ -769,7 +769,6 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) rec, err := client.StateWaitMsg(ctx, sm.Cid(), build.MessageConfidence) require.NoError(t, err) require.Zero(t, rec.Receipt.ExitCode, "dispute not accepted: %s", rec.Receipt.ExitCode.Error()) - fmt.Println("GASS!!!: ", rec.Receipt.GasUsed) } // Objection SUSTAINED! @@ -787,8 +786,8 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) minerInfo, err := client.StateMinerInfo(ctx, evilMinerAddr, types.EmptyTSK) require.NoError(t, err) - params := &miner.DeclareFaultsRecoveredParams{ - Recoveries: []miner.RecoveryDeclaration{{ + params := &minerActor.DeclareFaultsRecoveredParams{ + Recoveries: []minerActor.RecoveryDeclaration{{ Deadline: evilSectorLoc.Deadline, Partition: evilSectorLoc.Partition, Sectors: bitfield.NewFromSet([]uint64{uint64(evilSectorNo)}), @@ -800,7 +799,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) msg := &types.Message{ To: evilMinerAddr, - Method: miner.Methods.DeclareFaultsRecovered, + Method: minerActor.Methods.DeclareFaultsRecovered, Params: enc, Value: types.FromFil(30), // repay debt. From: minerInfo.Owner, @@ -859,11 +858,11 @@ func submitBadProof( if err != nil { return err } - params := &miner.SubmitWindowedPoStParams{ + params := &minerActor.SubmitWindowedPoStParams{ ChainCommitEpoch: commEpoch, ChainCommitRand: commRand, Deadline: dlIdx, - Partitions: []miner.PoStPartition{{Index: partIdx}}, + Partitions: []minerActor.PoStPartition{{Index: partIdx}}, Proofs: []proof3.PoStProof{{ PoStProof: minerInfo.WindowPoStProofType, ProofBytes: []byte("I'm soooo very evil."), @@ -877,7 +876,7 @@ func submitBadProof( msg := &types.Message{ To: maddr, - Method: miner.Methods.SubmitWindowedPoSt, + Method: minerActor.Methods.SubmitWindowedPoSt, Params: enc, Value: types.NewInt(0), From: from, @@ -896,3 +895,132 @@ func submitBadProof( } return nil } + +func TestWindowPostDisputeFails(t *testing.T, b APIBuilder, blocktime time.Duration) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV3At(2)}, 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) + } + } + + defaultFrom, err := client.WalletDefaultAddress(ctx) + require.NoError(t, err) + + maddr, err := miner.ActorAddress(ctx) + require.NoError(t, err) + + build.Clock.Sleep(time.Second) + + // Mine with the _second_ node (the good one). + done := make(chan struct{}) + go func() { + defer close(done) + for ctx.Err() == nil { + build.Clock.Sleep(blocktime) + if err := miner.MineOne(ctx, MineNext); err != nil { + if ctx.Err() != nil { + // context was canceled, ignore the error. + return + } + t.Error(err) + } + } + }() + defer func() { + cancel() + <-done + }() + + pledgeSectors(t, ctx, miner, 10, 0, nil) + + di, err := client.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + + fmt.Printf("Running one proving period\n") + fmt.Printf("End for head.Height > %d\n", di.PeriodStart+di.WPoStProvingPeriod*2) + + for { + head, err := client.ChainHead(ctx) + require.NoError(t, err) + + if head.Height() > di.PeriodStart+di.WPoStProvingPeriod*2 { + fmt.Printf("Now head.Height = %d\n", head.Height()) + break + } + build.Clock.Sleep(blocktime) + } + + ssz, err := miner.ActorSectorSize(ctx, maddr) + require.NoError(t, err) + expectedPower := types.NewInt(uint64(ssz) * (GenesisPreseals + 10)) + + p, err := client.StateMinerPower(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + + // make sure it has gained power. + require.Equal(t, p.MinerPower.RawBytePower, expectedPower) + + // Wait until a proof has been submitted. + var targetDeadline uint64 +waitForProof: + for { + deadlines, err := client.StateMinerDeadlines(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + for dlIdx, dl := range deadlines { + nonEmpty, err := dl.PostSubmissions.IsEmpty() + require.NoError(t, err) + if nonEmpty { + targetDeadline = uint64(dlIdx) + break waitForProof + } + } + + build.Clock.Sleep(blocktime) + } + + for { + di, err := client.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + // wait until the deadline finishes. + if di.Index == ((targetDeadline + 1) % di.WPoStPeriodDeadlines) { + break + } + + build.Clock.Sleep(blocktime) + } + + // Try to object to the proof. This should fail. + { + params := &minerActor.DisputeWindowedPoStParams{ + Deadline: targetDeadline, + PoStIndex: 0, + } + + enc, aerr := actors.SerializeParams(params) + require.NoError(t, aerr) + + msg := &types.Message{ + To: maddr, + Method: minerActor.Methods.DisputeWindowedPoSt, + Params: enc, + Value: types.NewInt(0), + From: defaultFrom, + } + _, err := client.MpoolPushMessage(ctx, msg, nil) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to dispute valid post (RetCode=16)") + } +} diff --git a/node/node_test.go b/node/node_test.go index a44b0f3a8..142e8875a 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -211,3 +211,16 @@ func TestWindowPostDispute(t *testing.T) { test.TestWindowPostDispute(t, builder.MockSbBuilder, 2*time.Millisecond) } + +func TestWindowPostDisputeFails(t *testing.T) { + if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" { + t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run") + } + logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("chainstore", "ERROR") + logging.SetLogLevel("chain", "ERROR") + logging.SetLogLevel("sub", "ERROR") + logging.SetLogLevel("storageminer", "ERROR") + + test.TestWindowPostDisputeFails(t, builder.MockSbBuilder, 2*time.Millisecond) +}