github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/miner/partition_state_test.go (about) 1 package miner_test 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "sort" 8 "strings" 9 "testing" 10 11 "github.com/filecoin-project/go-bitfield" 12 "github.com/filecoin-project/go-state-types/abi" 13 "github.com/filecoin-project/go-state-types/exitcode" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 17 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 18 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" 19 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" 20 "github.com/filecoin-project/specs-actors/v4/support/ipld" 21 ) 22 23 func TestPartitions(t *testing.T) { 24 sectors := []*miner.SectorOnChainInfo{ 25 testSector(2, 1, 50, 60, 1000), 26 testSector(3, 2, 51, 61, 1001), 27 testSector(7, 3, 52, 62, 1002), 28 testSector(8, 4, 53, 63, 1003), 29 testSector(11, 5, 54, 64, 1004), 30 testSector(13, 6, 55, 65, 1005), 31 } 32 sectorSize := abi.SectorSize(32 << 30) 33 34 quantSpec := miner.NewQuantSpec(4, 1) 35 36 setupUnproven := func(t *testing.T) (adt.Store, *miner.Partition) { 37 store := ipld.NewADTStore(context.Background()) 38 partition := emptyPartition(t, store) 39 40 power, err := partition.AddSectors(store, false, sectors, sectorSize, quantSpec) 41 require.NoError(t, err) 42 expectedPower := miner.PowerForSectors(sectorSize, sectors) 43 assert.True(t, expectedPower.Equals(power)) 44 45 return store, partition 46 } 47 48 setup := func(t *testing.T) (adt.Store, *miner.Partition) { 49 store, partition := setupUnproven(t) 50 51 power := partition.ActivateUnproven() 52 53 expectedPower := miner.PowerForSectors(sectorSize, sectors) 54 assert.True(t, expectedPower.Equals(power)) 55 56 return store, partition 57 } 58 59 t.Run("adds sectors then activates unproven", func(t *testing.T) { 60 _, partition := setupUnproven(t) 61 62 power := partition.ActivateUnproven() 63 expectedPower := miner.PowerForSectors(sectorSize, sectors) 64 assert.True(t, expectedPower.Equals(power)) 65 }) 66 67 t.Run("adds sectors and reports sector stats", func(t *testing.T) { 68 store, partition := setup(t) 69 70 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(), bf(), bf(), bf()) 71 72 // assert sectors have been arranged into 3 groups 73 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 74 {expiration: 5, sectors: bf(1, 2)}, 75 {expiration: 9, sectors: bf(3, 4)}, 76 {expiration: 13, sectors: bf(5, 6)}, 77 }) 78 }) 79 80 t.Run("doesn't add sectors twice", func(t *testing.T) { 81 store, partition := setup(t) 82 83 _, err := partition.AddSectors(store, false, sectors[:1], sectorSize, quantSpec) 84 require.EqualError(t, err, "not all added sectors are new") 85 }) 86 87 for _, proven := range []bool{true, false} { 88 t.Run(fmt.Sprintf("adds faults (proven:%v)", proven), func(t *testing.T) { 89 store, partition := setupUnproven(t) 90 if proven { 91 _ = partition.ActivateUnproven() 92 } 93 sectorArr := sectorsArr(t, store, sectors) 94 95 faultSet := bf(4, 5) 96 _, powerDelta, newFaultyPower, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 97 require.NoError(t, err) 98 99 expectedFaultyPower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, faultSet)) 100 expectedPowerDelta := miner.NewPowerPairZero() 101 if proven { 102 expectedPowerDelta = expectedFaultyPower.Neg() 103 } 104 assert.True(t, expectedFaultyPower.Equals(newFaultyPower)) 105 assert.True(t, powerDelta.Equals(expectedPowerDelta)) 106 107 sectorNos := bf(1, 2, 3, 4, 5, 6) 108 unprovenSet := bf(1, 2, 3, 6) // faults are no longer "unproven", just faulty. 109 if proven { 110 unprovenSet = bf() 111 } 112 113 assertPartitionState( 114 t, store, partition, quantSpec, sectorSize, sectors, 115 sectorNos, faultSet, bf(), bf(), unprovenSet, 116 ) 117 118 // moves faulty sectors after expiration to earlier group 119 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 120 {expiration: 5, sectors: bf(1, 2)}, 121 {expiration: 9, sectors: bf(3, 4, 5)}, 122 {expiration: 13, sectors: bf(6)}, 123 }) 124 }) 125 } 126 127 t.Run("re-adding faults is a no-op", func(t *testing.T) { 128 store, partition := setup(t) 129 sectorArr := sectorsArr(t, store, sectors) 130 131 faultSet := bf(4, 5) 132 _, powerDelta, newFaultyPower, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 133 require.NoError(t, err) 134 135 expectedFaultyPower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, faultSet)) 136 assert.True(t, expectedFaultyPower.Equals(newFaultyPower)) 137 assert.True(t, powerDelta.Equals(expectedFaultyPower.Neg())) 138 139 faultSet = bf(5, 6) 140 newFaults, powerDelta, newFaultyPower, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(3), sectorSize, quantSpec) 141 require.NoError(t, err) 142 assertBitfieldEquals(t, newFaults, 6) 143 expectedFaultyPower = miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(6))) 144 assert.True(t, expectedFaultyPower.Equals(newFaultyPower)) 145 assert.True(t, powerDelta.Equals(expectedFaultyPower.Neg())) 146 147 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(4, 5, 6), bf(), bf(), bf()) 148 149 // moves newly-faulty sector 150 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 151 {expiration: 5, sectors: bf(1, 2, 6)}, 152 {expiration: 9, sectors: bf(3, 4, 5)}, 153 }) 154 }) 155 156 t.Run("fails to add faults for missing sectors", func(t *testing.T) { 157 store, partition := setup(t) 158 sectorArr := sectorsArr(t, store, sectors) 159 160 faultSet := bf(99) 161 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 162 require.Error(t, err) 163 assert.Contains(t, err.Error(), "not all sectors are assigned to the partition") 164 }) 165 166 t.Run("adds recoveries", func(t *testing.T) { 167 store, partition := setup(t) 168 sectorArr := sectorsArr(t, store, sectors) 169 170 // make 4, 5 and 6 faulty 171 faultSet := bf(4, 5, 6) 172 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 173 require.NoError(t, err) 174 175 // add 4 and 5 as recoveries 176 recoverSet := bf(4, 5) 177 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, recoverSet) 178 require.NoError(t, err) 179 180 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(4, 5, 6), bf(4, 5), bf(), bf()) 181 }) 182 183 t.Run("remove recoveries", func(t *testing.T) { 184 store, partition := setup(t) 185 sectorArr := sectorsArr(t, store, sectors) 186 187 // make 4, 5 and 6 faulty 188 faultSet := bf(4, 5, 6) 189 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 190 require.NoError(t, err) 191 192 // add 4 and 5 as recoveries 193 recoverSet := bf(4, 5) 194 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, recoverSet) 195 require.NoError(t, err) 196 197 // declaring no faults doesn't do anything. 198 newFaults, _, _, err := partition.RecordFaults(store, sectorArr, bf(), abi.ChainEpoch(7), sectorSize, quantSpec) 199 require.NoError(t, err) 200 assertBitfieldEmpty(t, newFaults) // no new faults. 201 202 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(4, 5, 6), bf(4, 5), bf(), bf()) 203 204 // removing sector 5 alters recovery set and recovery power 205 newFaults, _, _, err = partition.RecordFaults(store, sectorArr, bf(5), abi.ChainEpoch(10), sectorSize, quantSpec) 206 require.NoError(t, err) 207 assertBitfieldEmpty(t, newFaults) // these faults aren't new. 208 209 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(4, 5, 6), bf(4), bf(), bf()) 210 }) 211 212 t.Run("recovers faults", func(t *testing.T) { 213 store, partition := setup(t) 214 sectorArr := sectorsArr(t, store, sectors) 215 216 // make 4, 5 and 6 faulty 217 faultSet := bf(4, 5, 6) 218 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 219 require.NoError(t, err) 220 221 // add 4 and 5 as recoveries 222 recoverSet := bf(4, 5) 223 recoveryPower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, recoverSet)) 224 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, recoverSet) 225 require.NoError(t, err) 226 227 // mark recoveries as recovered recover sectors 228 recoveredPower, err := partition.RecoverFaults(store, sectorArr, sectorSize, quantSpec) 229 require.NoError(t, err) 230 231 // recovered power should equal power of recovery sectors 232 assert.True(t, recoveryPower.Equals(recoveredPower)) 233 234 // state should be as if recovered sectors were never faults 235 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(6), bf(), bf(), bf()) 236 237 // restores recovered expirations to original state (unrecovered sector 6 still expires early) 238 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 239 {expiration: 5, sectors: bf(1, 2)}, 240 {expiration: 9, sectors: bf(3, 4, 6)}, 241 {expiration: 13, sectors: bf(5)}, 242 }) 243 }) 244 245 t.Run("faulty power recovered exactly once", func(t *testing.T) { 246 store, partition := setup(t) 247 sectorArr := sectorsArr(t, store, sectors) 248 249 // make 4, 5 and 6 faulty 250 faultSet := bf(4, 5, 6) 251 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 252 require.NoError(t, err) 253 254 // add 3, 4 and 5 as recoveries. 3 is not faulty so it's skipped 255 recoverSet := bf(3, 4, 5) 256 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, recoverSet) 257 require.NoError(t, err) 258 259 recoveringPower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, faultSet)) 260 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, faultSet) 261 require.NoError(t, err) 262 assert.True(t, partition.RecoveringPower.Equals(recoveringPower)) 263 264 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(4, 5, 6), bf(4, 5, 6), bf(), bf()) 265 }) 266 267 t.Run("missing sectors are not recovered", func(t *testing.T) { 268 store, partition := setup(t) 269 sectorArr := sectorsArr(t, store, sectors) 270 271 // try to add 99 as a recovery but it's not in the partition 272 err := partition.DeclareFaultsRecovered(sectorArr, sectorSize, bf(99)) 273 require.Error(t, err) 274 assert.Contains(t, err.Error(), "not all sectors are assigned to the partition") 275 }) 276 277 t.Run("reschedules expirations", func(t *testing.T) { 278 store, partition := setup(t) 279 280 unprovenSector := testSector(13, 7, 55, 65, 1006) 281 allSectors := append(sectors[:len(sectors):len(sectors)], unprovenSector) 282 sectorArr := sectorsArr(t, store, allSectors) 283 284 // Mark sector 2 faulty, we should skip it when rescheduling 285 faultSet := bf(2) 286 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 287 require.NoError(t, err) 288 289 // Add an unproven sector. We _should_ reschedule the expiration. 290 // This is fine as we don't allow actually _expiring_ sectors 291 // while there are unproven sectors. 292 power, err := partition.AddSectors( 293 store, false, 294 []*miner.SectorOnChainInfo{unprovenSector}, 295 sectorSize, quantSpec, 296 ) 297 require.NoError(t, err) 298 expectedPower := miner.PowerForSectors(sectorSize, []*miner.SectorOnChainInfo{unprovenSector}) 299 assert.True(t, expectedPower.Equals(power)) 300 301 // reschedule 302 replaced, err := partition.RescheduleExpirations(store, sectorArr, 18, bf(2, 4, 6, 7), sectorSize, quantSpec) 303 require.NoError(t, err) 304 305 // Assert we returned the sector infos of the replaced sectors 306 assert.Len(t, replaced, 3) 307 sort.Slice(replaced, func(i, j int) bool { 308 return replaced[i].SectorNumber < replaced[j].SectorNumber 309 }) 310 assert.Equal(t, abi.SectorNumber(4), replaced[0].SectorNumber) 311 assert.Equal(t, abi.SectorNumber(6), replaced[1].SectorNumber) 312 assert.Equal(t, abi.SectorNumber(7), replaced[2].SectorNumber) 313 314 // We need to change the actual sector infos so our queue validation works. 315 rescheduled := rescheduleSectors(t, 18, allSectors, bf(4, 6, 7)) 316 317 // partition power and sector categorization should remain the same 318 assertPartitionState(t, store, partition, quantSpec, sectorSize, rescheduled, bf(1, 2, 3, 4, 5, 6, 7), bf(2), bf(), bf(), bf(7)) 319 320 // sectors should move to new expiration group 321 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 322 {expiration: 5, sectors: bf(1, 2)}, 323 {expiration: 9, sectors: bf(3)}, 324 {expiration: 13, sectors: bf(5)}, 325 {expiration: 21, sectors: bf(4, 6, 7)}, 326 }) 327 }) 328 329 t.Run("replace sectors", func(t *testing.T) { 330 store, partition := setup(t) 331 332 // remove 3 sectors starting with 2 333 oldSectors := sectors[1:4] 334 oldSectorPower := miner.PowerForSectors(sectorSize, oldSectors) 335 oldSectorPledge := int64(1001 + 1002 + 1003) 336 337 // replace 1 and add 2 new sectors 338 newSectors := []*miner.SectorOnChainInfo{ 339 testSector(10, 2, 150, 260, 3000), 340 testSector(10, 7, 151, 261, 3001), 341 testSector(18, 8, 152, 262, 3002), 342 } 343 newSectorPower := miner.PowerForSectors(sectorSize, newSectors) 344 newSectorPledge := int64(3000 + 3001 + 3002) 345 346 powerDelta, pledgeDelta, err := partition.ReplaceSectors(store, oldSectors, newSectors, sectorSize, quantSpec) 347 require.NoError(t, err) 348 349 expectedPowerDelta := newSectorPower.Sub(oldSectorPower) 350 assert.True(t, expectedPowerDelta.Equals(powerDelta)) 351 assert.Equal(t, abi.NewTokenAmount(newSectorPledge-oldSectorPledge), pledgeDelta) 352 353 // partition state should contain new sectors and not old sectors 354 allSectors := append(newSectors, sectors[:1]...) 355 allSectors = append(allSectors, sectors[4:]...) 356 assertPartitionState(t, store, partition, quantSpec, sectorSize, allSectors, bf(1, 2, 5, 6, 7, 8), bf(), bf(), bf(), bf()) 357 358 // sector 2 should be moved, 3 and 4 should be removed, and 7 and 8 added 359 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 360 {expiration: 5, sectors: bf(1)}, 361 {expiration: 13, sectors: bf(2, 5, 6, 7)}, 362 {expiration: 21, sectors: bf(8)}, 363 }) 364 }) 365 366 t.Run("replace sectors errors when attempting to replace inactive sector", func(t *testing.T) { 367 store, partition := setup(t) 368 sectorArr := sectorsArr(t, store, sectors) 369 370 // fault sector 2 371 faultSet := bf(2) 372 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 373 require.NoError(t, err) 374 375 // remove 3 sectors starting with 2 376 oldSectors := sectors[1:4] 377 378 // replace sector 2 379 newSectors := []*miner.SectorOnChainInfo{ 380 testSector(10, 2, 150, 260, 3000), 381 } 382 383 _, _, err = partition.ReplaceSectors(store, oldSectors, newSectors, sectorSize, quantSpec) 384 require.Error(t, err) 385 assert.Contains(t, err.Error(), "refusing to replace inactive sectors") 386 }) 387 388 t.Run("replace sectors errors when attempting to unproven sector", func(t *testing.T) { 389 store, partition := setupUnproven(t) 390 391 // remove 3 sectors starting with 2 392 oldSectors := sectors[1:4] 393 394 // replace sector 2 395 newSectors := []*miner.SectorOnChainInfo{ 396 testSector(10, 2, 150, 260, 3000), 397 } 398 399 _, _, err := partition.ReplaceSectors(store, oldSectors, newSectors, sectorSize, quantSpec) 400 require.Error(t, err) 401 assert.Contains(t, err.Error(), "refusing to replace inactive sectors") 402 }) 403 404 t.Run("terminate sectors", func(t *testing.T) { 405 store, partition := setup(t) 406 407 unprovenSector := testSector(13, 7, 55, 65, 1006) 408 allSectors := append(sectors[:len(sectors):len(sectors)], unprovenSector) 409 sectorArr := sectorsArr(t, store, allSectors) 410 411 // Add an unproven sector. 412 power, err := partition.AddSectors( 413 store, false, 414 []*miner.SectorOnChainInfo{unprovenSector}, 415 sectorSize, quantSpec, 416 ) 417 require.NoError(t, err) 418 expectedPower := miner.PowerForSectors(sectorSize, []*miner.SectorOnChainInfo{unprovenSector}) 419 assert.True(t, expectedPower.Equals(power)) 420 421 // fault sector 3, 4, 5 and 6 422 faultSet := bf(3, 4, 5, 6) 423 _, _, _, err = partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 424 require.NoError(t, err) 425 426 // mark 4and 5 as a recoveries 427 recoverSet := bf(4, 5) 428 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, recoverSet) 429 require.NoError(t, err) 430 431 // now terminate 1, 3, 5, and 7 432 terminations := bf(1, 3, 5, 7) 433 terminationEpoch := abi.ChainEpoch(3) 434 removed, err := partition.TerminateSectors(store, sectorArr, terminationEpoch, terminations, sectorSize, quantSpec) 435 require.NoError(t, err) 436 437 expectedActivePower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(1))) 438 assert.True(t, expectedActivePower.Equals(removed.ActivePower)) 439 expectedFaultyPower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(3, 5))) 440 assert.True(t, expectedFaultyPower.Equals(removed.FaultyPower)) 441 442 // expect partition state to no longer reflect power and pledge from terminated sectors and terminations to contain new sectors 443 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6, 7), bf(4, 6), bf(4), terminations, bf()) 444 445 // sectors should move to new expiration group 446 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 447 {expiration: 5, sectors: bf(2)}, 448 {expiration: 9, sectors: bf(4, 6)}, 449 }) 450 451 // sectors should be added to early termination bitfield queue 452 queue, err := miner.LoadBitfieldQueue(store, partition.EarlyTerminated, miner.NoQuantization, miner.PartitionEarlyTerminationArrayAmtBitwidth) 453 require.NoError(t, err) 454 455 ExpectBQ(). 456 Add(terminationEpoch, 1, 3, 5, 7). 457 Equals(t, queue) 458 }) 459 460 t.Run("terminate non-existent sectors", func(t *testing.T) { 461 store, partition := setup(t) 462 sectorArr := sectorsArr(t, store, sectors) 463 464 terminations := bf(99) 465 terminationEpoch := abi.ChainEpoch(3) 466 _, err := partition.TerminateSectors(store, sectorArr, terminationEpoch, terminations, sectorSize, quantSpec) 467 require.EqualError(t, err, "can only terminate live sectors") 468 }) 469 470 t.Run("terminate already terminated sector", func(t *testing.T) { 471 store, partition := setup(t) 472 sectorArr := sectorsArr(t, store, sectors) 473 474 terminations := bf(1) 475 terminationEpoch := abi.ChainEpoch(3) 476 477 // First termination works. 478 removed, err := partition.TerminateSectors(store, sectorArr, terminationEpoch, terminations, sectorSize, quantSpec) 479 require.NoError(t, err) 480 expectedActivePower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(1))) 481 assert.True(t, expectedActivePower.Equals(removed.ActivePower)) 482 assert.True(t, removed.FaultyPower.Equals(miner.NewPowerPairZero())) 483 count, err := removed.Count() 484 require.NoError(t, err) 485 assert.EqualValues(t, 1, count) 486 487 // Second termination fails 488 _, err = partition.TerminateSectors(store, sectorArr, terminationEpoch, terminations, sectorSize, quantSpec) 489 require.EqualError(t, err, "can only terminate live sectors") 490 }) 491 492 t.Run("mark terminated sectors as faulty", func(t *testing.T) { 493 store, partition := setup(t) 494 sectorArr := sectorsArr(t, store, sectors) 495 496 terminations := bf(1) 497 terminationEpoch := abi.ChainEpoch(3) 498 499 // Termination works. 500 _, err := partition.TerminateSectors(store, sectorArr, terminationEpoch, terminations, sectorSize, quantSpec) 501 require.NoError(t, err) 502 503 // Fault declaration for terminated sectors fails. 504 newFaults, _, _, err := partition.RecordFaults(store, sectorArr, terminations, abi.ChainEpoch(5), sectorSize, quantSpec) 505 require.NoError(t, err) 506 empty, err := newFaults.IsEmpty() 507 require.NoError(t, err) 508 require.True(t, empty) 509 }) 510 511 t.Run("pop expiring sectors", func(t *testing.T) { 512 store, partition := setup(t) 513 sectorArr := sectorsArr(t, store, sectors) 514 515 // add one fault with an early termination 516 faultSet := bf(4) 517 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(2), sectorSize, quantSpec) 518 require.NoError(t, err) 519 520 // pop first expiration set 521 expireEpoch := abi.ChainEpoch(5) 522 expset, err := partition.PopExpiredSectors(store, expireEpoch, quantSpec) 523 require.NoError(t, err) 524 525 assertBitfieldEquals(t, expset.OnTimeSectors, 1, 2) 526 assertBitfieldEquals(t, expset.EarlySectors, 4) 527 assert.Equal(t, abi.NewTokenAmount(1000+1001), expset.OnTimePledge) 528 529 // active power only contains power from non-faulty sectors 530 assert.True(t, expset.ActivePower.Equals(miner.PowerForSectors(sectorSize, sectors[:2]))) 531 532 // faulty power comes from early termination 533 assert.True(t, expset.FaultyPower.Equals(miner.PowerForSectors(sectorSize, sectors[3:4]))) 534 535 // expect sectors to be moved to terminations 536 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(), bf(), bf(1, 2, 4), bf()) 537 538 // sectors should move to new expiration group 539 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 540 {expiration: 9, sectors: bf(3)}, 541 {expiration: 13, sectors: bf(5, 6)}, 542 }) 543 544 // sectors should be added to early termination bitfield queue 545 queue, err := miner.LoadBitfieldQueue(store, partition.EarlyTerminated, miner.NoQuantization, miner.PartitionEarlyTerminationArrayAmtBitwidth) 546 require.NoError(t, err) 547 548 // only early termination appears in bitfield queue 549 ExpectBQ(). 550 Add(expireEpoch, 4). 551 Equals(t, queue) 552 }) 553 554 t.Run("pop expiring sectors errors if a recovery exists", func(t *testing.T) { 555 store, partition := setup(t) 556 sectorArr := sectorsArr(t, store, sectors) 557 558 _, _, _, err := partition.RecordFaults(store, sectorArr, bf(5), abi.ChainEpoch(2), sectorSize, quantSpec) 559 require.NoError(t, err) 560 561 // add a recovery 562 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, bf(5)) 563 require.NoError(t, err) 564 565 // pop first expiration set 566 expireEpoch := abi.ChainEpoch(5) 567 _, err = partition.PopExpiredSectors(store, expireEpoch, quantSpec) 568 require.Error(t, err) 569 assert.Contains(t, err.Error(), "unexpected recoveries while processing expirations") 570 }) 571 572 t.Run("pop expiring sectors errors if a unproven sectors exist", func(t *testing.T) { 573 store, partition := setupUnproven(t) 574 575 // pop first expiration set 576 expireEpoch := abi.ChainEpoch(5) 577 _, err := partition.PopExpiredSectors(store, expireEpoch, quantSpec) 578 require.Error(t, err) 579 assert.Contains(t, err.Error(), "cannot pop expired sectors from a partition with unproven sectors") 580 }) 581 582 t.Run("records missing PoSt", func(t *testing.T) { 583 store, partition := setup(t) 584 585 unprovenSector := testSector(13, 7, 55, 65, 1006) 586 allSectors := append(sectors[:len(sectors):len(sectors)], unprovenSector) 587 sectorArr := sectorsArr(t, store, allSectors) 588 589 // Add an unproven sector. 590 power, err := partition.AddSectors( 591 store, false, 592 []*miner.SectorOnChainInfo{unprovenSector}, 593 sectorSize, quantSpec, 594 ) 595 require.NoError(t, err) 596 expectedPower := miner.PowerForSectors(sectorSize, []*miner.SectorOnChainInfo{unprovenSector}) 597 assert.True(t, expectedPower.Equals(power)) 598 599 // make 4, 5 and 6 faulty 600 faultSet := bf(4, 5, 6) 601 _, _, _, err = partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 602 require.NoError(t, err) 603 604 // add 4 and 5 as recoveries 605 recoverSet := bf(4, 5) 606 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, recoverSet) 607 require.NoError(t, err) 608 609 // record entire partition as faulted 610 powerDelta, penalizedPower, newFaultyPower, err := partition.RecordMissedPost(store, abi.ChainEpoch(6), quantSpec) 611 require.NoError(t, err) 612 613 expectedNewFaultPower := miner.PowerForSectors(sectorSize, append(allSectors[:3:3], allSectors[6])) 614 assert.True(t, expectedNewFaultPower.Equals(newFaultyPower)) 615 616 // 6 has always been faulty, so we shouldn't be penalized for it (except ongoing). 617 expectedPenalizedPower := miner.PowerForSectors(sectorSize, allSectors). 618 Sub(miner.PowerForSector(sectorSize, allSectors[5])) 619 assert.True(t, expectedPenalizedPower.Equals(penalizedPower)) 620 621 // We should lose power for sectors 1-3. 622 expectedPowerDelta := miner.PowerForSectors(sectorSize, allSectors[:3]).Neg() 623 assert.True(t, expectedPowerDelta.Equals(powerDelta)) 624 625 // everything is now faulty 626 assertPartitionState(t, store, partition, quantSpec, sectorSize, allSectors, bf(1, 2, 3, 4, 5, 6, 7), bf(1, 2, 3, 4, 5, 6, 7), bf(), bf(), bf()) 627 628 // everything not in first expiration group is now in second because fault expiration quantized to 9 629 assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ 630 {expiration: 5, sectors: bf(1, 2)}, 631 {expiration: 9, sectors: bf(3, 4, 5, 6, 7)}, 632 }) 633 }) 634 635 t.Run("pops early terminations", func(t *testing.T) { 636 store, partition := setup(t) 637 sectorArr := sectorsArr(t, store, sectors) 638 639 // fault sector 3, 4, 5 and 6 640 faultSet := bf(3, 4, 5, 6) 641 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 642 require.NoError(t, err) 643 644 // mark 4and 5 as a recoveries 645 recoverSet := bf(4, 5) 646 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, recoverSet) 647 require.NoError(t, err) 648 649 // now terminate 1, 3 and 5 650 terminations := bf(1, 3, 5) 651 terminationEpoch := abi.ChainEpoch(3) 652 _, err = partition.TerminateSectors(store, sectorArr, terminationEpoch, terminations, sectorSize, quantSpec) 653 require.NoError(t, err) 654 655 // pop first termination 656 result, hasMore, err := partition.PopEarlyTerminations(store, 1) 657 require.NoError(t, err) 658 659 // expect first sector to be in early terminations 660 assertBitfieldEquals(t, result.Sectors[terminationEpoch], 1) 661 662 // expect more results 663 assert.True(t, hasMore) 664 665 // expect terminations to still contain 3 and 5 666 queue, err := miner.LoadBitfieldQueue(store, partition.EarlyTerminated, miner.NoQuantization, miner.PartitionEarlyTerminationArrayAmtBitwidth) 667 require.NoError(t, err) 668 669 // only early termination appears in bitfield queue 670 ExpectBQ(). 671 Add(terminationEpoch, 3, 5). 672 Equals(t, queue) 673 674 // pop the rest 675 result, hasMore, err = partition.PopEarlyTerminations(store, 5) 676 require.NoError(t, err) 677 678 // expect 3 and 5 679 assertBitfieldEquals(t, result.Sectors[terminationEpoch], 3, 5) 680 681 // expect no more results 682 assert.False(t, hasMore) 683 684 // expect early terminations to be empty 685 queue, err = miner.LoadBitfieldQueue(store, partition.EarlyTerminated, miner.NoQuantization, miner.PartitionEarlyTerminationArrayAmtBitwidth) 686 require.NoError(t, err) 687 ExpectBQ().Equals(t, queue) 688 }) 689 690 t.Run("test max sectors", func(t *testing.T) { 691 store := ipld.NewADTStore(context.Background()) 692 partition := emptyPartition(t, store) 693 694 proofType := abi.RegisteredSealProof_StackedDrg32GiBV1_1 695 sectorSize, err := proofType.SectorSize() 696 require.NoError(t, err) 697 partitionSectors, err := builtin.SealProofWindowPoStPartitionSectors(proofType) 698 require.NoError(t, err) 699 700 manySectors := make([]*miner.SectorOnChainInfo, partitionSectors) 701 ids := make([]uint64, partitionSectors) 702 for i := range manySectors { 703 id := uint64((i + 1) << 50) 704 ids[i] = id 705 manySectors[i] = testSector(int64(i+1), int64(id), 50, 60, 1000) 706 } 707 sectorNos := bf(ids...) 708 709 power, err := partition.AddSectors(store, false, manySectors, sectorSize, miner.NoQuantization) 710 require.NoError(t, err) 711 expectedPower := miner.PowerForSectors(sectorSize, manySectors) 712 assert.True(t, expectedPower.Equals(power)) 713 714 assertPartitionState( 715 t, store, partition, 716 miner.NoQuantization, sectorSize, manySectors, 717 sectorNos, bf(), bf(), bf(), sectorNos, 718 ) 719 720 // Make sure we can still encode and decode. 721 var buf bytes.Buffer 722 err = partition.MarshalCBOR(&buf) 723 require.NoError(t, err) 724 725 var newPartition miner.Partition 726 err = newPartition.UnmarshalCBOR(&buf) 727 require.NoError(t, err) 728 729 assertPartitionState( 730 t, store, &newPartition, 731 miner.NoQuantization, sectorSize, manySectors, 732 sectorNos, bf(), bf(), bf(), sectorNos, 733 ) 734 735 }) 736 } 737 738 func TestRecordSkippedFaults(t *testing.T) { 739 sectors := []*miner.SectorOnChainInfo{ 740 testSector(2, 1, 50, 60, 1000), 741 testSector(3, 2, 51, 61, 1001), 742 testSector(7, 3, 52, 62, 1002), 743 testSector(8, 4, 53, 63, 1003), 744 testSector(11, 5, 54, 64, 1004), 745 testSector(13, 6, 55, 65, 1005), 746 } 747 sectorSize := abi.SectorSize(32 << 30) 748 749 quantSpec := miner.NewQuantSpec(4, 1) 750 exp := abi.ChainEpoch(100) 751 752 setup := func(t *testing.T) (adt.Store, *miner.Partition) { 753 store := ipld.NewADTStore(context.Background()) 754 partition := emptyPartition(t, store) 755 756 power, err := partition.AddSectors(store, true, sectors, sectorSize, quantSpec) 757 require.NoError(t, err) 758 expectedPower := miner.PowerForSectors(sectorSize, sectors) 759 assert.True(t, expectedPower.Equals(power)) 760 761 return store, partition 762 } 763 764 t.Run("fail if ALL declared sectors are NOT in the partition", func(t *testing.T) { 765 store, partition := setup(t) 766 sectorArr := sectorsArr(t, store, sectors) 767 768 skipped := bitfield.NewFromSet([]uint64{1, 100}) 769 770 powerDelta, newFaulty, retractedRecovery, newFaults, err := partition.RecordSkippedFaults( 771 store, sectorArr, sectorSize, quantSpec, exp, skipped, 772 ) 773 require.Error(t, err) 774 require.EqualValues(t, exitcode.ErrIllegalArgument, exitcode.Unwrap(err, exitcode.Ok)) 775 require.EqualValues(t, miner.NewPowerPairZero(), newFaulty) 776 require.EqualValues(t, miner.NewPowerPairZero(), retractedRecovery) 777 require.EqualValues(t, miner.NewPowerPairZero(), powerDelta) 778 require.False(t, newFaults) 779 }) 780 781 t.Run("already faulty and terminated sectors are ignored", func(t *testing.T) { 782 store, partition := setup(t) 783 sectorArr := sectorsArr(t, store, sectors) 784 785 // terminate 1 AND 2 786 terminations := bf(1, 2) 787 terminationEpoch := abi.ChainEpoch(3) 788 _, err := partition.TerminateSectors(store, sectorArr, terminationEpoch, terminations, sectorSize, quantSpec) 789 require.NoError(t, err) 790 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(), bf(), terminations, bf()) 791 792 // declare 4 & 5 as faulty 793 faultSet := bf(4, 5) 794 _, _, _, err = partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 795 require.NoError(t, err) 796 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), faultSet, bf(), terminations, bf()) 797 798 // record skipped faults such that some of them are already faulty/terminated 799 skipped := bitfield.NewFromSet([]uint64{1, 2, 3, 4, 5}) 800 powerDelta, newFaultPower, retractedPower, newFaults, err := partition.RecordSkippedFaults(store, sectorArr, sectorSize, quantSpec, exp, skipped) 801 require.NoError(t, err) 802 require.EqualValues(t, miner.NewPowerPairZero(), retractedPower) 803 expectedFaultyPower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(3))) 804 require.EqualValues(t, expectedFaultyPower, newFaultPower) 805 require.EqualValues(t, powerDelta, newFaultPower.Neg()) 806 require.True(t, newFaults) 807 808 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(3, 4, 5), bf(), bf(1, 2), bf()) 809 }) 810 811 t.Run("recoveries are retracted without being marked as new faulty power", func(t *testing.T) { 812 store, partition := setup(t) 813 sectorArr := sectorsArr(t, store, sectors) 814 815 // make 4, 5 and 6 faulty 816 faultSet := bf(4, 5, 6) 817 _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) 818 require.NoError(t, err) 819 820 // add 4 and 5 as recoveries 821 recoverSet := bf(4, 5) 822 err = partition.DeclareFaultsRecovered(sectorArr, sectorSize, recoverSet) 823 require.NoError(t, err) 824 825 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(4, 5, 6), bf(4, 5), bf(), bf()) 826 827 // record skipped faults such that some of them have been marked as recovered 828 skipped := bitfield.NewFromSet([]uint64{1, 4, 5}) 829 powerDelta, newFaultPower, recoveryPower, newFaults, err := partition.RecordSkippedFaults(store, sectorArr, sectorSize, quantSpec, exp, skipped) 830 require.NoError(t, err) 831 require.True(t, newFaults) 832 833 // only 1 is marked for fault power as 4 & 5 are recovering 834 expectedFaultyPower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(1))) 835 require.EqualValues(t, expectedFaultyPower, newFaultPower) 836 require.EqualValues(t, expectedFaultyPower.Neg(), powerDelta) 837 838 // 4 & 5 are marked for recovery power 839 expectedRecoveryPower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(4, 5))) 840 require.EqualValues(t, expectedRecoveryPower, recoveryPower) 841 842 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(1, 4, 5, 6), bf(), bf(), bf()) 843 }) 844 845 t.Run("successful when skipped fault set is empty", func(t *testing.T) { 846 store, partition := setup(t) 847 sectorArr := sectorsArr(t, store, sectors) 848 849 powerDelta, newFaultPower, recoveryPower, newFaults, err := partition.RecordSkippedFaults(store, sectorArr, sectorSize, quantSpec, exp, bf()) 850 require.NoError(t, err) 851 require.EqualValues(t, miner.NewPowerPairZero(), newFaultPower) 852 require.EqualValues(t, miner.NewPowerPairZero(), recoveryPower) 853 require.EqualValues(t, miner.NewPowerPairZero(), powerDelta) 854 require.False(t, newFaults) 855 856 assertPartitionState(t, store, partition, quantSpec, sectorSize, sectors, bf(1, 2, 3, 4, 5, 6), bf(), bf(), bf(), bf()) 857 }) 858 } 859 860 type expectExpirationGroup struct { 861 expiration abi.ChainEpoch 862 sectors bitfield.BitField 863 } 864 865 func assertPartitionExpirationQueue(t *testing.T, store adt.Store, partition *miner.Partition, quant miner.QuantSpec, groups []expectExpirationGroup) { 866 queue, err := miner.LoadExpirationQueue(store, partition.ExpirationsEpochs, quant, miner.PartitionExpirationAmtBitwidth) 867 require.NoError(t, err) 868 869 for _, group := range groups { 870 requireNoExpirationGroupsBefore(t, group.expiration, queue) 871 set, err := queue.PopUntil(group.expiration) 872 require.NoError(t, err) 873 874 // we pnly care whether the sectors are in the queue or not. ExpirationQueue tests can deal with early or on time. 875 allSectors, err := bitfield.MergeBitFields(set.OnTimeSectors, set.EarlySectors) 876 require.NoError(t, err) 877 assertBitfieldsEqual(t, group.sectors, allSectors) 878 } 879 } 880 881 func assertPartitionState(t *testing.T, 882 store adt.Store, 883 partition *miner.Partition, 884 quant miner.QuantSpec, 885 sectorSize abi.SectorSize, 886 sectors []*miner.SectorOnChainInfo, 887 allSectorIds bitfield.BitField, 888 faults bitfield.BitField, 889 recovering bitfield.BitField, 890 terminations bitfield.BitField, 891 unproven bitfield.BitField, 892 ) { 893 894 assertBitfieldsEqual(t, faults, partition.Faults) 895 assertBitfieldsEqual(t, recovering, partition.Recoveries) 896 assertBitfieldsEqual(t, terminations, partition.Terminated) 897 assertBitfieldsEqual(t, unproven, partition.Unproven) 898 assertBitfieldsEqual(t, allSectorIds, partition.Sectors) 899 900 msgs := &builtin.MessageAccumulator{} 901 _ = miner.CheckPartitionStateInvariants(partition, store, quant, sectorSize, sectorsAsMap(sectors), msgs) 902 assert.True(t, msgs.IsEmpty(), strings.Join(msgs.Messages(), "\n")) 903 } 904 905 func bf(secNos ...uint64) bitfield.BitField { 906 return bitfield.NewFromSet(secNos) 907 } 908 909 func selectSectors(t *testing.T, sectors []*miner.SectorOnChainInfo, field bitfield.BitField) []*miner.SectorOnChainInfo { 910 toInclude, err := field.AllMap(miner.AddressedSectorsMax) 911 require.NoError(t, err) 912 913 included := []*miner.SectorOnChainInfo{} 914 for _, s := range sectors { 915 if !toInclude[uint64(s.SectorNumber)] { 916 continue 917 } 918 included = append(included, s) 919 delete(toInclude, uint64(s.SectorNumber)) 920 } 921 assert.Empty(t, toInclude, "expected additional sectors") 922 return included 923 } 924 925 func emptyPartition(t *testing.T, store adt.Store) *miner.Partition { 926 p, err := miner.ConstructPartition(store) 927 require.NoError(t, err) 928 return p 929 } 930 931 func rescheduleSectors(t *testing.T, target abi.ChainEpoch, sectors []*miner.SectorOnChainInfo, filter bitfield.BitField) []*miner.SectorOnChainInfo { 932 toReschedule, err := filter.AllMap(miner.AddressedSectorsMax) 933 require.NoError(t, err) 934 output := make([]*miner.SectorOnChainInfo, len(sectors)) 935 for i, sector := range sectors { 936 cpy := *sector 937 if toReschedule[uint64(cpy.SectorNumber)] { 938 cpy.Expiration = target 939 } 940 output[i] = &cpy 941 } 942 return output 943 } 944 945 func sectorsAsMap(sectors []*miner.SectorOnChainInfo) map[abi.SectorNumber]*miner.SectorOnChainInfo { 946 m := map[abi.SectorNumber]*miner.SectorOnChainInfo{} 947 for _, s := range sectors { 948 m[s.SectorNumber] = s 949 } 950 return m 951 }