github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/states/check.go (about) 1 package states 2 3 import ( 4 "bytes" 5 6 addr "github.com/filecoin-project/go-address" 7 "github.com/filecoin-project/go-state-types/abi" 8 "github.com/filecoin-project/go-state-types/big" 9 "golang.org/x/xerrors" 10 11 "github.com/filecoin-project/specs-actors/v4/actors/builtin/power" 12 13 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 14 "github.com/filecoin-project/specs-actors/v4/actors/builtin/account" 15 "github.com/filecoin-project/specs-actors/v4/actors/builtin/cron" 16 init_ "github.com/filecoin-project/specs-actors/v4/actors/builtin/init" 17 "github.com/filecoin-project/specs-actors/v4/actors/builtin/market" 18 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" 19 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" 20 "github.com/filecoin-project/specs-actors/v4/actors/builtin/paych" 21 "github.com/filecoin-project/specs-actors/v4/actors/builtin/reward" 22 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg" 23 ) 24 25 // Within this code, Go errors are not expected, but are often converted to messages so that execution 26 // can continue to find more errors rather than fail with no insight. 27 // Only errors thar are particularly troublesome to recover from should propagate as Go errors. 28 func CheckStateInvariants(tree *Tree, expectedBalanceTotal abi.TokenAmount, priorEpoch abi.ChainEpoch) (*builtin.MessageAccumulator, error) { 29 acc := &builtin.MessageAccumulator{} 30 totalFIl := big.Zero() 31 var initSummary *init_.StateSummary 32 var cronSummary *cron.StateSummary 33 var verifregSummary *verifreg.StateSummary 34 var marketSummary *market.StateSummary 35 var rewardSummary *reward.StateSummary 36 var accountSummaries []*account.StateSummary 37 var powerSummary *power.StateSummary 38 var paychSummaries []*paych.StateSummary 39 var multisigSummaries []*multisig.StateSummary 40 minerSummaries := make(map[addr.Address]*miner.StateSummary) 41 42 if err := tree.ForEach(func(key addr.Address, actor *Actor) error { 43 acc := acc.WithPrefix("%v ", key) // Intentional shadow 44 if key.Protocol() != addr.ID { 45 acc.Addf("unexpected address protocol in state tree root: %v", key) 46 } 47 totalFIl = big.Add(totalFIl, actor.Balance) 48 49 switch actor.Code { 50 case builtin.SystemActorCodeID: 51 52 case builtin.InitActorCodeID: 53 var st init_.State 54 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 55 return err 56 } 57 summary, msgs := init_.CheckStateInvariants(&st, tree.Store) 58 acc.WithPrefix("init: ").AddAll(msgs) 59 initSummary = summary 60 case builtin.CronActorCodeID: 61 var st cron.State 62 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 63 return err 64 } 65 summary, msgs := cron.CheckStateInvariants(&st, tree.Store) 66 acc.WithPrefix("cron: ").AddAll(msgs) 67 cronSummary = summary 68 case builtin.AccountActorCodeID: 69 var st account.State 70 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 71 return err 72 } 73 summary, msgs := account.CheckStateInvariants(&st, key) 74 acc.WithPrefix("account: ").AddAll(msgs) 75 accountSummaries = append(accountSummaries, summary) 76 case builtin.StoragePowerActorCodeID: 77 var st power.State 78 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 79 return err 80 } 81 summary, msgs := power.CheckStateInvariants(&st, tree.Store) 82 acc.WithPrefix("power: ").AddAll(msgs) 83 powerSummary = summary 84 case builtin.StorageMinerActorCodeID: 85 var st miner.State 86 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 87 return err 88 } 89 summary, msgs := miner.CheckStateInvariants(&st, tree.Store, actor.Balance) 90 acc.WithPrefix("miner: ").AddAll(msgs) 91 minerSummaries[key] = summary 92 case builtin.StorageMarketActorCodeID: 93 var st market.State 94 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 95 return err 96 } 97 summary, msgs := market.CheckStateInvariants(&st, tree.Store, actor.Balance, priorEpoch) 98 acc.WithPrefix("market: ").AddAll(msgs) 99 marketSummary = summary 100 case builtin.PaymentChannelActorCodeID: 101 var st paych.State 102 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 103 return err 104 } 105 summary, msgs := paych.CheckStateInvariants(&st, tree.Store, actor.Balance) 106 acc.WithPrefix("paych: ").AddAll(msgs) 107 paychSummaries = append(paychSummaries, summary) 108 case builtin.MultisigActorCodeID: 109 var st multisig.State 110 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 111 return err 112 } 113 summary, msgs := multisig.CheckStateInvariants(&st, tree.Store) 114 acc.WithPrefix("multisig: ").AddAll(msgs) 115 multisigSummaries = append(multisigSummaries, summary) 116 case builtin.RewardActorCodeID: 117 var st reward.State 118 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 119 return err 120 } 121 summary, msgs := reward.CheckStateInvariants(&st, tree.Store, priorEpoch, actor.Balance) 122 acc.WithPrefix("reward: ").AddAll(msgs) 123 rewardSummary = summary 124 case builtin.VerifiedRegistryActorCodeID: 125 var st verifreg.State 126 if err := tree.Store.Get(tree.Store.Context(), actor.Head, &st); err != nil { 127 return err 128 } 129 summary, msgs := verifreg.CheckStateInvariants(&st, tree.Store) 130 acc.WithPrefix("verifreg: ").AddAll(msgs) 131 verifregSummary = summary 132 default: 133 return xerrors.Errorf("unexpected actor code CID %v for address %v", actor.Code, key) 134 } 135 return nil 136 }); err != nil { 137 return nil, err 138 } 139 140 // 141 // Perform cross-actor checks from state summaries here. 142 // 143 144 CheckMinersAgainstPower(acc, minerSummaries, powerSummary) 145 CheckDealStatesAgainstSectors(acc, minerSummaries, marketSummary) 146 147 _ = initSummary 148 _ = verifregSummary 149 _ = cronSummary 150 _ = marketSummary 151 _ = rewardSummary 152 153 if !totalFIl.Equals(expectedBalanceTotal) { 154 acc.Addf("total token balance is %v, expected %v", totalFIl, expectedBalanceTotal) 155 } 156 157 return acc, nil 158 } 159 160 func CheckMinersAgainstPower(acc *builtin.MessageAccumulator, minerSummaries map[addr.Address]*miner.StateSummary, powerSummary *power.StateSummary) { 161 for addr, minerSummary := range minerSummaries { // nolint:nomaprange 162 // check claim 163 claim, ok := powerSummary.Claims[addr] 164 acc.Require(ok, "miner %v has no power claim", addr) 165 if ok { 166 claimPower := miner.NewPowerPair(claim.RawBytePower, claim.QualityAdjPower) 167 acc.Require(minerSummary.ActivePower.Equals(claimPower), 168 "miner %v computed active power %v does not match claim %v", addr, minerSummary.ActivePower, claimPower) 169 acc.Require(minerSummary.WindowPoStProofType == claim.WindowPoStProofType, 170 "miner seal proof type %d does not match claim proof type %d", minerSummary.WindowPoStProofType, claim.WindowPoStProofType) 171 } 172 173 // check crons 174 crons, ok := powerSummary.Crons[addr] 175 if !ok { // with deferred and discontinued crons it is normal for a miner actor to have no cron events 176 continue 177 } 178 179 var payload miner.CronEventPayload 180 var provingPeriodCron *power.MinerCronEvent 181 for _, event := range crons { 182 err := payload.UnmarshalCBOR(bytes.NewReader(event.Payload)) 183 acc.Require(err == nil, "miner %v registered cron at epoch %d with wrong or corrupt payload", 184 addr, event.Epoch) 185 acc.Require(payload.EventType == miner.CronEventProcessEarlyTerminations || payload.EventType == miner.CronEventProvingDeadline, 186 "miner %v has unexpected cron event type %v", addr, payload.EventType) 187 188 if payload.EventType == miner.CronEventProvingDeadline { 189 if provingPeriodCron != nil { 190 acc.Require(false, "miner %v has duplicate proving period crons at epoch %d and %d", 191 addr, provingPeriodCron.Epoch, event.Epoch) 192 } 193 provingPeriodCron = &event 194 } 195 } 196 hasProvingPeriodCron := provingPeriodCron != nil 197 acc.Require(hasProvingPeriodCron == minerSummary.DeadlineCronActive, "miner %v has invalid DeadlineCronActive (%t) for hasProvingPeriodCron status (%t)", 198 addr, minerSummary.DeadlineCronActive, hasProvingPeriodCron) 199 200 acc.Require(provingPeriodCron != nil, "miner %v has no proving period cron", addr) 201 } 202 } 203 204 func CheckDealStatesAgainstSectors(acc *builtin.MessageAccumulator, minerSummaries map[addr.Address]*miner.StateSummary, marketSummary *market.StateSummary) { 205 // Check that all active deals are included within a non-terminated sector. 206 // We cannot check that all deals referenced within a sector are in the market, because deals 207 // can be terminated independently of the sector in which they are included. 208 for dealID, deal := range marketSummary.Deals { // nolint:nomaprange 209 if deal.SectorStartEpoch == abi.ChainEpoch(-1) { 210 // deal hasn't been activated yet, make no assertions about sector state 211 continue 212 } 213 214 minerSummary, found := minerSummaries[deal.Provider] 215 if !found { 216 acc.Addf("provider %v for deal %d not found among miners", deal.Provider, dealID) 217 continue 218 } 219 220 sectorDeal, found := minerSummary.Deals[dealID] 221 if !found { 222 acc.Require(deal.SlashEpoch >= 0, "un-slashed deal %d not referenced in active sectors of miner %v", dealID, deal.Provider) 223 continue 224 } 225 226 acc.Require(deal.SectorStartEpoch == sectorDeal.SectorStart, 227 "deal state start %d does not match sector start %d for miner %v", 228 deal.SectorStartEpoch, sectorDeal.SectorStart, deal.Provider) 229 230 acc.Require(deal.SectorStartEpoch <= sectorDeal.SectorExpiration, 231 "deal state start %d activated after sector expiration %d for miner %v", 232 deal.SectorStartEpoch, sectorDeal.SectorExpiration, deal.Provider) 233 234 acc.Require(deal.LastUpdatedEpoch <= sectorDeal.SectorExpiration, 235 "deal state update at %d after sector expiration %d for miner %v", 236 deal.LastUpdatedEpoch, sectorDeal.SectorExpiration, deal.Provider) 237 238 acc.Require(deal.SlashEpoch <= sectorDeal.SectorExpiration, 239 "deal state slashed at %d after sector expiration %d for miner %v", 240 deal.SlashEpoch, sectorDeal.SectorExpiration, deal.Provider) 241 } 242 }