github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/miner/expiration_queue.go (about) 1 package miner 2 3 import ( 4 "fmt" 5 "sort" 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 "github.com/ipfs/go-cid" 11 "golang.org/x/xerrors" 12 13 "github.com/filecoin-project/specs-actors/v4/actors/util" 14 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" 15 ) 16 17 // ExpirationSet is a collection of sector numbers that are expiring, either due to 18 // expected "on-time" expiration at the end of their life, or unexpected "early" termination 19 // due to being faulty for too long consecutively. 20 // Note that there is not a direct correspondence between on-time sectors and active power; 21 // a sector may be faulty but expiring on-time if it faults just prior to expected termination. 22 // Early sectors are always faulty, and active power always represents on-time sectors. 23 type ExpirationSet struct { 24 OnTimeSectors bitfield.BitField // Sectors expiring "on time" at the end of their committed life 25 EarlySectors bitfield.BitField // Sectors expiring "early" due to being faulty for too long 26 OnTimePledge abi.TokenAmount // Pledge total for the on-time sectors 27 ActivePower PowerPair // Power that is currently active (not faulty) 28 FaultyPower PowerPair // Power that is currently faulty 29 } 30 31 func NewExpirationSetEmpty() *ExpirationSet { 32 return NewExpirationSet(bitfield.New(), bitfield.New(), big.Zero(), NewPowerPairZero(), NewPowerPairZero()) 33 } 34 35 func NewExpirationSet(onTimeSectors, earlySectors bitfield.BitField, onTimePledge abi.TokenAmount, activePower, faultyPower PowerPair) *ExpirationSet { 36 return &ExpirationSet{ 37 OnTimeSectors: onTimeSectors, 38 EarlySectors: earlySectors, 39 OnTimePledge: onTimePledge, 40 ActivePower: activePower, 41 FaultyPower: faultyPower, 42 } 43 } 44 45 // Adds sectors and power to the expiration set in place. 46 func (es *ExpirationSet) Add(onTimeSectors, earlySectors bitfield.BitField, onTimePledge abi.TokenAmount, activePower, faultyPower PowerPair) error { 47 var err error 48 if es.OnTimeSectors, err = bitfield.MergeBitFields(es.OnTimeSectors, onTimeSectors); err != nil { 49 return err 50 } 51 if es.EarlySectors, err = bitfield.MergeBitFields(es.EarlySectors, earlySectors); err != nil { 52 return err 53 } 54 es.OnTimePledge = big.Add(es.OnTimePledge, onTimePledge) 55 es.ActivePower = es.ActivePower.Add(activePower) 56 es.FaultyPower = es.FaultyPower.Add(faultyPower) 57 return es.ValidateState() 58 } 59 60 // Removes sectors and power from the expiration set in place. 61 func (es *ExpirationSet) Remove(onTimeSectors, earlySectors bitfield.BitField, onTimePledge abi.TokenAmount, activePower, faultyPower PowerPair) error { 62 // Check for sector intersection. This could be cheaper with a combined intersection/difference method used below. 63 if found, err := util.BitFieldContainsAll(es.OnTimeSectors, onTimeSectors); err != nil { 64 return err 65 } else if !found { 66 return xerrors.Errorf("removing on-time sectors %v not contained in %v", onTimeSectors, es.OnTimeSectors) 67 } 68 if found, err := util.BitFieldContainsAll(es.EarlySectors, earlySectors); err != nil { 69 return err 70 } else if !found { 71 return xerrors.Errorf("removing early sectors %v not contained in %v", earlySectors, es.EarlySectors) 72 } 73 74 var err error 75 if es.OnTimeSectors, err = bitfield.SubtractBitField(es.OnTimeSectors, onTimeSectors); err != nil { 76 return err 77 } 78 if es.EarlySectors, err = bitfield.SubtractBitField(es.EarlySectors, earlySectors); err != nil { 79 return err 80 } 81 es.OnTimePledge = big.Sub(es.OnTimePledge, onTimePledge) 82 es.ActivePower = es.ActivePower.Sub(activePower) 83 es.FaultyPower = es.FaultyPower.Sub(faultyPower) 84 // Check underflow. 85 if es.OnTimePledge.LessThan(big.Zero()) { 86 return xerrors.Errorf("expiration set pledge underflow: %v", es) 87 } 88 if es.ActivePower.QA.LessThan(big.Zero()) || es.FaultyPower.QA.LessThan(big.Zero()) { 89 return xerrors.Errorf("expiration set power underflow: %v", es) 90 } 91 return es.ValidateState() 92 } 93 94 // A set is empty if it has no sectors. 95 // The power and pledge are not checked, but expected to be zero. 96 func (es *ExpirationSet) IsEmpty() (empty bool, err error) { 97 if empty, err = es.OnTimeSectors.IsEmpty(); err != nil { 98 return false, err 99 } else if empty { 100 if empty, err = es.EarlySectors.IsEmpty(); err != nil { 101 return false, err 102 } 103 return empty, nil 104 } else { 105 return false, nil 106 } 107 } 108 109 // Counts all sectors in the expiration set. 110 func (es *ExpirationSet) Count() (count uint64, err error) { 111 onTime, err := es.OnTimeSectors.Count() 112 if err != nil { 113 return 0, err 114 } 115 116 early, err := es.EarlySectors.Count() 117 if err != nil { 118 return 0, err 119 } 120 121 return onTime + early, nil 122 } 123 124 // validates a set of assertions that must hold for expiration sets 125 func (es *ExpirationSet) ValidateState() error { 126 if es.OnTimePledge.LessThan(big.Zero()) { 127 return xerrors.Errorf("ESet left with negative pledge: %+v", es) 128 } 129 130 if es.ActivePower.Raw.LessThan(big.Zero()) { 131 return xerrors.Errorf("ESet left with negative raw active power: %+v", es) 132 } 133 134 if es.ActivePower.QA.LessThan(big.Zero()) { 135 return xerrors.Errorf("ESet left with negative qa active power: %+v", es) 136 } 137 138 if es.FaultyPower.Raw.LessThan(big.Zero()) { 139 return xerrors.Errorf("ESet left with negative raw faulty power: %+v", es) 140 } 141 142 if es.FaultyPower.QA.LessThan(big.Zero()) { 143 return xerrors.Errorf("ESet left with negative qa faulty power: %+v", es) 144 } 145 146 return nil 147 } 148 149 // A queue of expiration sets by epoch, representing the on-time or early termination epoch for a collection of sectors. 150 // Wraps an AMT[ChainEpoch]*ExpirationSet. 151 // Keys in the queue are quantized (upwards), modulo some offset, to reduce the cardinality of keys. 152 type ExpirationQueue struct { 153 *adt.Array 154 quant QuantSpec 155 } 156 157 // An internal limit on the cardinality of a bitfield in a queue entry. 158 // This must be at least large enough to support the maximum number of sectors in a partition. 159 // It would be a bit better to derive this number from an enumeration over all partition sizes. 160 const entrySectorsMax = 10_000 161 162 // Loads a queue root. 163 // Epochs provided to subsequent method calls will be quantized upwards to quanta mod offsetSeed before being 164 // written to/read from queue entries. 165 func LoadExpirationQueue(store adt.Store, root cid.Cid, quant QuantSpec, bitwidth int) (ExpirationQueue, error) { 166 arr, err := adt.AsArray(store, root, bitwidth) 167 if err != nil { 168 return ExpirationQueue{}, xerrors.Errorf("failed to load epoch queue %v: %w", root, err) 169 } 170 return ExpirationQueue{arr, quant}, nil 171 } 172 173 // Adds a collection of sectors to their on-time target expiration entries (quantized). 174 // The sectors are assumed to be active (non-faulty). 175 // Returns the sector numbers, power, and pledge added. 176 func (q ExpirationQueue) AddActiveSectors(sectors []*SectorOnChainInfo, ssize abi.SectorSize) (bitfield.BitField, PowerPair, abi.TokenAmount, error) { 177 totalPower := NewPowerPairZero() 178 totalPledge := big.Zero() 179 var totalSectors []bitfield.BitField 180 noEarlySectors := bitfield.New() 181 noFaultyPower := NewPowerPairZero() 182 for _, group := range groupNewSectorsByDeclaredExpiration(ssize, sectors, q.quant) { 183 snos := bitfield.NewFromSet(group.sectors) 184 if err := q.add(group.epoch, snos, noEarlySectors, group.power, noFaultyPower, group.pledge); err != nil { 185 return bitfield.BitField{}, NewPowerPairZero(), big.Zero(), xerrors.Errorf("failed to record new sector expirations: %w", err) 186 } 187 totalSectors = append(totalSectors, snos) 188 totalPower = totalPower.Add(group.power) 189 totalPledge = big.Add(totalPledge, group.pledge) 190 } 191 snos, err := bitfield.MultiMerge(totalSectors...) 192 if err != nil { 193 return bitfield.BitField{}, NewPowerPairZero(), big.Zero(), err 194 } 195 return snos, totalPower, totalPledge, nil 196 } 197 198 // Reschedules some sectors to a new (quantized) expiration epoch. 199 // The sectors being rescheduled are assumed to be not faulty, and hence are removed from and re-scheduled for on-time 200 // rather than early expiration. 201 // The sectors' power and pledge are assumed not to change, despite the new expiration. 202 func (q ExpirationQueue) RescheduleExpirations(newExpiration abi.ChainEpoch, sectors []*SectorOnChainInfo, ssize abi.SectorSize) error { 203 if len(sectors) == 0 { 204 return nil 205 } 206 207 snos, power, pledge, err := q.removeActiveSectors(sectors, ssize) 208 if err != nil { 209 return xerrors.Errorf("failed to remove sector expirations: %w", err) 210 } 211 if err = q.add(newExpiration, snos, bitfield.New(), power, NewPowerPairZero(), pledge); err != nil { 212 return xerrors.Errorf("failed to record new sector expirations: %w", err) 213 } 214 return nil 215 } 216 217 // Re-schedules sectors to expire at an early expiration epoch (quantized), if they wouldn't expire before then anyway. 218 // The sectors must not be currently faulty, so must be registered as expiring on-time rather than early. 219 // The pledge for the now-early sectors is removed from the queue. 220 // Returns the total power represented by the sectors. 221 func (q ExpirationQueue) RescheduleAsFaults(newExpiration abi.ChainEpoch, sectors []*SectorOnChainInfo, ssize abi.SectorSize) (PowerPair, error) { 222 var sectorsTotal []uint64 223 expiringPower := NewPowerPairZero() 224 rescheduledPower := NewPowerPairZero() 225 226 // Group sectors by their target expiration, then remove from existing queue entries according to those groups. 227 groups, err := q.findSectorsByExpiration(ssize, sectors) 228 if err != nil { 229 return NewPowerPairZero(), err 230 } 231 232 for _, group := range groups { 233 var err error 234 if group.epoch <= q.quant.QuantizeUp(newExpiration) { 235 // Don't reschedule sectors that are already due to expire on-time before the fault-driven expiration, 236 // but do represent their power as now faulty. 237 // Their pledge remains as "on-time". 238 group.expirationSet.ActivePower = group.expirationSet.ActivePower.Sub(group.power) 239 group.expirationSet.FaultyPower = group.expirationSet.FaultyPower.Add(group.power) 240 expiringPower = expiringPower.Add(group.power) 241 } else { 242 // Remove sectors from on-time expiry and active power. 243 sectorsBf := bitfield.NewFromSet(group.sectors) 244 if group.expirationSet.OnTimeSectors, err = bitfield.SubtractBitField(group.expirationSet.OnTimeSectors, sectorsBf); err != nil { 245 return NewPowerPairZero(), err 246 } 247 group.expirationSet.OnTimePledge = big.Sub(group.expirationSet.OnTimePledge, group.pledge) 248 group.expirationSet.ActivePower = group.expirationSet.ActivePower.Sub(group.power) 249 250 // Accumulate the sectors and power removed. 251 sectorsTotal = append(sectorsTotal, group.sectors...) 252 rescheduledPower = rescheduledPower.Add(group.power) 253 } 254 if err = q.mustUpdateOrDelete(group.epoch, group.expirationSet); err != nil { 255 return NewPowerPairZero(), err 256 } 257 258 if err = group.expirationSet.ValidateState(); err != nil { 259 return NewPowerPairZero(), err 260 } 261 } 262 263 if len(sectorsTotal) > 0 { 264 // Add sectors to new expiration as early-terminating and faulty. 265 earlySectors := bitfield.NewFromSet(sectorsTotal) 266 noOnTimeSectors := bitfield.New() 267 noOnTimePledge := abi.NewTokenAmount(0) 268 noActivePower := NewPowerPairZero() 269 if err := q.add(newExpiration, noOnTimeSectors, earlySectors, noActivePower, rescheduledPower, noOnTimePledge); err != nil { 270 return NewPowerPairZero(), err 271 } 272 } 273 274 return rescheduledPower.Add(expiringPower), nil 275 } 276 277 // Re-schedules *all* sectors to expire at an early expiration epoch, if they wouldn't expire before then anyway. 278 func (q ExpirationQueue) RescheduleAllAsFaults(faultExpiration abi.ChainEpoch) error { 279 var rescheduledEpochs []uint64 280 var rescheduledSectors []bitfield.BitField 281 rescheduledPower := NewPowerPairZero() 282 283 var es ExpirationSet 284 if err := q.Array.ForEach(&es, func(e int64) error { 285 epoch := abi.ChainEpoch(e) 286 if epoch <= q.quant.QuantizeUp(faultExpiration) { 287 // Regardless of whether the sectors were expiring on-time or early, all the power is now faulty. 288 // Pledge is still on-time. 289 es.FaultyPower = es.FaultyPower.Add(es.ActivePower) 290 es.ActivePower = NewPowerPairZero() 291 if err := q.mustUpdate(epoch, &es); err != nil { 292 return err 293 } 294 } else { 295 rescheduledEpochs = append(rescheduledEpochs, uint64(epoch)) 296 // sanity check to make sure we're not trying to re-schedule already faulty sectors. 297 if isEmpty, err := es.EarlySectors.IsEmpty(); err != nil { 298 return xerrors.Errorf("failed to determine if epoch had early expirations: %w", err) 299 } else if !isEmpty { 300 return xerrors.Errorf("attempted to re-schedule early expirations to an even earlier epoch") 301 } 302 rescheduledSectors = append(rescheduledSectors, es.OnTimeSectors) 303 rescheduledPower = rescheduledPower.Add(es.ActivePower) 304 rescheduledPower = rescheduledPower.Add(es.FaultyPower) 305 } 306 307 if err := es.ValidateState(); err != nil { 308 return err 309 } 310 311 return nil 312 }); err != nil { 313 return err 314 } 315 316 // If we didn't reschedule anything, we're done. 317 if len(rescheduledEpochs) == 0 { 318 return nil 319 } 320 321 // Add rescheduled sectors to new expiration as early-terminating and faulty. 322 allRescheduled, err := bitfield.MultiMerge(rescheduledSectors...) 323 if err != nil { 324 return xerrors.Errorf("failed to merge rescheduled sectors: %w", err) 325 } 326 noOnTimeSectors := bitfield.New() 327 noActivePower := NewPowerPairZero() 328 noOnTimePledge := abi.NewTokenAmount(0) 329 if err = q.add(faultExpiration, noOnTimeSectors, allRescheduled, noActivePower, rescheduledPower, noOnTimePledge); err != nil { 330 return err 331 } 332 333 // Trim the rescheduled epochs from the queue. 334 if err = q.BatchDelete(rescheduledEpochs, true); err != nil { 335 return err 336 } 337 338 return nil 339 } 340 341 // Removes sectors from any queue entries in which they appear that are earlier then their scheduled expiration epoch, 342 // and schedules them at their expected termination epoch. 343 // Pledge for the sectors is re-added as on-time. 344 // Power for the sectors is changed from faulty to active (whether rescheduled or not). 345 // Returns the newly-recovered power. Fails if any sectors are not found in the queue. 346 func (q ExpirationQueue) RescheduleRecovered(sectors []*SectorOnChainInfo, ssize abi.SectorSize) (PowerPair, error) { 347 remaining := make(map[abi.SectorNumber]struct{}, len(sectors)) 348 for _, s := range sectors { 349 remaining[s.SectorNumber] = struct{}{} 350 } 351 352 // Traverse the expiration queue once to find each recovering sector and remove it from early/faulty there. 353 // We expect this to find all recovering sectors within the first FaultMaxAge/WPoStProvingPeriod entries 354 // (i.e. 14 for 14-day faults), but if something has gone wrong it's safer not to fail if that's not met. 355 var sectorsRescheduled []*SectorOnChainInfo 356 recoveredPower := NewPowerPairZero() 357 if err := q.traverseMutate(func(epoch abi.ChainEpoch, es *ExpirationSet) (changed, keepGoing bool, err error) { 358 onTimeSectors, err := es.OnTimeSectors.AllMap(entrySectorsMax) 359 if err != nil { 360 return false, false, err 361 } 362 earlySectors, err := es.EarlySectors.AllMap(entrySectorsMax) 363 if err != nil { 364 return false, false, err 365 } 366 367 // This loop could alternatively be done by constructing bitfields and intersecting them, but it's not 368 // clear that would be much faster (O(max(N, M)) vs O(N+M)). 369 // If faults are correlated, the first queue entry likely has them all anyway. 370 // The length of sectors has a maximum of one partition size. 371 for _, sector := range sectors { 372 sno := uint64(sector.SectorNumber) 373 power := PowerForSector(ssize, sector) 374 var found bool 375 if _, found = onTimeSectors[sno]; found { 376 // If the sector expires on-time at this epoch, leave it here but change faulty power to active. 377 // The pledge is already part of the on-time pledge at this entry. 378 es.FaultyPower = es.FaultyPower.Sub(power) 379 es.ActivePower = es.ActivePower.Add(power) 380 } else if _, found = earlySectors[sno]; found { 381 // If the sector expires early at this epoch, remove it for re-scheduling. 382 // It's not part of the on-time pledge number here. 383 es.EarlySectors.Unset(sno) 384 es.FaultyPower = es.FaultyPower.Sub(power) 385 sectorsRescheduled = append(sectorsRescheduled, sector) 386 } 387 if found { 388 recoveredPower = recoveredPower.Add(power) 389 delete(remaining, sector.SectorNumber) 390 changed = true 391 } 392 } 393 394 if err = es.ValidateState(); err != nil { 395 return false, false, err 396 } 397 398 return changed, len(remaining) > 0, nil 399 }); err != nil { 400 return NewPowerPairZero(), err 401 } 402 if len(remaining) > 0 { 403 return NewPowerPairZero(), xerrors.Errorf("sectors not found in expiration queue: %v", remaining) 404 } 405 406 // Re-schedule the removed sectors to their target expiration. 407 if _, _, _, err := q.AddActiveSectors(sectorsRescheduled, ssize); err != nil { 408 return NewPowerPairZero(), err 409 } 410 return recoveredPower, nil 411 } 412 413 // Removes some sectors and adds some others. 414 // The sectors being replaced must not be faulty, so must be scheduled for on-time rather than early expiration. 415 // The sectors added are assumed to be not faulty. 416 // Returns the old a new sector number bitfields, and delta to power and pledge, new minus old. 417 func (q ExpirationQueue) ReplaceSectors(oldSectors, newSectors []*SectorOnChainInfo, ssize abi.SectorSize) (bitfield.BitField, bitfield.BitField, PowerPair, abi.TokenAmount, error) { 418 oldSnos, oldPower, oldPledge, err := q.removeActiveSectors(oldSectors, ssize) 419 if err != nil { 420 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), big.Zero(), xerrors.Errorf("failed to remove replaced sectors: %w", err) 421 } 422 newSnos, newPower, newPledge, err := q.AddActiveSectors(newSectors, ssize) 423 if err != nil { 424 return bitfield.BitField{}, bitfield.BitField{}, NewPowerPairZero(), big.Zero(), xerrors.Errorf("failed to add replacement sectors: %w", err) 425 } 426 return oldSnos, newSnos, newPower.Sub(oldPower), big.Sub(newPledge, oldPledge), nil 427 } 428 429 // Remove some sectors from the queue. 430 // The sectors may be active or faulty, and scheduled either for on-time or early termination. 431 // Returns the aggregate of removed sectors and power, and recovering power. 432 // Fails if any sectors are not found in the queue. 433 func (q ExpirationQueue) RemoveSectors(sectors []*SectorOnChainInfo, faults bitfield.BitField, recovering bitfield.BitField, 434 ssize abi.SectorSize) (*ExpirationSet, PowerPair, error) { 435 remaining := make(map[abi.SectorNumber]struct{}, len(sectors)) 436 for _, s := range sectors { 437 remaining[s.SectorNumber] = struct{}{} 438 } 439 faultsMap, err := faults.AllMap(AddressedSectorsMax) 440 if err != nil { 441 return nil, NewPowerPairZero(), xerrors.Errorf("failed to expand faults: %w", err) 442 } 443 recoveringMap, err := recovering.AllMap(AddressedSectorsMax) 444 if err != nil { 445 return nil, NewPowerPairZero(), xerrors.Errorf("failed to expand recoveries: %w", err) 446 } 447 448 // results 449 removed := NewExpirationSetEmpty() 450 recoveringPower := NewPowerPairZero() 451 452 // Split into faulty and non-faulty. We process non-faulty sectors first 453 // because they always expire on-time so we know where to find them. 454 var ( 455 nonFaultySectors []*SectorOnChainInfo 456 faultySectors []*SectorOnChainInfo 457 ) 458 for _, sector := range sectors { 459 if _, found := faultsMap[uint64(sector.SectorNumber)]; found { 460 faultySectors = append(faultySectors, sector) 461 continue 462 } 463 nonFaultySectors = append(nonFaultySectors, sector) 464 // remove them from "remaining", we're going to process them below. 465 delete(remaining, sector.SectorNumber) 466 } 467 468 // Remove non-faulty sectors. 469 removed.OnTimeSectors, removed.ActivePower, removed.OnTimePledge, err = q.removeActiveSectors(nonFaultySectors, ssize) 470 if err != nil { 471 return nil, NewPowerPairZero(), xerrors.Errorf("failed to remove on-time recoveries: %w", err) 472 } 473 474 // Finally, remove faulty sectors (on time and not). These sectors can 475 // only appear within the first 14 days (fault max age). Given that this 476 // queue is quantized, we should be able to stop traversing the queue 477 // after 14 entries. 478 if err = q.traverseMutate(func(epoch abi.ChainEpoch, es *ExpirationSet) (changed, keepGoing bool, err error) { 479 onTimeSectors, err := es.OnTimeSectors.AllMap(entrySectorsMax) 480 if err != nil { 481 return false, false, err 482 } 483 earlySectors, err := es.EarlySectors.AllMap(entrySectorsMax) 484 if err != nil { 485 return false, false, err 486 } 487 488 // This loop could alternatively be done by constructing bitfields and intersecting them, but it's not 489 // clear that would be much faster (O(max(N, M)) vs O(N+M)). 490 // The length of sectors has a maximum of one partition size. 491 for _, sector := range faultySectors { 492 sno := uint64(sector.SectorNumber) 493 var found bool 494 if _, found = onTimeSectors[sno]; found { 495 es.OnTimeSectors.Unset(sno) 496 removed.OnTimeSectors.Set(sno) 497 es.OnTimePledge = big.Sub(es.OnTimePledge, sector.InitialPledge) 498 removed.OnTimePledge = big.Add(removed.OnTimePledge, sector.InitialPledge) 499 } else if _, found = earlySectors[sno]; found { 500 es.EarlySectors.Unset(sno) 501 removed.EarlySectors.Set(sno) 502 } 503 if found { 504 power := PowerForSector(ssize, sector) 505 if _, f := faultsMap[sno]; f { 506 es.FaultyPower = es.FaultyPower.Sub(power) 507 removed.FaultyPower = removed.FaultyPower.Add(power) 508 } else { 509 es.ActivePower = es.ActivePower.Sub(power) 510 removed.ActivePower = removed.ActivePower.Add(power) 511 } 512 if _, r := recoveringMap[sno]; r { 513 recoveringPower = recoveringPower.Add(power) 514 } 515 delete(remaining, sector.SectorNumber) 516 changed = true 517 } 518 } 519 520 if err = es.ValidateState(); err != nil { 521 return false, false, err 522 } 523 524 return changed, len(remaining) > 0, nil 525 }); err != nil { 526 return nil, recoveringPower, err 527 } 528 if len(remaining) > 0 { 529 return NewExpirationSetEmpty(), NewPowerPairZero(), xerrors.Errorf("sectors not found in expiration queue: %v", remaining) 530 } 531 532 return removed, recoveringPower, nil 533 } 534 535 // Removes and aggregates entries from the queue up to and including some epoch. 536 func (q ExpirationQueue) PopUntil(until abi.ChainEpoch) (*ExpirationSet, error) { 537 var onTimeSectors []bitfield.BitField 538 var earlySectors []bitfield.BitField 539 activePower := NewPowerPairZero() 540 faultyPower := NewPowerPairZero() 541 onTimePledge := big.Zero() 542 543 var poppedKeys []uint64 544 var thisValue ExpirationSet 545 stopErr := fmt.Errorf("stop") 546 if err := q.Array.ForEach(&thisValue, func(i int64) error { 547 if abi.ChainEpoch(i) > until { 548 return stopErr 549 } 550 poppedKeys = append(poppedKeys, uint64(i)) 551 onTimeSectors = append(onTimeSectors, thisValue.OnTimeSectors) 552 earlySectors = append(earlySectors, thisValue.EarlySectors) 553 activePower = activePower.Add(thisValue.ActivePower) 554 faultyPower = faultyPower.Add(thisValue.FaultyPower) 555 onTimePledge = big.Add(onTimePledge, thisValue.OnTimePledge) 556 return nil 557 }); err != nil && err != stopErr { 558 return nil, err 559 } 560 561 if err := q.Array.BatchDelete(poppedKeys, true); err != nil { 562 return nil, err 563 } 564 565 allOnTime, err := bitfield.MultiMerge(onTimeSectors...) 566 if err != nil { 567 return nil, err 568 } 569 allEarly, err := bitfield.MultiMerge(earlySectors...) 570 if err != nil { 571 return nil, err 572 } 573 return NewExpirationSet(allOnTime, allEarly, onTimePledge, activePower, faultyPower), nil 574 } 575 576 func (q ExpirationQueue) add(rawEpoch abi.ChainEpoch, onTimeSectors, earlySectors bitfield.BitField, activePower, faultyPower PowerPair, 577 pledge abi.TokenAmount) error { 578 epoch := q.quant.QuantizeUp(rawEpoch) 579 es, err := q.mayGet(epoch) 580 if err != nil { 581 return err 582 } 583 584 if err = es.Add(onTimeSectors, earlySectors, pledge, activePower, faultyPower); err != nil { 585 return xerrors.Errorf("failed to add expiration values for epoch %v: %w", epoch, err) 586 } 587 588 return q.mustUpdate(epoch, es) 589 } 590 591 func (q ExpirationQueue) remove(rawEpoch abi.ChainEpoch, onTimeSectors, earlySectors bitfield.BitField, activePower, faultyPower PowerPair, 592 pledge abi.TokenAmount) error { 593 epoch := q.quant.QuantizeUp(rawEpoch) 594 var es ExpirationSet 595 if found, err := q.Array.Get(uint64(epoch), &es); err != nil { 596 return xerrors.Errorf("failed to lookup queue epoch %v: %w", epoch, err) 597 } else if !found { 598 return xerrors.Errorf("missing expected expiration set at epoch %v", epoch) 599 } 600 601 if err := es.Remove(onTimeSectors, earlySectors, pledge, activePower, faultyPower); err != nil { 602 return xerrors.Errorf("failed to remove expiration values for queue epoch %v: %w", epoch, err) 603 } 604 605 return q.mustUpdateOrDelete(epoch, &es) 606 } 607 608 func (q ExpirationQueue) removeActiveSectors(sectors []*SectorOnChainInfo, ssize abi.SectorSize) (bitfield.BitField, PowerPair, abi.TokenAmount, error) { 609 removedSnos := bitfield.New() 610 removedPower := NewPowerPairZero() 611 removedPledge := big.Zero() 612 noEarlySectors := bitfield.New() 613 noFaultyPower := NewPowerPairZero() 614 615 // Group sectors by their expiration, then remove from existing queue entries according to those groups. 616 groups, err := q.findSectorsByExpiration(ssize, sectors) 617 if err != nil { 618 return bitfield.BitField{}, NewPowerPairZero(), big.Zero(), err 619 } 620 621 for _, group := range groups { 622 sectorsBf := bitfield.NewFromSet(group.sectors) 623 if err := q.remove(group.epoch, sectorsBf, noEarlySectors, group.power, noFaultyPower, group.pledge); err != nil { 624 return bitfield.BitField{}, NewPowerPairZero(), big.Zero(), err 625 } 626 for _, n := range group.sectors { 627 removedSnos.Set(n) 628 } 629 removedPower = removedPower.Add(group.power) 630 removedPledge = big.Add(removedPledge, group.pledge) 631 } 632 return removedSnos, removedPower, removedPledge, nil 633 } 634 635 // Traverses the entire queue with a callback function that may mutate entries. 636 // Iff the function returns that it changed an entry, the new entry will be re-written in the queue. Any changed 637 // entries that become empty are removed after iteration completes. 638 func (q ExpirationQueue) traverseMutate(f func(epoch abi.ChainEpoch, es *ExpirationSet) (changed, keepGoing bool, err error)) error { 639 var es ExpirationSet 640 var epochsEmptied []uint64 641 errStop := fmt.Errorf("stop") 642 if err := q.Array.ForEach(&es, func(epoch int64) error { 643 changed, keepGoing, err := f(abi.ChainEpoch(epoch), &es) 644 if err != nil { 645 return err 646 } else if changed { 647 if emptied, err := es.IsEmpty(); err != nil { 648 return err 649 } else if emptied { 650 epochsEmptied = append(epochsEmptied, uint64(epoch)) 651 } else if err = q.mustUpdate(abi.ChainEpoch(epoch), &es); err != nil { 652 return err 653 } 654 } 655 656 if !keepGoing { 657 return errStop 658 } 659 return nil 660 }); err != nil && err != errStop { 661 return err 662 } 663 if err := q.Array.BatchDelete(epochsEmptied, true); err != nil { 664 return err 665 } 666 return nil 667 } 668 669 func (q ExpirationQueue) traverse(f func(epoch abi.ChainEpoch, es *ExpirationSet) (keepGoing bool, err error)) error { 670 return q.traverseMutate(func(epoch abi.ChainEpoch, es *ExpirationSet) (bool, bool, error) { 671 keepGoing, err := f(epoch, es) 672 return false, keepGoing, err 673 }) 674 } 675 676 func (q ExpirationQueue) mayGet(key abi.ChainEpoch) (*ExpirationSet, error) { 677 es := NewExpirationSetEmpty() 678 if _, err := q.Array.Get(uint64(key), es); err != nil { 679 return nil, xerrors.Errorf("failed to lookup queue epoch %v: %w", key, err) 680 } 681 return es, nil 682 } 683 684 func (q ExpirationQueue) mustUpdate(epoch abi.ChainEpoch, es *ExpirationSet) error { 685 if err := q.Array.Set(uint64(epoch), es); err != nil { 686 return xerrors.Errorf("failed to set queue epoch %v: %w", epoch, err) 687 } 688 return nil 689 } 690 691 // Since this might delete the node, it's not safe for use inside an iteration. 692 func (q ExpirationQueue) mustUpdateOrDelete(epoch abi.ChainEpoch, es *ExpirationSet) error { 693 if empty, err := es.IsEmpty(); err != nil { 694 return err 695 } else if empty { 696 if err = q.Array.Delete(uint64(epoch)); err != nil { 697 return xerrors.Errorf("failed to delete queue epoch %d: %w", epoch, err) 698 } 699 } else if err = q.Array.Set(uint64(epoch), es); err != nil { 700 return xerrors.Errorf("failed to set queue epoch %v: %w", epoch, err) 701 } 702 return nil 703 } 704 705 type sectorEpochSet struct { 706 epoch abi.ChainEpoch 707 sectors []uint64 708 power PowerPair 709 pledge abi.TokenAmount 710 } 711 712 type sectorExpirationSet struct { 713 sectorEpochSet 714 expirationSet *ExpirationSet 715 } 716 717 // Takes a slice of sector infos and returns sector info sets grouped and 718 // sorted by expiration epoch, quantized. 719 // 720 // Note: While the result is sorted by epoch, the order of per-epoch sectors is maintained. 721 func groupNewSectorsByDeclaredExpiration(sectorSize abi.SectorSize, sectors []*SectorOnChainInfo, quant QuantSpec) []sectorEpochSet { 722 sectorsByExpiration := make(map[abi.ChainEpoch][]*SectorOnChainInfo) 723 724 for _, sector := range sectors { 725 qExpiration := quant.QuantizeUp(sector.Expiration) 726 sectorsByExpiration[qExpiration] = append(sectorsByExpiration[qExpiration], sector) 727 } 728 729 sectorEpochSets := make([]sectorEpochSet, 0, len(sectorsByExpiration)) 730 731 // This map iteration is non-deterministic but safe because we sort by epoch below. 732 for expiration, epochSectors := range sectorsByExpiration { //nolint:nomaprange // result is subsequently sorted 733 sectorNumbers := make([]uint64, len(epochSectors)) 734 totalPower := NewPowerPairZero() 735 totalPledge := big.Zero() 736 for i, sector := range epochSectors { 737 sectorNumbers[i] = uint64(sector.SectorNumber) 738 totalPower = totalPower.Add(PowerForSector(sectorSize, sector)) 739 totalPledge = big.Add(totalPledge, sector.InitialPledge) 740 } 741 sectorEpochSets = append(sectorEpochSets, sectorEpochSet{ 742 epoch: expiration, 743 sectors: sectorNumbers, 744 power: totalPower, 745 pledge: totalPledge, 746 }) 747 } 748 749 sort.Slice(sectorEpochSets, func(i, j int) bool { 750 return sectorEpochSets[i].epoch < sectorEpochSets[j].epoch 751 }) 752 return sectorEpochSets 753 } 754 755 // Groups sectors into sets based on their Expiration field. 756 // If sectors are not found in the expiration set corresponding to their expiration field 757 // (i.e. they have been rescheduled) traverse expiration sets to for groups where these 758 // sectors actually expire. 759 // Groups will be returned in expiration order, earliest first. 760 func (q *ExpirationQueue) findSectorsByExpiration(sectorSize abi.SectorSize, sectors []*SectorOnChainInfo) ([]sectorExpirationSet, error) { 761 declaredExpirations := make(map[abi.ChainEpoch]bool, len(sectors)) 762 sectorsByNumber := make(map[uint64]*SectorOnChainInfo, len(sectors)) 763 allRemaining := make(map[uint64]struct{}) 764 expirationGroups := make([]sectorExpirationSet, 0, len(declaredExpirations)) 765 766 for _, sector := range sectors { 767 qExpiration := q.quant.QuantizeUp(sector.Expiration) 768 declaredExpirations[qExpiration] = true 769 allRemaining[uint64(sector.SectorNumber)] = struct{}{} 770 sectorsByNumber[uint64(sector.SectorNumber)] = sector 771 } 772 773 // Traverse expiration sets first by expected expirations. This will find all groups if no sectors have been rescheduled. 774 // This map iteration is non-deterministic but safe because we sort by epoch below. 775 for expiration := range declaredExpirations { //nolint:nomaprange // result is subsequently sorted 776 es, err := q.mayGet(expiration) 777 if err != nil { 778 return nil, err 779 } 780 781 // create group from overlap 782 var group sectorExpirationSet 783 group, allRemaining, err = groupExpirationSet(sectorSize, sectorsByNumber, allRemaining, es, expiration) 784 if err != nil { 785 return nil, err 786 } 787 if len(group.sectors) > 0 { 788 expirationGroups = append(expirationGroups, group) 789 } 790 } 791 792 // If sectors remain, traverse next in epoch order. Remaining sectors should be rescheduled to expire soon, so 793 // this traversal should exit early. 794 if len(allRemaining) > 0 { 795 err := q.traverse(func(epoch abi.ChainEpoch, es *ExpirationSet) (bool, error) { 796 // If this set's epoch is one of our declared epochs, we've already processed it in the loop above, 797 // so skip processing here. Sectors rescheduled to this epoch would have been included in the earlier processing. 798 if _, found := declaredExpirations[epoch]; found { 799 return true, nil 800 } 801 802 // Sector should not be found in EarlyExpirations which holds faults. An implicit assumption 803 // of grouping is that it only returns sectors with active power. ExpirationQueue should not 804 // provide operations that allow this to happen. 805 if err := assertNoEarlySectors(allRemaining, es); err != nil { 806 return true, err 807 } 808 809 var group sectorExpirationSet 810 var err error 811 group, allRemaining, err = groupExpirationSet(sectorSize, sectorsByNumber, allRemaining, es, epoch) 812 if err != nil { 813 return false, err 814 } 815 if len(group.sectors) > 0 { 816 expirationGroups = append(expirationGroups, group) 817 } 818 819 return len(allRemaining) > 0, nil 820 }) 821 if err != nil { 822 return nil, err 823 } 824 } 825 826 if len(allRemaining) > 0 { 827 return nil, xerrors.New("some sectors not found in expiration queue") 828 } 829 830 // sort groups, earliest first. 831 sort.Slice(expirationGroups, func(i, j int) bool { 832 return expirationGroups[i].epoch < expirationGroups[j].epoch 833 }) 834 return expirationGroups, nil 835 } 836 837 // Takes a slice of sector infos a bitfield of sector numbers and returns a single group for all bitfield sectors 838 // Also returns a bitfield containing sectors not found in expiration set. 839 // This method mutates includeSet by removing sector numbers of sectors found in expiration set. 840 func groupExpirationSet(sectorSize abi.SectorSize, sectors map[uint64]*SectorOnChainInfo, 841 includeSet map[uint64]struct{}, es *ExpirationSet, expiration abi.ChainEpoch, 842 ) (sectorExpirationSet, map[uint64]struct{}, error) { 843 var sectorNumbers []uint64 844 totalPower := NewPowerPairZero() 845 totalPledge := big.Zero() 846 err := es.OnTimeSectors.ForEach(func(u uint64) error { 847 if _, found := includeSet[u]; found { 848 sector := sectors[u] 849 sectorNumbers = append(sectorNumbers, u) 850 totalPower = totalPower.Add(PowerForSector(sectorSize, sector)) 851 totalPledge = big.Add(totalPledge, sector.InitialPledge) 852 delete(includeSet, u) 853 } 854 return nil 855 }) 856 if err != nil { 857 return sectorExpirationSet{}, nil, err 858 } 859 860 return sectorExpirationSet{ 861 sectorEpochSet: sectorEpochSet{ 862 epoch: expiration, 863 sectors: sectorNumbers, 864 power: totalPower, 865 pledge: totalPledge, 866 }, 867 expirationSet: es, 868 }, includeSet, nil 869 } 870 871 // assertNoEarlySectors checks for an invalid overlap between a bitfield an a set's early sectors. 872 func assertNoEarlySectors(set map[uint64]struct{}, es *ExpirationSet) error { 873 return es.EarlySectors.ForEach(func(u uint64) error { 874 if _, found := set[u]; found { 875 return xerrors.Errorf("Invalid attempt to group sector %d with an early expiration", u) 876 } 877 return nil 878 }) 879 }