github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/miner/deadline_state.go (about) 1 package miner 2 3 import ( 4 "bytes" 5 "errors" 6 7 "github.com/filecoin-project/go-bitfield" 8 "github.com/filecoin-project/go-state-types/abi" 9 "github.com/filecoin-project/go-state-types/big" 10 xc "github.com/filecoin-project/go-state-types/exitcode" 11 "github.com/ipfs/go-cid" 12 cbg "github.com/whyrusleeping/cbor-gen" 13 "golang.org/x/xerrors" 14 15 "github.com/filecoin-project/specs-actors/v4/actors/runtime/proof" 16 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" 17 ) 18 19 // Deadlines contains Deadline objects, describing the sectors due at the given 20 // deadline and their state (faulty, terminated, recovering, etc.). 21 type Deadlines struct { 22 // Note: we could inline part of the deadline struct (e.g., active/assigned sectors) 23 // to make new sector assignment cheaper. At the moment, assigning a sector requires 24 // loading all deadlines to figure out where best to assign new sectors. 25 Due [WPoStPeriodDeadlines]cid.Cid // []Deadline 26 } 27 28 // Deadline holds the state for all sectors due at a specific deadline. 29 type Deadline struct { 30 // Partitions in this deadline, in order. 31 // The keys of this AMT are always sequential integers beginning with zero. 32 Partitions cid.Cid // AMT[PartitionNumber]Partition 33 34 // Maps epochs to partitions that _may_ have sectors that expire in or 35 // before that epoch, either on-time or early as faults. 36 // Keys are quantized to final epochs in each proving deadline. 37 // 38 // NOTE: Partitions MUST NOT be removed from this queue (until the 39 // associated epoch has passed) even if they no longer have sectors 40 // expiring at that epoch. Sectors expiring at this epoch may later be 41 // recovered, and this queue will not be updated at that time. 42 ExpirationsEpochs cid.Cid // AMT[ChainEpoch]BitField 43 44 // Partitions that have been proved by window PoSts so far during the 45 // current challenge window. 46 // NOTE: This bitfield includes both partitions whose proofs 47 // were optimistically accepted and stored in 48 // OptimisticPoStSubmissions, and those whose proofs were 49 // verified on-chain. 50 PartitionsPoSted bitfield.BitField 51 52 // Partitions with sectors that terminated early. 53 EarlyTerminations bitfield.BitField 54 55 // The number of non-terminated sectors in this deadline (incl faulty). 56 LiveSectors uint64 57 58 // The total number of sectors in this deadline (incl dead). 59 TotalSectors uint64 60 61 // Memoized sum of faulty power in partitions. 62 FaultyPower PowerPair 63 64 // AMT of optimistically accepted WindowPoSt proofs, submitted during 65 // the current challenge window. At the end of the challenge window, 66 // this AMT will be moved to PoStSubmissionsSnapshot. WindowPoSt proofs 67 // verified on-chain do not appear in this AMT. 68 OptimisticPoStSubmissions cid.Cid // AMT[]WindowedPoSt 69 70 // Snapshot of partition state at the end of the previous challenge 71 // window for this deadline. 72 PartitionsSnapshot cid.Cid 73 // Snapshot of the proofs submitted by the end of the previous challenge 74 // window for this deadline. 75 // 76 // These proofs may be disputed via DisputeWindowedPoSt. Successfully 77 // disputed window PoSts are removed from the snapshot. 78 OptimisticPoStSubmissionsSnapshot cid.Cid 79 } 80 81 type WindowedPoSt struct { 82 // Partitions proved by this WindowedPoSt. 83 Partitions bitfield.BitField 84 // Array of proofs, one per distinct registered proof type present in 85 // the sectors being proven. In the usual case of a single proof type, 86 // this array will always have a single element (independent of number 87 // of partitions). 88 Proofs []proof.PoStProof 89 } 90 91 // Bitwidth of AMTs determined empirically from mutation patterns and projections of mainnet data. 92 const DeadlinePartitionsAmtBitwidth = 3 // Usually a small array 93 const DeadlineExpirationAmtBitwidth = 5 94 95 // Given that 4 partitions can be proven in one post, this AMT's height will 96 // only exceed the partition AMT's height at ~0.75EiB of storage. 97 const DeadlineOptimisticPoStSubmissionsAmtBitwidth = 2 98 99 // 100 // Deadlines (plural) 101 // 102 103 func ConstructDeadlines(emptyDeadlineCid cid.Cid) *Deadlines { 104 d := new(Deadlines) 105 for i := range d.Due { 106 d.Due[i] = emptyDeadlineCid 107 } 108 return d 109 } 110 111 func (d *Deadlines) LoadDeadline(store adt.Store, dlIdx uint64) (*Deadline, error) { 112 if dlIdx >= uint64(len(d.Due)) { 113 return nil, xc.ErrIllegalArgument.Wrapf("invalid deadline %d", dlIdx) 114 } 115 deadline := new(Deadline) 116 err := store.Get(store.Context(), d.Due[dlIdx], deadline) 117 if err != nil { 118 return nil, xc.ErrIllegalState.Wrapf("failed to lookup deadline %d: %w", dlIdx, err) 119 } 120 return deadline, nil 121 } 122 123 func (d *Deadlines) ForEach(store adt.Store, cb func(dlIdx uint64, dl *Deadline) error) error { 124 for dlIdx := range d.Due { 125 dl, err := d.LoadDeadline(store, uint64(dlIdx)) 126 if err != nil { 127 return err 128 } 129 err = cb(uint64(dlIdx), dl) 130 if err != nil { 131 return err 132 } 133 } 134 return nil 135 } 136 137 func (d *Deadlines) UpdateDeadline(store adt.Store, dlIdx uint64, deadline *Deadline) error { 138 if dlIdx >= uint64(len(d.Due)) { 139 return xerrors.Errorf("invalid deadline %d", dlIdx) 140 } 141 142 if err := deadline.ValidateState(); err != nil { 143 return err 144 } 145 146 dlCid, err := store.Put(store.Context(), deadline) 147 if err != nil { 148 return err 149 } 150 d.Due[dlIdx] = dlCid 151 152 return nil 153 } 154 155 // 156 // Deadline (singular) 157 // 158 159 func ConstructDeadline(store adt.Store) (*Deadline, error) { 160 emptyPartitionsArrayCid, err := adt.StoreEmptyArray(store, DeadlinePartitionsAmtBitwidth) 161 if err != nil { 162 return nil, xerrors.Errorf("failed to construct empty partitions array: %w", err) 163 } 164 emptyDeadlineExpirationArrayCid, err := adt.StoreEmptyArray(store, DeadlineExpirationAmtBitwidth) 165 if err != nil { 166 return nil, xerrors.Errorf("failed to construct empty deadline expiration array: %w", err) 167 } 168 169 emptyPoStSubmissionsArrayCid, err := adt.StoreEmptyArray(store, DeadlineOptimisticPoStSubmissionsAmtBitwidth) 170 if err != nil { 171 return nil, xerrors.Errorf("failed to construct empty proofs array: %w", err) 172 } 173 174 return &Deadline{ 175 Partitions: emptyPartitionsArrayCid, 176 ExpirationsEpochs: emptyDeadlineExpirationArrayCid, 177 EarlyTerminations: bitfield.New(), 178 LiveSectors: 0, 179 TotalSectors: 0, 180 FaultyPower: NewPowerPairZero(), 181 PartitionsPoSted: bitfield.New(), 182 OptimisticPoStSubmissions: emptyPoStSubmissionsArrayCid, 183 PartitionsSnapshot: emptyPartitionsArrayCid, 184 OptimisticPoStSubmissionsSnapshot: emptyPoStSubmissionsArrayCid, 185 }, nil 186 } 187 188 func (d *Deadline) PartitionsArray(store adt.Store) (*adt.Array, error) { 189 arr, err := adt.AsArray(store, d.Partitions, DeadlinePartitionsAmtBitwidth) 190 if err != nil { 191 return nil, xc.ErrIllegalState.Wrapf("failed to load partitions: %w", err) 192 } 193 return arr, nil 194 } 195 196 func (d *Deadline) OptimisticProofsArray(store adt.Store) (*adt.Array, error) { 197 arr, err := adt.AsArray(store, d.OptimisticPoStSubmissions, DeadlineOptimisticPoStSubmissionsAmtBitwidth) 198 if err != nil { 199 return nil, xerrors.Errorf("failed to load proofs: %w", err) 200 } 201 return arr, nil 202 } 203 204 func (d *Deadline) PartitionsSnapshotArray(store adt.Store) (*adt.Array, error) { 205 arr, err := adt.AsArray(store, d.PartitionsSnapshot, DeadlinePartitionsAmtBitwidth) 206 if err != nil { 207 return nil, xerrors.Errorf("failed to load partitions snapshot: %w", err) 208 } 209 return arr, nil 210 } 211 212 func (d *Deadline) OptimisticProofsSnapshotArray(store adt.Store) (*adt.Array, error) { 213 arr, err := adt.AsArray(store, d.OptimisticPoStSubmissionsSnapshot, DeadlineOptimisticPoStSubmissionsAmtBitwidth) 214 if err != nil { 215 return nil, xerrors.Errorf("failed to load proofs snapshot: %w", err) 216 } 217 return arr, nil 218 } 219 220 func (d *Deadline) LoadPartition(store adt.Store, partIdx uint64) (*Partition, error) { 221 partitions, err := d.PartitionsArray(store) 222 if err != nil { 223 return nil, err 224 } 225 var partition Partition 226 found, err := partitions.Get(partIdx, &partition) 227 if err != nil { 228 return nil, xc.ErrIllegalState.Wrapf("failed to lookup partition %d: %w", partIdx, err) 229 } 230 if !found { 231 return nil, xc.ErrNotFound.Wrapf("no partition %d", partIdx) 232 } 233 return &partition, nil 234 } 235 236 func (d *Deadline) LoadPartitionSnapshot(store adt.Store, partIdx uint64) (*Partition, error) { 237 partitions, err := d.PartitionsSnapshotArray(store) 238 if err != nil { 239 return nil, err 240 } 241 var partition Partition 242 found, err := partitions.Get(partIdx, &partition) 243 if err != nil { 244 return nil, xerrors.Errorf("failed to lookup partition %d: %w", partIdx, err) 245 } 246 if !found { 247 return nil, xc.ErrNotFound.Wrapf("no partition %d", partIdx) 248 } 249 return &partition, nil 250 } 251 252 // Adds some partition numbers to the set expiring at an epoch. 253 func (d *Deadline) AddExpirationPartitions(store adt.Store, expirationEpoch abi.ChainEpoch, partitions []uint64, quant QuantSpec) error { 254 // Avoid doing any work if there's nothing to reschedule. 255 if len(partitions) == 0 { 256 return nil 257 } 258 259 queue, err := LoadBitfieldQueue(store, d.ExpirationsEpochs, quant, DeadlineExpirationAmtBitwidth) 260 if err != nil { 261 return xerrors.Errorf("failed to load expiration queue: %w", err) 262 } 263 if err = queue.AddToQueueValues(expirationEpoch, partitions...); err != nil { 264 return xerrors.Errorf("failed to mutate expiration queue: %w", err) 265 } 266 if d.ExpirationsEpochs, err = queue.Root(); err != nil { 267 return xerrors.Errorf("failed to save expiration queue: %w", err) 268 } 269 return nil 270 } 271 272 // PopExpiredSectors terminates expired sectors from all partitions. 273 // Returns the expired sector aggregates. 274 func (dl *Deadline) PopExpiredSectors(store adt.Store, until abi.ChainEpoch, quant QuantSpec) (*ExpirationSet, error) { 275 expiredPartitions, modified, err := dl.popExpiredPartitions(store, until, quant) 276 if err != nil { 277 return nil, err 278 } else if !modified { 279 return NewExpirationSetEmpty(), nil // nothing to do. 280 } 281 282 partitions, err := dl.PartitionsArray(store) 283 if err != nil { 284 return nil, err 285 } 286 287 var onTimeSectors []bitfield.BitField 288 var earlySectors []bitfield.BitField 289 allOnTimePledge := big.Zero() 290 allActivePower := NewPowerPairZero() 291 allFaultyPower := NewPowerPairZero() 292 var partitionsWithEarlyTerminations []uint64 293 294 // For each partition with an expiry, remove and collect expirations from the partition queue. 295 if err = expiredPartitions.ForEach(func(partIdx uint64) error { 296 var partition Partition 297 if found, err := partitions.Get(partIdx, &partition); err != nil { 298 return err 299 } else if !found { 300 return xerrors.Errorf("missing expected partition %d", partIdx) 301 } 302 303 partExpiration, err := partition.PopExpiredSectors(store, until, quant) 304 if err != nil { 305 return xerrors.Errorf("failed to pop expired sectors from partition %d: %w", partIdx, err) 306 } 307 308 onTimeSectors = append(onTimeSectors, partExpiration.OnTimeSectors) 309 earlySectors = append(earlySectors, partExpiration.EarlySectors) 310 allActivePower = allActivePower.Add(partExpiration.ActivePower) 311 allFaultyPower = allFaultyPower.Add(partExpiration.FaultyPower) 312 allOnTimePledge = big.Add(allOnTimePledge, partExpiration.OnTimePledge) 313 314 if empty, err := partExpiration.EarlySectors.IsEmpty(); err != nil { 315 return xerrors.Errorf("failed to count early expirations from partition %d: %w", partIdx, err) 316 } else if !empty { 317 partitionsWithEarlyTerminations = append(partitionsWithEarlyTerminations, partIdx) 318 } 319 320 return partitions.Set(partIdx, &partition) 321 }); err != nil { 322 return nil, err 323 } 324 325 if dl.Partitions, err = partitions.Root(); err != nil { 326 return nil, err 327 } 328 329 // Update early expiration bitmap. 330 for _, partIdx := range partitionsWithEarlyTerminations { 331 dl.EarlyTerminations.Set(partIdx) 332 } 333 334 allOnTimeSectors, err := bitfield.MultiMerge(onTimeSectors...) 335 if err != nil { 336 return nil, err 337 } 338 allEarlySectors, err := bitfield.MultiMerge(earlySectors...) 339 if err != nil { 340 return nil, err 341 } 342 343 // Update live sector count. 344 onTimeCount, err := allOnTimeSectors.Count() 345 if err != nil { 346 return nil, xerrors.Errorf("failed to count on-time expired sectors: %w", err) 347 } 348 earlyCount, err := allEarlySectors.Count() 349 if err != nil { 350 return nil, xerrors.Errorf("failed to count early expired sectors: %w", err) 351 } 352 dl.LiveSectors -= onTimeCount + earlyCount 353 354 dl.FaultyPower = dl.FaultyPower.Sub(allFaultyPower) 355 356 return NewExpirationSet(allOnTimeSectors, allEarlySectors, allOnTimePledge, allActivePower, allFaultyPower), nil 357 } 358 359 // Adds sectors to a deadline. It's the caller's responsibility to make sure 360 // that this deadline isn't currently "open" (i.e., being proved at this point 361 // in time). 362 // The sectors are assumed to be non-faulty. 363 // Returns the power of the added sectors (which is active yet if proven=false). 364 func (dl *Deadline) AddSectors( 365 store adt.Store, partitionSize uint64, proven bool, sectors []*SectorOnChainInfo, 366 ssize abi.SectorSize, quant QuantSpec, 367 ) (PowerPair, error) { 368 totalPower := NewPowerPairZero() 369 if len(sectors) == 0 { 370 return totalPower, nil 371 } 372 373 // First update partitions, consuming the sectors 374 partitionDeadlineUpdates := make(map[abi.ChainEpoch][]uint64) 375 dl.LiveSectors += uint64(len(sectors)) 376 dl.TotalSectors += uint64(len(sectors)) 377 378 { 379 partitions, err := dl.PartitionsArray(store) 380 if err != nil { 381 return NewPowerPairZero(), err 382 } 383 384 partIdx := partitions.Length() 385 if partIdx > 0 { 386 partIdx -= 1 // try filling up the last partition first. 387 } 388 389 for ; len(sectors) > 0; partIdx++ { 390 // Get/create partition to update. 391 partition := new(Partition) 392 if found, err := partitions.Get(partIdx, partition); err != nil { 393 return NewPowerPairZero(), err 394 } else if !found { 395 // This case will usually happen zero times. 396 // It would require adding more than a full partition in one go to happen more than once. 397 partition, err = ConstructPartition(store) 398 if err != nil { 399 return NewPowerPairZero(), err 400 } 401 } 402 403 // Figure out which (if any) sectors we want to add to this partition. 404 sectorCount, err := partition.Sectors.Count() 405 if err != nil { 406 return NewPowerPairZero(), err 407 } 408 if sectorCount >= partitionSize { 409 continue 410 } 411 412 size := min64(partitionSize-sectorCount, uint64(len(sectors))) 413 partitionNewSectors := sectors[:size] 414 sectors = sectors[size:] 415 416 // Add sectors to partition. 417 partitionPower, err := partition.AddSectors(store, proven, partitionNewSectors, ssize, quant) 418 if err != nil { 419 return NewPowerPairZero(), err 420 } 421 totalPower = totalPower.Add(partitionPower) 422 423 // Save partition back. 424 err = partitions.Set(partIdx, partition) 425 if err != nil { 426 return NewPowerPairZero(), err 427 } 428 429 // Record deadline -> partition mapping so we can later update the deadlines. 430 for _, sector := range partitionNewSectors { 431 partitionUpdate := partitionDeadlineUpdates[sector.Expiration] 432 // Record each new partition once. 433 if len(partitionUpdate) > 0 && partitionUpdate[len(partitionUpdate)-1] == partIdx { 434 continue 435 } 436 partitionDeadlineUpdates[sector.Expiration] = append(partitionUpdate, partIdx) 437 } 438 } 439 440 // Save partitions back. 441 dl.Partitions, err = partitions.Root() 442 if err != nil { 443 return NewPowerPairZero(), err 444 } 445 } 446 447 // Next, update the expiration queue. 448 { 449 deadlineExpirations, err := LoadBitfieldQueue(store, dl.ExpirationsEpochs, quant, DeadlineExpirationAmtBitwidth) 450 if err != nil { 451 return NewPowerPairZero(), xerrors.Errorf("failed to load expiration epochs: %w", err) 452 } 453 454 if err = deadlineExpirations.AddManyToQueueValues(partitionDeadlineUpdates); err != nil { 455 return NewPowerPairZero(), xerrors.Errorf("failed to add expirations for new deadlines: %w", err) 456 } 457 458 if dl.ExpirationsEpochs, err = deadlineExpirations.Root(); err != nil { 459 return NewPowerPairZero(), err 460 } 461 } 462 463 return totalPower, nil 464 } 465 466 func (dl *Deadline) PopEarlyTerminations(store adt.Store, maxPartitions, maxSectors uint64) (result TerminationResult, hasMore bool, err error) { 467 stopErr := errors.New("stop error") 468 469 partitions, err := dl.PartitionsArray(store) 470 if err != nil { 471 return TerminationResult{}, false, err 472 } 473 474 var partitionsFinished []uint64 475 if err = dl.EarlyTerminations.ForEach(func(partIdx uint64) error { 476 // Load partition. 477 var partition Partition 478 found, err := partitions.Get(partIdx, &partition) 479 if err != nil { 480 return xerrors.Errorf("failed to load partition %d: %w", partIdx, err) 481 } 482 483 if !found { 484 // If the partition doesn't exist any more, no problem. 485 // We don't expect this to happen (compaction should re-index altered partitions), 486 // but it's not worth failing if it does. 487 partitionsFinished = append(partitionsFinished, partIdx) 488 return nil 489 } 490 491 // Pop early terminations. 492 partitionResult, more, err := partition.PopEarlyTerminations( 493 store, maxSectors-result.SectorsProcessed, 494 ) 495 if err != nil { 496 return xerrors.Errorf("failed to pop terminations from partition: %w", err) 497 } 498 499 err = result.Add(partitionResult) 500 if err != nil { 501 return xerrors.Errorf("failed to merge termination result: %w", err) 502 } 503 504 // If we've processed all of them for this partition, unmark it in the deadline. 505 if !more { 506 partitionsFinished = append(partitionsFinished, partIdx) 507 } 508 509 // Save partition 510 err = partitions.Set(partIdx, &partition) 511 if err != nil { 512 return xerrors.Errorf("failed to store partition %v", partIdx) 513 } 514 515 if result.BelowLimit(maxPartitions, maxSectors) { 516 return nil 517 } 518 519 return stopErr 520 }); err != nil && err != stopErr { 521 return TerminationResult{}, false, xerrors.Errorf("failed to walk early terminations bitfield for deadlines: %w", err) 522 } 523 524 // Removed finished partitions from the index. 525 for _, finished := range partitionsFinished { 526 dl.EarlyTerminations.Unset(finished) 527 } 528 529 // Save deadline's partitions 530 dl.Partitions, err = partitions.Root() 531 if err != nil { 532 return TerminationResult{}, false, xerrors.Errorf("failed to update partitions") 533 } 534 535 // Update global early terminations bitfield. 536 noEarlyTerminations, err := dl.EarlyTerminations.IsEmpty() 537 if err != nil { 538 return TerminationResult{}, false, xerrors.Errorf("failed to count remaining early terminations partitions: %w", err) 539 } 540 541 return result, !noEarlyTerminations, nil 542 } 543 544 // Returns nil if nothing was popped. 545 func (dl *Deadline) popExpiredPartitions(store adt.Store, until abi.ChainEpoch, quant QuantSpec) (bitfield.BitField, bool, error) { 546 expirations, err := LoadBitfieldQueue(store, dl.ExpirationsEpochs, quant, DeadlineExpirationAmtBitwidth) 547 if err != nil { 548 return bitfield.BitField{}, false, err 549 } 550 551 popped, modified, err := expirations.PopUntil(until) 552 if err != nil { 553 return bitfield.BitField{}, false, xerrors.Errorf("failed to pop expiring partitions: %w", err) 554 } 555 556 if modified { 557 dl.ExpirationsEpochs, err = expirations.Root() 558 if err != nil { 559 return bitfield.BitField{}, false, err 560 } 561 } 562 563 return popped, modified, nil 564 } 565 566 func (dl *Deadline) TerminateSectors( 567 store adt.Store, 568 sectors Sectors, 569 epoch abi.ChainEpoch, 570 partitionSectors PartitionSectorMap, 571 ssize abi.SectorSize, 572 quant QuantSpec, 573 ) (powerLost PowerPair, err error) { 574 575 partitions, err := dl.PartitionsArray(store) 576 if err != nil { 577 return NewPowerPairZero(), err 578 } 579 580 powerLost = NewPowerPairZero() 581 var partition Partition 582 if err := partitionSectors.ForEach(func(partIdx uint64, sectorNos bitfield.BitField) error { 583 if found, err := partitions.Get(partIdx, &partition); err != nil { 584 return xerrors.Errorf("failed to load partition %d: %w", partIdx, err) 585 } else if !found { 586 return xc.ErrNotFound.Wrapf("failed to find partition %d", partIdx) 587 } 588 589 removed, err := partition.TerminateSectors(store, sectors, epoch, sectorNos, ssize, quant) 590 if err != nil { 591 return xerrors.Errorf("failed to terminate sectors in partition %d: %w", partIdx, err) 592 } 593 594 err = partitions.Set(partIdx, &partition) 595 if err != nil { 596 return xerrors.Errorf("failed to store updated partition %d: %w", partIdx, err) 597 } 598 599 if count, err := removed.Count(); err != nil { 600 return xerrors.Errorf("failed to count terminated sectors in partition %d: %w", partIdx, err) 601 } else if count > 0 { 602 // Record that partition now has pending early terminations. 603 dl.EarlyTerminations.Set(partIdx) 604 // Record change to sectors and power 605 dl.LiveSectors -= count 606 } // note: we should _always_ have early terminations, unless the early termination bitfield is empty. 607 608 dl.FaultyPower = dl.FaultyPower.Sub(removed.FaultyPower) 609 610 // Aggregate power lost from active sectors 611 powerLost = powerLost.Add(removed.ActivePower) 612 return nil 613 }); err != nil { 614 return NewPowerPairZero(), err 615 } 616 617 // save partitions back 618 dl.Partitions, err = partitions.Root() 619 if err != nil { 620 return NewPowerPairZero(), xerrors.Errorf("failed to persist partitions: %w", err) 621 } 622 623 return powerLost, nil 624 } 625 626 // RemovePartitions removes the specified partitions, shifting the remaining 627 // ones to the left, and returning the live and dead sectors they contained. 628 // 629 // Returns an error if any of the partitions contained faulty sectors or early 630 // terminations. 631 func (dl *Deadline) RemovePartitions(store adt.Store, toRemove bitfield.BitField, quant QuantSpec) ( 632 live, dead bitfield.BitField, removedPower PowerPair, err error, 633 ) { 634 oldPartitions, err := dl.PartitionsArray(store) 635 if err != nil { 636 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to load partitions: %w", err) 637 } 638 639 partitionCount := oldPartitions.Length() 640 toRemoveSet, err := toRemove.AllMap(partitionCount) 641 if err != nil { 642 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xc.ErrIllegalArgument.Wrapf("failed to expand partitions into map: %w", err) 643 } 644 645 // Nothing to do. 646 if len(toRemoveSet) == 0 { 647 return bitfield.NewFromSet(nil), bitfield.NewFromSet(nil), NewPowerPairZero(), nil 648 } 649 650 for partIdx := range toRemoveSet { //nolint:nomaprange 651 if partIdx >= partitionCount { 652 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xc.ErrIllegalArgument.Wrapf( 653 "partition index %d out of range [0, %d)", partIdx, partitionCount, 654 ) 655 } 656 } 657 658 // Should already be checked earlier, but we might as well check again. 659 noEarlyTerminations, err := dl.EarlyTerminations.IsEmpty() 660 if err != nil { 661 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to check for early terminations: %w", err) 662 } 663 if !noEarlyTerminations { 664 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("cannot remove partitions from deadline with early terminations: %w", err) 665 } 666 667 newPartitions, err := adt.MakeEmptyArray(store, DeadlinePartitionsAmtBitwidth) 668 if err != nil { 669 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to create empty array for initializing partitions: %w", err) 670 } 671 allDeadSectors := make([]bitfield.BitField, 0, len(toRemoveSet)) 672 allLiveSectors := make([]bitfield.BitField, 0, len(toRemoveSet)) 673 removedPower = NewPowerPairZero() 674 675 // Define all of these out here to save allocations. 676 var ( 677 lazyPartition cbg.Deferred 678 byteReader bytes.Reader 679 partition Partition 680 ) 681 if err = oldPartitions.ForEach(&lazyPartition, func(partIdx int64) error { 682 // If we're keeping the partition as-is, append it to the new partitions array. 683 if _, ok := toRemoveSet[uint64(partIdx)]; !ok { 684 return newPartitions.AppendContinuous(&lazyPartition) 685 } 686 687 // Ok, actually unmarshal the partition. 688 byteReader.Reset(lazyPartition.Raw) 689 err := partition.UnmarshalCBOR(&byteReader) 690 byteReader.Reset(nil) 691 if err != nil { 692 return xc.ErrIllegalState.Wrapf("failed to decode partition %d: %w", partIdx, err) 693 } 694 695 // Don't allow removing partitions with faulty sectors. 696 hasNoFaults, err := partition.Faults.IsEmpty() 697 if err != nil { 698 return xc.ErrIllegalState.Wrapf("failed to decode faults for partition %d: %w", partIdx, err) 699 } 700 if !hasNoFaults { 701 return xc.ErrIllegalArgument.Wrapf("cannot remove partition %d: has faults", partIdx) 702 } 703 704 // Don't allow removing partitions with unproven sectors. 705 allProven, err := partition.Unproven.IsEmpty() 706 if err != nil { 707 return xc.ErrIllegalState.Wrapf("failed to decode unproven for partition %d: %w", partIdx, err) 708 } 709 if !allProven { 710 return xc.ErrIllegalArgument.Wrapf("cannot remove partition %d: has unproven sectors", partIdx) 711 } 712 713 // Get the live sectors. 714 liveSectors, err := partition.LiveSectors() 715 if err != nil { 716 return xc.ErrIllegalState.Wrapf("failed to calculate live sectors for partition %d: %w", partIdx, err) 717 } 718 719 allDeadSectors = append(allDeadSectors, partition.Terminated) 720 allLiveSectors = append(allLiveSectors, liveSectors) 721 removedPower = removedPower.Add(partition.LivePower) 722 return nil 723 }); err != nil { 724 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("while removing partitions: %w", err) 725 } 726 727 dl.Partitions, err = newPartitions.Root() 728 if err != nil { 729 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to persist new partition table: %w", err) 730 } 731 732 dead, err = bitfield.MultiMerge(allDeadSectors...) 733 if err != nil { 734 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to merge dead sector bitfields: %w", err) 735 } 736 live, err = bitfield.MultiMerge(allLiveSectors...) 737 if err != nil { 738 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to merge live sector bitfields: %w", err) 739 } 740 741 // Update sector counts. 742 removedDeadSectors, err := dead.Count() 743 if err != nil { 744 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to count dead sectors: %w", err) 745 } 746 747 removedLiveSectors, err := live.Count() 748 if err != nil { 749 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to count live sectors: %w", err) 750 } 751 752 dl.LiveSectors -= removedLiveSectors 753 dl.TotalSectors -= removedLiveSectors + removedDeadSectors 754 755 // Update expiration bitfields. 756 { 757 expirationEpochs, err := LoadBitfieldQueue(store, dl.ExpirationsEpochs, quant, DeadlineExpirationAmtBitwidth) 758 if err != nil { 759 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed to load expiration queue: %w", err) 760 } 761 762 err = expirationEpochs.Cut(toRemove) 763 if err != nil { 764 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed cut removed partitions from deadline expiration queue: %w", err) 765 } 766 767 dl.ExpirationsEpochs, err = expirationEpochs.Root() 768 if err != nil { 769 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), xerrors.Errorf("failed persist deadline expiration queue: %w", err) 770 } 771 } 772 773 return live, dead, removedPower, nil 774 } 775 776 func (dl *Deadline) RecordFaults( 777 store adt.Store, sectors Sectors, ssize abi.SectorSize, quant QuantSpec, 778 faultExpirationEpoch abi.ChainEpoch, partitionSectors PartitionSectorMap, 779 ) (powerDelta PowerPair, err error) { 780 partitions, err := dl.PartitionsArray(store) 781 if err != nil { 782 return NewPowerPairZero(), err 783 } 784 785 // Record partitions with some fault, for subsequently indexing in the deadline. 786 // Duplicate entries don't matter, they'll be stored in a bitfield (a set). 787 partitionsWithFault := make([]uint64, 0, len(partitionSectors)) 788 powerDelta = NewPowerPairZero() 789 if err := partitionSectors.ForEach(func(partIdx uint64, sectorNos bitfield.BitField) error { 790 var partition Partition 791 if found, err := partitions.Get(partIdx, &partition); err != nil { 792 return xc.ErrIllegalState.Wrapf("failed to load partition %d: %w", partIdx, err) 793 } else if !found { 794 return xc.ErrNotFound.Wrapf("no such partition %d", partIdx) 795 } 796 797 newFaults, partitionPowerDelta, partitionNewFaultyPower, err := partition.RecordFaults( 798 store, sectors, sectorNos, faultExpirationEpoch, ssize, quant, 799 ) 800 if err != nil { 801 return xerrors.Errorf("failed to declare faults in partition %d: %w", partIdx, err) 802 } 803 dl.FaultyPower = dl.FaultyPower.Add(partitionNewFaultyPower) 804 powerDelta = powerDelta.Add(partitionPowerDelta) 805 if empty, err := newFaults.IsEmpty(); err != nil { 806 return xerrors.Errorf("failed to count new faults: %w", err) 807 } else if !empty { 808 partitionsWithFault = append(partitionsWithFault, partIdx) 809 } 810 811 err = partitions.Set(partIdx, &partition) 812 if err != nil { 813 return xc.ErrIllegalState.Wrapf("failed to store partition %d: %w", partIdx, err) 814 } 815 816 return nil 817 }); err != nil { 818 return NewPowerPairZero(), err 819 } 820 821 dl.Partitions, err = partitions.Root() 822 if err != nil { 823 return NewPowerPairZero(), xc.ErrIllegalState.Wrapf("failed to store partitions root: %w", err) 824 } 825 826 err = dl.AddExpirationPartitions(store, faultExpirationEpoch, partitionsWithFault, quant) 827 if err != nil { 828 return NewPowerPairZero(), xc.ErrIllegalState.Wrapf("failed to update expirations for partitions with faults: %w", err) 829 } 830 831 return powerDelta, nil 832 } 833 834 func (dl *Deadline) DeclareFaultsRecovered( 835 store adt.Store, sectors Sectors, ssize abi.SectorSize, 836 partitionSectors PartitionSectorMap, 837 ) (err error) { 838 partitions, err := dl.PartitionsArray(store) 839 if err != nil { 840 return err 841 } 842 843 if err := partitionSectors.ForEach(func(partIdx uint64, sectorNos bitfield.BitField) error { 844 var partition Partition 845 if found, err := partitions.Get(partIdx, &partition); err != nil { 846 return xc.ErrIllegalState.Wrapf("failed to load partition %d: %w", partIdx, err) 847 } else if !found { 848 return xc.ErrNotFound.Wrapf("no such partition %d", partIdx) 849 } 850 851 if err = partition.DeclareFaultsRecovered(sectors, ssize, sectorNos); err != nil { 852 return xc.ErrIllegalState.Wrapf("failed to add recoveries: %w", err) 853 } 854 855 err = partitions.Set(partIdx, &partition) 856 if err != nil { 857 return xc.ErrIllegalState.Wrapf("failed to update partition %d: %w", partIdx, err) 858 } 859 return nil 860 }); err != nil { 861 return err 862 } 863 864 // Power is not regained until the deadline end, when the recovery is confirmed. 865 866 dl.Partitions, err = partitions.Root() 867 if err != nil { 868 return xc.ErrIllegalState.Wrapf("failed to store partitions root: %w", err) 869 } 870 return nil 871 } 872 873 // ProcessDeadlineEnd processes all PoSt submissions, marking unproven sectors as 874 // faulty and clearing failed recoveries. It returns the power delta, and any 875 // power that should be penalized (new faults and failed recoveries). 876 func (dl *Deadline) ProcessDeadlineEnd(store adt.Store, quant QuantSpec, faultExpirationEpoch abi.ChainEpoch) ( 877 powerDelta, penalizedPower PowerPair, err error, 878 ) { 879 powerDelta = NewPowerPairZero() 880 penalizedPower = NewPowerPairZero() 881 882 partitions, err := dl.PartitionsArray(store) 883 if err != nil { 884 return powerDelta, penalizedPower, xerrors.Errorf("failed to load partitions: %w", err) 885 } 886 887 detectedAny := false 888 var rescheduledPartitions []uint64 889 for partIdx := uint64(0); partIdx < partitions.Length(); partIdx++ { 890 proven, err := dl.PartitionsPoSted.IsSet(partIdx) 891 if err != nil { 892 return powerDelta, penalizedPower, xerrors.Errorf("failed to check submission for partition %d: %w", partIdx, err) 893 } 894 if proven { 895 continue 896 } 897 898 var partition Partition 899 found, err := partitions.Get(partIdx, &partition) 900 if err != nil { 901 return powerDelta, penalizedPower, xerrors.Errorf("failed to load partition %d: %w", partIdx, err) 902 } 903 if !found { 904 return powerDelta, penalizedPower, xerrors.Errorf("no partition %d", partIdx) 905 } 906 907 // If we have no recovering power/sectors, and all power is faulty, skip 908 // this. This lets us skip some work if a miner repeatedly fails to PoSt. 909 if partition.RecoveringPower.IsZero() && partition.FaultyPower.Equals(partition.LivePower) { 910 continue 911 } 912 913 // Ok, we actually need to process this partition. Make sure we save the partition state back. 914 detectedAny = true 915 916 partPowerDelta, partPenalizedPower, partNewFaultyPower, err := partition.RecordMissedPost(store, faultExpirationEpoch, quant) 917 if err != nil { 918 return powerDelta, penalizedPower, xerrors.Errorf("failed to record missed PoSt for partition %v: %w", partIdx, err) 919 } 920 921 // We marked some sectors faulty, we need to record the new 922 // expiration. We don't want to do this if we're just penalizing 923 // the miner for failing to recover power. 924 if !partNewFaultyPower.IsZero() { 925 rescheduledPartitions = append(rescheduledPartitions, partIdx) 926 } 927 928 // Save new partition state. 929 err = partitions.Set(partIdx, &partition) 930 if err != nil { 931 return powerDelta, penalizedPower, xerrors.Errorf("failed to update partition %v: %w", partIdx, err) 932 } 933 934 dl.FaultyPower = dl.FaultyPower.Add(partNewFaultyPower) 935 936 powerDelta = powerDelta.Add(partPowerDelta) 937 penalizedPower = penalizedPower.Add(partPenalizedPower) 938 } 939 940 // Save modified deadline state. 941 if detectedAny { 942 dl.Partitions, err = partitions.Root() 943 if err != nil { 944 return powerDelta, penalizedPower, xc.ErrIllegalState.Wrapf("failed to store partitions: %w", err) 945 } 946 } 947 948 err = dl.AddExpirationPartitions(store, faultExpirationEpoch, rescheduledPartitions, quant) 949 if err != nil { 950 return powerDelta, penalizedPower, xc.ErrIllegalState.Wrapf("failed to update deadline expiration queue: %w", err) 951 } 952 953 // Reset PoSt submissions, snapshot proofs. 954 dl.PartitionsPoSted = bitfield.New() 955 dl.PartitionsSnapshot = dl.Partitions 956 dl.OptimisticPoStSubmissionsSnapshot = dl.OptimisticPoStSubmissions 957 dl.OptimisticPoStSubmissions, err = adt.StoreEmptyArray(store, DeadlineOptimisticPoStSubmissionsAmtBitwidth) 958 if err != nil { 959 return powerDelta, penalizedPower, xerrors.Errorf("failed to clear pending proofs array: %w", err) 960 } 961 return powerDelta, penalizedPower, nil 962 } 963 964 type PoStResult struct { 965 // Power activated or deactivated (positive or negative). 966 PowerDelta PowerPair 967 // Powers used for calculating penalties. 968 NewFaultyPower, RetractedRecoveryPower, RecoveredPower PowerPair 969 // Sectors is a bitfield of all sectors in the proven partitions. 970 Sectors bitfield.BitField 971 // IgnoredSectors is a subset of Sectors that should be ignored. 972 IgnoredSectors bitfield.BitField 973 // Bitfield of partitions that were proven. 974 Partitions bitfield.BitField 975 } 976 977 // RecordProvenSectors processes a series of posts, recording proven partitions 978 // and marking skipped sectors as faulty. 979 // 980 // It returns a PoStResult containing the list of proven and skipped sectors and 981 // changes to power (newly faulty power, power that should have been proven 982 // recovered but wasn't, and newly recovered power). 983 // 984 // NOTE: This function does not actually _verify_ any proofs. 985 func (dl *Deadline) RecordProvenSectors( 986 store adt.Store, sectors Sectors, 987 ssize abi.SectorSize, quant QuantSpec, faultExpiration abi.ChainEpoch, 988 postPartitions []PoStPartition, 989 ) (*PoStResult, error) { 990 991 partitionIndexes := bitfield.New() 992 for _, partition := range postPartitions { 993 partitionIndexes.Set(partition.Index) 994 } 995 if numPartitions, err := partitionIndexes.Count(); err != nil { 996 return nil, xerrors.Errorf("failed to count posted partitions: %w", err) 997 } else if numPartitions != uint64(len(postPartitions)) { 998 return nil, xc.ErrIllegalArgument.Wrapf("duplicate partitions proven") 999 } 1000 1001 // First check to see if we're proving any already proven partitions. 1002 // This is faster than checking one by one. 1003 if alreadyProven, err := bitfield.IntersectBitField(dl.PartitionsPoSted, partitionIndexes); err != nil { 1004 return nil, xerrors.Errorf("failed to check proven partitions: %w", err) 1005 } else if empty, err := alreadyProven.IsEmpty(); err != nil { 1006 return nil, xerrors.Errorf("failed to check proven intersection is empty: %w", err) 1007 } else if !empty { 1008 return nil, xc.ErrIllegalArgument.Wrapf("partition already proven: %v", alreadyProven) 1009 } 1010 1011 partitions, err := dl.PartitionsArray(store) 1012 if err != nil { 1013 return nil, err 1014 } 1015 1016 allSectors := make([]bitfield.BitField, 0, len(postPartitions)) 1017 allIgnored := make([]bitfield.BitField, 0, len(postPartitions)) 1018 newFaultyPowerTotal := NewPowerPairZero() 1019 retractedRecoveryPowerTotal := NewPowerPairZero() 1020 recoveredPowerTotal := NewPowerPairZero() 1021 powerDelta := NewPowerPairZero() 1022 var rescheduledPartitions []uint64 1023 1024 // Accumulate sectors info for proof verification. 1025 for _, post := range postPartitions { 1026 var partition Partition 1027 found, err := partitions.Get(post.Index, &partition) 1028 if err != nil { 1029 return nil, xerrors.Errorf("failed to load partition %d: %w", post.Index, err) 1030 } else if !found { 1031 return nil, xc.ErrNotFound.Wrapf("no such partition %d", post.Index) 1032 } 1033 1034 // Process new faults and accumulate new faulty power. 1035 // This updates the faults in partition state ahead of calculating the sectors to include for proof. 1036 newPowerDelta, newFaultPower, retractedRecoveryPower, hasNewFaults, err := partition.RecordSkippedFaults( 1037 store, sectors, ssize, quant, faultExpiration, post.Skipped, 1038 ) 1039 if err != nil { 1040 return nil, xerrors.Errorf("failed to add skipped faults to partition %d: %w", post.Index, err) 1041 } 1042 1043 // If we have new faulty power, we've added some faults. We need 1044 // to record the new expiration in the deadline. 1045 if hasNewFaults { 1046 rescheduledPartitions = append(rescheduledPartitions, post.Index) 1047 } 1048 1049 recoveredPower, err := partition.RecoverFaults(store, sectors, ssize, quant) 1050 if err != nil { 1051 return nil, xerrors.Errorf("failed to recover faulty sectors for partition %d: %w", post.Index, err) 1052 } 1053 1054 // Finally, activate power for newly proven sectors. 1055 newPowerDelta = newPowerDelta.Add(partition.ActivateUnproven()) 1056 1057 // This will be rolled back if the method aborts with a failed proof. 1058 err = partitions.Set(post.Index, &partition) 1059 if err != nil { 1060 return nil, xc.ErrIllegalState.Wrapf("failed to update partition %v: %w", post.Index, err) 1061 } 1062 1063 newFaultyPowerTotal = newFaultyPowerTotal.Add(newFaultPower) 1064 retractedRecoveryPowerTotal = retractedRecoveryPowerTotal.Add(retractedRecoveryPower) 1065 recoveredPowerTotal = recoveredPowerTotal.Add(recoveredPower) 1066 powerDelta = powerDelta.Add(newPowerDelta).Add(recoveredPower) 1067 1068 // Record the post. 1069 dl.PartitionsPoSted.Set(post.Index) 1070 1071 // At this point, the partition faults represents the expected faults for the proof, with new skipped 1072 // faults and recoveries taken into account. 1073 allSectors = append(allSectors, partition.Sectors) 1074 allIgnored = append(allIgnored, partition.Faults) 1075 allIgnored = append(allIgnored, partition.Terminated) 1076 } 1077 1078 err = dl.AddExpirationPartitions(store, faultExpiration, rescheduledPartitions, quant) 1079 if err != nil { 1080 return nil, xc.ErrIllegalState.Wrapf("failed to update expirations for partitions with faults: %w", err) 1081 } 1082 1083 // Save everything back. 1084 dl.FaultyPower = dl.FaultyPower.Sub(recoveredPowerTotal).Add(newFaultyPowerTotal) 1085 1086 dl.Partitions, err = partitions.Root() 1087 if err != nil { 1088 return nil, xc.ErrIllegalState.Wrapf("failed to persist partitions: %w", err) 1089 } 1090 1091 // Collect all sectors, faults, and recoveries for proof verification. 1092 allSectorNos, err := bitfield.MultiMerge(allSectors...) 1093 if err != nil { 1094 return nil, xc.ErrIllegalState.Wrapf("failed to merge all sectors bitfields: %w", err) 1095 } 1096 allIgnoredSectorNos, err := bitfield.MultiMerge(allIgnored...) 1097 if err != nil { 1098 return nil, xc.ErrIllegalState.Wrapf("failed to merge ignored sectors bitfields: %w", err) 1099 } 1100 1101 return &PoStResult{ 1102 Sectors: allSectorNos, 1103 IgnoredSectors: allIgnoredSectorNos, 1104 PowerDelta: powerDelta, 1105 NewFaultyPower: newFaultyPowerTotal, 1106 RecoveredPower: recoveredPowerTotal, 1107 RetractedRecoveryPower: retractedRecoveryPowerTotal, 1108 Partitions: partitionIndexes, 1109 }, nil 1110 } 1111 1112 // RecordPoStProofs records a set of optimistically accepted PoSt proofs 1113 // (usually one), associating them with the given partitions. 1114 func (dl *Deadline) RecordPoStProofs(store adt.Store, partitions bitfield.BitField, proofs []proof.PoStProof) error { 1115 proofArr, err := dl.OptimisticProofsArray(store) 1116 if err != nil { 1117 return xerrors.Errorf("failed to load proofs: %w", err) 1118 } 1119 err = proofArr.AppendContinuous(&WindowedPoSt{ 1120 Partitions: partitions, 1121 Proofs: proofs, 1122 }) 1123 if err != nil { 1124 return xerrors.Errorf("failed to store proof: %w", err) 1125 } 1126 1127 root, err := proofArr.Root() 1128 if err != nil { 1129 return xerrors.Errorf("failed to save proofs: %w", err) 1130 } 1131 dl.OptimisticPoStSubmissions = root 1132 return nil 1133 } 1134 1135 // TakePoStProofs removes and returns a PoSt proof by index, along with the 1136 // associated partitions. This method takes the PoSt from the PoSt submissions 1137 // snapshot. 1138 func (dl *Deadline) TakePoStProofs(store adt.Store, idx uint64) (partitions bitfield.BitField, proofs []proof.PoStProof, err error) { 1139 proofArr, err := dl.OptimisticProofsSnapshotArray(store) 1140 if err != nil { 1141 return bitfield.New(), nil, xerrors.Errorf("failed to load proofs: %w", err) 1142 } 1143 1144 // Extract and remove the proof from the proofs array, leaving a hole. 1145 // This will not affect concurrent attempts to refute other proofs. 1146 var post WindowedPoSt 1147 if found, err := proofArr.Pop(idx, &post); err != nil { 1148 return bitfield.New(), nil, xerrors.Errorf("failed to retrieve proof %d: %w", idx, err) 1149 } else if !found { 1150 return bitfield.New(), nil, xc.ErrIllegalArgument.Wrapf("proof %d not found", idx) 1151 } 1152 1153 root, err := proofArr.Root() 1154 if err != nil { 1155 return bitfield.New(), nil, xerrors.Errorf("failed to save proofs: %w", err) 1156 } 1157 dl.OptimisticPoStSubmissionsSnapshot = root 1158 return post.Partitions, post.Proofs, nil 1159 } 1160 1161 // RescheduleSectorExpirations reschedules the expirations of the given sectors 1162 // to the target epoch, skipping any sectors it can't find. 1163 // 1164 // The power of the rescheduled sectors is assumed to have not changed since 1165 // initial scheduling. 1166 // 1167 // Note: see the docs on State.RescheduleSectorExpirations for details on why we 1168 // skip sectors/partitions we can't find. 1169 func (dl *Deadline) RescheduleSectorExpirations( 1170 store adt.Store, sectors Sectors, 1171 expiration abi.ChainEpoch, partitionSectors PartitionSectorMap, 1172 ssize abi.SectorSize, quant QuantSpec, 1173 ) ([]*SectorOnChainInfo, error) { 1174 partitions, err := dl.PartitionsArray(store) 1175 if err != nil { 1176 return nil, err 1177 } 1178 1179 var rescheduledPartitions []uint64 // track partitions with moved expirations. 1180 var allReplaced []*SectorOnChainInfo 1181 if err := partitionSectors.ForEach(func(partIdx uint64, sectorNos bitfield.BitField) error { 1182 var partition Partition 1183 if found, err := partitions.Get(partIdx, &partition); err != nil { 1184 return xerrors.Errorf("failed to load partition %d: %w", partIdx, err) 1185 } else if !found { 1186 // We failed to find the partition, it could have moved 1187 // due to compaction. This function is only reschedules 1188 // sectors it can find so we'll just skip it. 1189 return nil 1190 } 1191 1192 replaced, err := partition.RescheduleExpirations(store, sectors, expiration, sectorNos, ssize, quant) 1193 if err != nil { 1194 return xerrors.Errorf("failed to reschedule expirations in partition %d: %w", partIdx, err) 1195 } 1196 if len(replaced) == 0 { 1197 // nothing moved. 1198 return nil 1199 } 1200 allReplaced = append(allReplaced, replaced...) 1201 1202 rescheduledPartitions = append(rescheduledPartitions, partIdx) 1203 if err = partitions.Set(partIdx, &partition); err != nil { 1204 return xerrors.Errorf("failed to store partition %d: %w", partIdx, err) 1205 } 1206 return nil 1207 }); err != nil { 1208 return nil, err 1209 } 1210 1211 if len(rescheduledPartitions) > 0 { 1212 dl.Partitions, err = partitions.Root() 1213 if err != nil { 1214 return nil, xerrors.Errorf("failed to save partitions: %w", err) 1215 } 1216 err := dl.AddExpirationPartitions(store, expiration, rescheduledPartitions, quant) 1217 if err != nil { 1218 return nil, xerrors.Errorf("failed to reschedule partition expirations: %w", err) 1219 } 1220 } 1221 1222 return allReplaced, nil 1223 } 1224 1225 // DisputeInfo includes all the information necessary to dispute a post to the 1226 // given partitions. 1227 type DisputeInfo struct { 1228 AllSectorNos, IgnoredSectorNos bitfield.BitField 1229 DisputedSectors PartitionSectorMap 1230 DisputedPower PowerPair 1231 } 1232 1233 // LoadPartitionsForDispute 1234 func (dl *Deadline) LoadPartitionsForDispute(store adt.Store, partitions bitfield.BitField) (*DisputeInfo, error) { 1235 partitionsSnapshot, err := dl.PartitionsSnapshotArray(store) 1236 if err != nil { 1237 return nil, xerrors.Errorf("failed to load partitions: %w", err) 1238 } 1239 1240 var allSectors, allIgnored []bitfield.BitField 1241 disputedSectors := make(PartitionSectorMap) 1242 disputedPower := NewPowerPairZero() 1243 err = partitions.ForEach(func(partIdx uint64) error { 1244 var partitionSnapshot Partition 1245 if found, err := partitionsSnapshot.Get(partIdx, &partitionSnapshot); err != nil { 1246 return err 1247 } else if !found { 1248 return xerrors.Errorf("failed to find partition %d", partIdx) 1249 } 1250 1251 // Record sectors for proof verification 1252 allSectors = append(allSectors, partitionSnapshot.Sectors) 1253 allIgnored = append(allIgnored, partitionSnapshot.Faults) 1254 allIgnored = append(allIgnored, partitionSnapshot.Terminated) 1255 allIgnored = append(allIgnored, partitionSnapshot.Unproven) 1256 1257 // Record active sectors for marking faults. 1258 active, err := partitionSnapshot.ActiveSectors() 1259 if err != nil { 1260 return err 1261 } 1262 err = disputedSectors.Add(partIdx, active) 1263 if err != nil { 1264 return err 1265 } 1266 1267 // Record disputed power for penalties. 1268 // 1269 // NOTE: This also includes power that was 1270 // activated at the end of the last challenge 1271 // window, and power from sectors that have since 1272 // expired. 1273 disputedPower = disputedPower.Add(partitionSnapshot.ActivePower()) 1274 return nil 1275 }) 1276 if err != nil { 1277 return nil, xerrors.Errorf("when disputing post: %w", err) 1278 } 1279 1280 allSectorsNos, err := bitfield.MultiMerge(allSectors...) 1281 if err != nil { 1282 return nil, xerrors.Errorf("failed to merge sector bitfields: %w", err) 1283 } 1284 1285 allIgnoredNos, err := bitfield.MultiMerge(allIgnored...) 1286 if err != nil { 1287 return nil, xerrors.Errorf("failed to merge fault bitfields: %w", err) 1288 } 1289 1290 return &DisputeInfo{ 1291 AllSectorNos: allSectorsNos, 1292 IgnoredSectorNos: allIgnoredNos, 1293 DisputedSectors: disputedSectors, 1294 DisputedPower: disputedPower, 1295 }, nil 1296 } 1297 1298 // IsLive returns true if the deadline has any live sectors or any other state that should be 1299 // updated at the end of the challenge window. 1300 func (d *Deadline) IsLive() (bool, error) { 1301 // If we have live sectors, we're definitely live. 1302 if d.LiveSectors > 0 { 1303 return true, nil 1304 } 1305 1306 if hasNoProofs, err := d.PartitionsPoSted.IsEmpty(); err != nil { 1307 return true, xerrors.Errorf("invalid partitions posted bitfield: %w", err) 1308 } else if !hasNoProofs { 1309 // _This_ case should be impossible, but there's no good way to log from here. We 1310 // might as well just process the deadline end and move on. 1311 return true, nil 1312 } 1313 1314 // If the partitions have changed, we may have work to do. We should at least update the 1315 // partitions snapshot one last time. 1316 if d.Partitions != d.PartitionsSnapshot { 1317 return true, nil 1318 } 1319 1320 // If we don't have any proofs, and the proofs snapshot isn't the same as the current proofs 1321 // snapshot (which should be empty), we should update the deadline one last time to empty 1322 // the proofs snapshot. 1323 if d.OptimisticPoStSubmissions != d.OptimisticPoStSubmissionsSnapshot { 1324 return true, nil 1325 } 1326 1327 // Otherwise, the deadline is definitely dead. 1328 return false, nil 1329 } 1330 1331 func (d *Deadline) ValidateState() error { 1332 if d.LiveSectors > d.TotalSectors { 1333 return xerrors.Errorf("Deadline left with more live sectors than total: %v", d) 1334 } 1335 1336 if d.FaultyPower.Raw.LessThan(big.Zero()) || d.FaultyPower.QA.LessThan(big.Zero()) { 1337 return xerrors.Errorf("Deadline left with negative faulty power: %v", d) 1338 } 1339 1340 return nil 1341 }