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 }