github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/power/power_state.go (about)

     1  package power
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	addr "github.com/filecoin-project/go-address"
     8  	"github.com/filecoin-project/go-state-types/abi"
     9  	"github.com/filecoin-project/go-state-types/big"
    10  	"github.com/filecoin-project/go-state-types/exitcode"
    11  	cid "github.com/ipfs/go-cid"
    12  	errors "github.com/pkg/errors"
    13  	"golang.org/x/xerrors"
    14  
    15  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
    16  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    17  	"github.com/filecoin-project/specs-actors/v4/actors/util/smoothing"
    18  )
    19  
    20  // genesis power in bytes = 750,000 GiB
    21  var InitialQAPowerEstimatePosition = big.Mul(big.NewInt(750_000), big.NewInt(1<<30))
    22  
    23  // max chain throughput in bytes per epoch = 120 ProveCommits / epoch = 3,840 GiB
    24  var InitialQAPowerEstimateVelocity = big.Mul(big.NewInt(3_840), big.NewInt(1<<30))
    25  
    26  // Bitwidth of CronEventQueue HAMT determined empirically from mutation
    27  // patterns and projections of mainnet data.
    28  const CronQueueHamtBitwidth = 6
    29  
    30  // Bitwidth of CronEventQueue AMT determined empirically from mutation
    31  // patterns and projections of mainnet data.
    32  const CronQueueAmtBitwidth = 6
    33  
    34  // Bitwidth of ProofValidationBatch AMT determined empirically from mutation
    35  // pattersn and projections of mainnet data.
    36  const ProofValidationBatchAmtBitwidth = 4
    37  
    38  type State struct {
    39  	TotalRawBytePower abi.StoragePower
    40  	// TotalBytesCommitted includes claims from miners below min power threshold
    41  	TotalBytesCommitted  abi.StoragePower
    42  	TotalQualityAdjPower abi.StoragePower
    43  	// TotalQABytesCommitted includes claims from miners below min power threshold
    44  	TotalQABytesCommitted abi.StoragePower
    45  	TotalPledgeCollateral abi.TokenAmount
    46  
    47  	// These fields are set once per epoch in the previous cron tick and used
    48  	// for consistent values across a single epoch's state transition.
    49  	ThisEpochRawBytePower     abi.StoragePower
    50  	ThisEpochQualityAdjPower  abi.StoragePower
    51  	ThisEpochPledgeCollateral abi.TokenAmount
    52  	ThisEpochQAPowerSmoothed  smoothing.FilterEstimate
    53  
    54  	MinerCount int64
    55  	// Number of miners having proven the minimum consensus power.
    56  	MinerAboveMinPowerCount int64
    57  
    58  	// A queue of events to be triggered by cron, indexed by epoch.
    59  	CronEventQueue cid.Cid // Multimap, (HAMT[ChainEpoch]AMT[CronEvent])
    60  
    61  	// First epoch in which a cron task may be stored.
    62  	// Cron will iterate every epoch between this and the current epoch inclusively to find tasks to execute.
    63  	FirstCronEpoch abi.ChainEpoch
    64  
    65  	// Claimed power for each miner.
    66  	Claims cid.Cid // Map, HAMT[address]Claim
    67  
    68  	ProofValidationBatch *cid.Cid // Multimap, (HAMT[Address]AMT[SealVerifyInfo])
    69  }
    70  
    71  type Claim struct {
    72  	// Miner's proof type used to determine minimum miner size
    73  	WindowPoStProofType abi.RegisteredPoStProof
    74  
    75  	// Sum of raw byte power for a miner's sectors.
    76  	RawBytePower abi.StoragePower
    77  
    78  	// Sum of quality adjusted power for a miner's sectors.
    79  	QualityAdjPower abi.StoragePower
    80  }
    81  
    82  type CronEvent struct {
    83  	MinerAddr       addr.Address
    84  	CallbackPayload []byte
    85  }
    86  
    87  func ConstructState(store adt.Store) (*State, error) {
    88  	emptyClaimsMapCid, err := adt.StoreEmptyMap(store, builtin.DefaultHamtBitwidth)
    89  	if err != nil {
    90  		return nil, xerrors.Errorf("failed to create empty map: %w", err)
    91  	}
    92  	emptyCronQueueMMapCid, err := adt.StoreEmptyMultimap(store, CronQueueHamtBitwidth, CronQueueAmtBitwidth)
    93  	if err != nil {
    94  		return nil, xerrors.Errorf("failed to create empty multimap: %w", err)
    95  	}
    96  
    97  	return &State{
    98  		TotalRawBytePower:         abi.NewStoragePower(0),
    99  		TotalBytesCommitted:       abi.NewStoragePower(0),
   100  		TotalQualityAdjPower:      abi.NewStoragePower(0),
   101  		TotalQABytesCommitted:     abi.NewStoragePower(0),
   102  		TotalPledgeCollateral:     abi.NewTokenAmount(0),
   103  		ThisEpochRawBytePower:     abi.NewStoragePower(0),
   104  		ThisEpochQualityAdjPower:  abi.NewStoragePower(0),
   105  		ThisEpochPledgeCollateral: abi.NewTokenAmount(0),
   106  		ThisEpochQAPowerSmoothed:  smoothing.NewEstimate(InitialQAPowerEstimatePosition, InitialQAPowerEstimateVelocity),
   107  		FirstCronEpoch:            0,
   108  		CronEventQueue:            emptyCronQueueMMapCid,
   109  		Claims:                    emptyClaimsMapCid,
   110  		MinerCount:                0,
   111  		MinerAboveMinPowerCount:   0,
   112  	}, nil
   113  }
   114  
   115  // MinerNominalPowerMeetsConsensusMinimum is used to validate Election PoSt
   116  // winners outside the chain state. If the miner has over a threshold of power
   117  // the miner meets the minimum.  If the network is a below a threshold of
   118  // miners and has power > zero the miner meets the minimum.
   119  func (st *State) MinerNominalPowerMeetsConsensusMinimum(s adt.Store, miner addr.Address) (bool, error) { //nolint:deadcode,unused
   120  	claims, err := adt.AsMap(s, st.Claims, builtin.DefaultHamtBitwidth)
   121  	if err != nil {
   122  		return false, xerrors.Errorf("failed to load claims: %w", err)
   123  	}
   124  
   125  	claim, ok, err := getClaim(claims, miner)
   126  	if err != nil {
   127  		return false, err
   128  	}
   129  	if !ok {
   130  		return false, errors.Errorf("no claim for actor %v", miner)
   131  	}
   132  
   133  	minerNominalPower := claim.RawBytePower
   134  	minerMinPower, err := builtin.ConsensusMinerMinPower(claim.WindowPoStProofType)
   135  	if err != nil {
   136  		return false, errors.Wrap(err, "could not get miner min power from proof type")
   137  	}
   138  
   139  	// if miner is larger than min power requirement, we're set
   140  	if minerNominalPower.GreaterThanEqual(minerMinPower) {
   141  		return true, nil
   142  	}
   143  
   144  	// otherwise, if ConsensusMinerMinMiners miners meet min power requirement, return false
   145  	if st.MinerAboveMinPowerCount >= ConsensusMinerMinMiners {
   146  		return false, nil
   147  	}
   148  
   149  	// If fewer than ConsensusMinerMinMiners over threshold miner can win a block with non-zero power
   150  	return minerNominalPower.GreaterThan(abi.NewStoragePower(0)), nil
   151  }
   152  
   153  // Parameters may be negative to subtract.
   154  func (st *State) AddToClaim(s adt.Store, miner addr.Address, power abi.StoragePower, qapower abi.StoragePower) error {
   155  	claims, err := adt.AsMap(s, st.Claims, builtin.DefaultHamtBitwidth)
   156  	if err != nil {
   157  		return xerrors.Errorf("failed to load claims: %w", err)
   158  	}
   159  
   160  	if err := st.addToClaim(claims, miner, power, qapower); err != nil {
   161  		return xerrors.Errorf("failed to add claim: %w", err)
   162  	}
   163  
   164  	st.Claims, err = claims.Root()
   165  	if err != nil {
   166  		return xerrors.Errorf("failed to flush claims: %w", err)
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  func (st *State) GetClaim(s adt.Store, a addr.Address) (*Claim, bool, error) {
   173  	claims, err := adt.AsMap(s, st.Claims, builtin.DefaultHamtBitwidth)
   174  	if err != nil {
   175  		return nil, false, xerrors.Errorf("failed to load claims: %w", err)
   176  	}
   177  	return getClaim(claims, a)
   178  }
   179  
   180  func (st *State) addToClaim(claims *adt.Map, miner addr.Address, power abi.StoragePower, qapower abi.StoragePower) error {
   181  	oldClaim, ok, err := getClaim(claims, miner)
   182  	if err != nil {
   183  		return fmt.Errorf("failed to get claim: %w", err)
   184  	}
   185  	if !ok {
   186  		return exitcode.ErrNotFound.Wrapf("no claim for actor %v", miner)
   187  	}
   188  
   189  	// TotalBytes always update directly
   190  	st.TotalQABytesCommitted = big.Add(st.TotalQABytesCommitted, qapower)
   191  	st.TotalBytesCommitted = big.Add(st.TotalBytesCommitted, power)
   192  
   193  	newClaim := Claim{
   194  		WindowPoStProofType: oldClaim.WindowPoStProofType,
   195  		RawBytePower:        big.Add(oldClaim.RawBytePower, power),
   196  		QualityAdjPower:     big.Add(oldClaim.QualityAdjPower, qapower),
   197  	}
   198  
   199  	minPower, err := builtin.ConsensusMinerMinPower(oldClaim.WindowPoStProofType)
   200  	if err != nil {
   201  		return fmt.Errorf("could not get consensus miner min power: %w", err)
   202  	}
   203  
   204  	prevBelow := oldClaim.RawBytePower.LessThan(minPower)
   205  	stillBelow := newClaim.RawBytePower.LessThan(minPower)
   206  
   207  	if prevBelow && !stillBelow {
   208  		// just passed min miner size
   209  		st.MinerAboveMinPowerCount++
   210  		st.TotalQualityAdjPower = big.Add(st.TotalQualityAdjPower, newClaim.QualityAdjPower)
   211  		st.TotalRawBytePower = big.Add(st.TotalRawBytePower, newClaim.RawBytePower)
   212  	} else if !prevBelow && stillBelow {
   213  		// just went below min miner size
   214  		st.MinerAboveMinPowerCount--
   215  		st.TotalQualityAdjPower = big.Sub(st.TotalQualityAdjPower, oldClaim.QualityAdjPower)
   216  		st.TotalRawBytePower = big.Sub(st.TotalRawBytePower, oldClaim.RawBytePower)
   217  	} else if !prevBelow && !stillBelow {
   218  		// Was above the threshold, still above
   219  		st.TotalQualityAdjPower = big.Add(st.TotalQualityAdjPower, qapower)
   220  		st.TotalRawBytePower = big.Add(st.TotalRawBytePower, power)
   221  	}
   222  
   223  	if newClaim.RawBytePower.LessThan(big.Zero()) {
   224  		return xerrors.Errorf("negative claimed raw byte power: %v", newClaim.RawBytePower)
   225  	}
   226  	if newClaim.QualityAdjPower.LessThan(big.Zero()) {
   227  		return xerrors.Errorf("negative claimed quality adjusted power: %v", newClaim.QualityAdjPower)
   228  	}
   229  	if st.MinerAboveMinPowerCount < 0 {
   230  		return xerrors.Errorf("negative number of miners larger than min: %v", st.MinerAboveMinPowerCount)
   231  	}
   232  	return setClaim(claims, miner, &newClaim)
   233  }
   234  
   235  func (st *State) updateStatsForNewMiner(windowPoStProof abi.RegisteredPoStProof) error {
   236  	minPower, err := builtin.ConsensusMinerMinPower(windowPoStProof)
   237  	if err != nil {
   238  		return fmt.Errorf("could not get consensus miner min power: %w", err)
   239  	}
   240  
   241  	if minPower.LessThanEqual(big.Zero()) {
   242  		st.MinerAboveMinPowerCount++
   243  	}
   244  	return nil
   245  }
   246  
   247  func (st *State) deleteClaim(claims *adt.Map, miner addr.Address) (bool, error) {
   248  	// Note: this flow loads the claim multiple times, unnecessarily.
   249  	// We should refactor to use claims.Pop().
   250  	oldClaim, ok, err := getClaim(claims, miner)
   251  	if err != nil {
   252  		return false, fmt.Errorf("failed to get claim: %w", err)
   253  	}
   254  	if !ok {
   255  		return false, nil // no record, we're done
   256  	}
   257  
   258  	// subtract from stats as if we were simply removing power
   259  	err = st.addToClaim(claims, miner, oldClaim.RawBytePower.Neg(), oldClaim.QualityAdjPower.Neg())
   260  	if err != nil {
   261  		return false, fmt.Errorf("failed to subtract miner power before deleting claim: %w", err)
   262  	}
   263  
   264  	// delete claim from state to invalidate miner
   265  	return true, claims.Delete(abi.AddrKey(miner))
   266  }
   267  
   268  func getClaim(claims *adt.Map, a addr.Address) (*Claim, bool, error) {
   269  	var out Claim
   270  	found, err := claims.Get(abi.AddrKey(a), &out)
   271  	if err != nil {
   272  		return nil, false, errors.Wrapf(err, "failed to get claim for address %v", a)
   273  	}
   274  	if !found {
   275  		return nil, false, nil
   276  	}
   277  	return &out, true, nil
   278  }
   279  
   280  func (st *State) addPledgeTotal(amount abi.TokenAmount) {
   281  	st.TotalPledgeCollateral = big.Add(st.TotalPledgeCollateral, amount)
   282  }
   283  
   284  func (st *State) appendCronEvent(events *adt.Multimap, epoch abi.ChainEpoch, event *CronEvent) error {
   285  	// if event is in past, alter FirstCronEpoch so it will be found.
   286  	if epoch < st.FirstCronEpoch {
   287  		st.FirstCronEpoch = epoch
   288  	}
   289  
   290  	if err := events.Add(epochKey(epoch), event); err != nil {
   291  		return xerrors.Errorf("failed to store cron event at epoch %v for miner %v: %w", epoch, event, err)
   292  	}
   293  
   294  	return nil
   295  }
   296  
   297  func (st *State) updateSmoothedEstimate(delta abi.ChainEpoch) {
   298  	filterQAPower := smoothing.LoadFilter(st.ThisEpochQAPowerSmoothed, smoothing.DefaultAlpha, smoothing.DefaultBeta)
   299  	st.ThisEpochQAPowerSmoothed = filterQAPower.NextEstimate(st.ThisEpochQualityAdjPower, delta)
   300  }
   301  
   302  func loadCronEvents(mmap *adt.Multimap, epoch abi.ChainEpoch) ([]CronEvent, error) {
   303  	var events []CronEvent
   304  	var ev CronEvent
   305  	err := mmap.ForEach(epochKey(epoch), &ev, func(i int64) error {
   306  		events = append(events, ev)
   307  		return nil
   308  	})
   309  	return events, err
   310  }
   311  
   312  func setClaim(claims *adt.Map, a addr.Address, claim *Claim) error {
   313  	if claim.RawBytePower.LessThan(big.Zero()) {
   314  		return xerrors.Errorf("negative claim raw power %v", claim.RawBytePower)
   315  	}
   316  	if claim.QualityAdjPower.LessThan(big.Zero()) {
   317  		return xerrors.Errorf("negative claim quality-adjusted power %v", claim.QualityAdjPower)
   318  	}
   319  	if err := claims.Put(abi.AddrKey(a), claim); err != nil {
   320  		return xerrors.Errorf("failed to put claim with address %s power %v: %w", a, claim, err)
   321  	}
   322  	return nil
   323  }
   324  
   325  // CurrentTotalPower returns current power values accounting for minimum miner
   326  // and minimum power
   327  func CurrentTotalPower(st *State) (abi.StoragePower, abi.StoragePower) {
   328  	if st.MinerAboveMinPowerCount < ConsensusMinerMinMiners {
   329  		return st.TotalBytesCommitted, st.TotalQABytesCommitted
   330  	}
   331  	return st.TotalRawBytePower, st.TotalQualityAdjPower
   332  }
   333  
   334  func epochKey(e abi.ChainEpoch) abi.Keyer {
   335  	return abi.IntKey(int64(e))
   336  }
   337  
   338  func init() {
   339  	// Check that ChainEpoch is indeed a signed integer to confirm that epochKey is making the right interpretation.
   340  	var e abi.ChainEpoch
   341  	if reflect.TypeOf(e).Kind() != reflect.Int64 {
   342  		panic("incorrect chain epoch encoding")
   343  	}
   344  
   345  }