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  }