github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/miner/deadline_state_test.go (about) 1 package miner_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/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 14 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" 15 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" 16 "github.com/filecoin-project/specs-actors/v4/support/ipld" 17 ) 18 19 func TestDeadlines(t *testing.T) { 20 sectors := []*miner.SectorOnChainInfo{ 21 testSector(2, 1, 50, 60, 1000), 22 testSector(3, 2, 51, 61, 1001), 23 testSector(7, 3, 52, 62, 1002), 24 testSector(8, 4, 53, 63, 1003), 25 26 testSector(8, 5, 54, 64, 1004), 27 testSector(11, 6, 55, 65, 1005), 28 testSector(13, 7, 56, 66, 1006), 29 testSector(8, 8, 57, 67, 1007), 30 31 testSector(8, 9, 58, 68, 1008), 32 } 33 extraSectors := []*miner.SectorOnChainInfo{ 34 testSector(8, 10, 58, 68, 1008), 35 } 36 allSectors := append(sectors, extraSectors...) 37 38 sectorSize := abi.SectorSize(32 << 30) 39 quantSpec := miner.NewQuantSpec(4, 1) 40 partitionSize := uint64(4) 41 42 dlState := expectedDeadlineState{ 43 quant: quantSpec, 44 partitionSize: partitionSize, 45 sectorSize: sectorSize, 46 sectors: allSectors, 47 } 48 49 sectorPower := func(t *testing.T, sectorNos ...uint64) miner.PowerPair { 50 return miner.PowerForSectors(sectorSize, selectSectors(t, allSectors, bf(sectorNos...))) 51 } 52 53 // 54 // Define some basic test scenarios that build one each other. 55 // 56 57 // Adds sectors, and proves them if requested. 58 // 59 // Partition 1: sectors 1, 2, 3, 4 60 // Partition 2: sectors 5, 6, 7, 8 61 // Partition 3: sectors 9 62 addSectors := func(t *testing.T, store adt.Store, dl *miner.Deadline, prove bool) { 63 power := miner.PowerForSectors(sectorSize, sectors) 64 activatedPower, err := dl.AddSectors(store, partitionSize, false, sectors, sectorSize, quantSpec) 65 require.NoError(t, err) 66 assert.True(t, activatedPower.Equals(power)) 67 68 dlState.withUnproven(1, 2, 3, 4, 5, 6, 7, 8, 9). 69 withPartitions( 70 bf(1, 2, 3, 4), 71 bf(5, 6, 7, 8), 72 bf(9), 73 ).assert(t, store, dl) 74 75 if !prove { 76 return 77 } 78 79 sectorArr := sectorsArr(t, store, sectors) 80 81 // Prove everything 82 result, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 0, []miner.PoStPartition{{Index: 0}, {Index: 1}, {Index: 2}}) 83 require.NoError(t, err) 84 require.True(t, result.PowerDelta.Equals(power)) 85 86 faultyPower, recoveryPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 0) 87 require.NoError(t, err) 88 require.True(t, faultyPower.IsZero()) 89 require.True(t, recoveryPower.IsZero()) 90 91 dlState.withPartitions( 92 bf(1, 2, 3, 4), 93 bf(5, 6, 7, 8), 94 bf(9), 95 ).assert(t, store, dl) 96 } 97 98 // Adds sectors according to addSectors, then terminates them: 99 // 100 // From partition 0: sectors 1 & 3 101 // From partition 1: sectors 6 102 addThenTerminate := func(t *testing.T, store adt.Store, dl *miner.Deadline, proveFirst bool) { 103 addSectors(t, store, dl, proveFirst) 104 105 removedPower, err := dl.TerminateSectors(store, sectorsArr(t, store, sectors), 15, miner.PartitionSectorMap{ 106 0: bf(1, 3), 107 1: bf(6), 108 }, sectorSize, quantSpec) 109 require.NoError(t, err) 110 111 expectedPower := miner.NewPowerPairZero() 112 unproven := []uint64{2, 4, 5, 7, 8, 9} // not 1, 3, 6 113 if proveFirst { 114 unproven = nil 115 expectedPower = sectorPower(t, 1, 3, 6) 116 } 117 require.True(t, expectedPower.Equals(removedPower), "dlState to remove power for terminated sectors") 118 119 dlState.withTerminations(1, 3, 6). 120 withUnproven(unproven...). 121 withPartitions( 122 bf(1, 2, 3, 4), 123 bf(5, 6, 7, 8), 124 bf(9), 125 ).assert(t, store, dl) 126 } 127 128 // Adds and terminates sectors according to the previous two functions, 129 // then pops early terminations. 130 addThenTerminateThenPopEarly := func(t *testing.T, store adt.Store, dl *miner.Deadline) { 131 addThenTerminate(t, store, dl, true) 132 133 earlyTerminations, more, err := dl.PopEarlyTerminations(store, 100, 100) 134 require.NoError(t, err) 135 assert.False(t, more) 136 assert.Equal(t, uint64(2), earlyTerminations.PartitionsProcessed) 137 assert.Equal(t, uint64(3), earlyTerminations.SectorsProcessed) 138 assert.Len(t, earlyTerminations.Sectors, 1) 139 assertBitfieldEquals(t, earlyTerminations.Sectors[15], 1, 3, 6) 140 141 // Popping early terminations doesn't affect the terminations bitfield. 142 dlState.withTerminations(1, 3, 6). 143 withPartitions( 144 bf(1, 2, 3, 4), 145 bf(5, 6, 7, 8), 146 bf(9), 147 ).assert(t, store, dl) 148 } 149 150 // Runs the above scenarios, then removes partition 0. 151 addThenTerminateThenRemovePartition := func(t *testing.T, store adt.Store, dl *miner.Deadline) { 152 addThenTerminateThenPopEarly(t, store, dl) 153 154 live, dead, removedPower, err := dl.RemovePartitions(store, bf(0), quantSpec) 155 require.NoError(t, err, "should have removed partitions") 156 assertBitfieldEquals(t, live, 2, 4) 157 assertBitfieldEquals(t, dead, 1, 3) 158 livePower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, live)) 159 require.True(t, livePower.Equals(removedPower)) 160 161 dlState.withTerminations(6). 162 withPartitions( 163 bf(5, 6, 7, 8), 164 bf(9), 165 ).assert(t, store, dl) 166 } 167 168 // Adds sectors according to addSectors, then marks sectors 1, 5, 6 169 // faulty, expiring at epoch 9. 170 // 171 // Sector 5 will expire on-time at epoch 9 while 6 will expire early at epoch 9. 172 addThenMarkFaulty := func(t *testing.T, store adt.Store, dl *miner.Deadline, proveFirst bool) { 173 addSectors(t, store, dl, proveFirst) 174 175 // Mark faulty. 176 powerDelta, err := dl.RecordFaults( 177 store, sectorsArr(t, store, sectors), sectorSize, quantSpec, 9, 178 map[uint64]bitfield.BitField{ 179 0: bf(1), 180 1: bf(5, 6), 181 }, 182 ) 183 require.NoError(t, err) 184 185 expectedPower := miner.NewPowerPairZero() 186 unproven := []uint64{2, 3, 4, 7, 8, 9} // not 1, 5, 6 187 if proveFirst { 188 unproven = nil 189 expectedPower = sectorPower(t, 1, 5, 6) 190 } 191 assert.True(t, powerDelta.Equals(expectedPower.Neg())) 192 193 dlState.withFaults(1, 5, 6). 194 withUnproven(unproven...). 195 withPartitions( 196 bf(1, 2, 3, 4), 197 bf(5, 6, 7, 8), 198 bf(9), 199 ).assert(t, store, dl) 200 } 201 202 // Test the basic scenarios (technically, we could just run the final one). 203 204 t.Run("adds sectors", func(t *testing.T) { 205 store := ipld.NewADTStore(context.Background()) 206 dl := emptyDeadline(t, store) 207 addSectors(t, store, dl, false) 208 }) 209 210 t.Run("adds sectors and proves", func(t *testing.T) { 211 store := ipld.NewADTStore(context.Background()) 212 dl := emptyDeadline(t, store) 213 addSectors(t, store, dl, true) 214 }) 215 216 t.Run("terminates sectors", func(t *testing.T) { 217 store := ipld.NewADTStore(context.Background()) 218 dl := emptyDeadline(t, store) 219 addThenTerminate(t, store, dl, true) 220 }) 221 222 t.Run("terminates unproven sectors", func(t *testing.T) { 223 store := ipld.NewADTStore(context.Background()) 224 dl := emptyDeadline(t, store) 225 addThenTerminate(t, store, dl, false) 226 }) 227 228 t.Run("pops early terminations", func(t *testing.T) { 229 store := ipld.NewADTStore(context.Background()) 230 dl := emptyDeadline(t, store) 231 232 addThenTerminateThenPopEarly(t, store, dl) 233 }) 234 235 t.Run("removes partitions", func(t *testing.T) { 236 store := ipld.NewADTStore(context.Background()) 237 dl := emptyDeadline(t, store) 238 239 addThenTerminateThenRemovePartition(t, store, dl) 240 }) 241 242 t.Run("marks faulty", func(t *testing.T) { 243 store := ipld.NewADTStore(context.Background()) 244 dl := emptyDeadline(t, store) 245 246 addThenMarkFaulty(t, store, dl, true) 247 }) 248 249 t.Run("marks unproven sectors faulty", func(t *testing.T) { 250 store := ipld.NewADTStore(context.Background()) 251 dl := emptyDeadline(t, store) 252 253 addThenMarkFaulty(t, store, dl, false) 254 }) 255 256 // 257 // Now, build on these basic scenarios with some "what ifs". 258 // 259 260 t.Run("cannot remove partitions with early terminations", func(t *testing.T) { 261 store := ipld.NewADTStore(context.Background()) 262 dl := emptyDeadline(t, store) 263 addThenTerminate(t, store, dl, false) 264 265 _, _, _, err := dl.RemovePartitions(store, bf(0), quantSpec) 266 require.Error(t, err, "should have failed to remove a partition with early terminations") 267 }) 268 269 t.Run("can pop early terminations in multiple steps", func(t *testing.T) { 270 store := ipld.NewADTStore(context.Background()) 271 dl := emptyDeadline(t, store) 272 addThenTerminate(t, store, dl, true) 273 274 var result miner.TerminationResult 275 276 // process 1 sector, 2 partitions (should pop 1 sector) 277 result1, hasMore, err := dl.PopEarlyTerminations(store, 2, 1) 278 require.NoError(t, err) 279 require.True(t, hasMore) 280 require.NoError(t, result.Add(result1)) 281 282 // process 2 sectors, 1 partitions (should pop 1 sector) 283 result2, hasMore, err := dl.PopEarlyTerminations(store, 2, 1) 284 require.NoError(t, err) 285 require.True(t, hasMore) 286 require.NoError(t, result.Add(result2)) 287 288 // process 1 sectors, 1 partitions (should pop 1 sector) 289 result3, hasMore, err := dl.PopEarlyTerminations(store, 1, 1) 290 require.NoError(t, err) 291 require.False(t, hasMore) 292 require.NoError(t, result.Add(result3)) 293 294 assert.Equal(t, uint64(3), result.PartitionsProcessed) 295 assert.Equal(t, uint64(3), result.SectorsProcessed) 296 assert.Len(t, result.Sectors, 1) 297 assertBitfieldEquals(t, result.Sectors[15], 1, 3, 6) 298 299 // Popping early terminations doesn't affect the terminations bitfield. 300 dlState.withTerminations(1, 3, 6). 301 withPartitions( 302 bf(1, 2, 3, 4), 303 bf(5, 6, 7, 8), 304 bf(9), 305 ).assert(t, store, dl) 306 }) 307 308 t.Run("cannot remove missing partition", func(t *testing.T) { 309 store := ipld.NewADTStore(context.Background()) 310 dl := emptyDeadline(t, store) 311 312 addThenTerminateThenRemovePartition(t, store, dl) 313 314 _, _, _, err := dl.RemovePartitions(store, bf(2), quantSpec) 315 require.Error(t, err, "should have failed to remove missing partition") 316 }) 317 318 t.Run("removing no partitions does nothing", func(t *testing.T) { 319 store := ipld.NewADTStore(context.Background()) 320 dl := emptyDeadline(t, store) 321 322 addThenTerminateThenPopEarly(t, store, dl) 323 324 live, dead, removedPower, err := dl.RemovePartitions(store, bf(), quantSpec) 325 require.NoError(t, err, "should not have failed to remove no partitions") 326 require.True(t, removedPower.IsZero()) 327 assertBitfieldEquals(t, live) 328 assertBitfieldEquals(t, dead) 329 330 // Popping early terminations doesn't affect the terminations bitfield. 331 dlState.withTerminations(1, 3, 6). 332 withPartitions( 333 bf(1, 2, 3, 4), 334 bf(5, 6, 7, 8), 335 bf(9), 336 ).assert(t, store, dl) 337 }) 338 339 t.Run("fails to remove partitions with faulty sectors", func(t *testing.T) { 340 store := ipld.NewADTStore(context.Background()) 341 342 dl := emptyDeadline(t, store) 343 addThenMarkFaulty(t, store, dl, false) 344 345 // Try to remove a partition with faulty sectors. 346 _, _, _, err := dl.RemovePartitions(store, bf(1), quantSpec) 347 require.Error(t, err, "should have failed to remove a partition with faults") 348 }) 349 350 t.Run("terminate proven & faulty", func(t *testing.T) { 351 store := ipld.NewADTStore(context.Background()) 352 dl := emptyDeadline(t, store) 353 354 addThenMarkFaulty(t, store, dl, true) // 1, 5, 6 faulty 355 356 sectorArr := sectorsArr(t, store, sectors) 357 removedPower, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{ 358 0: bf(1, 3), 359 1: bf(6), 360 }, sectorSize, quantSpec) 361 require.NoError(t, err) 362 363 // Sector 3 active, 1, 6 faulty 364 expectedPowerLoss := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(3))) 365 require.True(t, expectedPowerLoss.Equals(removedPower), "dlState to remove power for terminated sectors") 366 367 dlState.withTerminations(1, 3, 6). 368 withFaults(5). 369 withPartitions( 370 bf(1, 2, 3, 4), 371 bf(5, 6, 7, 8), 372 bf(9), 373 ).assert(t, store, dl) 374 }) 375 376 t.Run("terminate unproven & faulty", func(t *testing.T) { 377 store := ipld.NewADTStore(context.Background()) 378 dl := emptyDeadline(t, store) 379 380 addThenMarkFaulty(t, store, dl, false) // 1, 5, 6 faulty 381 382 sectorArr := sectorsArr(t, store, sectors) 383 removedPower, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{ 384 0: bf(1, 3), 385 1: bf(6), 386 }, sectorSize, quantSpec) 387 require.NoError(t, err) 388 389 // Sector 3 unproven, 1, 6 faulty 390 require.True(t, removedPower.Equals(miner.NewPowerPairZero()), "should remove no power") 391 392 dlState.withTerminations(1, 3, 6). 393 withUnproven(2, 4, 7, 8, 9). // not 1, 3, 5, & 6 394 withFaults(5). 395 withPartitions( 396 bf(1, 2, 3, 4), 397 bf(5, 6, 7, 8), 398 bf(9), 399 ).assert(t, store, dl) 400 }) 401 402 t.Run("fails to terminate missing sector", func(t *testing.T) { 403 store := ipld.NewADTStore(context.Background()) 404 dl := emptyDeadline(t, store) 405 406 addThenMarkFaulty(t, store, dl, false) // 1, 5, 6 faulty 407 408 sectorArr := sectorsArr(t, store, sectors) 409 _, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{ 410 0: bf(6), 411 }, sectorSize, quantSpec) 412 require.Error(t, err) 413 require.Contains(t, err.Error(), "can only terminate live sectors") 414 }) 415 416 t.Run("fails to terminate missing partition", func(t *testing.T) { 417 store := ipld.NewADTStore(context.Background()) 418 dl := emptyDeadline(t, store) 419 420 addThenMarkFaulty(t, store, dl, false) // 1, 5, 6 faulty 421 422 sectorArr := sectorsArr(t, store, sectors) 423 _, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{ 424 4: bf(6), 425 }, sectorSize, quantSpec) 426 require.Error(t, err) 427 require.Contains(t, err.Error(), "failed to find partition 4") 428 }) 429 430 t.Run("fails to terminate already terminated sector", func(t *testing.T) { 431 store := ipld.NewADTStore(context.Background()) 432 dl := emptyDeadline(t, store) 433 434 addThenTerminate(t, store, dl, false) // terminates 1, 3, & 6 435 436 sectorArr := sectorsArr(t, store, sectors) 437 _, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{ 438 0: bf(1, 2), 439 }, sectorSize, quantSpec) 440 require.Error(t, err) 441 require.Contains(t, err.Error(), "can only terminate live sectors") 442 }) 443 444 t.Run("faulty sectors expire", func(t *testing.T) { 445 store := ipld.NewADTStore(context.Background()) 446 447 dl := emptyDeadline(t, store) 448 // Mark sectors 5 & 6 faulty, expiring at epoch 9. 449 addThenMarkFaulty(t, store, dl, true) 450 451 // We expect all sectors but 7 to have expired at this point. 452 exp, err := dl.PopExpiredSectors(store, 9, quantSpec) 453 require.NoError(t, err) 454 455 onTimeExpected := bf(1, 2, 3, 4, 5, 8, 9) 456 earlyExpected := bf(6) 457 458 assertBitfieldsEqual(t, onTimeExpected, exp.OnTimeSectors) 459 assertBitfieldsEqual(t, earlyExpected, exp.EarlySectors) 460 461 dlState.withTerminations(1, 2, 3, 4, 5, 6, 8, 9). 462 withPartitions( 463 bf(1, 2, 3, 4), 464 bf(5, 6, 7, 8), 465 bf(9), 466 ).assert(t, store, dl) 467 468 // Check early terminations. 469 earlyTerminations, more, err := dl.PopEarlyTerminations(store, 100, 100) 470 require.NoError(t, err) 471 assert.False(t, more) 472 assert.Equal(t, uint64(1), earlyTerminations.PartitionsProcessed) 473 assert.Equal(t, uint64(1), earlyTerminations.SectorsProcessed) 474 assert.Len(t, earlyTerminations.Sectors, 1) 475 assertBitfieldEquals(t, earlyTerminations.Sectors[9], 6) 476 477 // Popping early terminations doesn't affect the terminations bitfield. 478 dlState.withTerminations(1, 2, 3, 4, 5, 6, 8, 9). 479 withPartitions( 480 bf(1, 2, 3, 4), 481 bf(5, 6, 7, 8), 482 bf(9), 483 ).assert(t, store, dl) 484 }) 485 486 t.Run("cannot pop expired sectors before proving", func(t *testing.T) { 487 store := ipld.NewADTStore(context.Background()) 488 489 dl := emptyDeadline(t, store) 490 // Add sectors, but don't prove. 491 addSectors(t, store, dl, false) 492 493 // Try to pop some expirations. 494 _, err := dl.PopExpiredSectors(store, 9, quantSpec) 495 require.Error(t, err) 496 require.Contains(t, err.Error(), "cannot pop expired sectors from a partition with unproven sectors") 497 }) 498 499 t.Run("post all the things", func(t *testing.T) { 500 store := ipld.NewADTStore(context.Background()) 501 502 dl := emptyDeadline(t, store) 503 addSectors(t, store, dl, true) 504 505 // add an inactive sector 506 power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec) 507 require.NoError(t, err) 508 expectedPower := miner.PowerForSectors(sectorSize, extraSectors) 509 assert.True(t, expectedPower.Equals(power)) 510 511 sectorArr := sectorsArr(t, store, allSectors) 512 513 postResult1, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{ 514 {Index: 0, Skipped: bf()}, 515 {Index: 1, Skipped: bf()}, 516 }) 517 require.NoError(t, err) 518 assertBitfieldEquals(t, postResult1.Sectors, 1, 2, 3, 4, 5, 6, 7, 8) 519 assertEmptyBitfield(t, postResult1.IgnoredSectors) 520 require.True(t, postResult1.NewFaultyPower.Equals(miner.NewPowerPairZero())) 521 require.True(t, postResult1.RetractedRecoveryPower.Equals(miner.NewPowerPairZero())) 522 require.True(t, postResult1.RecoveredPower.Equals(miner.NewPowerPairZero())) 523 524 // First two partitions posted 525 dlState.withPosts(0, 1). 526 withUnproven(10). 527 withPartitions( 528 bf(1, 2, 3, 4), 529 bf(5, 6, 7, 8), 530 bf(9, 10), 531 ).assert(t, store, dl) 532 533 postResult2, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{ 534 {Index: 2, Skipped: bf()}, 535 }) 536 require.NoError(t, err) 537 assertBitfieldEquals(t, postResult2.Sectors, 9, 10) 538 assertEmptyBitfield(t, postResult2.IgnoredSectors) 539 require.True(t, postResult2.NewFaultyPower.Equals(miner.NewPowerPairZero())) 540 require.True(t, postResult2.RetractedRecoveryPower.Equals(miner.NewPowerPairZero())) 541 require.True(t, postResult2.RecoveredPower.Equals(miner.NewPowerPairZero())) 542 // activate sector 10 543 require.True(t, postResult2.PowerDelta.Equals(sectorPower(t, 10))) 544 545 // All 3 partitions posted, unproven sector 10 proven and power activated. 546 dlState.withPosts(0, 1, 2). 547 withPartitions( 548 bf(1, 2, 3, 4), 549 bf(5, 6, 7, 8), 550 bf(9, 10), 551 ).assert(t, store, dl) 552 553 powerDelta, penalizedPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 13) 554 require.NoError(t, err) 555 556 // No power delta for successful post. 557 require.True(t, powerDelta.IsZero()) 558 require.True(t, penalizedPower.IsZero()) 559 560 // Everything back to normal. 561 dlState.withPartitions( 562 bf(1, 2, 3, 4), 563 bf(5, 6, 7, 8), 564 bf(9, 10), 565 ).assert(t, store, dl) 566 }) 567 568 t.Run("post with unproven, faults, recoveries, and retracted recoveries", func(t *testing.T) { 569 store := ipld.NewADTStore(context.Background()) 570 dl := emptyDeadline(t, store) 571 572 // Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty. 573 addThenMarkFaulty(t, store, dl, true) 574 575 // add an inactive sector 576 power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec) 577 require.NoError(t, err) 578 expectedPower := miner.PowerForSectors(sectorSize, extraSectors) 579 assert.True(t, expectedPower.Equals(power)) 580 581 sectorArr := sectorsArr(t, store, allSectors) 582 583 // Declare sectors 1 & 6 recovered. 584 require.NoError(t, dl.DeclareFaultsRecovered(store, sectorArr, sectorSize, map[uint64]bitfield.BitField{ 585 0: bf(1), 586 1: bf(6), 587 })) 588 589 // We're now recovering 1 & 6. 590 dlState.withRecovering(1, 6). 591 withFaults(1, 5, 6). 592 withUnproven(10). 593 withPartitions( 594 bf(1, 2, 3, 4), 595 bf(5, 6, 7, 8), 596 bf(9, 10), 597 ).assert(t, store, dl) 598 599 // Prove partitions 0 & 1, skipping sectors 1 & 7. 600 postResult, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{ 601 {Index: 0, Skipped: bf(1)}, 602 {Index: 1, Skipped: bf(7)}, 603 }) 604 605 require.NoError(t, err) 606 // 1, 5, and 7 are expected to be faulty. 607 // - 1 should have recovered but didn't (retracted) 608 // - 5 was already marked faulty. 609 // - 7 is newly faulty. 610 // - 6 has recovered. 611 assertBitfieldEquals(t, postResult.Sectors, 1, 2, 3, 4, 5, 6, 7, 8) 612 assertBitfieldEquals(t, postResult.IgnoredSectors, 1, 5, 7) 613 // sector 7 is newly faulty 614 require.True(t, postResult.NewFaultyPower.Equals(sectorPower(t, 7))) 615 // we failed to recover 1 (retracted) 616 require.True(t, postResult.RetractedRecoveryPower.Equals(sectorPower(t, 1))) 617 // we recovered 6 618 require.True(t, postResult.RecoveredPower.Equals(sectorPower(t, 6))) 619 // no power delta from these deadlines. 620 require.True(t, postResult.PowerDelta.IsZero()) 621 622 // First two partitions should be posted. 623 dlState.withPosts(0, 1). 624 withFaults(1, 5, 7). 625 withUnproven(10). 626 withPartitions( 627 bf(1, 2, 3, 4), 628 bf(5, 6, 7, 8), 629 bf(9, 10), 630 ).assert(t, store, dl) 631 632 powerDelta, penalizedPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 13) 633 require.NoError(t, err) 634 635 expFaultPower := sectorPower(t, 9, 10) 636 expPowerDelta := sectorPower(t, 9).Neg() 637 638 // Sector 9 wasn't proven. 639 require.True(t, powerDelta.Equals(expPowerDelta)) 640 // No new changes to recovering power. 641 require.True(t, penalizedPower.Equals(expFaultPower)) 642 643 // Posts taken care of. 644 // Unproven now faulty. 645 dlState.withFaults(1, 5, 7, 9, 10). 646 withPartitions( 647 bf(1, 2, 3, 4), 648 bf(5, 6, 7, 8), 649 bf(9, 10), 650 ).assert(t, store, dl) 651 }) 652 653 t.Run("post with skipped unproven", func(t *testing.T) { 654 store := ipld.NewADTStore(context.Background()) 655 656 dl := emptyDeadline(t, store) 657 addSectors(t, store, dl, true) 658 659 // add an inactive sector 660 power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec) 661 require.NoError(t, err) 662 expectedPower := miner.PowerForSectors(sectorSize, extraSectors) 663 assert.True(t, expectedPower.Equals(power)) 664 665 sectorArr := sectorsArr(t, store, allSectors) 666 667 postResult1, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{ 668 {Index: 0, Skipped: bf()}, 669 {Index: 1, Skipped: bf()}, 670 {Index: 2, Skipped: bf(10)}, 671 }) 672 673 require.NoError(t, err) 674 assertBitfieldEquals(t, postResult1.Sectors, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 675 assertBitfieldEquals(t, postResult1.IgnoredSectors, 10) 676 require.True(t, postResult1.NewFaultyPower.Equals(sectorPower(t, 10))) 677 require.True(t, postResult1.PowerDelta.IsZero()) // not proven yet. 678 require.True(t, postResult1.RetractedRecoveryPower.IsZero()) 679 require.True(t, postResult1.RecoveredPower.IsZero()) 680 681 // All posted 682 dlState.withPosts(0, 1, 2). 683 withFaults(10). 684 withPartitions( 685 bf(1, 2, 3, 4), 686 bf(5, 6, 7, 8), 687 bf(9, 10), 688 ).assert(t, store, dl) 689 690 powerDelta, penalizedPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 13) 691 require.NoError(t, err) 692 693 // All posts submitted, no power delta, no extra penalties. 694 require.True(t, powerDelta.IsZero()) 695 // Penalize for skipped sector 10. 696 require.True(t, penalizedPower.IsZero()) 697 698 // Everything back to normal, except that we have a fault. 699 dlState.withFaults(10). 700 withPartitions( 701 bf(1, 2, 3, 4), 702 bf(5, 6, 7, 8), 703 bf(9, 10), 704 ).assert(t, store, dl) 705 }) 706 707 t.Run("post missing partition", func(t *testing.T) { 708 store := ipld.NewADTStore(context.Background()) 709 710 dl := emptyDeadline(t, store) 711 addSectors(t, store, dl, true) 712 713 // add an inactive sector 714 power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec) 715 require.NoError(t, err) 716 expectedPower := miner.PowerForSectors(sectorSize, extraSectors) 717 assert.True(t, expectedPower.Equals(power)) 718 719 sectorArr := sectorsArr(t, store, allSectors) 720 721 _, err = dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{ 722 {Index: 0, Skipped: bf()}, 723 {Index: 3, Skipped: bf()}, 724 }) 725 require.Error(t, err) 726 require.Contains(t, err.Error(), "no such partition") 727 }) 728 729 t.Run("post partition twice", func(t *testing.T) { 730 store := ipld.NewADTStore(context.Background()) 731 732 dl := emptyDeadline(t, store) 733 addSectors(t, store, dl, true) 734 735 // add an inactive sector 736 power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec) 737 require.NoError(t, err) 738 expectedPower := miner.PowerForSectors(sectorSize, extraSectors) 739 assert.True(t, expectedPower.Equals(power)) 740 741 sectorArr := sectorsArr(t, store, allSectors) 742 743 _, err = dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{ 744 {Index: 0, Skipped: bf()}, 745 {Index: 0, Skipped: bf()}, 746 }) 747 require.Error(t, err) 748 require.Contains(t, err.Error(), "duplicate partitions proven") 749 }) 750 751 t.Run("retract recoveries", func(t *testing.T) { 752 store := ipld.NewADTStore(context.Background()) 753 dl := emptyDeadline(t, store) 754 755 // Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty. 756 addThenMarkFaulty(t, store, dl, true) 757 758 sectorArr := sectorsArr(t, store, sectors) 759 760 // Declare sectors 1 & 6 recovered. 761 require.NoError(t, dl.DeclareFaultsRecovered(store, sectorArr, sectorSize, map[uint64]bitfield.BitField{ 762 0: bf(1), 763 1: bf(6), 764 })) 765 766 // Retract recovery for sector 1. 767 powerDelta, err := dl.RecordFaults(store, sectorArr, sectorSize, quantSpec, 13, map[uint64]bitfield.BitField{ 768 0: bf(1), 769 }) 770 771 // We're just retracting a recovery, this doesn't count as a new fault. 772 require.NoError(t, err) 773 require.True(t, powerDelta.Equals(miner.NewPowerPairZero())) 774 775 // We're now recovering 6. 776 dlState.withRecovering(6). 777 withFaults(1, 5, 6). 778 withPartitions( 779 bf(1, 2, 3, 4), 780 bf(5, 6, 7, 8), 781 bf(9), 782 ).assert(t, store, dl) 783 784 // Prove all partitions. 785 postResult, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{ 786 {Index: 0, Skipped: bf()}, 787 {Index: 1, Skipped: bf()}, 788 {Index: 2, Skipped: bf()}, 789 }) 790 791 require.NoError(t, err) 792 // 1 & 5 are still faulty 793 assertBitfieldEquals(t, postResult.Sectors, 1, 2, 3, 4, 5, 6, 7, 8, 9) 794 assertBitfieldEquals(t, postResult.IgnoredSectors, 1, 5) 795 // All faults were declared. 796 require.True(t, postResult.NewFaultyPower.Equals(miner.NewPowerPairZero())) 797 // we didn't fail to recover anything. 798 require.True(t, postResult.RetractedRecoveryPower.Equals(miner.NewPowerPairZero())) 799 // we recovered 6. 800 require.True(t, postResult.RecoveredPower.Equals(sectorPower(t, 6))) 801 802 // First two partitions should be posted. 803 dlState.withPosts(0, 1, 2). 804 withFaults(1, 5). 805 withPartitions( 806 bf(1, 2, 3, 4), 807 bf(5, 6, 7, 8), 808 bf(9), 809 ).assert(t, store, dl) 810 811 newFaultyPower, failedRecoveryPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 13) 812 require.NoError(t, err) 813 814 // No power changes. 815 require.True(t, newFaultyPower.Equals(miner.NewPowerPairZero())) 816 require.True(t, failedRecoveryPower.Equals(miner.NewPowerPairZero())) 817 818 // Posts taken care of. 819 dlState.withFaults(1, 5). 820 withPartitions( 821 bf(1, 2, 3, 4), 822 bf(5, 6, 7, 8), 823 bf(9), 824 ).assert(t, store, dl) 825 }) 826 827 t.Run("reschedule expirations", func(t *testing.T) { 828 store := ipld.NewADTStore(context.Background()) 829 dl := emptyDeadline(t, store) 830 831 sectorArr := sectorsArr(t, store, sectors) 832 833 // Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty. 834 addThenMarkFaulty(t, store, dl, true) 835 836 // Try to reschedule two sectors, only the 7 (non faulty) should succeed. 837 replaced, err := dl.RescheduleSectorExpirations(store, sectorArr, 1, miner.PartitionSectorMap{ 838 1: bf(6, 7, 99), // 99 should be skipped, it doesn't exist. 839 5: bf(100), // partition 5 doesn't exist. 840 2: bf(), // empty bitfield should be fine. 841 }, sectorSize, quantSpec) 842 require.NoError(t, err) 843 844 assert.Len(t, replaced, 1) 845 846 exp, err := dl.PopExpiredSectors(store, 1, quantSpec) 847 require.NoError(t, err) 848 849 sector7 := selectSectors(t, sectors, bf(7))[0] 850 851 dlState.withFaults(1, 5, 6). 852 withTerminations(7). 853 withPartitions( 854 bf(1, 2, 3, 4), 855 bf(5, 6, 7, 8), 856 bf(9), 857 ).assert(t, store, dl) 858 assertBitfieldEmpty(t, exp.EarlySectors) 859 assertBitfieldEquals(t, exp.OnTimeSectors, 7) 860 assert.True(t, exp.ActivePower.Equals(miner.PowerForSector(sectorSize, sector7))) 861 assert.True(t, exp.FaultyPower.IsZero()) 862 assert.True(t, exp.OnTimePledge.Equals(sector7.InitialPledge)) 863 }) 864 865 t.Run("cannot declare faults in missing partitions", func(t *testing.T) { 866 store := ipld.NewADTStore(context.Background()) 867 dl := emptyDeadline(t, store) 868 869 addSectors(t, store, dl, true) 870 sectorArr := sectorsArr(t, store, allSectors) 871 872 // Declare sectors 1 & 6 faulty. 873 _, err := dl.RecordFaults(store, sectorArr, sectorSize, quantSpec, 17, map[uint64]bitfield.BitField{ 874 0: bf(1), 875 4: bf(6), 876 }) 877 require.Error(t, err) 878 require.Contains(t, err.Error(), "no such partition 4") 879 }) 880 881 t.Run("cannot declare faults recovered in missing partitions", func(t *testing.T) { 882 store := ipld.NewADTStore(context.Background()) 883 dl := emptyDeadline(t, store) 884 885 // Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty. 886 addThenMarkFaulty(t, store, dl, true) 887 sectorArr := sectorsArr(t, store, allSectors) 888 889 // Declare sectors 1 & 6 faulty. 890 err := dl.DeclareFaultsRecovered(store, sectorArr, sectorSize, map[uint64]bitfield.BitField{ 891 0: bf(1), 892 4: bf(6), 893 }) 894 require.Error(t, err) 895 require.Contains(t, err.Error(), "no such partition 4") 896 }) 897 } 898 899 func emptyDeadline(t *testing.T, store adt.Store) *miner.Deadline { 900 dl, err := miner.ConstructDeadline(store) 901 require.NoError(t, err) 902 return dl 903 } 904 905 // Helper type for validating deadline state. 906 // 907 // All methods take and the state by _value_ so one can (and should) construct a 908 // sane base-state. 909 type expectedDeadlineState struct { 910 quant miner.QuantSpec 911 sectorSize abi.SectorSize 912 partitionSize uint64 913 sectors []*miner.SectorOnChainInfo 914 915 faults bitfield.BitField 916 recovering bitfield.BitField 917 terminations bitfield.BitField 918 unproven bitfield.BitField 919 posts bitfield.BitField 920 921 partitionSectors []bitfield.BitField 922 } 923 924 //nolint:unused 925 func (s expectedDeadlineState) withQuantSpec(quant miner.QuantSpec) expectedDeadlineState { 926 s.quant = quant 927 return s 928 } 929 930 //nolint:unused 931 func (s expectedDeadlineState) withFaults(faults ...uint64) expectedDeadlineState { 932 s.faults = bf(faults...) 933 return s 934 } 935 936 //nolint:unused 937 func (s expectedDeadlineState) withRecovering(recovering ...uint64) expectedDeadlineState { 938 s.recovering = bf(recovering...) 939 return s 940 } 941 942 //nolint:unused 943 func (s expectedDeadlineState) withTerminations(terminations ...uint64) expectedDeadlineState { 944 s.terminations = bf(terminations...) 945 return s 946 } 947 948 //nolint:unused 949 func (s expectedDeadlineState) withUnproven(unproven ...uint64) expectedDeadlineState { 950 s.unproven = bf(unproven...) 951 return s 952 } 953 954 //nolint:unused 955 func (s expectedDeadlineState) withPosts(posts ...uint64) expectedDeadlineState { 956 s.posts = bf(posts...) 957 return s 958 } 959 960 //nolint:unused 961 func (s expectedDeadlineState) withPartitions(partitions ...bitfield.BitField) expectedDeadlineState { 962 s.partitionSectors = partitions 963 return s 964 } 965 966 // Assert that the deadline's state matches the expected state. 967 func (s expectedDeadlineState) assert(t *testing.T, store adt.Store, dl *miner.Deadline) { 968 _, faults, recoveries, terminations, unproven := checkDeadlineInvariants( 969 t, store, dl, s.quant, s.sectorSize, s.sectors, 970 ) 971 972 assertBitfieldsEqual(t, s.faults, faults) 973 assertBitfieldsEqual(t, s.recovering, recoveries) 974 assertBitfieldsEqual(t, s.terminations, terminations) 975 assertBitfieldsEqual(t, s.unproven, unproven) 976 assertBitfieldsEqual(t, s.posts, dl.PartitionsPoSted) 977 978 partitions, err := dl.PartitionsArray(store) 979 require.NoError(t, err) 980 require.Equal(t, uint64(len(s.partitionSectors)), partitions.Length(), "unexpected number of partitions") 981 for i, partSectors := range s.partitionSectors { 982 var partition miner.Partition 983 found, err := partitions.Get(uint64(i), &partition) 984 require.NoError(t, err) 985 require.True(t, found) 986 assertBitfieldsEqual(t, partSectors, partition.Sectors) 987 } 988 } 989 990 // check the deadline's invariants, returning all contained sectors, faults, 991 // recoveries, terminations, and partition/sector assignments. 992 func checkDeadlineInvariants( 993 t *testing.T, store adt.Store, dl *miner.Deadline, 994 quant miner.QuantSpec, ssize abi.SectorSize, 995 sectors []*miner.SectorOnChainInfo, 996 ) ( 997 allSectors bitfield.BitField, 998 allFaults bitfield.BitField, 999 allRecoveries bitfield.BitField, 1000 allTerminations bitfield.BitField, 1001 allUnproven bitfield.BitField, 1002 ) { 1003 msgs := &builtin.MessageAccumulator{} 1004 summary := miner.CheckDeadlineStateInvariants(dl, store, quant, ssize, sectorsAsMap(sectors), msgs) 1005 assert.True(t, msgs.IsEmpty(), strings.Join(msgs.Messages(), "\n")) 1006 1007 allSectors = summary.AllSectors 1008 allFaults = summary.FaultySectors 1009 allRecoveries = summary.RecoveringSectors 1010 allTerminations = summary.TerminatedSectors 1011 allUnproven = summary.UnprovenSectors 1012 return 1013 }