diff --git a/actor/builtin/miner/miner.go b/actor/builtin/miner/miner.go index d073ba58d7..80c89c97cc 100644 --- a/actor/builtin/miner/miner.go +++ b/actor/builtin/miner/miner.go @@ -137,20 +137,22 @@ type State struct { NextAskID *big.Int // SectorCommitments maps sector id to commitments, for all sectors this - // miner has committed. Due to a bug in refmt, the sector id-keys need to be + // miner has committed. Sector ids are removed from this collection + // when they are included in the done or fault parameters of submitPoSt. + // Due to a bug in refmt, the sector id-keys need to be // stringified. // // See also: https://github.com/polydawn/refmt/issues/35 SectorCommitments SectorSet - // NextDoneSet is a set of sectorIDs reported during the last PoSt + // NextDoneSet is a set of sector ids reported during the last PoSt // submission as being 'done'. The collateral for them is still being // held until the next PoSt submission in case early sector removal // penalization is needed. NextDoneSet types.IntSet - // ProvingSet is the set of sectorIDs this miner is currently proving. - // It is only updated when a PoSt is submitted. + // ProvingSet is the set of sector ids of sectors this miner is + // currently required to prove. ProvingSet types.IntSet LastUsedSectorID uint64 @@ -159,7 +161,8 @@ type State struct { ProvingPeriodEnd *types.BlockHeight LastPoSt *types.BlockHeight - // The amount of space committed to the network by this miner. + // The amount of space proven to the network by this miner in the + // latest proving period. Power *types.BytesAmount // SectorSize is the amount of space in each sector committed to the network @@ -561,11 +564,18 @@ func (ma *Actor) CommitSector(ctx exec.VMContext, sectorID uint64, commD, commR, state.ActiveCollateral = state.ActiveCollateral.Add(collateral) - if state.Power.Equal(types.NewBytesAmount(0)) { + // Case 1: If the miner is not currently proving any sectors, + // start proving immediately on this sector. + // + // Case 2: If the miner is adding sectors during genesis + // construction all committed sectors accumulate in their + // proving set. This allows us to add power immediately in + // genesis with commitSector and submitPoSt calls without + // adding special casing for bootstrappers. + if state.ProvingSet.Size() == 0 || ctx.BlockHeight().Equal(types.NewBlockHeight(0)) { + state.ProvingSet = state.ProvingSet.Add(sectorID) state.ProvingPeriodEnd = ctx.BlockHeight().Add(types.NewBlockHeight(ProvingPeriodDuration(state.SectorSize))) } - inc := state.SectorSize - state.Power = state.Power.Add(inc) comms := types.Commitments{ CommD: types.CommD{}, CommR: types.CommR{}, @@ -577,13 +587,6 @@ func (ma *Actor) CommitSector(ctx exec.VMContext, sectorID uint64, commD, commR, state.LastUsedSectorID = sectorID state.SectorCommitments.Add(sectorID, comms) - _, ret, err := ctx.Send(address.StorageMarketAddress, "updateStorage", types.ZeroAttoFIL, []interface{}{inc}) - if err != nil { - return nil, err - } - if ret != 0 { - return nil, Errors[ErrStoragemarketCallFailed] - } return nil, nil }) if err != nil { @@ -839,18 +842,33 @@ func (ma *Actor) SubmitPoSt(ctx exec.VMContext, poStProofs []types.PoStProof, do state.ProvingPeriodEnd = state.ProvingPeriodEnd.Add(types.NewBlockHeight(ProvingPeriodDuration(state.SectorSize))) state.LastPoSt = chainHeight + // Update miner power to the amount of data actually proved + // during the last proving period. + oldPower := state.Power + // TODO subtract total faulted size from ProvingSet size #2889 + newPower := types.NewBytesAmount(uint64(state.ProvingSet.Size())).Mul(state.SectorSize) + state.Power = newPower + delta := newPower.Sub(oldPower) + _, ret, err := ctx.Send(address.StorageMarketAddress, "updateStorage", types.ZeroAttoFIL, []interface{}{delta}) + if err != nil { + return nil, err + } + if ret != 0 { + return nil, Errors[ErrStoragemarketCallFailed] + } + // Update SectorSet, DoneSet and ProvingSet if err = state.SectorCommitments.Drop(done.Values()); err != nil { return nil, err } - + sectorIDsToProve, err := state.SectorCommitments.IDs() if err != nil { return nil, err } state.ProvingSet = types.NewIntSet(sectorIDsToProve...) state.NextDoneSet = done - + return nil, nil }) if err != nil { diff --git a/actor/builtin/miner/miner_test.go b/actor/builtin/miner/miner_test.go index 699afc856a..454c548cb3 100644 --- a/actor/builtin/miner/miner_test.go +++ b/actor/builtin/miner/miner_test.go @@ -461,103 +461,347 @@ func TestMinerCommitSector(t *testing.T) { }) } -func TestMinerSubmitPoStSectorSetUpdates(t *testing.T) { - tf.UnitTest(t) +// minerActorLiason provides a set of test friendly calls for setting up, reading +// internals, and transitioning the portion of the filecoin state machine +// related to a particular miner actor. +type minerActorLiason struct { + st state.Tree + vms vm.StorageMap + ancestors []types.TipSet + minerAddr address.Address + t *testing.T + currentHeight uint64 +} +func (mal *minerActorLiason) requireHeightNotPast(blockHeight uint64) { + require.True(mal.t, blockHeight >= mal.currentHeight) + mal.currentHeight = blockHeight +} + +func (mal *minerActorLiason) requireCommit(blockHeight, sectorID uint64) { + mal.requireHeightNotPast(blockHeight) + res, err := th.CreateAndApplyTestMessage(mal.t, mal.st, mal.vms, mal.minerAddr, 0, blockHeight, "commitSector", mal.ancestors, sectorID, th.MakeCommitment(), th.MakeCommitment(), th.MakeCommitment(), th.MakeRandomBytes(types.TwoPoRepProofPartitions.ProofLen())) + require.NoError(mal.t, err) + require.NoError(mal.t, res.ExecutionError) + require.Equal(mal.t, uint8(0), res.Receipt.ExitCode) +} + +func (mal *minerActorLiason) requirePoSt(blockHeight uint64, done types.IntSet) { + mal.requireHeightNotPast(blockHeight) + res, err := th.CreateAndApplyTestMessage(mal.t, mal.st, mal.vms, mal.minerAddr, 0, blockHeight, "submitPoSt", mal.ancestors, []types.PoStProof{th.MakeRandomPoStProofForTest()}, done) + assert.NoError(mal.t, err) + assert.NoError(mal.t, res.ExecutionError) + assert.Equal(mal.t, uint8(0), res.Receipt.ExitCode) +} + +func (mal *minerActorLiason) requireReadState() State { + miner := state.MustGetActor(mal.st, mal.minerAddr) + storage := mal.vms.NewStorage(mal.minerAddr, miner) + stateBytes, err := storage.Get(storage.Head()) + require.NoError(mal.t, err) + var minerState State + err = actor.UnmarshalStorage(stateBytes, &minerState) + require.NoError(mal.t, err) + return minerState +} + +func (mal *minerActorLiason) requirePower(queryHeight uint64) *types.BytesAmount { + mal.requireHeightNotPast(queryHeight) + res, err := th.CreateAndApplyTestMessage(mal.t, mal.st, mal.vms, mal.minerAddr, 0, queryHeight, "getPower", mal.ancestors) + require.NoError(mal.t, err) + require.NoError(mal.t, res.ExecutionError) + require.Equal(mal.t, uint8(0), res.Receipt.ExitCode) + require.Equal(mal.t, 1, len(res.Receipt.Return)) + return types.NewBytesAmountFromBytes(res.Receipt.Return[0]) +} + +func (mal *minerActorLiason) requireTotalStorage(queryHeight uint64) *types.BytesAmount { + mal.requireHeightNotPast(queryHeight) + res, err := th.CreateAndApplyTestMessage(mal.t, mal.st, mal.vms, address.StorageMarketAddress, 0, queryHeight, "getTotalStorage", mal.ancestors) + require.NoError(mal.t, err) + require.NoError(mal.t, res.ExecutionError) + require.Equal(mal.t, uint8(0), res.Receipt.ExitCode) + require.Equal(mal.t, 1, len(res.Receipt.Return)) + return types.NewBytesAmountFromBytes(res.Receipt.Return[0]) +} + +func (mal *minerActorLiason) assertPoStFail(blockHeight uint64, done types.IntSet, exitCode uint8) { + mal.requireHeightNotPast(blockHeight) + res, err := th.CreateAndApplyTestMessage(mal.t, mal.st, mal.vms, mal.minerAddr, 0, blockHeight, "submitPoSt", mal.ancestors, []types.PoStProof{th.MakeRandomPoStProofForTest()}, done) + assert.NoError(mal.t, err) + assert.Error(mal.t, res.ExecutionError) + assert.Equal(mal.t, exitCode, res.Receipt.ExitCode) +} + +func newMinerActorLiason(t *testing.T, st state.Tree, vms vm.StorageMap, ancestors []types.TipSet, minerAddr address.Address ) *minerActorLiason { + return &minerActorLiason{ + t: t, + st: st, + vms: vms, + ancestors: ancestors, + minerAddr: minerAddr, + currentHeight: 0, + } +} + +func setupMinerActorLiason(t *testing.T) *minerActorLiason { ctx := context.Background() st, vms := th.RequireCreateStorages(ctx, t) ancestors := th.RequireTipSetChain(t, 10) origPid := th.RequireRandomPeerID(t) minerAddr := th.CreateTestMiner(t, st, vms, address.TestAddress, origPid) - proof := th.MakeRandomPoStProofForTest() - done := types.EmptyIntSet() + return newMinerActorLiason(t, st, vms, ancestors, minerAddr) +} + +func TestMinerSubmitPoStPowerUpdates(t *testing.T) { + tf.UnitTest(t) firstCommitBlockHeight := uint64(3) secondProvingPeriodStart := LargestSectorSizeProvingPeriodBlocks + firstCommitBlockHeight thirdProvingPeriodStart := 2*LargestSectorSizeProvingPeriodBlocks + firstCommitBlockHeight fourthProvingPeriodStart := 3*LargestSectorSizeProvingPeriodBlocks + firstCommitBlockHeight + t.Run("power is 0 until first PoSt", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // commit several sectors + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(3)) + + power := mal.requirePower(firstCommitBlockHeight+2) + assert.Equal(t, types.NewBytesAmount(0), power) + }) + + t.Run("power is 1 after first PoSt", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // commit several sectors + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+2, uint64(3)) - // add a sector - res, err := th.CreateAndApplyTestMessage(t, st, vms, minerAddr, 0, firstCommitBlockHeight, "commitSector", ancestors, uint64(1), th.MakeCommitment(), th.MakeCommitment(), th.MakeCommitment(), th.MakeRandomBytes(types.TwoPoRepProofPartitions.ProofLen())) - require.NoError(t, err) - require.NoError(t, res.ExecutionError) - require.Equal(t, uint8(0), res.Receipt.ExitCode) + // submit PoSt and add some power. + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) - // add another sector - res, err = th.CreateAndApplyTestMessage(t, st, vms, minerAddr, 0, firstCommitBlockHeight+1, "commitSector", ancestors, uint64(2), th.MakeCommitment(), th.MakeCommitment(), th.MakeCommitment(), th.MakeRandomBytes(types.TwoPoRepProofPartitions.ProofLen())) - require.NoError(t, err) - require.NoError(t, res.ExecutionError) - require.Equal(t, uint8(0), res.Receipt.ExitCode) + power := mal.requirePower(firstCommitBlockHeight+5) + assert.Equal(t, types.OneKiBSectorSize, power) + }) + + t.Run("power accumulates over multiple proving periods", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // Period 1 commit and prove + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) + power := mal.requirePower(firstCommitBlockHeight+6) + assert.Equal(t, types.OneKiBSectorSize, power) + + // Period 2 commit and prove + mal.requireCommit(secondProvingPeriodStart+1, uint64(16)) + mal.requireCommit(secondProvingPeriodStart+2, uint64(17)) + mal.requirePoSt(secondProvingPeriodStart+5, done) + power = mal.requirePower(secondProvingPeriodStart+6) + assert.Equal(t, types.NewBytesAmount(2).Mul(types.OneKiBSectorSize), power) + + // Period 3 prove over 4 sectors and measure power + mal.requirePoSt(thirdProvingPeriodStart+5, done) + power = mal.requirePower(thirdProvingPeriodStart+6) + assert.Equal(t, types.NewBytesAmount(4).Mul(types.OneKiBSectorSize), power) + }) + + t.Run("power removed with sectors", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // Period 1 commit and prove + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) - t.Run("proving-set updates to newly added sectors", func(t *testing.T) { - // submit post - res, err = th.CreateAndApplyTestMessage(t, st, vms, minerAddr, 0, firstCommitBlockHeight+5, "submitPoSt", ancestors, []types.PoStProof{proof}, done) - assert.NoError(t, err) - assert.NoError(t, res.ExecutionError) - assert.Equal(t, uint8(0), res.Receipt.ExitCode) + // Period 2 commit and prove + mal.requireCommit(secondProvingPeriodStart+1, uint64(16)) + mal.requireCommit(secondProvingPeriodStart+2, uint64(17)) + mal.requirePoSt(secondProvingPeriodStart+5, done) - // Check that state.ProvingSet is now [1, 2] and NextDoneSet is [] - mSt := requireReadMinerActorState(t, vms, st, minerAddr) - assert.Equal(t, types.NewIntSet(1, 2).Values(), mSt.ProvingSet.Values()) - assert.Equal(t, types.EmptyIntSet().Values(), mSt.NextDoneSet.Values()) + // Period 3 prove and drop 1 and 2 + done = types.NewIntSet(1,2) + mal.requirePoSt(thirdProvingPeriodStart+5, done) + + // power lags removal by a proving period + power := mal.requirePower(thirdProvingPeriodStart+6) + assert.Equal(t, types.NewBytesAmount(4).Mul(types.OneKiBSectorSize), power) + + // next period power is removed + done = types.EmptyIntSet() + mal.requirePoSt(fourthProvingPeriodStart+1, done) + power = mal.requirePower(fourthProvingPeriodStart+2) + assert.Equal(t, types.NewBytesAmount(2).Mul(types.OneKiBSectorSize), power) + }) +} + +func TestMinerSubmitPoStProvingSet(t *testing.T) { + tf.UnitTest(t) + + firstCommitBlockHeight := uint64(3) + secondProvingPeriodStart := LargestSectorSizeProvingPeriodBlocks + firstCommitBlockHeight + thirdProvingPeriodStart := 2*LargestSectorSizeProvingPeriodBlocks + firstCommitBlockHeight + + t.Run("empty proving set before first commit", func(t *testing.T) { + mal := setupMinerActorLiason(t) + mSt := mal.requireReadState() + assert.Equal(t, types.EmptyIntSet().Values(), mSt.ProvingSet.Values()) }) - t.Run("proving-set updates across proving periods", func(t *testing.T) { - // commit sector in new proving period - res, err := th.CreateAndApplyTestMessage(t, st, vms, minerAddr, 0, secondProvingPeriodStart + 1, "commitSector", ancestors, uint64(5), th.MakeCommitment(), th.MakeCommitment(), th.MakeCommitment(), th.MakeRandomBytes(types.TwoPoRepProofPartitions.ProofLen())) - require.NoError(t, err) - require.NoError(t, res.ExecutionError) - require.Equal(t, uint8(0), res.Receipt.ExitCode) + t.Run("only one sector added to proving set during first period", func(t *testing.T) { + mal := setupMinerActorLiason(t) + // commit several sectors + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(3)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(4)) + mal.requireCommit(firstCommitBlockHeight+2, uint64(16)) + mal.requireCommit(firstCommitBlockHeight+2, uint64(17)) + + mSt := mal.requireReadState() + assert.Equal(t, types.NewIntSet(1).Values(), mSt.ProvingSet.Values()) + }) + + t.Run("all committed sectors added to proving set after PoSt submission", func(t *testing.T) { + mal := setupMinerActorLiason(t) - // submit post - res, err = th.CreateAndApplyTestMessage(t, st, vms, minerAddr, 0, secondProvingPeriodStart + 2, "submitPoSt", ancestors, []types.PoStProof{proof}, done) - assert.NoError(t, err) - assert.NoError(t, res.ExecutionError) - assert.Equal(t, uint8(0), res.Receipt.ExitCode) + // commit several sectors + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+2, uint64(16)) + mal.requireCommit(firstCommitBlockHeight+2, uint64(17)) - // Check that state.ProvingSet is now [1, 2, 5] and NextDone set is [] - mSt := requireReadMinerActorState(t, vms, st, minerAddr) - assert.Equal(t, types.NewIntSet(1, 2, 5).Values(), mSt.ProvingSet.Values()) - assert.Equal(t, types.EmptyIntSet().Values(), mSt.NextDoneSet.Values()) - }) + // submit PoSt to update proving set + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) - t.Run("submitPoSt fails if miner does not have done ids stored", func(t *testing.T) { - failingDone := done.Add(uint64(30)) - res, err = th.CreateAndApplyTestMessage(t, st, vms, minerAddr, 0, thirdProvingPeriodStart + 2, "submitPoSt", ancestors, []types.PoStProof{proof}, failingDone) - assert.NoError(t, err) - assert.Error(t, res.ExecutionError) - assert.Equal(t, uint8(ErrInvalidSector), res.Receipt.ExitCode) + mSt := mal.requireReadState() + assert.Equal(t, types.NewIntSet(1,2,16,17).Values(), mSt.ProvingSet.Values()) }) - t.Run("next-done-set and proving-set update on non-empty done", func(t *testing.T) { - // finish sectors - done = done.Add(1) - done = done.Add(5) - res, err = th.CreateAndApplyTestMessage(t, st, vms, minerAddr, 0, thirdProvingPeriodStart + 2, "submitPoSt", ancestors, []types.PoStProof{proof}, done) - assert.NoError(t, err) - assert.NoError(t, res.ExecutionError) - assert.Equal(t, uint8(0), res.Receipt.ExitCode) + t.Run("committed sectors acrue across multiple PoSt submissions", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // Period 1 commit and prove + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) - // Check that state.ProvingSet is now [2] and NextDone set is [1, 5] - mSt := requireReadMinerActorState(t, vms, st, minerAddr) - assert.Equal(t, types.NewIntSet(2).Values(), mSt.ProvingSet.Values()) - assert.Equal(t, types.NewIntSet(1, 5).Values(), mSt.NextDoneSet.Values()) - }) + // Period 2 commit and prove + mal.requireCommit(secondProvingPeriodStart+1, uint64(16)) + mal.requireCommit(secondProvingPeriodStart+2, uint64(17)) + mal.requirePoSt(secondProvingPeriodStart+5, done) - t.Run("submiting empty done clears next done set", func(t *testing.T) { - res, err = th.CreateAndApplyTestMessage(t, st, vms, minerAddr, 0, fourthProvingPeriodStart + 2, "submitPoSt", ancestors, []types.PoStProof{proof}, types.EmptyIntSet()) - assert.NoError(t, err) - assert.NoError(t, res.ExecutionError) - assert.Equal(t, uint8(0), res.Receipt.ExitCode) + // Period 3 commit and prove + mal.requireCommit(thirdProvingPeriodStart+1, uint64(4)) + mal.requirePoSt(thirdProvingPeriodStart+5, done) - // Check that state.ProvingSet is now [2] and NextDone set is [] - mSt := requireReadMinerActorState(t, vms, st, minerAddr) - assert.Equal(t, types.NewIntSet(2).Values(), mSt.ProvingSet.Values()) - assert.Equal(t, types.EmptyIntSet().Values(), mSt.NextDoneSet.Values()) + mSt := mal.requireReadState() + assert.Equal(t, types.NewIntSet(1,2,4,16,17).Values(), mSt.ProvingSet.Values()) }) + + t.Run("done sectors removed from proving set", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // commit several sectors + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+2, uint64(17)) + + // submit PoSt to update proving set and remove sector 17 + done := types.NewIntSet(17) + mal.requirePoSt(firstCommitBlockHeight+5, done) + mSt := mal.requireReadState() + assert.Equal(t, types.NewIntSet(1,2).Values(), mSt.ProvingSet.Values()) + }) + } +func TestMinerSubmitPoStNextDoneSet(t *testing.T) { + tf.UnitTest(t) + + firstCommitBlockHeight := uint64(3) + secondProvingPeriodStart := LargestSectorSizeProvingPeriodBlocks + firstCommitBlockHeight + thirdProvingPeriodStart := 2*LargestSectorSizeProvingPeriodBlocks + firstCommitBlockHeight + + t.Run("next done set empty when done arg empty", func(t *testing.T) { + mal := setupMinerActorLiason(t) + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+2, uint64(17)) + + // submit PoSt to update proving set with no done sectors + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) + mSt := mal.requireReadState() + assert.Equal(t, types.EmptyIntSet().Values(), mSt.NextDoneSet.Values()) + }) + + t.Run("next done set updates when sectors completed", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // Period 1 commit and prove + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(3)) + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) + + // Period 2 remove id 2 and 3 + done = types.NewIntSet(2, 3) + mal.requirePoSt(secondProvingPeriodStart+5, done) + mSt := mal.requireReadState() + assert.Equal(t, done.Values(), mSt.NextDoneSet.Values()) + }) + + t.Run("next done set resets after additional post", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // Period 1 commit and prove + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(3)) + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) + + // Period 2 remove id 2 and 3 + done = types.NewIntSet(2, 3) + mal.requirePoSt(secondProvingPeriodStart+5, done) + + // Period 3 commit and prove + done = types.EmptyIntSet() + mal.requirePoSt(thirdProvingPeriodStart+5, done) + + mSt := mal.requireReadState() + assert.Equal(t, types.EmptyIntSet().Values(), mSt.NextDoneSet.Values()) + + }) + + t.Run("submitPoSt fails if miner does not have done ids stored", func(t *testing.T) { + mal := setupMinerActorLiason(t) + + // Period 1 commit and prove + mal.requireCommit(firstCommitBlockHeight, uint64(1)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(2)) + mal.requireCommit(firstCommitBlockHeight+1, uint64(3)) + done := types.EmptyIntSet() + mal.requirePoSt(firstCommitBlockHeight+5, done) + + failingDone := done.Add(uint64(30)) + mal.assertPoStFail(secondProvingPeriodStart+5, failingDone, uint8(ErrInvalidSector)) + }) + +} + func TestMinerSubmitPoSt(t *testing.T) { tf.UnitTest(t) @@ -829,16 +1073,6 @@ func mustDeserializeAddress(t *testing.T, result [][]byte) address.Address { return addr } -func requireReadMinerActorState(t *testing.T, vms vm.StorageMap, st state.Tree, minerAddr address.Address) State { - miner := state.MustGetActor(st, minerAddr) - storage := vms.NewStorage(minerAddr, miner) - stateBytes, err := storage.Get(storage.Head()) - require.NoError(t, err) - var minerState State - err = actor.UnmarshalStorage(stateBytes, &minerState) - require.NoError(t, err) - return minerState -} func af(h int64) types.AttoFIL { return types.NewAttoFIL(big.NewInt(h)) diff --git a/actor/builtin/miner/sector_set.go b/actor/builtin/miner/sector_set.go index c08eb54bea..f5a2e66fe1 100644 --- a/actor/builtin/miner/sector_set.go +++ b/actor/builtin/miner/sector_set.go @@ -63,6 +63,11 @@ func (ss SectorSet) IDs() ([]uint64, error) { return ids, nil } +// Size returns the number of sectorIDs in the SectorSet. +func (ss SectorSet) Size() int { + return len(ss) +} + // TODO: use uint64 as map keys instead of this abomination, once refmt is fixed. // https://github.com/polydawn/refmt/issues/35 func idStr(sectorID uint64) string { diff --git a/chain/power_table_view_test.go b/chain/power_table_view_test.go index d13a7a588b..0ba4addde5 100644 --- a/chain/power_table_view_test.go +++ b/chain/power_table_view_test.go @@ -49,7 +49,7 @@ func TestMiner(t *testing.T) { expected := types.NewBytesAmount(types.OneKiBSectorSize.Uint64() * numCommittedSectors) - assert.True(t, expected.Equal(actual)) + assert.Equal(t, expected, actual) } func requireMinerWithNumCommittedSectors(ctx context.Context, t *testing.T, numCommittedSectors uint64) (bstore.Blockstore, address.Address, state.Tree) { diff --git a/commands/deals_daemon_test.go b/commands/deals_daemon_test.go index ef7f4dc94b..9883e7140d 100644 --- a/commands/deals_daemon_test.go +++ b/commands/deals_daemon_test.go @@ -260,12 +260,10 @@ func TestDealsShowPaymentVouchers(t *testing.T) { price := big.NewFloat(0.000000001) // price per byte/block expiry := big.NewInt(24 * 60 * 60 / 30) // ~24 hours - // This also starts the Miner + // Calls MiningOnce on genesis (client). This also starts the Miner. ask, err := series.CreateStorageMinerWithAsk(ctx, minerNode, collateral, price, expiry) require.NoError(t, err) - defer func() { - require.NoError(t, minerNode.MiningStop(ctx)) - }() + require.NoError(t, minerNode.MiningStop(ctx)) // Create some data that is the full sector size and make it autoseal asap maxBytesi64 := int64(getMaxUserBytesPerStagedSector()) @@ -280,6 +278,7 @@ func TestDealsShowPaymentVouchers(t *testing.T) { _, deal, err := series.ImportAndStoreWithDuration(ctx, clientNode, ask, durationui64, files.NewReaderFile(dataReader)) require.NoError(t, err) + require.NoError(t, clientNode.MiningStop(ctx)) t.Run("Vouchers output as JSON have the correct info", func(t *testing.T) { res, err := clientNode.DealsShow(ctx, deal.ProposalCid) @@ -293,11 +292,14 @@ func TestDealsShowPaymentVouchers(t *testing.T) { // ValidAt block height should be at least as high as the (period index + 1) * duration / # of proving periods // so if there are 2 periods, 1 is valid at block height >= 1*duration/2, // 2 is valid at 2*duration/2 + // The channelID == message nonce, which happens to be 5 + msgNonceChID := uint64(5) + expected := []*commands.PaymenVoucherResult{ { Index: 0, Amount: &firstAmount, - Channel: types.NewChannelID(4), + Channel: types.NewChannelID(msgNonceChID), Condition: &types.Predicate{ Method: "verifyPieceInclusion", To: ask.Miner, @@ -308,7 +310,7 @@ func TestDealsShowPaymentVouchers(t *testing.T) { { Index: 1, Amount: totalPrice, - Channel: types.NewChannelID(4), + Channel: types.NewChannelID(msgNonceChID), Condition: nil, Payer: &clientAddr, ValidAt: types.NewBlockHeight(durationui64), @@ -353,7 +355,7 @@ func assertEqualVoucherResults(t *testing.T, expected, actual []*commands.Paymen } assert.True(t, vr.Amount.Equal(*actual[i].Amount)) - assert.True(t, vr.ValidAt.LessEqual(actual[i].ValidAt)) - assert.True(t, vr.Channel.Equal(actual[i].Channel)) + assert.True(t, vr.ValidAt.LessEqual(actual[i].ValidAt), "expva %s, actualva %s", vr.ValidAt.String(), actual[i].Channel.String()) + assert.True(t, vr.Channel.Equal(actual[i].Channel), "expch %s, actualch %s", vr.Channel.String(), actual[i].Channel.String()) } } diff --git a/gengen/util/gengen.go b/gengen/util/gengen.go index 4ae83cb4ed..4a2291d191 100644 --- a/gengen/util/gengen.go +++ b/gengen/util/gengen.go @@ -292,6 +292,19 @@ func setupMiners(st state.Tree, sm vm.StorageMap, keys []*types.KeyInfo, miners return nil, err } } + if m.NumCommittedSectors > 0 { + // Now submit a dummy PoSt right away to trigger power updates. + // Don't worry, bootstrap miner actors don't need to verify + // that the PoSt is well formed. + poStProof := make([]byte, types.OnePoStProofPartition.ProofLen()) + if _, err := pnrg.Read(poStProof[:]); err != nil { + return nil, err + } + _, err = applyMessageDirect(ctx, st, sm, addr, maddr, types.NewAttoFILFromFIL(0), "submitPoSt", []types.PoStProof{poStProof}, types.EmptyIntSet()) + if err != nil { + return nil, err + } + } } return minfos, nil diff --git a/go.sum b/go.sum index 7098d05a27..11037379bb 100644 --- a/go.sum +++ b/go.sum @@ -157,6 +157,7 @@ github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSW github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= diff --git a/types/intset.go b/types/intset.go index ab2b3dfc00..d83999101b 100644 --- a/types/intset.go +++ b/types/intset.go @@ -96,6 +96,20 @@ func (is IntSet) String() string { return fmt.Sprintf("%d", is.Values()) } +// Size returns the size of an IntSet. It should be more efficient than +// len(is.Values()). +func (is IntSet) Size() int { + var size int + it := is.ba.Blocks() + // it.Next() must be called to point it at the first block + for it.Next() { + _, bitBlock := it.Value() + bm := bitarray.Bitmap64(bitBlock) + size += bm.PopCount() + } + return size +} + // EmptyIntSet returns an empty IntSet. func EmptyIntSet() IntSet { return IntSet{ba: bitarray.NewSparseBitArray()} diff --git a/types/intset_test.go b/types/intset_test.go index 1edd49c4e5..3d014c5185 100644 --- a/types/intset_test.go +++ b/types/intset_test.go @@ -90,4 +90,22 @@ func TestIntSet(t *testing.T) { assert.Equal(t, ints, result) }) + + t.Run("Size", func(t *testing.T) { + intsA := make([]uint64, 33) + for idx := range intsA { + intsA[idx] = rand.Uint64() + } + + assert.Equal(t, 33, types.NewIntSet(intsA...).Size()) + + intsB := make([]uint64, 1024) + for idx := range intsB { + intsB[idx] = rand.Uint64() + } + + assert.Equal(t, 1024, types.NewIntSet(intsB...).Size()) + + assert.Equal(t, 0, types.EmptyIntSet().Size()) + }) }