github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/test/commit_post_test.go (about) 1 package test_test 2 3 import ( 4 "context" 5 "strings" 6 "testing" 7 8 "github.com/filecoin-project/go-bitfield" 9 "github.com/filecoin-project/go-state-types/abi" 10 "github.com/filecoin-project/go-state-types/big" 11 "github.com/filecoin-project/go-state-types/exitcode" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 16 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" 17 "github.com/filecoin-project/specs-actors/v4/actors/builtin/power" 18 "github.com/filecoin-project/specs-actors/v4/actors/runtime/proof" 19 "github.com/filecoin-project/specs-actors/v4/actors/states" 20 "github.com/filecoin-project/specs-actors/v4/support/ipld" 21 tutil "github.com/filecoin-project/specs-actors/v4/support/testing" 22 vm "github.com/filecoin-project/specs-actors/v4/support/vm" 23 ) 24 25 func TestCommitPoStFlow(t *testing.T) { 26 ctx := context.Background() 27 v := vm.NewVMWithSingletons(ctx, t, ipld.NewBlockStoreInMemory()) 28 addrs := vm.CreateAccounts(ctx, t, v, 1, big.Mul(big.NewInt(10_000), big.NewInt(1e18)), 93837778) 29 30 minerBalance := big.Mul(big.NewInt(10_000), vm.FIL) 31 sealProof := abi.RegisteredSealProof_StackedDrg32GiBV1_1 32 33 // create miner 34 params := power.CreateMinerParams{ 35 Owner: addrs[0], 36 Worker: addrs[0], 37 WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, 38 Peer: abi.PeerID("not really a peer id"), 39 } 40 ret := vm.ApplyOk(t, v, addrs[0], builtin.StoragePowerActorAddr, minerBalance, builtin.MethodsPower.CreateMiner, ¶ms) 41 42 minerAddrs, ok := ret.(*power.CreateMinerReturn) 43 require.True(t, ok) 44 45 // advance vm so we can have seal randomness epoch in the past 46 v, err := v.WithEpoch(200) 47 require.NoError(t, err) 48 49 // 50 // precommit sector 51 // 52 53 sectorNumber := abi.SectorNumber(100) 54 sealedCid := tutil.MakeCID("100", &miner.SealedCIDPrefix) 55 sectorSize, err := sealProof.SectorSize() 56 require.NoError(t, err) 57 58 preCommitParams := miner.PreCommitSectorParams{ 59 SealProof: sealProof, 60 SectorNumber: sectorNumber, 61 SealedCID: sealedCid, 62 SealRandEpoch: v.GetEpoch() - 1, 63 DealIDs: nil, 64 Expiration: v.GetEpoch() + miner.MinSectorExpiration + miner.MaxProveCommitDuration[sealProof] + 100, 65 } 66 vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.PreCommitSector, &preCommitParams) 67 68 // assert successful precommit invocation 69 vm.ExpectInvocation{ 70 To: minerAddrs.IDAddress, 71 Method: builtin.MethodsMiner.PreCommitSector, 72 Params: vm.ExpectObject(&preCommitParams), 73 SubInvocations: []vm.ExpectInvocation{ 74 {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, 75 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, 76 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.EnrollCronEvent}}, 77 }.Matches(t, v.Invocations()[0]) 78 79 balances := vm.GetMinerBalances(t, v, minerAddrs.IDAddress) 80 assert.True(t, balances.PreCommitDeposit.GreaterThan(big.Zero())) 81 82 // find information about precommited sector 83 var minerState miner.State 84 err = v.GetState(minerAddrs.IDAddress, &minerState) 85 require.NoError(t, err) 86 87 precommit, found, err := minerState.GetPrecommittedSector(v.Store(), sectorNumber) 88 require.NoError(t, err) 89 require.True(t, found) 90 91 // advance time to max seal duration 92 proveTime := v.GetEpoch() + miner.MaxProveCommitDuration[sealProof] 93 v, dlInfo := vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, proveTime) 94 95 // 96 // overdue precommit 97 // 98 99 t.Run("missed prove commit results in precommit expiry", func(t *testing.T) { 100 // advanced one more deadline so precommit is late 101 tv, err := v.WithEpoch(dlInfo.Close) 102 require.NoError(t, err) 103 104 // run cron which should expire precommit 105 vm.ApplyOk(t, tv, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) 106 107 vm.ExpectInvocation{ 108 To: builtin.CronActorAddr, 109 Method: builtin.MethodsCron.EpochTick, 110 SubInvocations: []vm.ExpectInvocation{ 111 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{ 112 {To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.OnDeferredCronEvent, SubInvocations: []vm.ExpectInvocation{ 113 {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, 114 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, 115 116 // The call to burnt funds indicates the overdue precommit has been penalized 117 {To: builtin.BurntFundsActorAddr, Method: builtin.MethodSend, Value: vm.ExpectAttoFil(precommit.PreCommitDeposit)}, 118 // No re-enrollment of cron because burning of PCD discontinues miner cron scheduling 119 }}, 120 //{To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.ConfirmSectorProofsValid}, 121 {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI}, 122 }}, 123 {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick}, 124 }, 125 }.Matches(t, tv.Invocations()[0]) 126 127 // precommit deposit has been reset 128 balances := vm.GetMinerBalances(t, tv, minerAddrs.IDAddress) 129 assert.Equal(t, big.Zero(), balances.InitialPledge) 130 assert.Equal(t, big.Zero(), balances.PreCommitDeposit) 131 132 // no power is added 133 networkStats := vm.GetNetworkStats(t, tv) 134 assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted) 135 assert.Equal(t, big.Zero(), networkStats.TotalPledgeCollateral) 136 assert.Equal(t, big.Zero(), networkStats.TotalRawBytePower) 137 assert.Equal(t, big.Zero(), networkStats.TotalQualityAdjPower) 138 }) 139 140 // 141 // prove and verify 142 // 143 144 // Prove commit sector after max seal duration 145 v, err = v.WithEpoch(proveTime) 146 require.NoError(t, err) 147 proveCommitParams := miner.ProveCommitSectorParams{ 148 SectorNumber: sectorNumber, 149 } 150 vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitSector, &proveCommitParams) 151 152 vm.ExpectInvocation{ 153 To: minerAddrs.IDAddress, 154 Method: builtin.MethodsMiner.ProveCommitSector, 155 Params: vm.ExpectObject(&proveCommitParams), 156 SubInvocations: []vm.ExpectInvocation{ 157 {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.ComputeDataCommitment}, 158 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.SubmitPoRepForBulkVerify}, 159 }, 160 }.Matches(t, v.Invocations()[0]) 161 162 // In the same epoch, trigger cron to validate prove commit 163 vm.ApplyOk(t, v, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) 164 165 vm.ExpectInvocation{ 166 To: builtin.CronActorAddr, 167 Method: builtin.MethodsCron.EpochTick, 168 SubInvocations: []vm.ExpectInvocation{ 169 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{ 170 // expect confirm sector proofs valid because we prove committed, 171 // but not an on deferred cron event because this is not a deadline boundary 172 {To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.ConfirmSectorProofsValid, SubInvocations: []vm.ExpectInvocation{ 173 {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, 174 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, 175 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdatePledgeTotal}, 176 }}, 177 {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI}, 178 }}, 179 {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick}, 180 }, 181 }.Matches(t, v.Invocations()[1]) 182 183 // precommit deposit is released, ipr is added 184 balances = vm.GetMinerBalances(t, v, minerAddrs.IDAddress) 185 assert.True(t, balances.InitialPledge.GreaterThan(big.Zero())) 186 assert.Equal(t, big.Zero(), balances.PreCommitDeposit) 187 188 // power is unproven so network stats are unchanged 189 networkStats := vm.GetNetworkStats(t, v) 190 assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted) 191 assert.True(t, networkStats.TotalPledgeCollateral.GreaterThan(big.Zero())) 192 193 // 194 // Submit PoSt 195 // 196 197 // advance to proving period 198 dlInfo, pIdx, v := vm.AdvanceTillProvingDeadline(t, v, minerAddrs.IDAddress, sectorNumber) 199 err = v.GetState(minerAddrs.IDAddress, &minerState) 200 require.NoError(t, err) 201 202 sector, found, err := minerState.GetSector(v.Store(), sectorNumber) 203 require.NoError(t, err) 204 require.True(t, found) 205 206 t.Run("submit PoSt succeeds", func(t *testing.T) { 207 tv, err := v.WithEpoch(v.GetEpoch()) 208 require.NoError(t, err) 209 210 // Submit PoSt 211 submitParams := miner.SubmitWindowedPoStParams{ 212 Deadline: dlInfo.Index, 213 Partitions: []miner.PoStPartition{{ 214 Index: pIdx, 215 Skipped: bitfield.New(), 216 }}, 217 Proofs: []proof.PoStProof{{ 218 PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, 219 }}, 220 ChainCommitEpoch: dlInfo.Challenge, 221 ChainCommitRand: []byte("not really random"), 222 } 223 vm.ApplyOk(t, tv, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams) 224 225 sectorPower := miner.PowerForSector(sectorSize, sector) 226 updatePowerParams := &power.UpdateClaimedPowerParams{ 227 RawByteDelta: sectorPower.Raw, 228 QualityAdjustedDelta: sectorPower.QA, 229 } 230 231 vm.ExpectInvocation{ 232 To: minerAddrs.IDAddress, 233 Method: builtin.MethodsMiner.SubmitWindowedPoSt, 234 Params: vm.ExpectObject(&submitParams), 235 SubInvocations: []vm.ExpectInvocation{ 236 // This call to the power actor indicates power has been added for the sector 237 { 238 To: builtin.StoragePowerActorAddr, 239 Method: builtin.MethodsPower.UpdateClaimedPower, 240 Params: vm.ExpectObject(updatePowerParams), 241 }, 242 }, 243 }.Matches(t, tv.Invocations()[0]) 244 245 // miner still has initial pledge 246 balances = vm.GetMinerBalances(t, tv, minerAddrs.IDAddress) 247 assert.True(t, balances.InitialPledge.GreaterThan(big.Zero())) 248 249 // committed bytes are added (miner would have gained power if minimum requirement were met) 250 networkStats := vm.GetNetworkStats(t, tv) 251 assert.Equal(t, big.NewInt(int64(sectorSize)), networkStats.TotalBytesCommitted) 252 assert.True(t, networkStats.TotalPledgeCollateral.GreaterThan(big.Zero())) 253 254 // Trigger cron to keep reward accounting correct 255 vm.ApplyOk(t, tv, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) 256 257 stateTree, err := tv.GetStateTree() 258 require.NoError(t, err) 259 totalBalance, err := tv.GetTotalActorBalance() 260 require.NoError(t, err) 261 acc, err := states.CheckStateInvariants(stateTree, totalBalance, tv.GetEpoch()) 262 require.NoError(t, err) 263 assert.True(t, acc.IsEmpty(), strings.Join(acc.Messages(), "\n")) 264 }) 265 266 t.Run("skip sector", func(t *testing.T) { 267 tv, err := v.WithEpoch(v.GetEpoch()) 268 require.NoError(t, err) 269 270 // Submit PoSt 271 submitParams := miner.SubmitWindowedPoStParams{ 272 Deadline: dlInfo.Index, 273 Partitions: []miner.PoStPartition{{ 274 Index: pIdx, 275 Skipped: bitfield.NewFromSet([]uint64{uint64(sectorNumber)}), 276 }}, 277 Proofs: []proof.PoStProof{{ 278 PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, 279 }}, 280 ChainCommitEpoch: dlInfo.Challenge, 281 ChainCommitRand: []byte("not really random"), 282 } 283 // PoSt is rejected for skipping all sectors. 284 _, code := tv.ApplyMessage(addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams) 285 assert.Equal(t, exitcode.ErrIllegalArgument, code) 286 287 vm.ExpectInvocation{ 288 To: minerAddrs.IDAddress, 289 Method: builtin.MethodsMiner.SubmitWindowedPoSt, 290 Params: vm.ExpectObject(&submitParams), 291 Exitcode: exitcode.ErrIllegalArgument, 292 }.Matches(t, tv.Invocations()[0]) 293 294 // miner still has initial pledge 295 balances = vm.GetMinerBalances(t, v, minerAddrs.IDAddress) 296 assert.True(t, balances.InitialPledge.GreaterThan(big.Zero())) 297 298 // network power is unchanged 299 networkStats := vm.GetNetworkStats(t, tv) 300 assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted) 301 assert.True(t, networkStats.TotalPledgeCollateral.GreaterThan(big.Zero())) 302 }) 303 304 t.Run("missed first PoSt deadline", func(t *testing.T) { 305 // move to proving period end 306 tv, err := v.WithEpoch(dlInfo.Last()) 307 require.NoError(t, err) 308 309 // Run cron to detect missing PoSt 310 vm.ApplyOk(t, tv, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) 311 312 vm.ExpectInvocation{ 313 To: builtin.CronActorAddr, 314 Method: builtin.MethodsCron.EpochTick, 315 SubInvocations: []vm.ExpectInvocation{ 316 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{ 317 {To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.OnDeferredCronEvent, SubInvocations: []vm.ExpectInvocation{ 318 {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, 319 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, 320 {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.EnrollCronEvent}, 321 }}, 322 {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI}, 323 }}, 324 {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick}, 325 }, 326 }.Matches(t, tv.Invocations()[0]) 327 328 // network power is unchanged 329 networkStats := vm.GetNetworkStats(t, tv) 330 assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted) 331 assert.True(t, networkStats.TotalPledgeCollateral.GreaterThan(big.Zero())) 332 }) 333 }