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

     1  package power
     2  
     3  import (
     4  	"github.com/filecoin-project/go-address"
     5  	"github.com/filecoin-project/go-state-types/abi"
     6  	"github.com/filecoin-project/go-state-types/big"
     7  
     8  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
     9  	"github.com/filecoin-project/specs-actors/v4/actors/runtime/proof"
    10  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    11  )
    12  
    13  type MinerCronEvent struct {
    14  	Epoch   abi.ChainEpoch
    15  	Payload []byte
    16  }
    17  
    18  type CronEventsByAddress map[address.Address][]MinerCronEvent
    19  type ClaimsByAddress map[address.Address]Claim
    20  type ProofsByAddress map[address.Address][]proof.SealVerifyInfo
    21  
    22  type StateSummary struct {
    23  	Crons  CronEventsByAddress
    24  	Claims ClaimsByAddress
    25  	Proofs ProofsByAddress
    26  }
    27  
    28  // Checks internal invariants of power state.
    29  func CheckStateInvariants(st *State, store adt.Store) (*StateSummary, *builtin.MessageAccumulator) {
    30  	acc := &builtin.MessageAccumulator{}
    31  
    32  	// basic invariants around recorded power
    33  	acc.Require(st.TotalRawBytePower.GreaterThanEqual(big.Zero()), "total raw power is negative %v", st.TotalRawBytePower)
    34  	acc.Require(st.TotalQualityAdjPower.GreaterThanEqual(big.Zero()), "total qa power is negative %v", st.TotalQualityAdjPower)
    35  	acc.Require(st.TotalBytesCommitted.GreaterThanEqual(big.Zero()), "total raw power committed is negative %v", st.TotalBytesCommitted)
    36  	acc.Require(st.TotalQABytesCommitted.GreaterThanEqual(big.Zero()), "total qa power committed is negative %v", st.TotalQABytesCommitted)
    37  
    38  	acc.Require(st.TotalRawBytePower.LessThanEqual(st.TotalQualityAdjPower),
    39  		"total raw power %v is greater than total quality adjusted power %v", st.TotalRawBytePower, st.TotalQualityAdjPower)
    40  	acc.Require(st.TotalBytesCommitted.LessThanEqual(st.TotalQABytesCommitted),
    41  		"committed raw power %v is greater than committed quality adjusted power %v", st.TotalBytesCommitted, st.TotalQABytesCommitted)
    42  	acc.Require(st.TotalRawBytePower.LessThanEqual(st.TotalBytesCommitted),
    43  		"total raw power %v is greater than raw power committed %v", st.TotalRawBytePower, st.TotalBytesCommitted)
    44  	acc.Require(st.TotalQualityAdjPower.LessThanEqual(st.TotalQABytesCommitted),
    45  		"total qa power %v is greater than qa power committed %v", st.TotalQualityAdjPower, st.TotalQABytesCommitted)
    46  
    47  	crons := CheckCronInvariants(st, store, acc)
    48  	claims := CheckClaimInvariants(st, store, acc)
    49  	proofs := CheckProofValidationInvariants(st, store, claims, acc)
    50  
    51  	return &StateSummary{
    52  		Crons:  crons,
    53  		Claims: claims,
    54  		Proofs: proofs,
    55  	}, acc
    56  }
    57  
    58  func CheckCronInvariants(st *State, store adt.Store, acc *builtin.MessageAccumulator) CronEventsByAddress {
    59  	byAddress := make(CronEventsByAddress)
    60  	queue, err := adt.AsMultimap(store, st.CronEventQueue, CronQueueHamtBitwidth, CronQueueAmtBitwidth)
    61  	if err != nil {
    62  		acc.Addf("error loading cron event queue: %v", err)
    63  		// Bail here.
    64  		return byAddress
    65  	}
    66  
    67  	err = queue.ForAll(func(ekey string, arr *adt.Array) error {
    68  		epoch, err := abi.ParseIntKey(ekey)
    69  		acc.Require(err == nil, "non-int key in cron array")
    70  		if err != nil {
    71  			return nil // error noted above
    72  		}
    73  
    74  		acc.Require(abi.ChainEpoch(epoch) >= st.FirstCronEpoch, "cron event at epoch %d before FirstCronEpoch %d",
    75  			epoch, st.FirstCronEpoch)
    76  
    77  		var event CronEvent
    78  		return arr.ForEach(&event, func(i int64) error {
    79  			byAddress[event.MinerAddr] = append(byAddress[event.MinerAddr], MinerCronEvent{
    80  				Epoch:   abi.ChainEpoch(epoch),
    81  				Payload: event.CallbackPayload,
    82  			})
    83  
    84  			return nil
    85  		})
    86  	})
    87  	acc.RequireNoError(err, "error iterating cron tasks")
    88  	return byAddress
    89  }
    90  
    91  func CheckClaimInvariants(st *State, store adt.Store, acc *builtin.MessageAccumulator) ClaimsByAddress {
    92  	byAddress := make(ClaimsByAddress)
    93  	claims, err := adt.AsMap(store, st.Claims, builtin.DefaultHamtBitwidth)
    94  	if err != nil {
    95  		acc.Addf("error loading power claims: %v", err)
    96  		// Bail here
    97  		return byAddress
    98  	}
    99  
   100  	committedRawPower := abi.NewStoragePower(0)
   101  	committedQAPower := abi.NewStoragePower(0)
   102  	rawPower := abi.NewStoragePower(0)
   103  	qaPower := abi.NewStoragePower(0)
   104  	claimsWithSufficientPowerCount := int64(0)
   105  	var claim Claim
   106  	err = claims.ForEach(&claim, func(key string) error {
   107  		addr, err := address.NewFromBytes([]byte(key))
   108  		if err != nil {
   109  			return err
   110  		}
   111  		byAddress[addr] = claim
   112  		committedRawPower = big.Add(committedRawPower, claim.RawBytePower)
   113  		committedQAPower = big.Add(committedQAPower, claim.QualityAdjPower)
   114  
   115  		minPower, err := builtin.ConsensusMinerMinPower(claim.WindowPoStProofType)
   116  		acc.Require(err == nil, "could not get consensus miner min power for miner %v: %v", addr, err)
   117  		if err != nil {
   118  			return nil // noted above
   119  		}
   120  
   121  		if claim.RawBytePower.GreaterThanEqual(minPower) {
   122  			claimsWithSufficientPowerCount += 1
   123  			rawPower = big.Add(rawPower, claim.RawBytePower)
   124  			qaPower = big.Add(qaPower, claim.QualityAdjPower)
   125  		}
   126  		return nil
   127  	})
   128  	acc.RequireNoError(err, "error iterating power claims")
   129  
   130  	acc.Require(committedRawPower.Equals(st.TotalBytesCommitted),
   131  		"sum of raw power in claims %v does not match recorded bytes committed %v",
   132  		committedRawPower, st.TotalBytesCommitted)
   133  	acc.Require(committedQAPower.Equals(st.TotalQABytesCommitted),
   134  		"sum of qa power in claims %v does not match recorded qa power committed %v",
   135  		committedQAPower, st.TotalQABytesCommitted)
   136  
   137  	acc.Require(claimsWithSufficientPowerCount == st.MinerAboveMinPowerCount,
   138  		"claims with sufficient power %d does not match MinerAboveMinPowerCount %d",
   139  		claimsWithSufficientPowerCount, st.MinerAboveMinPowerCount)
   140  
   141  	acc.Require(st.TotalRawBytePower.Equals(rawPower),
   142  		"recorded raw power %v does not match raw power in claims %v", st.TotalRawBytePower, rawPower)
   143  	acc.Require(st.TotalQualityAdjPower.Equals(qaPower),
   144  		"recorded qa power %v does not match qa power in claims %v", st.TotalQualityAdjPower, qaPower)
   145  
   146  	return byAddress
   147  }
   148  
   149  func CheckProofValidationInvariants(st *State, store adt.Store, claims ClaimsByAddress, acc *builtin.MessageAccumulator) ProofsByAddress {
   150  	if st.ProofValidationBatch == nil {
   151  		return nil
   152  	}
   153  
   154  	proofs := make(ProofsByAddress)
   155  	if queue, err := adt.AsMultimap(store, *st.ProofValidationBatch, builtin.DefaultHamtBitwidth, ProofValidationBatchAmtBitwidth); err != nil {
   156  		acc.Addf("error loading proof validation queue: %v", err)
   157  	} else {
   158  		err = queue.ForAll(func(key string, arr *adt.Array) error {
   159  			addr, err := address.NewFromBytes([]byte(key))
   160  			if err != nil {
   161  				return err
   162  			}
   163  
   164  			claim, found := claims[addr]
   165  			acc.Require(found, "miner %v has proofs awaiting validation but no claim", addr)
   166  			if !found {
   167  				return nil
   168  			}
   169  
   170  			var info proof.SealVerifyInfo
   171  			err = arr.ForEach(&info, func(i int64) error {
   172  				sectorWindowPoStProofType, err := info.SealProof.RegisteredWindowPoStProof()
   173  				acc.RequireNoError(err, "failed to get PoSt proof type for seal proof %d", info.SealProof)
   174  				acc.Require(claim.WindowPoStProofType == sectorWindowPoStProofType, "miner submitted proof with proof type %d different from claim %d",
   175  					sectorWindowPoStProofType, claim.WindowPoStProofType)
   176  				proofs[addr] = append(proofs[addr], info)
   177  				return nil
   178  			})
   179  			if err != nil {
   180  				return err
   181  			}
   182  			acc.Require(len(proofs[addr]) <= MaxMinerProveCommitsPerEpoch,
   183  				"miner %v has submitted too many proofs (%d) for batch verification", addr, len(proofs[addr]))
   184  			return nil
   185  		})
   186  		acc.RequireNoError(err, "error iterating proof validation queue")
   187  	}
   188  	return proofs
   189  }