github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/miner/miner_actor.go (about) 1 package miner 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "math" 8 9 addr "github.com/filecoin-project/go-address" 10 "github.com/filecoin-project/go-bitfield" 11 "github.com/filecoin-project/go-state-types/abi" 12 "github.com/filecoin-project/go-state-types/big" 13 "github.com/filecoin-project/go-state-types/cbor" 14 "github.com/filecoin-project/go-state-types/crypto" 15 "github.com/filecoin-project/go-state-types/dline" 16 "github.com/filecoin-project/go-state-types/exitcode" 17 rtt "github.com/filecoin-project/go-state-types/rt" 18 miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" 19 miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" 20 miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" 21 cid "github.com/ipfs/go-cid" 22 cbg "github.com/whyrusleeping/cbor-gen" 23 "golang.org/x/xerrors" 24 25 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 26 "github.com/filecoin-project/specs-actors/v4/actors/builtin/market" 27 "github.com/filecoin-project/specs-actors/v4/actors/builtin/power" 28 "github.com/filecoin-project/specs-actors/v4/actors/builtin/reward" 29 "github.com/filecoin-project/specs-actors/v4/actors/runtime" 30 "github.com/filecoin-project/specs-actors/v4/actors/runtime/proof" 31 . "github.com/filecoin-project/specs-actors/v4/actors/util" 32 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" 33 "github.com/filecoin-project/specs-actors/v4/actors/util/smoothing" 34 ) 35 36 type Runtime = runtime.Runtime 37 38 const ( 39 // The first 1000 actor-specific codes are left open for user error, i.e. things that might 40 // actually happen without programming error in the actor code. 41 //ErrToBeDetermined = exitcode.FirstActorSpecificExitCode + iota 42 43 // The following errors are particular cases of illegal state. 44 // They're not expected to ever happen, but if they do, distinguished codes can help us 45 // diagnose the problem. 46 ErrBalanceInvariantBroken = 1000 47 ) 48 49 type Actor struct{} 50 51 func (a Actor) Exports() []interface{} { 52 return []interface{}{ 53 builtin.MethodConstructor: a.Constructor, 54 2: a.ControlAddresses, 55 3: a.ChangeWorkerAddress, 56 4: a.ChangePeerID, 57 5: a.SubmitWindowedPoSt, 58 6: a.PreCommitSector, 59 7: a.ProveCommitSector, 60 8: a.ExtendSectorExpiration, 61 9: a.TerminateSectors, 62 10: a.DeclareFaults, 63 11: a.DeclareFaultsRecovered, 64 12: a.OnDeferredCronEvent, 65 13: a.CheckSectorProven, 66 14: a.ApplyRewards, 67 15: a.ReportConsensusFault, 68 16: a.WithdrawBalance, 69 17: a.ConfirmSectorProofsValid, 70 18: a.ChangeMultiaddrs, 71 19: a.CompactPartitions, 72 20: a.CompactSectorNumbers, 73 21: a.ConfirmUpdateWorkerKey, 74 22: a.RepayDebt, 75 23: a.ChangeOwnerAddress, 76 24: a.DisputeWindowedPoSt, 77 } 78 } 79 80 func (a Actor) Code() cid.Cid { 81 return builtin.StorageMinerActorCodeID 82 } 83 84 func (a Actor) State() cbor.Er { 85 return new(State) 86 } 87 88 var _ runtime.VMActor = Actor{} 89 90 ///////////////// 91 // Constructor // 92 ///////////////// 93 94 // Storage miner actors are created exclusively by the storage power actor. In order to break a circular dependency 95 // between the two, the construction parameters are defined in the power actor. 96 type ConstructorParams = power.MinerConstructorParams 97 98 func (a Actor) Constructor(rt Runtime, params *ConstructorParams) *abi.EmptyValue { 99 rt.ValidateImmediateCallerIs(builtin.InitActorAddr) 100 101 checkControlAddresses(rt, params.ControlAddrs) 102 checkPeerInfo(rt, params.PeerId, params.Multiaddrs) 103 104 owner := resolveControlAddress(rt, params.OwnerAddr) 105 worker := resolveWorkerAddress(rt, params.WorkerAddr) 106 controlAddrs := make([]addr.Address, 0, len(params.ControlAddrs)) 107 for _, ca := range params.ControlAddrs { 108 resolved := resolveControlAddress(rt, ca) 109 controlAddrs = append(controlAddrs, resolved) 110 } 111 112 currEpoch := rt.CurrEpoch() 113 offset, err := assignProvingPeriodOffset(rt.Receiver(), currEpoch, rt.HashBlake2b) 114 builtin.RequireNoErr(rt, err, exitcode.ErrSerialization, "failed to assign proving period offset") 115 periodStart := currentProvingPeriodStart(currEpoch, offset) 116 builtin.RequireState(rt, periodStart <= currEpoch, "computed proving period start %d after current epoch %d", periodStart, currEpoch) 117 deadlineIndex := currentDeadlineIndex(currEpoch, periodStart) 118 builtin.RequireState(rt, deadlineIndex < WPoStPeriodDeadlines, "computed proving deadline index %d invalid", deadlineIndex) 119 120 info, err := ConstructMinerInfo(owner, worker, controlAddrs, params.PeerId, params.Multiaddrs, params.WindowPoStProofType) 121 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to construct initial miner info") 122 infoCid := rt.StorePut(info) 123 124 store := adt.AsStore(rt) 125 state, err := ConstructState(store, infoCid, periodStart, deadlineIndex) 126 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to construct state") 127 rt.StateCreate(state) 128 129 return nil 130 } 131 132 ///////////// 133 // Control // 134 ///////////// 135 136 // type GetControlAddressesReturn struct { 137 // Owner addr.Address 138 // Worker addr.Address 139 // ControlAddrs []addr.Address 140 // } 141 type GetControlAddressesReturn = miner2.GetControlAddressesReturn 142 143 func (a Actor) ControlAddresses(rt Runtime, _ *abi.EmptyValue) *GetControlAddressesReturn { 144 rt.ValidateImmediateCallerAcceptAny() 145 var st State 146 rt.StateReadonly(&st) 147 info := getMinerInfo(rt, &st) 148 return &GetControlAddressesReturn{ 149 Owner: info.Owner, 150 Worker: info.Worker, 151 ControlAddrs: info.ControlAddresses, 152 } 153 } 154 155 //type ChangeWorkerAddressParams struct { 156 // NewWorker addr.Address 157 // NewControlAddrs []addr.Address 158 //} 159 type ChangeWorkerAddressParams = miner0.ChangeWorkerAddressParams 160 161 // ChangeWorkerAddress will ALWAYS overwrite the existing control addresses with the control addresses passed in the params. 162 // If a nil addresses slice is passed, the control addresses will be cleared. 163 // A worker change will be scheduled if the worker passed in the params is different from the existing worker. 164 func (a Actor) ChangeWorkerAddress(rt Runtime, params *ChangeWorkerAddressParams) *abi.EmptyValue { 165 checkControlAddresses(rt, params.NewControlAddrs) 166 167 newWorker := resolveWorkerAddress(rt, params.NewWorker) 168 169 var controlAddrs []addr.Address 170 for _, ca := range params.NewControlAddrs { 171 resolved := resolveControlAddress(rt, ca) 172 controlAddrs = append(controlAddrs, resolved) 173 } 174 175 var st State 176 rt.StateTransaction(&st, func() { 177 info := getMinerInfo(rt, &st) 178 179 // Only the Owner is allowed to change the newWorker and control addresses. 180 rt.ValidateImmediateCallerIs(info.Owner) 181 182 // save the new control addresses 183 info.ControlAddresses = controlAddrs 184 185 // save newWorker addr key change request 186 if newWorker != info.Worker && info.PendingWorkerKey == nil { 187 info.PendingWorkerKey = &WorkerKeyChange{ 188 NewWorker: newWorker, 189 EffectiveAt: rt.CurrEpoch() + WorkerKeyChangeDelay, 190 } 191 } 192 193 err := st.SaveInfo(adt.AsStore(rt), info) 194 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "could not save miner info") 195 }) 196 197 return nil 198 } 199 200 // Triggers a worker address change if a change has been requested and its effective epoch has arrived. 201 func (a Actor) ConfirmUpdateWorkerKey(rt Runtime, params *abi.EmptyValue) *abi.EmptyValue { 202 var st State 203 rt.StateTransaction(&st, func() { 204 info := getMinerInfo(rt, &st) 205 206 // Only the Owner is allowed to change the newWorker. 207 rt.ValidateImmediateCallerIs(info.Owner) 208 209 processPendingWorker(info, rt, &st) 210 }) 211 212 return nil 213 } 214 215 // Proposes or confirms a change of owner address. 216 // If invoked by the current owner, proposes a new owner address for confirmation. If the proposed address is the 217 // current owner address, revokes any existing proposal. 218 // If invoked by the previously proposed address, with the same proposal, changes the current owner address to be 219 // that proposed address. 220 func (a Actor) ChangeOwnerAddress(rt Runtime, newAddress *addr.Address) *abi.EmptyValue { 221 if newAddress.Empty() { 222 rt.Abortf(exitcode.ErrIllegalArgument, "empty address") 223 } 224 if newAddress.Protocol() != addr.ID { 225 rt.Abortf(exitcode.ErrIllegalArgument, "owner address must be an ID address") 226 } 227 var st State 228 rt.StateTransaction(&st, func() { 229 info := getMinerInfo(rt, &st) 230 if rt.Caller() == info.Owner || info.PendingOwnerAddress == nil { 231 // Propose new address. 232 rt.ValidateImmediateCallerIs(info.Owner) 233 info.PendingOwnerAddress = newAddress 234 } else { // info.PendingOwnerAddress != nil 235 // Confirm the proposal. 236 // This validates that the operator can in fact use the proposed new address to sign messages. 237 rt.ValidateImmediateCallerIs(*info.PendingOwnerAddress) 238 if *newAddress != *info.PendingOwnerAddress { 239 rt.Abortf(exitcode.ErrIllegalArgument, "expected confirmation of %v, got %v", 240 info.PendingOwnerAddress, newAddress) 241 } 242 info.Owner = *info.PendingOwnerAddress 243 } 244 245 // Clear any resulting no-op change. 246 if info.PendingOwnerAddress != nil && *info.PendingOwnerAddress == info.Owner { 247 info.PendingOwnerAddress = nil 248 } 249 250 err := st.SaveInfo(adt.AsStore(rt), info) 251 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save miner info") 252 }) 253 return nil 254 } 255 256 //type ChangePeerIDParams struct { 257 // NewID abi.PeerID 258 //} 259 type ChangePeerIDParams = miner0.ChangePeerIDParams 260 261 func (a Actor) ChangePeerID(rt Runtime, params *ChangePeerIDParams) *abi.EmptyValue { 262 checkPeerInfo(rt, params.NewID, nil) 263 264 var st State 265 rt.StateTransaction(&st, func() { 266 info := getMinerInfo(rt, &st) 267 268 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 269 270 info.PeerId = params.NewID 271 err := st.SaveInfo(adt.AsStore(rt), info) 272 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "could not save miner info") 273 }) 274 return nil 275 } 276 277 //type ChangeMultiaddrsParams struct { 278 // NewMultiaddrs []abi.Multiaddrs 279 //} 280 type ChangeMultiaddrsParams = miner0.ChangeMultiaddrsParams 281 282 func (a Actor) ChangeMultiaddrs(rt Runtime, params *ChangeMultiaddrsParams) *abi.EmptyValue { 283 checkPeerInfo(rt, nil, params.NewMultiaddrs) 284 285 var st State 286 rt.StateTransaction(&st, func() { 287 info := getMinerInfo(rt, &st) 288 289 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 290 291 info.Multiaddrs = params.NewMultiaddrs 292 err := st.SaveInfo(adt.AsStore(rt), info) 293 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "could not save miner info") 294 }) 295 return nil 296 } 297 298 ////////////////// 299 // WindowedPoSt // 300 ////////////////// 301 302 //type PoStPartition struct { 303 // // Partitions are numbered per-deadline, from zero. 304 // Index uint64 305 // // Sectors skipped while proving that weren't already declared faulty 306 // Skipped bitfield.BitField 307 //} 308 type PoStPartition = miner0.PoStPartition 309 310 // Information submitted by a miner to provide a Window PoSt. 311 //type SubmitWindowedPoStParams struct { 312 // // The deadline index which the submission targets. 313 // Deadline uint64 314 // // The partitions being proven. 315 // Partitions []PoStPartition 316 // // Array of proofs, one per distinct registered proof type present in the sectors being proven. 317 // // In the usual case of a single proof type, this array will always have a single element (independent of number of partitions). 318 // Proofs []proof.PoStProof 319 // // The epoch at which these proofs is being committed to a particular chain. 320 // // NOTE: This field should be removed in the future. See 321 // // https://github.com/filecoin-project/specs-actors/issues/1094 322 // ChainCommitEpoch abi.ChainEpoch 323 // // The ticket randomness on the chain at the chain commit epoch. 324 // ChainCommitRand abi.Randomness 325 //} 326 type SubmitWindowedPoStParams = miner0.SubmitWindowedPoStParams 327 328 // Invoked by miner's worker address to submit their fallback post 329 func (a Actor) SubmitWindowedPoSt(rt Runtime, params *SubmitWindowedPoStParams) *abi.EmptyValue { 330 currEpoch := rt.CurrEpoch() 331 store := adt.AsStore(rt) 332 var st State 333 334 if params.Deadline >= WPoStPeriodDeadlines { 335 rt.Abortf(exitcode.ErrIllegalArgument, "invalid deadline %d of %d", params.Deadline, WPoStPeriodDeadlines) 336 } 337 // Technically, ChainCommitRand should be _exactly_ 32 bytes. However: 338 // 1. It's convenient to allow smaller slices when testing. 339 // 2. Nothing bad will happen if the caller provides too little randomness. 340 if len(params.ChainCommitRand) > abi.RandomnessLength { 341 rt.Abortf(exitcode.ErrIllegalArgument, "expected at most %d bytes of randomness, got %d", abi.RandomnessLength, len(params.ChainCommitRand)) 342 } 343 344 var postResult *PoStResult 345 var info *MinerInfo 346 rt.StateTransaction(&st, func() { 347 info = getMinerInfo(rt, &st) 348 maxProofSize, err := info.WindowPoStProofType.ProofSize() 349 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to determine max window post proof size") 350 351 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 352 353 // Verify that the miner has passed 0 or 1 proofs. If they've 354 // passed 1, verify that it's a good proof. 355 // 356 // This can be 0 if the miner isn't actually proving anything, 357 // just skipping all sectors. 358 if len(params.Proofs) != 1 { 359 rt.Abortf(exitcode.ErrIllegalArgument, "expected exactly one proof, got %d", len(params.Proofs)) 360 } 361 362 // Make sure the miner is using the correct proof type. 363 if params.Proofs[0].PoStProof != info.WindowPoStProofType { 364 rt.Abortf(exitcode.ErrIllegalArgument, "expected proof of type %s, got proof of type %s", info.WindowPoStProofType, params.Proofs[0]) 365 } 366 367 // Make sure the proof size doesn't exceed the max. We could probably check for an exact match, but this is safer. 368 if maxSize := maxProofSize * uint64(len(params.Partitions)); uint64(len(params.Proofs[0].ProofBytes)) > maxSize { 369 rt.Abortf(exitcode.ErrIllegalArgument, "expected proof to be smaller than %d bytes", maxSize) 370 } 371 372 // Validate that the miner didn't try to prove too many partitions at once. 373 submissionPartitionLimit := loadPartitionsSectorsMax(info.WindowPoStPartitionSectors) 374 if uint64(len(params.Partitions)) > submissionPartitionLimit { 375 rt.Abortf(exitcode.ErrIllegalArgument, "too many partitions %d, limit %d", len(params.Partitions), submissionPartitionLimit) 376 } 377 378 currDeadline := st.DeadlineInfo(currEpoch) 379 // Check that the miner state indicates that the current proving deadline has started. 380 // This should only fail if the cron actor wasn't invoked, and matters only in case that it hasn't been 381 // invoked for a whole proving period, and hence the missed PoSt submissions from the prior occurrence 382 // of this deadline haven't been processed yet. 383 if !currDeadline.IsOpen() { 384 rt.Abortf(exitcode.ErrIllegalState, "proving period %d not yet open at %d", currDeadline.PeriodStart, currEpoch) 385 } 386 387 // The miner may only submit a proof for the current deadline. 388 if params.Deadline != currDeadline.Index { 389 rt.Abortf(exitcode.ErrIllegalArgument, "invalid deadline %d at epoch %d, expected %d", 390 params.Deadline, currEpoch, currDeadline.Index) 391 } 392 393 // Verify that the PoSt was committed to the chain at most WPoStChallengeLookback+WPoStChallengeWindow in the past. 394 if params.ChainCommitEpoch < currDeadline.Challenge { 395 rt.Abortf(exitcode.ErrIllegalArgument, "expected chain commit epoch %d to be after %d", params.ChainCommitEpoch, currDeadline.Challenge) 396 } 397 if params.ChainCommitEpoch >= currEpoch { 398 rt.Abortf(exitcode.ErrIllegalArgument, "chain commit epoch %d must be less than the current epoch %d", params.ChainCommitEpoch, currEpoch) 399 } 400 // Verify the chain commit randomness. 401 commRand := rt.GetRandomnessFromTickets(crypto.DomainSeparationTag_PoStChainCommit, params.ChainCommitEpoch, nil) 402 if !bytes.Equal(commRand, params.ChainCommitRand) { 403 rt.Abortf(exitcode.ErrIllegalArgument, "post commit randomness mismatched") 404 } 405 406 sectors, err := LoadSectors(store, st.Sectors) 407 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors") 408 409 deadlines, err := st.LoadDeadlines(adt.AsStore(rt)) 410 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadlines") 411 412 deadline, err := deadlines.LoadDeadline(store, params.Deadline) 413 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadline %d", params.Deadline) 414 415 // Record proven sectors/partitions, returning updates to power and the final set of sectors 416 // proven/skipped. 417 // 418 // NOTE: This function does not actually check the proofs but does assume that they're correct. Instead, 419 // it snapshots the deadline's state and the submitted proofs at the end of the challenge window and 420 // allows third-parties to dispute these proofs. 421 // 422 // While we could perform _all_ operations at the end of challenge window, we do as we can here to avoid 423 // overloading cron. 424 faultExpiration := currDeadline.Last() + FaultMaxAge 425 postResult, err = deadline.RecordProvenSectors(store, sectors, info.SectorSize, QuantSpecForDeadline(currDeadline), faultExpiration, params.Partitions) 426 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to process post submission for deadline %d", params.Deadline) 427 428 // Make sure we actually proved something. 429 430 provenSectors, err := bitfield.SubtractBitField(postResult.Sectors, postResult.IgnoredSectors) 431 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to determine proven sectors for deadline %d", params.Deadline) 432 433 noSectors, err := provenSectors.IsEmpty() 434 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to determine if any sectors were proven", params.Deadline) 435 if noSectors { 436 // Abort verification if all sectors are (now) faults. There's nothing to prove. 437 // It's not rational for a miner to submit a Window PoSt marking *all* non-faulty sectors as skipped, 438 // since that will just cause them to pay a penalty at deadline end that would otherwise be zero 439 // if they had *not* declared them. 440 rt.Abortf(exitcode.ErrIllegalArgument, "cannot prove partitions with no active sectors") 441 } 442 443 // If we're not recovering power, record the proof for optimistic verification. 444 if postResult.RecoveredPower.IsZero() { 445 err = deadline.RecordPoStProofs(store, postResult.Partitions, params.Proofs) 446 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to record proof for optimistic verification", params.Deadline) 447 } else { 448 // otherwise, check the proof 449 sectorInfos, err := sectors.LoadForProof(postResult.Sectors, postResult.IgnoredSectors) 450 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors for post verification") 451 452 err = verifyWindowedPost(rt, currDeadline.Challenge, sectorInfos, params.Proofs) 453 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "window post failed") 454 } 455 456 err = deadlines.UpdateDeadline(store, params.Deadline, deadline) 457 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to update deadline %d", params.Deadline) 458 459 err = st.SaveDeadlines(store, deadlines) 460 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadlines") 461 }) 462 463 // Restore power for recovered sectors. Remove power for new faults. 464 // NOTE: It would be permissible to delay the power loss until the deadline closes, but that would require 465 // additional accounting state. 466 // https://github.com/filecoin-project/specs-actors/issues/414 467 requestUpdatePower(rt, postResult.PowerDelta) 468 469 rt.StateReadonly(&st) 470 err := st.CheckBalanceInvariants(rt.CurrentBalance()) 471 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 472 473 return nil 474 } 475 476 // type DisputeWindowedPoStParams struct { 477 // Deadline uint64 478 // PoStIndex uint64 // only one is allowed at a time to avoid loading too many sector infos. 479 // } 480 type DisputeWindowedPoStParams = miner3.DisputeWindowedPoStParams 481 482 func (a Actor) DisputeWindowedPoSt(rt Runtime, params *DisputeWindowedPoStParams) *abi.EmptyValue { 483 rt.ValidateImmediateCallerType(builtin.CallerTypesSignable...) 484 reporter := rt.Caller() 485 486 if params.Deadline >= WPoStPeriodDeadlines { 487 rt.Abortf(exitcode.ErrIllegalArgument, "invalid deadline %d of %d", params.Deadline, WPoStPeriodDeadlines) 488 } 489 490 currEpoch := rt.CurrEpoch() 491 492 // Note: these are going to be slightly inaccurate as time 493 // will have moved on from when the post was actually 494 // submitted. 495 // 496 // However, these are estimates _anyways_. 497 epochReward := requestCurrentEpochBlockReward(rt) 498 pwrTotal := requestCurrentTotalPower(rt) 499 500 toBurn := abi.NewTokenAmount(0) 501 toReward := abi.NewTokenAmount(0) 502 pledgeDelta := abi.NewTokenAmount(0) 503 powerDelta := NewPowerPairZero() 504 var st State 505 rt.StateTransaction(&st, func() { 506 dlInfo := st.DeadlineInfo(currEpoch) 507 if !deadlineAvailableForOptimisticPoStDispute(dlInfo.PeriodStart, params.Deadline, currEpoch) { 508 rt.Abortf(exitcode.ErrForbidden, "can only dispute window posts during the dispute window (%d epochs after the challenge window closes)", WPoStDisputeWindow) 509 } 510 511 info := getMinerInfo(rt, &st) 512 penalisedPower := NewPowerPairZero() 513 store := adt.AsStore(rt) 514 515 // Check proof 516 { 517 // Find the proving period start for the deadline in question. 518 ppStart := dlInfo.PeriodStart 519 if dlInfo.Index < params.Deadline { 520 ppStart -= WPoStProvingPeriod 521 } 522 targetDeadline := NewDeadlineInfo(ppStart, params.Deadline, currEpoch) 523 524 // Load the target deadline. 525 deadlinesCurrent, err := st.LoadDeadlines(store) 526 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadlines") 527 528 dlCurrent, err := deadlinesCurrent.LoadDeadline(store, params.Deadline) 529 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadline") 530 531 // Take the post from the snapshot for dispute. 532 // This operation REMOVES the PoSt from the snapshot so 533 // it can't be disputed again. If this method fails, 534 // this operation must be rolled back. 535 partitions, proofs, err := dlCurrent.TakePoStProofs(store, params.PoStIndex) 536 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load proof for dispute") 537 538 // Load the partition info we need for the dispute. 539 disputeInfo, err := dlCurrent.LoadPartitionsForDispute(store, partitions) 540 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load partition info for dispute") 541 // This includes power that is no longer active (e.g., due to sector terminations). 542 // It must only be used for penalty calculations, not power adjustments. 543 penalisedPower = disputeInfo.DisputedPower 544 545 // Load sectors for the dispute. 546 sectors, err := LoadSectors(store, st.Sectors) 547 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors array") 548 549 sectorInfos, err := sectors.LoadForProof(disputeInfo.AllSectorNos, disputeInfo.IgnoredSectorNos) 550 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors to dispute window post") 551 552 // Check proof, we fail if validation succeeds. 553 err = verifyWindowedPost(rt, targetDeadline.Challenge, sectorInfos, proofs) 554 if err == nil { 555 rt.Abortf(exitcode.ErrIllegalArgument, "failed to dispute valid post") 556 return 557 } 558 rt.Log(rtt.INFO, "successfully disputed: %s", err) 559 560 // Ok, now we record faults. This always works because 561 // we don't allow compaction/moving sectors during the 562 // challenge window. 563 // 564 // However, some of these sectors may have been 565 // terminated. That's fine, we'll skip them. 566 faultExpirationEpoch := targetDeadline.Last() + FaultMaxAge 567 powerDelta, err = dlCurrent.RecordFaults(store, sectors, info.SectorSize, QuantSpecForDeadline(targetDeadline), faultExpirationEpoch, disputeInfo.DisputedSectors) 568 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to declare faults") 569 570 err = deadlinesCurrent.UpdateDeadline(store, params.Deadline, dlCurrent) 571 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to update deadline %d", params.Deadline) 572 err = st.SaveDeadlines(store, deadlinesCurrent) 573 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadlines") 574 } 575 576 // Penalties. 577 { 578 // Calculate the base penalty. 579 penaltyBase := PledgePenaltyForInvalidWindowPoSt( 580 epochReward.ThisEpochRewardSmoothed, 581 pwrTotal.QualityAdjPowerSmoothed, 582 penalisedPower.QA, 583 ) 584 585 // Calculate the target reward. 586 rewardTarget := RewardForDisputedWindowPoSt(info.WindowPoStProofType, penalisedPower) 587 588 // Compute the target penalty by adding the 589 // base penalty to the target reward. We don't 590 // take reward out of the penalty as the miner 591 // could end up receiving a substantial 592 // portion of their fee back as a reward. 593 penaltyTarget := big.Add(penaltyBase, rewardTarget) 594 595 err := st.ApplyPenalty(penaltyTarget) 596 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to apply penalty") 597 penaltyFromVesting, penaltyFromBalance, err := st.RepayPartialDebtInPriorityOrder(store, currEpoch, rt.CurrentBalance()) 598 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to pay debt") 599 toBurn = big.Add(penaltyFromVesting, penaltyFromBalance) 600 601 // Now, move as much of the target reward as 602 // we can from the burn to the reward. 603 toReward = big.Min(toBurn, rewardTarget) 604 toBurn = big.Sub(toBurn, toReward) 605 606 pledgeDelta = penaltyFromVesting.Neg() 607 } 608 }) 609 610 requestUpdatePower(rt, powerDelta) 611 612 if !toReward.IsZero() { 613 // Try to send the reward to the reporter. 614 code := rt.Send(reporter, builtin.MethodSend, nil, toReward, &builtin.Discard{}) 615 616 // If we fail, log and burn the reward to make sure the balances remain correct. 617 if !code.IsSuccess() { 618 rt.Log(rtt.ERROR, "failed to send reward") 619 toBurn = big.Add(toBurn, toReward) 620 } 621 } 622 burnFunds(rt, toBurn) 623 notifyPledgeChanged(rt, pledgeDelta) 624 rt.StateReadonly(&st) 625 626 err := st.CheckBalanceInvariants(rt.CurrentBalance()) 627 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 628 return nil 629 } 630 631 /////////////////////// 632 // Sector Commitment // 633 /////////////////////// 634 635 //type SectorPreCommitInfo struct { 636 // SealProof abi.RegisteredSealProof 637 // SectorNumber abi.SectorNumber 638 // SealedCID cid.Cid `checked:"true"` // CommR 639 // SealRandEpoch abi.ChainEpoch 640 // DealIDs []abi.DealID 641 // Expiration abi.ChainEpoch 642 // ReplaceCapacity bool // Whether to replace a "committed capacity" no-deal sector (requires non-empty DealIDs) 643 // // The committed capacity sector to replace, and it's deadline/partition location 644 // ReplaceSectorDeadline uint64 645 // ReplaceSectorPartition uint64 646 // ReplaceSectorNumber abi.SectorNumber 647 //} 648 type PreCommitSectorParams = miner0.SectorPreCommitInfo 649 650 // Proposals must be posted on chain via sma.PublishStorageDeals before PreCommitSector. 651 // Optimization: PreCommitSector could contain a list of deals that are not published yet. 652 func (a Actor) PreCommitSector(rt Runtime, params *PreCommitSectorParams) *abi.EmptyValue { 653 nv := rt.NetworkVersion() 654 if !CanPreCommitSealProof(params.SealProof, nv) { 655 rt.Abortf(exitcode.ErrIllegalArgument, "unsupported seal proof type %v at network version %v", params.SealProof, nv) 656 } 657 if params.SectorNumber > abi.MaxSectorNumber { 658 rt.Abortf(exitcode.ErrIllegalArgument, "sector number %d out of range 0..(2^63-1)", params.SectorNumber) 659 } 660 if !params.SealedCID.Defined() { 661 rt.Abortf(exitcode.ErrIllegalArgument, "sealed CID undefined") 662 } 663 if params.SealedCID.Prefix() != SealedCIDPrefix { 664 rt.Abortf(exitcode.ErrIllegalArgument, "sealed CID had wrong prefix") 665 } 666 if params.SealRandEpoch >= rt.CurrEpoch() { 667 rt.Abortf(exitcode.ErrIllegalArgument, "seal challenge epoch %v must be before now %v", params.SealRandEpoch, rt.CurrEpoch()) 668 } 669 670 challengeEarliest := rt.CurrEpoch() - MaxPreCommitRandomnessLookback 671 if params.SealRandEpoch < challengeEarliest { 672 rt.Abortf(exitcode.ErrIllegalArgument, "seal challenge epoch %v too old, must be after %v", params.SealRandEpoch, challengeEarliest) 673 } 674 675 // Require sector lifetime meets minimum by assuming activation happens at last epoch permitted for seal proof. 676 // This could make sector maximum lifetime validation more lenient if the maximum sector limit isn't hit first. 677 maxActivation := rt.CurrEpoch() + MaxProveCommitDuration[params.SealProof] 678 validateExpiration(rt, maxActivation, params.Expiration, params.SealProof) 679 680 if params.ReplaceCapacity && len(params.DealIDs) == 0 { 681 rt.Abortf(exitcode.ErrIllegalArgument, "cannot replace sector without committing deals") 682 } 683 if params.ReplaceSectorDeadline >= WPoStPeriodDeadlines { 684 rt.Abortf(exitcode.ErrIllegalArgument, "invalid deadline %d", params.ReplaceSectorDeadline) 685 } 686 if params.ReplaceSectorNumber > abi.MaxSectorNumber { 687 rt.Abortf(exitcode.ErrIllegalArgument, "invalid sector number %d", params.ReplaceSectorNumber) 688 } 689 690 // gather information from other actors 691 692 rewardStats := requestCurrentEpochBlockReward(rt) 693 pwrTotal := requestCurrentTotalPower(rt) 694 dealWeights := requestDealWeights(rt, []market.SectorDeals{ 695 { 696 SectorExpiry: params.Expiration, 697 DealIDs: params.DealIDs, 698 }, 699 }) 700 if len(dealWeights.Sectors) == 0 { 701 rt.Abortf(exitcode.ErrIllegalState, "deal weight request returned no records") 702 } 703 dealWeight := dealWeights.Sectors[0] 704 705 store := adt.AsStore(rt) 706 var st State 707 var err error 708 newlyVested := big.Zero() 709 feeToBurn := abi.NewTokenAmount(0) 710 var needsCron bool 711 rt.StateTransaction(&st, func() { 712 // available balance already accounts for fee debt so it is correct to call 713 // this before RepayDebts. We would have to 714 // subtract fee debt explicitly if we called this after. 715 availableBalance, err := st.GetAvailableBalance(rt.CurrentBalance()) 716 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to calculate available balance") 717 feeToBurn = RepayDebtsOrAbort(rt, &st) 718 719 info := getMinerInfo(rt, &st) 720 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 721 722 if ConsensusFaultActive(info, rt.CurrEpoch()) { 723 rt.Abortf(exitcode.ErrForbidden, "precommit not allowed during active consensus fault") 724 } 725 726 // From network version 7, the pre-commit seal type must have the same Window PoSt proof type as the miner, 727 // rather than be exactly the same seal type. 728 // This permits a transition window from V1 to V1_1 seal types (which share Window PoSt proof type). 729 sectorWPoStProof, err := params.SealProof.RegisteredWindowPoStProof() 730 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed to lookup Window PoSt proof type for sector seal proof %d", params.SealProof) 731 if sectorWPoStProof != info.WindowPoStProofType { 732 rt.Abortf(exitcode.ErrIllegalArgument, "sector Window PoSt proof type %d must match miner Window PoSt proof type %d (seal proof type %d)", 733 sectorWPoStProof, info.WindowPoStProofType, params.SealProof) 734 } 735 736 dealCountMax := SectorDealsMax(info.SectorSize) 737 if uint64(len(params.DealIDs)) > dealCountMax { 738 rt.Abortf(exitcode.ErrIllegalArgument, "too many deals for sector %d > %d", len(params.DealIDs), dealCountMax) 739 } 740 741 // Ensure total deal space does not exceed sector size. 742 if dealWeight.DealSpace > uint64(info.SectorSize) { 743 rt.Abortf(exitcode.ErrIllegalArgument, "deals too large to fit in sector %d > %d", dealWeight.DealSpace, info.SectorSize) 744 } 745 746 err = st.AllocateSectorNumber(store, params.SectorNumber) 747 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to allocate sector id %d", params.SectorNumber) 748 749 // This sector check is redundant given the allocated sectors bitfield, but remains for safety. 750 sectorFound, err := st.HasSectorNo(store, params.SectorNumber) 751 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to check sector %v", params.SectorNumber) 752 if sectorFound { 753 rt.Abortf(exitcode.ErrIllegalState, "sector %v already committed", params.SectorNumber) 754 } 755 756 if params.ReplaceCapacity { 757 validateReplaceSector(rt, &st, store, params) 758 } 759 760 duration := params.Expiration - rt.CurrEpoch() 761 sectorWeight := QAPowerForWeight(info.SectorSize, duration, dealWeight.DealWeight, dealWeight.VerifiedDealWeight) 762 depositReq := PreCommitDepositForPower(rewardStats.ThisEpochRewardSmoothed, pwrTotal.QualityAdjPowerSmoothed, sectorWeight) 763 if availableBalance.LessThan(depositReq) { 764 rt.Abortf(exitcode.ErrInsufficientFunds, "insufficient funds for pre-commit deposit: %v", depositReq) 765 } 766 767 err = st.AddPreCommitDeposit(depositReq) 768 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add pre-commit deposit %v", depositReq) 769 770 if err := st.PutPrecommittedSector(store, &SectorPreCommitOnChainInfo{ 771 Info: SectorPreCommitInfo(*params), 772 PreCommitDeposit: depositReq, 773 PreCommitEpoch: rt.CurrEpoch(), 774 DealWeight: dealWeight.DealWeight, 775 VerifiedDealWeight: dealWeight.VerifiedDealWeight, 776 }); err != nil { 777 rt.Abortf(exitcode.ErrIllegalState, "failed to write pre-committed sector %v: %v", params.SectorNumber, err) 778 } 779 // add precommit expiry to the queue 780 msd, ok := MaxProveCommitDuration[params.SealProof] 781 if !ok { 782 rt.Abortf(exitcode.ErrIllegalArgument, "no max seal duration set for proof type: %d", params.SealProof) 783 } 784 // The +1 here is critical for the batch verification of proofs. Without it, if a proof arrived exactly on the 785 // due epoch, ProveCommitSector would accept it, then the expiry event would remove it, and then 786 // ConfirmSectorProofsValid would fail to find it. 787 expiryBound := rt.CurrEpoch() + msd + 1 788 789 err = st.AddPreCommitExpiry(store, expiryBound, params.SectorNumber) 790 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add pre-commit expiry to queue") 791 792 // activate miner cron 793 needsCron = !st.DeadlineCronActive 794 st.DeadlineCronActive = true 795 }) 796 burnFunds(rt, feeToBurn) 797 rt.StateReadonly(&st) 798 err = st.CheckBalanceInvariants(rt.CurrentBalance()) 799 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 800 if needsCron { 801 newDlInfo := st.DeadlineInfo(rt.CurrEpoch()) 802 enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ 803 EventType: CronEventProvingDeadline, 804 }) 805 } 806 807 notifyPledgeChanged(rt, newlyVested.Neg()) 808 809 return nil 810 } 811 812 //type ProveCommitSectorParams struct { 813 // SectorNumber abi.SectorNumber 814 // Proof []byte 815 //} 816 type ProveCommitSectorParams = miner0.ProveCommitSectorParams 817 818 // Checks state of the corresponding sector pre-commitment, then schedules the proof to be verified in bulk 819 // by the power actor. 820 // If valid, the power actor will call ConfirmSectorProofsValid at the end of the same epoch as this message. 821 func (a Actor) ProveCommitSector(rt Runtime, params *ProveCommitSectorParams) *abi.EmptyValue { 822 rt.ValidateImmediateCallerAcceptAny() 823 824 if params.SectorNumber > abi.MaxSectorNumber { 825 rt.Abortf(exitcode.ErrIllegalArgument, "sector number greater than maximum") 826 } 827 828 store := adt.AsStore(rt) 829 sectorNo := params.SectorNumber 830 831 var st State 832 rt.StateReadonly(&st) 833 834 precommit, found, err := st.GetPrecommittedSector(store, sectorNo) 835 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load pre-committed sector %v", sectorNo) 836 if !found { 837 rt.Abortf(exitcode.ErrNotFound, "no pre-committed sector %v", sectorNo) 838 } 839 840 maxProofSize, err := precommit.Info.SealProof.ProofSize() 841 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to determine max proof size for sector %v", sectorNo) 842 if uint64(len(params.Proof)) > maxProofSize { 843 rt.Abortf(exitcode.ErrIllegalArgument, "sector prove-commit proof of size %d exceeds max size of %d", 844 len(params.Proof), maxProofSize) 845 } 846 847 msd, ok := MaxProveCommitDuration[precommit.Info.SealProof] 848 if !ok { 849 rt.Abortf(exitcode.ErrIllegalState, "no max seal duration for proof type: %d", precommit.Info.SealProof) 850 } 851 proveCommitDue := precommit.PreCommitEpoch + msd 852 if rt.CurrEpoch() > proveCommitDue { 853 rt.Abortf(exitcode.ErrIllegalArgument, "commitment proof for %d too late at %d, due %d", sectorNo, rt.CurrEpoch(), proveCommitDue) 854 } 855 856 svi := getVerifyInfo(rt, &SealVerifyStuff{ 857 SealedCID: precommit.Info.SealedCID, 858 InteractiveEpoch: precommit.PreCommitEpoch + PreCommitChallengeDelay, 859 SealRandEpoch: precommit.Info.SealRandEpoch, 860 Proof: params.Proof, 861 DealIDs: precommit.Info.DealIDs, 862 SectorNumber: precommit.Info.SectorNumber, 863 RegisteredSealProof: precommit.Info.SealProof, 864 }) 865 866 code := rt.Send( 867 builtin.StoragePowerActorAddr, 868 builtin.MethodsPower.SubmitPoRepForBulkVerify, 869 svi, 870 abi.NewTokenAmount(0), 871 &builtin.Discard{}, 872 ) 873 builtin.RequireSuccess(rt, code, "failed to submit proof for bulk verification") 874 return nil 875 } 876 877 func (a Actor) ConfirmSectorProofsValid(rt Runtime, params *builtin.ConfirmSectorProofsParams) *abi.EmptyValue { 878 rt.ValidateImmediateCallerIs(builtin.StoragePowerActorAddr) 879 880 // This should be enforced by the power actor. We log here just in case 881 // something goes wrong. 882 if len(params.Sectors) > power.MaxMinerProveCommitsPerEpoch { 883 rt.Log(rtt.WARN, "confirmed more prove commits in an epoch than permitted: %d > %d", 884 len(params.Sectors), power.MaxMinerProveCommitsPerEpoch, 885 ) 886 } 887 888 // get network stats from other actors 889 rewardStats := requestCurrentEpochBlockReward(rt) 890 pwrTotal := requestCurrentTotalPower(rt) 891 circulatingSupply := rt.TotalFilCircSupply() 892 893 // 1. Activate deals, skipping pre-commits with invalid deals. 894 // - calls the market actor. 895 // 2. Reschedule replacement sector expiration. 896 // - loads and saves sectors 897 // - loads and saves deadlines/partitions 898 // 3. Add new sectors. 899 // - loads and saves sectors. 900 // - loads and saves deadlines/partitions 901 // 902 // Ideally, we'd combine some of these operations, but at least we have 903 // a constant number of them. 904 905 var st State 906 rt.StateReadonly(&st) 907 store := adt.AsStore(rt) 908 info := getMinerInfo(rt, &st) 909 910 // 911 // Activate storage deals. 912 // 913 914 // This skips missing pre-commits. 915 precommittedSectors, err := st.FindPrecommittedSectors(store, params.Sectors...) 916 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load pre-committed sectors") 917 918 // Committed-capacity sectors licensed for early removal by new sectors being proven. 919 replaceSectors := make(DeadlineSectorMap) 920 // Pre-commits for new sectors. 921 var preCommits []*SectorPreCommitOnChainInfo 922 for _, precommit := range precommittedSectors { 923 if len(precommit.Info.DealIDs) > 0 { 924 // Check (and activate) storage deals associated to sector. Abort if checks failed. 925 // TODO: we should batch these calls... 926 // https://github.com/filecoin-project/specs-actors/issues/474 927 code := rt.Send( 928 builtin.StorageMarketActorAddr, 929 builtin.MethodsMarket.ActivateDeals, 930 &market.ActivateDealsParams{ 931 DealIDs: precommit.Info.DealIDs, 932 SectorExpiry: precommit.Info.Expiration, 933 }, 934 abi.NewTokenAmount(0), 935 &builtin.Discard{}, 936 ) 937 938 if code != exitcode.Ok { 939 rt.Log(rtt.INFO, "failed to activate deals on sector %d, dropping from prove commit set", precommit.Info.SectorNumber) 940 continue 941 } 942 } 943 944 preCommits = append(preCommits, precommit) 945 946 if precommit.Info.ReplaceCapacity { 947 err := replaceSectors.AddValues( 948 precommit.Info.ReplaceSectorDeadline, 949 precommit.Info.ReplaceSectorPartition, 950 uint64(precommit.Info.ReplaceSectorNumber), 951 ) 952 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed to record sectors for replacement") 953 } 954 } 955 956 // When all prove commits have failed abort early 957 if len(preCommits) == 0 { 958 rt.Abortf(exitcode.ErrIllegalArgument, "all prove commits failed to validate") 959 } 960 961 totalPledge := big.Zero() 962 depositToUnlock := big.Zero() 963 newSectors := make([]*SectorOnChainInfo, 0) 964 newlyVested := big.Zero() 965 rt.StateTransaction(&st, func() { 966 // Schedule expiration for replaced sectors to the end of their next deadline window. 967 // They can't be removed right now because we want to challenge them immediately before termination. 968 replaced, err := st.RescheduleSectorExpirations(store, rt.CurrEpoch(), info.SectorSize, replaceSectors) 969 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to replace sector expirations") 970 replacedBySectorNumber := asMapBySectorNumber(replaced) 971 972 newSectorNos := make([]abi.SectorNumber, 0, len(preCommits)) 973 for _, precommit := range preCommits { 974 // compute initial pledge 975 activation := rt.CurrEpoch() 976 duration := precommit.Info.Expiration - activation 977 978 // This should have been caught in precommit, but don't let other sectors fail because of it. 979 if duration < MinSectorExpiration { 980 rt.Log(rtt.WARN, "precommit %d has lifetime %d less than minimum. ignoring", precommit.Info.SectorNumber, duration, MinSectorExpiration) 981 continue 982 } 983 984 pwr := QAPowerForWeight(info.SectorSize, duration, precommit.DealWeight, precommit.VerifiedDealWeight) 985 dayReward := ExpectedRewardForPower(rewardStats.ThisEpochRewardSmoothed, pwrTotal.QualityAdjPowerSmoothed, pwr, builtin.EpochsInDay) 986 // The storage pledge is recorded for use in computing the penalty if this sector is terminated 987 // before its declared expiration. 988 // It's not capped to 1 FIL, so can exceed the actual initial pledge requirement. 989 storagePledge := ExpectedRewardForPower(rewardStats.ThisEpochRewardSmoothed, pwrTotal.QualityAdjPowerSmoothed, pwr, InitialPledgeProjectionPeriod) 990 initialPledge := InitialPledgeForPower(pwr, rewardStats.ThisEpochBaselinePower, rewardStats.ThisEpochRewardSmoothed, 991 pwrTotal.QualityAdjPowerSmoothed, circulatingSupply) 992 993 // Lower-bound the pledge by that of the sector being replaced. 994 // Record the replaced age and reward rate for termination fee calculations. 995 replacedPledge, replacedAge, replacedDayReward := replacedSectorParameters(rt, precommit, replacedBySectorNumber) 996 initialPledge = big.Max(initialPledge, replacedPledge) 997 998 newSectorInfo := SectorOnChainInfo{ 999 SectorNumber: precommit.Info.SectorNumber, 1000 SealProof: precommit.Info.SealProof, 1001 SealedCID: precommit.Info.SealedCID, 1002 DealIDs: precommit.Info.DealIDs, 1003 Expiration: precommit.Info.Expiration, 1004 Activation: activation, 1005 DealWeight: precommit.DealWeight, 1006 VerifiedDealWeight: precommit.VerifiedDealWeight, 1007 InitialPledge: initialPledge, 1008 ExpectedDayReward: dayReward, 1009 ExpectedStoragePledge: storagePledge, 1010 ReplacedSectorAge: replacedAge, 1011 ReplacedDayReward: replacedDayReward, 1012 } 1013 1014 depositToUnlock = big.Add(depositToUnlock, precommit.PreCommitDeposit) 1015 newSectors = append(newSectors, &newSectorInfo) 1016 newSectorNos = append(newSectorNos, newSectorInfo.SectorNumber) 1017 totalPledge = big.Add(totalPledge, initialPledge) 1018 } 1019 1020 err = st.PutSectors(store, newSectors...) 1021 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to put new sectors") 1022 1023 err = st.DeletePrecommittedSectors(store, newSectorNos...) 1024 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to delete precommited sectors") 1025 1026 err = st.AssignSectorsToDeadlines(store, rt.CurrEpoch(), newSectors, info.WindowPoStPartitionSectors, info.SectorSize) 1027 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to assign new sectors to deadlines") 1028 1029 // Unlock deposit for successful proofs, make it available for lock-up as initial pledge. 1030 err = st.AddPreCommitDeposit(depositToUnlock.Neg()) 1031 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add pre-commit deposit %v", depositToUnlock.Neg()) 1032 1033 unlockedBalance, err := st.GetUnlockedBalance(rt.CurrentBalance()) 1034 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to calculate unlocked balance") 1035 if unlockedBalance.LessThan(totalPledge) { 1036 rt.Abortf(exitcode.ErrInsufficientFunds, "insufficient funds for aggregate initial pledge requirement %s, available: %s", totalPledge, unlockedBalance) 1037 } 1038 1039 err = st.AddInitialPledge(totalPledge) 1040 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add initial pledge %v", totalPledge) 1041 err = st.CheckBalanceInvariants(rt.CurrentBalance()) 1042 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 1043 }) 1044 1045 // Request pledge update for activated sector. 1046 notifyPledgeChanged(rt, big.Sub(totalPledge, newlyVested)) 1047 return nil 1048 } 1049 1050 //type CheckSectorProvenParams struct { 1051 // SectorNumber abi.SectorNumber 1052 //} 1053 type CheckSectorProvenParams = miner0.CheckSectorProvenParams 1054 1055 func (a Actor) CheckSectorProven(rt Runtime, params *CheckSectorProvenParams) *abi.EmptyValue { 1056 rt.ValidateImmediateCallerAcceptAny() 1057 1058 if params.SectorNumber > abi.MaxSectorNumber { 1059 rt.Abortf(exitcode.ErrIllegalArgument, "sector number out of range") 1060 } 1061 1062 var st State 1063 rt.StateReadonly(&st) 1064 store := adt.AsStore(rt) 1065 sectorNo := params.SectorNumber 1066 1067 if _, found, err := st.GetSector(store, sectorNo); err != nil { 1068 rt.Abortf(exitcode.ErrIllegalState, "failed to load proven sector %v", sectorNo) 1069 } else if !found { 1070 rt.Abortf(exitcode.ErrNotFound, "sector %v not proven", sectorNo) 1071 } 1072 return nil 1073 } 1074 1075 ///////////////////////// 1076 // Sector Modification // 1077 ///////////////////////// 1078 1079 //type ExtendSectorExpirationParams struct { 1080 // Extensions []ExpirationExtension 1081 //} 1082 type ExtendSectorExpirationParams = miner0.ExtendSectorExpirationParams 1083 1084 //type ExpirationExtension struct { 1085 // Deadline uint64 1086 // Partition uint64 1087 // Sectors bitfield.BitField 1088 // NewExpiration abi.ChainEpoch 1089 //} 1090 type ExpirationExtension = miner0.ExpirationExtension 1091 1092 // Changes the expiration epoch for a sector to a new, later one. 1093 // The sector must not be terminated or faulty. 1094 // The sector's power is recomputed for the new expiration. 1095 func (a Actor) ExtendSectorExpiration(rt Runtime, params *ExtendSectorExpirationParams) *abi.EmptyValue { 1096 if uint64(len(params.Extensions)) > DeclarationsMax { 1097 rt.Abortf(exitcode.ErrIllegalArgument, "too many declarations %d, max %d", len(params.Extensions), DeclarationsMax) 1098 } 1099 1100 // limit the number of sectors declared at once 1101 // https://github.com/filecoin-project/specs-actors/issues/416 1102 var sectorCount uint64 1103 for _, decl := range params.Extensions { 1104 if decl.Deadline >= WPoStPeriodDeadlines { 1105 rt.Abortf(exitcode.ErrIllegalArgument, "deadline %d not in range 0..%d", decl.Deadline, WPoStPeriodDeadlines) 1106 } 1107 count, err := decl.Sectors.Count() 1108 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, 1109 "failed to count sectors for deadline %d, partition %d", 1110 decl.Deadline, decl.Partition, 1111 ) 1112 if sectorCount > math.MaxUint64-count { 1113 rt.Abortf(exitcode.ErrIllegalArgument, "sector bitfield integer overflow") 1114 } 1115 sectorCount += count 1116 } 1117 if sectorCount > AddressedSectorsMax { 1118 rt.Abortf(exitcode.ErrIllegalArgument, 1119 "too many sectors for declaration %d, max %d", 1120 sectorCount, AddressedSectorsMax, 1121 ) 1122 } 1123 1124 currEpoch := rt.CurrEpoch() 1125 1126 powerDelta := NewPowerPairZero() 1127 pledgeDelta := big.Zero() 1128 store := adt.AsStore(rt) 1129 var st State 1130 rt.StateTransaction(&st, func() { 1131 info := getMinerInfo(rt, &st) 1132 1133 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 1134 1135 deadlines, err := st.LoadDeadlines(adt.AsStore(rt)) 1136 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadlines") 1137 1138 // Group declarations by deadline, and remember iteration order. 1139 // This should be merged with the iteration outside the state transaction. 1140 declsByDeadline := map[uint64][]*ExpirationExtension{} 1141 var deadlinesToLoad []uint64 1142 for i := range params.Extensions { 1143 // Take a pointer to the value inside the slice, don't 1144 // take a reference to the temporary loop variable as it 1145 // will be overwritten every iteration. 1146 decl := ¶ms.Extensions[i] 1147 if _, ok := declsByDeadline[decl.Deadline]; !ok { 1148 deadlinesToLoad = append(deadlinesToLoad, decl.Deadline) 1149 } 1150 declsByDeadline[decl.Deadline] = append(declsByDeadline[decl.Deadline], decl) 1151 } 1152 1153 sectors, err := LoadSectors(store, st.Sectors) 1154 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors array") 1155 1156 for _, dlIdx := range deadlinesToLoad { 1157 deadline, err := deadlines.LoadDeadline(store, dlIdx) 1158 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadline %d", dlIdx) 1159 1160 partitions, err := deadline.PartitionsArray(store) 1161 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load partitions for deadline %d", dlIdx) 1162 1163 quant := st.QuantSpecForDeadline(dlIdx) 1164 1165 // Group modified partitions by epoch to which they are extended. Duplicates are ok. 1166 partitionsByNewEpoch := map[abi.ChainEpoch][]uint64{} 1167 // Remember iteration order of epochs. 1168 var epochsToReschedule []abi.ChainEpoch 1169 1170 for _, decl := range declsByDeadline[dlIdx] { 1171 var partition Partition 1172 found, err := partitions.Get(decl.Partition, &partition) 1173 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadline %v partition %v", dlIdx, decl.Partition) 1174 if !found { 1175 rt.Abortf(exitcode.ErrNotFound, "no such deadline %v partition %v", dlIdx, decl.Partition) 1176 } 1177 1178 oldSectors, err := sectors.Load(decl.Sectors) 1179 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors in deadline %v partition %v", dlIdx, decl.Partition) 1180 newSectors := make([]*SectorOnChainInfo, len(oldSectors)) 1181 for i, sector := range oldSectors { 1182 if !CanExtendSealProofType(sector.SealProof, rt.NetworkVersion()) { 1183 rt.Abortf(exitcode.ErrForbidden, "cannot extend expiration for sector %v with unsupported seal type %v", 1184 sector.SectorNumber, sector.SealProof) 1185 } 1186 // This can happen if the sector should have already expired, but hasn't 1187 // because the end of its deadline hasn't passed yet. 1188 if sector.Expiration < currEpoch { 1189 rt.Abortf(exitcode.ErrForbidden, "cannot extend expiration for expired sector %v, expired at %d, now %d", 1190 sector.SectorNumber, 1191 sector.Expiration, 1192 currEpoch, 1193 ) 1194 } 1195 if decl.NewExpiration < sector.Expiration { 1196 rt.Abortf(exitcode.ErrIllegalArgument, "cannot reduce sector %v's expiration to %d from %d", 1197 sector.SectorNumber, decl.NewExpiration, sector.Expiration) 1198 } 1199 validateExpiration(rt, sector.Activation, decl.NewExpiration, sector.SealProof) 1200 1201 newSector := *sector 1202 newSector.Expiration = decl.NewExpiration 1203 1204 newSectors[i] = &newSector 1205 } 1206 1207 // Overwrite sector infos. 1208 err = sectors.Store(newSectors...) 1209 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to update sectors %v", decl.Sectors) 1210 1211 // Remove old sectors from partition and assign new sectors. 1212 partitionPowerDelta, partitionPledgeDelta, err := partition.ReplaceSectors(store, oldSectors, newSectors, info.SectorSize, quant) 1213 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to replace sector expirations at deadline %v partition %v", dlIdx, decl.Partition) 1214 1215 powerDelta = powerDelta.Add(partitionPowerDelta) 1216 pledgeDelta = big.Add(pledgeDelta, partitionPledgeDelta) // expected to be zero, see note below. 1217 1218 err = partitions.Set(decl.Partition, &partition) 1219 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadline %v partition %v", dlIdx, decl.Partition) 1220 1221 // Record the new partition expiration epoch for setting outside this loop over declarations. 1222 prevEpochPartitions, ok := partitionsByNewEpoch[decl.NewExpiration] 1223 partitionsByNewEpoch[decl.NewExpiration] = append(prevEpochPartitions, decl.Partition) 1224 if !ok { 1225 epochsToReschedule = append(epochsToReschedule, decl.NewExpiration) 1226 } 1227 } 1228 1229 deadline.Partitions, err = partitions.Root() 1230 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save partitions for deadline %d", dlIdx) 1231 1232 // Record partitions in deadline expiration queue 1233 for _, epoch := range epochsToReschedule { 1234 pIdxs := partitionsByNewEpoch[epoch] 1235 err := deadline.AddExpirationPartitions(store, epoch, pIdxs, quant) 1236 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add expiration partitions to deadline %v epoch %v: %v", 1237 dlIdx, epoch, pIdxs) 1238 } 1239 1240 err = deadlines.UpdateDeadline(store, dlIdx, deadline) 1241 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadline %d", dlIdx) 1242 } 1243 1244 st.Sectors, err = sectors.Root() 1245 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save sectors") 1246 1247 err = st.SaveDeadlines(store, deadlines) 1248 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadlines") 1249 }) 1250 1251 requestUpdatePower(rt, powerDelta) 1252 // Note: the pledge delta is expected to be zero, since pledge is not re-calculated for the extension. 1253 // But in case that ever changes, we can do the right thing here. 1254 notifyPledgeChanged(rt, pledgeDelta) 1255 return nil 1256 } 1257 1258 //type TerminateSectorsParams struct { 1259 // Terminations []TerminationDeclaration 1260 //} 1261 type TerminateSectorsParams = miner0.TerminateSectorsParams 1262 1263 //type TerminationDeclaration struct { 1264 // Deadline uint64 1265 // Partition uint64 1266 // Sectors bitfield.BitField 1267 //} 1268 type TerminationDeclaration = miner0.TerminationDeclaration 1269 1270 //type TerminateSectorsReturn struct { 1271 // // Set to true if all early termination work has been completed. When 1272 // // false, the miner may choose to repeatedly invoke TerminateSectors 1273 // // with no new sectors to process the remainder of the pending 1274 // // terminations. While pending terminations are outstanding, the miner 1275 // // will not be able to withdraw funds. 1276 // Done bool 1277 //} 1278 type TerminateSectorsReturn = miner0.TerminateSectorsReturn 1279 1280 // Marks some sectors as terminated at the present epoch, earlier than their 1281 // scheduled termination, and adds these sectors to the early termination queue. 1282 // This method then processes up to AddressedSectorsMax sectors and 1283 // AddressedPartitionsMax partitions from the early termination queue, 1284 // terminating deals, paying fines, and returning pledge collateral. While 1285 // sectors remain in this queue: 1286 // 1287 // 1. The miner will be unable to withdraw funds. 1288 // 2. The chain will process up to AddressedSectorsMax sectors and 1289 // AddressedPartitionsMax per epoch until the queue is empty. 1290 // 1291 // The sectors are immediately ignored for Window PoSt proofs, and should be 1292 // masked in the same way as faulty sectors. A miner may not terminate sectors in the 1293 // current deadline or the next deadline to be proven. 1294 // 1295 // This function may be invoked with no new sectors to explicitly process the 1296 // next batch of sectors. 1297 func (a Actor) TerminateSectors(rt Runtime, params *TerminateSectorsParams) *TerminateSectorsReturn { 1298 // Note: this cannot terminate pre-committed but un-proven sectors. 1299 // They must be allowed to expire (and deposit burnt). 1300 1301 if len(params.Terminations) > DeclarationsMax { 1302 rt.Abortf(exitcode.ErrIllegalArgument, 1303 "too many declarations when terminating sectors: %d > %d", 1304 len(params.Terminations), DeclarationsMax, 1305 ) 1306 } 1307 1308 toProcess := make(DeadlineSectorMap) 1309 for _, term := range params.Terminations { 1310 err := toProcess.Add(term.Deadline, term.Partition, term.Sectors) 1311 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, 1312 "failed to process deadline %d, partition %d", term.Deadline, term.Partition, 1313 ) 1314 } 1315 err := toProcess.Check(AddressedPartitionsMax, AddressedSectorsMax) 1316 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "cannot process requested parameters") 1317 1318 var hadEarlyTerminations bool 1319 var st State 1320 store := adt.AsStore(rt) 1321 currEpoch := rt.CurrEpoch() 1322 powerDelta := NewPowerPairZero() 1323 rt.StateTransaction(&st, func() { 1324 hadEarlyTerminations = havePendingEarlyTerminations(rt, &st) 1325 1326 info := getMinerInfo(rt, &st) 1327 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 1328 1329 deadlines, err := st.LoadDeadlines(adt.AsStore(rt)) 1330 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadlines") 1331 1332 // We're only reading the sectors, so there's no need to save this back. 1333 // However, we still want to avoid re-loading this array per-partition. 1334 sectors, err := LoadSectors(store, st.Sectors) 1335 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors") 1336 1337 err = toProcess.ForEach(func(dlIdx uint64, partitionSectors PartitionSectorMap) error { 1338 // If the deadline the current or next deadline to prove, don't allow terminating sectors. 1339 // We assume that deadlines are immutable when being proven. 1340 if !deadlineIsMutable(st.CurrentProvingPeriodStart(currEpoch), dlIdx, currEpoch) { 1341 rt.Abortf(exitcode.ErrIllegalArgument, "cannot terminate sectors in immutable deadline %d", dlIdx) 1342 } 1343 1344 quant := st.QuantSpecForDeadline(dlIdx) 1345 1346 deadline, err := deadlines.LoadDeadline(store, dlIdx) 1347 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadline %d", dlIdx) 1348 1349 removedPower, err := deadline.TerminateSectors(store, sectors, currEpoch, partitionSectors, info.SectorSize, quant) 1350 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to terminate sectors in deadline %d", dlIdx) 1351 1352 st.EarlyTerminations.Set(dlIdx) 1353 1354 powerDelta = powerDelta.Sub(removedPower) 1355 1356 err = deadlines.UpdateDeadline(store, dlIdx, deadline) 1357 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to update deadline %d", dlIdx) 1358 1359 return nil 1360 }) 1361 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to walk sectors") 1362 1363 err = st.SaveDeadlines(store, deadlines) 1364 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadlines") 1365 }) 1366 1367 // Now, try to process these sectors. 1368 more := processEarlyTerminations(rt) 1369 if more && !hadEarlyTerminations { 1370 // We have remaining terminations, and we didn't _previously_ 1371 // have early terminations to process, schedule a cron job. 1372 // NOTE: This isn't quite correct. If we repeatedly fill, empty, 1373 // fill, and empty, the queue, we'll keep scheduling new cron 1374 // jobs. However, in practice, that shouldn't be all that bad. 1375 scheduleEarlyTerminationWork(rt) 1376 } 1377 1378 rt.StateReadonly(&st) 1379 err = st.CheckBalanceInvariants(rt.CurrentBalance()) 1380 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 1381 1382 requestUpdatePower(rt, powerDelta) 1383 return &TerminateSectorsReturn{Done: !more} 1384 } 1385 1386 //////////// 1387 // Faults // 1388 //////////// 1389 1390 //type DeclareFaultsParams struct { 1391 // Faults []FaultDeclaration 1392 //} 1393 type DeclareFaultsParams = miner0.DeclareFaultsParams 1394 1395 //type FaultDeclaration struct { 1396 // // The deadline to which the faulty sectors are assigned, in range [0..WPoStPeriodDeadlines) 1397 // Deadline uint64 1398 // // Partition index within the deadline containing the faulty sectors. 1399 // Partition uint64 1400 // // Sectors in the partition being declared faulty. 1401 // Sectors bitfield.BitField 1402 //} 1403 type FaultDeclaration = miner0.FaultDeclaration 1404 1405 func (a Actor) DeclareFaults(rt Runtime, params *DeclareFaultsParams) *abi.EmptyValue { 1406 if len(params.Faults) > DeclarationsMax { 1407 rt.Abortf(exitcode.ErrIllegalArgument, 1408 "too many fault declarations for a single message: %d > %d", 1409 len(params.Faults), DeclarationsMax, 1410 ) 1411 } 1412 1413 toProcess := make(DeadlineSectorMap) 1414 for _, term := range params.Faults { 1415 err := toProcess.Add(term.Deadline, term.Partition, term.Sectors) 1416 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, 1417 "failed to process deadline %d, partition %d", term.Deadline, term.Partition, 1418 ) 1419 } 1420 err := toProcess.Check(AddressedPartitionsMax, AddressedSectorsMax) 1421 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "cannot process requested parameters") 1422 1423 store := adt.AsStore(rt) 1424 var st State 1425 powerDelta := NewPowerPairZero() 1426 rt.StateTransaction(&st, func() { 1427 info := getMinerInfo(rt, &st) 1428 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 1429 1430 deadlines, err := st.LoadDeadlines(store) 1431 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadlines") 1432 1433 sectors, err := LoadSectors(store, st.Sectors) 1434 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors array") 1435 1436 currEpoch := rt.CurrEpoch() 1437 err = toProcess.ForEach(func(dlIdx uint64, pm PartitionSectorMap) error { 1438 targetDeadline, err := declarationDeadlineInfo(st.CurrentProvingPeriodStart(currEpoch), dlIdx, currEpoch) 1439 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "invalid fault declaration deadline %d", dlIdx) 1440 1441 err = validateFRDeclarationDeadline(targetDeadline) 1442 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed fault declaration at deadline %d", dlIdx) 1443 1444 deadline, err := deadlines.LoadDeadline(store, dlIdx) 1445 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadline %d", dlIdx) 1446 1447 faultExpirationEpoch := targetDeadline.Last() + FaultMaxAge 1448 deadlinePowerDelta, err := deadline.RecordFaults(store, sectors, info.SectorSize, QuantSpecForDeadline(targetDeadline), faultExpirationEpoch, pm) 1449 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to declare faults for deadline %d", dlIdx) 1450 1451 err = deadlines.UpdateDeadline(store, dlIdx, deadline) 1452 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to store deadline %d partitions", dlIdx) 1453 1454 powerDelta = powerDelta.Add(deadlinePowerDelta) 1455 return nil 1456 }) 1457 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to iterate deadlines") 1458 1459 err = st.SaveDeadlines(store, deadlines) 1460 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadlines") 1461 }) 1462 1463 // Remove power for new faulty sectors. 1464 // NOTE: It would be permissible to delay the power loss until the deadline closes, but that would require 1465 // additional accounting state. 1466 // https://github.com/filecoin-project/specs-actors/issues/414 1467 requestUpdatePower(rt, powerDelta) 1468 1469 // Payment of penalty for declared faults is deferred to the deadline cron. 1470 return nil 1471 } 1472 1473 //type DeclareFaultsRecoveredParams struct { 1474 // Recoveries []RecoveryDeclaration 1475 //} 1476 type DeclareFaultsRecoveredParams = miner0.DeclareFaultsRecoveredParams 1477 1478 //type RecoveryDeclaration struct { 1479 // // The deadline to which the recovered sectors are assigned, in range [0..WPoStPeriodDeadlines) 1480 // Deadline uint64 1481 // // Partition index within the deadline containing the recovered sectors. 1482 // Partition uint64 1483 // // Sectors in the partition being declared recovered. 1484 // Sectors bitfield.BitField 1485 //} 1486 type RecoveryDeclaration = miner0.RecoveryDeclaration 1487 1488 func (a Actor) DeclareFaultsRecovered(rt Runtime, params *DeclareFaultsRecoveredParams) *abi.EmptyValue { 1489 if len(params.Recoveries) > DeclarationsMax { 1490 rt.Abortf(exitcode.ErrIllegalArgument, 1491 "too many recovery declarations for a single message: %d > %d", 1492 len(params.Recoveries), DeclarationsMax, 1493 ) 1494 } 1495 1496 toProcess := make(DeadlineSectorMap) 1497 for _, term := range params.Recoveries { 1498 err := toProcess.Add(term.Deadline, term.Partition, term.Sectors) 1499 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, 1500 "failed to process deadline %d, partition %d", term.Deadline, term.Partition, 1501 ) 1502 } 1503 err := toProcess.Check(AddressedPartitionsMax, AddressedSectorsMax) 1504 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "cannot process requested parameters") 1505 1506 store := adt.AsStore(rt) 1507 var st State 1508 feeToBurn := abi.NewTokenAmount(0) 1509 rt.StateTransaction(&st, func() { 1510 // Verify unlocked funds cover both InitialPledgeRequirement and FeeDebt 1511 // and repay fee debt now. 1512 feeToBurn = RepayDebtsOrAbort(rt, &st) 1513 1514 info := getMinerInfo(rt, &st) 1515 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 1516 if ConsensusFaultActive(info, rt.CurrEpoch()) { 1517 rt.Abortf(exitcode.ErrForbidden, "recovery not allowed during active consensus fault") 1518 } 1519 1520 deadlines, err := st.LoadDeadlines(adt.AsStore(rt)) 1521 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadlines") 1522 1523 sectors, err := LoadSectors(store, st.Sectors) 1524 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors array") 1525 1526 currEpoch := rt.CurrEpoch() 1527 err = toProcess.ForEach(func(dlIdx uint64, pm PartitionSectorMap) error { 1528 targetDeadline, err := declarationDeadlineInfo(st.CurrentProvingPeriodStart(currEpoch), dlIdx, currEpoch) 1529 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "invalid recovery declaration deadline %d", dlIdx) 1530 err = validateFRDeclarationDeadline(targetDeadline) 1531 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed recovery declaration at deadline %d", dlIdx) 1532 1533 deadline, err := deadlines.LoadDeadline(store, dlIdx) 1534 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadline %d", dlIdx) 1535 1536 err = deadline.DeclareFaultsRecovered(store, sectors, info.SectorSize, pm) 1537 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to declare recoveries for deadline %d", dlIdx) 1538 1539 err = deadlines.UpdateDeadline(store, dlIdx, deadline) 1540 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to store deadline %d", dlIdx) 1541 return nil 1542 }) 1543 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to walk sectors") 1544 1545 err = st.SaveDeadlines(store, deadlines) 1546 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadlines") 1547 }) 1548 1549 burnFunds(rt, feeToBurn) 1550 rt.StateReadonly(&st) 1551 err = st.CheckBalanceInvariants(rt.CurrentBalance()) 1552 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 1553 1554 // Power is not restored yet, but when the recovered sectors are successfully PoSted. 1555 return nil 1556 } 1557 1558 ///////////////// 1559 // Maintenance // 1560 ///////////////// 1561 1562 //type CompactPartitionsParams struct { 1563 // Deadline uint64 1564 // Partitions bitfield.BitField 1565 //} 1566 type CompactPartitionsParams = miner0.CompactPartitionsParams 1567 1568 // Compacts a number of partitions at one deadline by removing terminated sectors, re-ordering the remaining sectors, 1569 // and assigning them to new partitions so as to completely fill all but one partition with live sectors. 1570 // The addressed partitions are removed from the deadline, and new ones appended. 1571 // The final partition in the deadline is always included in the compaction, whether or not explicitly requested. 1572 // Removed sectors are removed from state entirely. 1573 // May not be invoked if the deadline has any un-processed early terminations. 1574 func (a Actor) CompactPartitions(rt Runtime, params *CompactPartitionsParams) *abi.EmptyValue { 1575 if params.Deadline >= WPoStPeriodDeadlines { 1576 rt.Abortf(exitcode.ErrIllegalArgument, "invalid deadline %v", params.Deadline) 1577 } 1578 1579 partitionCount, err := params.Partitions.Count() 1580 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed to parse partitions bitfield") 1581 1582 store := adt.AsStore(rt) 1583 var st State 1584 rt.StateTransaction(&st, func() { 1585 info := getMinerInfo(rt, &st) 1586 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 1587 1588 if !deadlineAvailableForCompaction(st.CurrentProvingPeriodStart(rt.CurrEpoch()), params.Deadline, rt.CurrEpoch()) { 1589 rt.Abortf(exitcode.ErrForbidden, 1590 "cannot compact deadline %d during its challenge window, or the prior challenge window, or before %d epochs have passed since its last challenge window ended", params.Deadline, WPoStDisputeWindow) 1591 } 1592 1593 submissionPartitionLimit := loadPartitionsSectorsMax(info.WindowPoStPartitionSectors) 1594 if partitionCount > submissionPartitionLimit { 1595 rt.Abortf(exitcode.ErrIllegalArgument, "too many partitions %d, limit %d", partitionCount, submissionPartitionLimit) 1596 } 1597 1598 quant := st.QuantSpecForDeadline(params.Deadline) 1599 1600 deadlines, err := st.LoadDeadlines(store) 1601 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadlines") 1602 1603 deadline, err := deadlines.LoadDeadline(store, params.Deadline) 1604 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deadline %d", params.Deadline) 1605 1606 live, dead, removedPower, err := deadline.RemovePartitions(store, params.Partitions, quant) 1607 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to remove partitions from deadline %d", params.Deadline) 1608 1609 err = st.DeleteSectors(store, dead) 1610 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to delete dead sectors") 1611 1612 sectors, err := st.LoadSectorInfos(store, live) 1613 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load moved sectors") 1614 1615 proven := true 1616 addedPower, err := deadline.AddSectors(store, info.WindowPoStPartitionSectors, proven, sectors, info.SectorSize, quant) 1617 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add back moved sectors") 1618 1619 if !removedPower.Equals(addedPower) { 1620 rt.Abortf(exitcode.ErrIllegalState, "power changed when compacting partitions: was %v, is now %v", removedPower, addedPower) 1621 } 1622 err = deadlines.UpdateDeadline(store, params.Deadline, deadline) 1623 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to update deadline %d", params.Deadline) 1624 1625 err = st.SaveDeadlines(store, deadlines) 1626 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to save deadlines") 1627 }) 1628 return nil 1629 } 1630 1631 //type CompactSectorNumbersParams struct { 1632 // MaskSectorNumbers bitfield.BitField 1633 //} 1634 type CompactSectorNumbersParams = miner0.CompactSectorNumbersParams 1635 1636 // Compacts sector number allocations to reduce the size of the allocated sector 1637 // number bitfield. 1638 // 1639 // When allocating sector numbers sequentially, or in sequential groups, this 1640 // bitfield should remain fairly small. However, if the bitfield grows large 1641 // enough such that PreCommitSector fails (or becomes expensive), this method 1642 // can be called to mask out (throw away) entire ranges of unused sector IDs. 1643 // For example, if sectors 1-99 and 101-200 have been allocated, sector number 1644 // 99 can be masked out to collapse these two ranges into one. 1645 func (a Actor) CompactSectorNumbers(rt Runtime, params *CompactSectorNumbersParams) *abi.EmptyValue { 1646 lastSectorNo, err := params.MaskSectorNumbers.Last() 1647 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "invalid mask bitfield") 1648 if lastSectorNo > abi.MaxSectorNumber { 1649 rt.Abortf(exitcode.ErrIllegalArgument, "masked sector number %d exceeded max sector number", lastSectorNo) 1650 } 1651 1652 store := adt.AsStore(rt) 1653 var st State 1654 rt.StateTransaction(&st, func() { 1655 info := getMinerInfo(rt, &st) 1656 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 1657 1658 err := st.MaskSectorNumbers(store, params.MaskSectorNumbers) 1659 1660 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to mask sector numbers") 1661 }) 1662 return nil 1663 } 1664 1665 /////////////////////// 1666 // Pledge Collateral // 1667 /////////////////////// 1668 1669 // Locks up some amount of the miner's unlocked balance (including funds received alongside the invoking message). 1670 func (a Actor) ApplyRewards(rt Runtime, params *builtin.ApplyRewardParams) *abi.EmptyValue { 1671 if params.Reward.Sign() < 0 { 1672 rt.Abortf(exitcode.ErrIllegalArgument, "cannot lock up a negative amount of funds") 1673 } 1674 if params.Penalty.Sign() < 0 { 1675 rt.Abortf(exitcode.ErrIllegalArgument, "cannot penalize a negative amount of funds") 1676 } 1677 1678 var st State 1679 pledgeDeltaTotal := big.Zero() 1680 toBurn := big.Zero() 1681 rt.StateTransaction(&st, func() { 1682 var err error 1683 store := adt.AsStore(rt) 1684 rt.ValidateImmediateCallerIs(builtin.RewardActorAddr) 1685 1686 rewardToLock, lockedRewardVestingSpec := LockedRewardFromReward(params.Reward) 1687 1688 // This ensures the miner has sufficient funds to lock up amountToLock. 1689 // This should always be true if reward actor sends reward funds with the message. 1690 unlockedBalance, err := st.GetUnlockedBalance(rt.CurrentBalance()) 1691 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to calculate unlocked balance") 1692 if unlockedBalance.LessThan(rewardToLock) { 1693 rt.Abortf(exitcode.ErrInsufficientFunds, "insufficient funds to lock, available: %v, requested: %v", unlockedBalance, rewardToLock) 1694 } 1695 1696 newlyVested, err := st.AddLockedFunds(store, rt.CurrEpoch(), rewardToLock, lockedRewardVestingSpec) 1697 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to lock funds in vesting table") 1698 pledgeDeltaTotal = big.Sub(pledgeDeltaTotal, newlyVested) 1699 pledgeDeltaTotal = big.Add(pledgeDeltaTotal, rewardToLock) 1700 1701 // If the miner incurred block mining penalties charge these to miner's fee debt 1702 err = st.ApplyPenalty(params.Penalty) 1703 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to apply penalty") 1704 // Attempt to repay all fee debt in this call. In most cases the miner will have enough 1705 // funds in the *reward alone* to cover the penalty. In the rare case a miner incurs more 1706 // penalty than it can pay for with reward and existing funds, it will go into fee debt. 1707 penaltyFromVesting, penaltyFromBalance, err := st.RepayPartialDebtInPriorityOrder(store, rt.CurrEpoch(), rt.CurrentBalance()) 1708 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to repay penalty") 1709 pledgeDeltaTotal = big.Sub(pledgeDeltaTotal, penaltyFromVesting) 1710 toBurn = big.Add(penaltyFromVesting, penaltyFromBalance) 1711 }) 1712 1713 notifyPledgeChanged(rt, pledgeDeltaTotal) 1714 burnFunds(rt, toBurn) 1715 rt.StateReadonly(&st) 1716 err := st.CheckBalanceInvariants(rt.CurrentBalance()) 1717 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 1718 1719 return nil 1720 } 1721 1722 //type ReportConsensusFaultParams struct { 1723 // BlockHeader1 []byte 1724 // BlockHeader2 []byte 1725 // BlockHeaderExtra []byte 1726 //} 1727 type ReportConsensusFaultParams = miner0.ReportConsensusFaultParams 1728 1729 func (a Actor) ReportConsensusFault(rt Runtime, params *ReportConsensusFaultParams) *abi.EmptyValue { 1730 // Note: only the first report of any fault is processed because it sets the 1731 // ConsensusFaultElapsed state variable to an epoch after the fault, and reports prior to 1732 // that epoch are no longer valid. 1733 rt.ValidateImmediateCallerType(builtin.CallerTypesSignable...) 1734 reporter := rt.Caller() 1735 1736 fault, err := rt.VerifyConsensusFault(params.BlockHeader1, params.BlockHeader2, params.BlockHeaderExtra) 1737 if err != nil { 1738 rt.Abortf(exitcode.ErrIllegalArgument, "fault not verified: %s", err) 1739 } 1740 if fault.Target != rt.Receiver() { 1741 rt.Abortf(exitcode.ErrIllegalArgument, "fault by %v reported to miner %v", fault.Target, rt.Receiver()) 1742 } 1743 1744 // Elapsed since the fault (i.e. since the higher of the two blocks) 1745 currEpoch := rt.CurrEpoch() 1746 faultAge := currEpoch - fault.Epoch 1747 if faultAge <= 0 { 1748 rt.Abortf(exitcode.ErrIllegalArgument, "invalid fault epoch %v ahead of current %v", fault.Epoch, currEpoch) 1749 } 1750 1751 // Penalize miner consensus fault fee 1752 // Give a portion of this to the reporter as reward 1753 var st State 1754 rewardStats := requestCurrentEpochBlockReward(rt) 1755 // The policy amounts we should burn and send to reporter 1756 // These may differ from actual funds send when miner goes into fee debt 1757 faultPenalty := ConsensusFaultPenalty(rewardStats.ThisEpochRewardSmoothed.Estimate()) 1758 slasherReward := RewardForConsensusSlashReport(faultAge, faultPenalty) 1759 pledgeDelta := big.Zero() 1760 1761 // The amounts actually sent to burnt funds and reporter 1762 burnAmount := big.Zero() 1763 rewardAmount := big.Zero() 1764 rt.StateTransaction(&st, func() { 1765 info := getMinerInfo(rt, &st) 1766 1767 // verify miner hasn't already been faulted 1768 if fault.Epoch < info.ConsensusFaultElapsed { 1769 rt.Abortf(exitcode.ErrForbidden, "fault epoch %d is too old, last exclusion period ended at %d", fault.Epoch, info.ConsensusFaultElapsed) 1770 } 1771 1772 err := st.ApplyPenalty(faultPenalty) 1773 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to apply penalty") 1774 1775 // Pay penalty 1776 penaltyFromVesting, penaltyFromBalance, err := st.RepayPartialDebtInPriorityOrder(adt.AsStore(rt), currEpoch, rt.CurrentBalance()) 1777 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to pay fees") 1778 // Burn the amount actually payable. Any difference in this and faultPenalty already recorded as FeeDebt 1779 burnAmount = big.Add(penaltyFromVesting, penaltyFromBalance) 1780 pledgeDelta = big.Add(pledgeDelta, penaltyFromVesting.Neg()) 1781 1782 // clamp reward at funds burnt 1783 rewardAmount = big.Min(burnAmount, slasherReward) 1784 // reduce burnAmount by rewardAmount 1785 burnAmount = big.Sub(burnAmount, rewardAmount) 1786 info.ConsensusFaultElapsed = currEpoch + ConsensusFaultIneligibilityDuration 1787 err = st.SaveInfo(adt.AsStore(rt), info) 1788 builtin.RequireNoErr(rt, err, exitcode.ErrSerialization, "failed to save miner info") 1789 }) 1790 code := rt.Send(reporter, builtin.MethodSend, nil, rewardAmount, &builtin.Discard{}) 1791 if !code.IsSuccess() { 1792 rt.Log(rtt.ERROR, "failed to send reward") 1793 } 1794 burnFunds(rt, burnAmount) 1795 notifyPledgeChanged(rt, pledgeDelta) 1796 1797 rt.StateReadonly(&st) 1798 err = st.CheckBalanceInvariants(rt.CurrentBalance()) 1799 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 1800 1801 return nil 1802 } 1803 1804 //type WithdrawBalanceParams struct { 1805 // AmountRequested abi.TokenAmount 1806 //} 1807 type WithdrawBalanceParams = miner0.WithdrawBalanceParams 1808 1809 func (a Actor) WithdrawBalance(rt Runtime, params *WithdrawBalanceParams) *abi.EmptyValue { 1810 var st State 1811 if params.AmountRequested.LessThan(big.Zero()) { 1812 rt.Abortf(exitcode.ErrIllegalArgument, "negative fund requested for withdrawal: %s", params.AmountRequested) 1813 } 1814 var info *MinerInfo 1815 newlyVested := big.Zero() 1816 feeToBurn := big.Zero() 1817 availableBalance := big.Zero() 1818 rt.StateTransaction(&st, func() { 1819 var err error 1820 info = getMinerInfo(rt, &st) 1821 // Only the owner is allowed to withdraw the balance as it belongs to/is controlled by the owner 1822 // and not the worker. 1823 rt.ValidateImmediateCallerIs(info.Owner) 1824 1825 // Ensure we don't have any pending terminations. 1826 if count, err := st.EarlyTerminations.Count(); err != nil { 1827 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to count early terminations") 1828 } else if count > 0 { 1829 rt.Abortf(exitcode.ErrForbidden, 1830 "cannot withdraw funds while %d deadlines have terminated sectors with outstanding fees", 1831 count, 1832 ) 1833 } 1834 1835 // Unlock vested funds so we can spend them. 1836 newlyVested, err = st.UnlockVestedFunds(adt.AsStore(rt), rt.CurrEpoch()) 1837 if err != nil { 1838 rt.Abortf(exitcode.ErrIllegalState, "failed to vest fund: %v", err) 1839 } 1840 // available balance already accounts for fee debt so it is correct to call 1841 // this before RepayDebts. We would have to 1842 // subtract fee debt explicitly if we called this after. 1843 availableBalance, err = st.GetAvailableBalance(rt.CurrentBalance()) 1844 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to calculate available balance") 1845 1846 // Verify unlocked funds cover both InitialPledgeRequirement and FeeDebt 1847 // and repay fee debt now. 1848 feeToBurn = RepayDebtsOrAbort(rt, &st) 1849 }) 1850 1851 amountWithdrawn := big.Min(availableBalance, params.AmountRequested) 1852 builtin.RequireState(rt, amountWithdrawn.GreaterThanEqual(big.Zero()), "negative amount to withdraw: %v", amountWithdrawn) 1853 builtin.RequireState(rt, amountWithdrawn.LessThanEqual(availableBalance), "amount to withdraw %v < available %v", amountWithdrawn, availableBalance) 1854 1855 if amountWithdrawn.GreaterThan(abi.NewTokenAmount(0)) { 1856 code := rt.Send(info.Owner, builtin.MethodSend, nil, amountWithdrawn, &builtin.Discard{}) 1857 builtin.RequireSuccess(rt, code, "failed to withdraw balance") 1858 } 1859 1860 burnFunds(rt, feeToBurn) 1861 1862 pledgeDelta := newlyVested.Neg() 1863 notifyPledgeChanged(rt, pledgeDelta) 1864 1865 err := st.CheckBalanceInvariants(rt.CurrentBalance()) 1866 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 1867 1868 return nil 1869 } 1870 1871 func (a Actor) RepayDebt(rt Runtime, _ *abi.EmptyValue) *abi.EmptyValue { 1872 var st State 1873 var fromVesting, fromBalance abi.TokenAmount 1874 rt.StateTransaction(&st, func() { 1875 var err error 1876 info := getMinerInfo(rt, &st) 1877 rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) 1878 1879 // Repay as much fee debt as possible. 1880 fromVesting, fromBalance, err = st.RepayPartialDebtInPriorityOrder(adt.AsStore(rt), rt.CurrEpoch(), rt.CurrentBalance()) 1881 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to unlock fee debt") 1882 }) 1883 1884 notifyPledgeChanged(rt, fromVesting.Neg()) 1885 burnFunds(rt, big.Sum(fromVesting, fromBalance)) 1886 err := st.CheckBalanceInvariants(rt.CurrentBalance()) 1887 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 1888 1889 return nil 1890 } 1891 1892 ////////// 1893 // Cron // 1894 ////////// 1895 1896 //type CronEventPayload struct { 1897 // EventType CronEventType 1898 //} 1899 type CronEventPayload = miner0.CronEventPayload 1900 1901 type CronEventType = miner0.CronEventType 1902 1903 const ( 1904 CronEventProvingDeadline = miner0.CronEventProvingDeadline 1905 CronEventProcessEarlyTerminations = miner0.CronEventProcessEarlyTerminations 1906 ) 1907 1908 func (a Actor) OnDeferredCronEvent(rt Runtime, payload *CronEventPayload) *abi.EmptyValue { 1909 rt.ValidateImmediateCallerIs(builtin.StoragePowerActorAddr) 1910 1911 switch payload.EventType { 1912 case CronEventProvingDeadline: 1913 handleProvingDeadline(rt) 1914 case CronEventProcessEarlyTerminations: 1915 if processEarlyTerminations(rt) { 1916 scheduleEarlyTerminationWork(rt) 1917 } 1918 } 1919 1920 var st State 1921 rt.StateReadonly(&st) 1922 err := st.CheckBalanceInvariants(rt.CurrentBalance()) 1923 builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") 1924 return nil 1925 } 1926 1927 //////////////////////////////////////////////////////////////////////////////// 1928 // Utility functions & helpers 1929 //////////////////////////////////////////////////////////////////////////////// 1930 1931 func processEarlyTerminations(rt Runtime) (more bool) { 1932 store := adt.AsStore(rt) 1933 1934 // TODO: We're using the current power+epoch reward. Technically, we 1935 // should use the power/reward at the time of termination. 1936 // https://github.com/filecoin-project/specs-actors/v4/pull/648 1937 rewardStats := requestCurrentEpochBlockReward(rt) 1938 pwrTotal := requestCurrentTotalPower(rt) 1939 1940 var ( 1941 result TerminationResult 1942 dealsToTerminate []market.OnMinerSectorsTerminateParams 1943 penalty = big.Zero() 1944 pledgeDelta = big.Zero() 1945 ) 1946 1947 var st State 1948 rt.StateTransaction(&st, func() { 1949 var err error 1950 result, more, err = st.PopEarlyTerminations(store, AddressedPartitionsMax, AddressedSectorsMax) 1951 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to pop early terminations") 1952 1953 // Nothing to do, don't waste any time. 1954 // This can happen if we end up processing early terminations 1955 // before the cron callback fires. 1956 if result.IsEmpty() { 1957 return 1958 } 1959 1960 info := getMinerInfo(rt, &st) 1961 1962 sectors, err := LoadSectors(store, st.Sectors) 1963 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors array") 1964 1965 totalInitialPledge := big.Zero() 1966 dealsToTerminate = make([]market.OnMinerSectorsTerminateParams, 0, len(result.Sectors)) 1967 err = result.ForEach(func(epoch abi.ChainEpoch, sectorNos bitfield.BitField) error { 1968 sectors, err := sectors.Load(sectorNos) 1969 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sector infos") 1970 params := market.OnMinerSectorsTerminateParams{ 1971 Epoch: epoch, 1972 DealIDs: make([]abi.DealID, 0, len(sectors)), // estimate ~one deal per sector. 1973 } 1974 for _, sector := range sectors { 1975 params.DealIDs = append(params.DealIDs, sector.DealIDs...) 1976 totalInitialPledge = big.Add(totalInitialPledge, sector.InitialPledge) 1977 } 1978 penalty = big.Add(penalty, terminationPenalty(info.SectorSize, epoch, 1979 rewardStats.ThisEpochRewardSmoothed, pwrTotal.QualityAdjPowerSmoothed, sectors)) 1980 dealsToTerminate = append(dealsToTerminate, params) 1981 1982 return nil 1983 }) 1984 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to process terminations") 1985 1986 // Pay penalty 1987 err = st.ApplyPenalty(penalty) 1988 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to apply penalty") 1989 1990 // Remove pledge requirement. 1991 err = st.AddInitialPledge(totalInitialPledge.Neg()) 1992 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add initial pledge %v", totalInitialPledge.Neg()) 1993 pledgeDelta = big.Sub(pledgeDelta, totalInitialPledge) 1994 1995 // Use unlocked pledge to pay down outstanding fee debt 1996 penaltyFromVesting, penaltyFromBalance, err := st.RepayPartialDebtInPriorityOrder(store, rt.CurrEpoch(), rt.CurrentBalance()) 1997 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to pay penalty") 1998 penalty = big.Add(penaltyFromVesting, penaltyFromBalance) 1999 pledgeDelta = big.Sub(pledgeDelta, penaltyFromVesting) 2000 }) 2001 2002 // We didn't do anything, abort. 2003 if result.IsEmpty() { 2004 return more 2005 } 2006 2007 // Burn penalty. 2008 burnFunds(rt, penalty) 2009 2010 // Return pledge. 2011 notifyPledgeChanged(rt, pledgeDelta) 2012 2013 // Terminate deals. 2014 for _, params := range dealsToTerminate { 2015 requestTerminateDeals(rt, params.Epoch, params.DealIDs) 2016 } 2017 2018 // reschedule cron worker, if necessary. 2019 return more 2020 } 2021 2022 // Invoked at the end of the last epoch for each proving deadline. 2023 func handleProvingDeadline(rt Runtime) { 2024 currEpoch := rt.CurrEpoch() 2025 store := adt.AsStore(rt) 2026 2027 epochReward := requestCurrentEpochBlockReward(rt) 2028 pwrTotal := requestCurrentTotalPower(rt) 2029 2030 hadEarlyTerminations := false 2031 2032 powerDeltaTotal := NewPowerPairZero() 2033 penaltyTotal := abi.NewTokenAmount(0) 2034 pledgeDeltaTotal := abi.NewTokenAmount(0) 2035 2036 var continueCron bool 2037 var st State 2038 rt.StateTransaction(&st, func() { 2039 { 2040 // Vest locked funds. 2041 // This happens first so that any subsequent penalties are taken 2042 // from locked vesting funds before funds free this epoch. 2043 newlyVested, err := st.UnlockVestedFunds(store, rt.CurrEpoch()) 2044 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to vest funds") 2045 pledgeDeltaTotal = big.Add(pledgeDeltaTotal, newlyVested.Neg()) 2046 } 2047 2048 { 2049 // Process pending worker change if any 2050 info := getMinerInfo(rt, &st) 2051 processPendingWorker(info, rt, &st) 2052 } 2053 2054 { 2055 depositToBurn, err := st.ExpirePreCommits(store, currEpoch) 2056 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to expire pre-committed sectors") 2057 2058 err = st.ApplyPenalty(depositToBurn) 2059 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to apply penalty") 2060 } 2061 2062 // Record whether or not we _had_ early terminations in the queue before this method. 2063 // That way, don't re-schedule a cron callback if one is already scheduled. 2064 hadEarlyTerminations = havePendingEarlyTerminations(rt, &st) 2065 2066 { 2067 result, err := st.AdvanceDeadline(store, currEpoch) 2068 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to advance deadline") 2069 2070 // Faults detected by this missed PoSt pay no penalty, but sectors that were already faulty 2071 // and remain faulty through this deadline pay the fault fee. 2072 penaltyTarget := PledgePenaltyForContinuedFault( 2073 epochReward.ThisEpochRewardSmoothed, 2074 pwrTotal.QualityAdjPowerSmoothed, 2075 result.PreviouslyFaultyPower.QA, 2076 ) 2077 2078 powerDeltaTotal = powerDeltaTotal.Add(result.PowerDelta) 2079 pledgeDeltaTotal = big.Add(pledgeDeltaTotal, result.PledgeDelta) 2080 2081 err = st.ApplyPenalty(penaltyTarget) 2082 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to apply penalty") 2083 2084 penaltyFromVesting, penaltyFromBalance, err := st.RepayPartialDebtInPriorityOrder(store, currEpoch, rt.CurrentBalance()) 2085 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to unlock penalty") 2086 penaltyTotal = big.Add(penaltyFromVesting, penaltyFromBalance) 2087 pledgeDeltaTotal = big.Sub(pledgeDeltaTotal, penaltyFromVesting) 2088 } 2089 2090 continueCron = st.ContinueDeadlineCron() 2091 if !continueCron { 2092 st.DeadlineCronActive = false 2093 } 2094 }) 2095 2096 // Remove power for new faults, and burn penalties. 2097 requestUpdatePower(rt, powerDeltaTotal) 2098 burnFunds(rt, penaltyTotal) 2099 notifyPledgeChanged(rt, pledgeDeltaTotal) 2100 2101 // Schedule cron callback for next deadline's last epoch. 2102 if continueCron { 2103 newDlInfo := st.DeadlineInfo(currEpoch + 1) 2104 enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ 2105 EventType: CronEventProvingDeadline, 2106 }) 2107 } else { 2108 rt.Log(rtt.INFO, "miner %s going inactive, deadline cron discontinued", rt.Receiver()) 2109 } 2110 2111 // Record whether or not we _have_ early terminations now. 2112 hasEarlyTerminations := havePendingEarlyTerminations(rt, &st) 2113 2114 // If we didn't have pending early terminations before, but we do now, 2115 // handle them at the next epoch. 2116 if !hadEarlyTerminations && hasEarlyTerminations { 2117 // First, try to process some of these terminations. 2118 if processEarlyTerminations(rt) { 2119 // If that doesn't work, just defer till the next epoch. 2120 scheduleEarlyTerminationWork(rt) 2121 } 2122 // Note: _don't_ process early terminations if we had a cron 2123 // callback already scheduled. In that case, we'll already have 2124 // processed AddressedSectorsMax terminations this epoch. 2125 } 2126 } 2127 2128 // Check expiry is exactly *the epoch before* the start of a proving period. 2129 func validateExpiration(rt Runtime, activation, expiration abi.ChainEpoch, sealProof abi.RegisteredSealProof) { 2130 // Expiration must be after activation. Check this explicitly to avoid an underflow below. 2131 if expiration <= activation { 2132 rt.Abortf(exitcode.ErrIllegalArgument, "sector expiration %v must be after activation (%v)", expiration, activation) 2133 } 2134 // expiration cannot be less than minimum after activation 2135 if expiration-activation < MinSectorExpiration { 2136 rt.Abortf(exitcode.ErrIllegalArgument, "invalid expiration %d, total sector lifetime (%d) must exceed %d after activation %d", 2137 expiration, expiration-activation, MinSectorExpiration, activation) 2138 } 2139 2140 // expiration cannot exceed MaxSectorExpirationExtension from now 2141 if expiration > rt.CurrEpoch()+MaxSectorExpirationExtension { 2142 rt.Abortf(exitcode.ErrIllegalArgument, "invalid expiration %d, cannot be more than %d past current epoch %d", 2143 expiration, MaxSectorExpirationExtension, rt.CurrEpoch()) 2144 } 2145 2146 // total sector lifetime cannot exceed SectorMaximumLifetime for the sector's seal proof 2147 maxLifetime, err := builtin.SealProofSectorMaximumLifetime(sealProof, rt.NetworkVersion()) 2148 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "unrecognized seal proof type %d", sealProof) 2149 if expiration-activation > maxLifetime { 2150 rt.Abortf(exitcode.ErrIllegalArgument, "invalid expiration %d, total sector lifetime (%d) cannot exceed %d after activation %d", 2151 expiration, expiration-activation, maxLifetime, activation) 2152 } 2153 } 2154 2155 func validateReplaceSector(rt Runtime, st *State, store adt.Store, params *PreCommitSectorParams) { 2156 replaceSector, found, err := st.GetSector(store, params.ReplaceSectorNumber) 2157 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sector %v", params.SectorNumber) 2158 if !found { 2159 rt.Abortf(exitcode.ErrNotFound, "no such sector %v to replace", params.ReplaceSectorNumber) 2160 } 2161 2162 if len(replaceSector.DealIDs) > 0 { 2163 rt.Abortf(exitcode.ErrIllegalArgument, "cannot replace sector %v which has deals", params.ReplaceSectorNumber) 2164 } 2165 // From network version 7, the new sector's seal type must have the same Window PoSt proof type as the one 2166 // being replaced, rather than be exactly the same seal type. 2167 // This permits replacing sectors with V1 seal types with V1_1 seal types. 2168 replaceWPoStProof, err := replaceSector.SealProof.RegisteredWindowPoStProof() 2169 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to lookup Window PoSt proof type for sector seal proof %d", replaceSector.SealProof) 2170 newWPoStProof, err := params.SealProof.RegisteredWindowPoStProof() 2171 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed to lookup Window PoSt proof type for new seal proof %d", params.SealProof) 2172 if newWPoStProof != replaceWPoStProof { 2173 rt.Abortf(exitcode.ErrIllegalArgument, "new sector window PoSt proof type %d must match replaced proof type %d (seal proof type %d)", 2174 newWPoStProof, replaceWPoStProof, params.SealProof) 2175 } 2176 if params.Expiration < replaceSector.Expiration { 2177 rt.Abortf(exitcode.ErrIllegalArgument, "cannot replace sector %v expiration %v with sooner expiration %v", 2178 params.ReplaceSectorNumber, replaceSector.Expiration, params.Expiration) 2179 } 2180 2181 err = st.CheckSectorHealth(store, params.ReplaceSectorDeadline, params.ReplaceSectorPartition, params.ReplaceSectorNumber) 2182 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to replace sector %v", params.ReplaceSectorNumber) 2183 } 2184 2185 func enrollCronEvent(rt Runtime, eventEpoch abi.ChainEpoch, callbackPayload *CronEventPayload) { 2186 payload := new(bytes.Buffer) 2187 err := callbackPayload.MarshalCBOR(payload) 2188 if err != nil { 2189 rt.Abortf(exitcode.ErrIllegalArgument, "failed to serialize payload: %v", err) 2190 } 2191 code := rt.Send( 2192 builtin.StoragePowerActorAddr, 2193 builtin.MethodsPower.EnrollCronEvent, 2194 &power.EnrollCronEventParams{ 2195 EventEpoch: eventEpoch, 2196 Payload: payload.Bytes(), 2197 }, 2198 abi.NewTokenAmount(0), 2199 &builtin.Discard{}, 2200 ) 2201 builtin.RequireSuccess(rt, code, "failed to enroll cron event") 2202 } 2203 2204 func requestUpdatePower(rt Runtime, delta PowerPair) { 2205 if delta.IsZero() { 2206 return 2207 } 2208 code := rt.Send( 2209 builtin.StoragePowerActorAddr, 2210 builtin.MethodsPower.UpdateClaimedPower, 2211 &power.UpdateClaimedPowerParams{ 2212 RawByteDelta: delta.Raw, 2213 QualityAdjustedDelta: delta.QA, 2214 }, 2215 abi.NewTokenAmount(0), 2216 &builtin.Discard{}, 2217 ) 2218 builtin.RequireSuccess(rt, code, "failed to update power with %v", delta) 2219 } 2220 2221 func requestTerminateDeals(rt Runtime, epoch abi.ChainEpoch, dealIDs []abi.DealID) { 2222 for len(dealIDs) > 0 { 2223 size := min64(cbg.MaxLength, uint64(len(dealIDs))) 2224 code := rt.Send( 2225 builtin.StorageMarketActorAddr, 2226 builtin.MethodsMarket.OnMinerSectorsTerminate, 2227 &market.OnMinerSectorsTerminateParams{ 2228 Epoch: epoch, 2229 DealIDs: dealIDs[:size], 2230 }, 2231 abi.NewTokenAmount(0), 2232 &builtin.Discard{}, 2233 ) 2234 builtin.RequireSuccess(rt, code, "failed to terminate deals, exit code %v", code) 2235 dealIDs = dealIDs[size:] 2236 } 2237 } 2238 2239 func scheduleEarlyTerminationWork(rt Runtime) { 2240 enrollCronEvent(rt, rt.CurrEpoch()+1, &CronEventPayload{ 2241 EventType: CronEventProcessEarlyTerminations, 2242 }) 2243 } 2244 2245 func havePendingEarlyTerminations(rt Runtime, st *State) bool { 2246 // Record this up-front 2247 noEarlyTerminations, err := st.EarlyTerminations.IsEmpty() 2248 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to count early terminations") 2249 return !noEarlyTerminations 2250 } 2251 2252 func verifyWindowedPost(rt Runtime, challengeEpoch abi.ChainEpoch, sectors []*SectorOnChainInfo, proofs []proof.PoStProof) error { 2253 minerActorID, err := addr.IDFromAddress(rt.Receiver()) 2254 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "runtime provided bad receiver address %v", rt.Receiver()) 2255 2256 // Regenerate challenge randomness, which must match that generated for the proof. 2257 var addrBuf bytes.Buffer 2258 receiver := rt.Receiver() 2259 err = receiver.MarshalCBOR(&addrBuf) 2260 builtin.RequireNoErr(rt, err, exitcode.ErrSerialization, "failed to marshal address for window post challenge") 2261 postRandomness := rt.GetRandomnessFromBeacon(crypto.DomainSeparationTag_WindowedPoStChallengeSeed, challengeEpoch, addrBuf.Bytes()) 2262 2263 sectorProofInfo := make([]proof.SectorInfo, len(sectors)) 2264 for i, s := range sectors { 2265 sectorProofInfo[i] = proof.SectorInfo{ 2266 SealProof: s.SealProof, 2267 SectorNumber: s.SectorNumber, 2268 SealedCID: s.SealedCID, 2269 } 2270 } 2271 2272 // Get public inputs 2273 pvInfo := proof.WindowPoStVerifyInfo{ 2274 Randomness: abi.PoStRandomness(postRandomness), 2275 Proofs: proofs, 2276 ChallengedSectors: sectorProofInfo, 2277 Prover: abi.ActorID(minerActorID), 2278 } 2279 2280 // Verify the PoSt Proof 2281 err = rt.VerifyPoSt(pvInfo) 2282 if err != nil { 2283 return fmt.Errorf("invalid PoSt %+v: %w", pvInfo, err) 2284 } 2285 return nil 2286 } 2287 2288 // SealVerifyParams is the structure of information that must be sent with a 2289 // message to commit a sector. Most of this information is not needed in the 2290 // state tree but will be verified in sm.CommitSector. See SealCommitment for 2291 // data stored on the state tree for each sector. 2292 type SealVerifyStuff struct { 2293 SealedCID cid.Cid // CommR 2294 InteractiveEpoch abi.ChainEpoch // Used to derive the interactive PoRep challenge. 2295 abi.RegisteredSealProof 2296 Proof []byte 2297 DealIDs []abi.DealID 2298 abi.SectorNumber 2299 SealRandEpoch abi.ChainEpoch // Used to tie the seal to a chain. 2300 } 2301 2302 func getVerifyInfo(rt Runtime, params *SealVerifyStuff) *proof.SealVerifyInfo { 2303 if rt.CurrEpoch() <= params.InteractiveEpoch { 2304 rt.Abortf(exitcode.ErrForbidden, "too early to prove sector") 2305 } 2306 2307 commD := requestUnsealedSectorCID(rt, params.RegisteredSealProof, params.DealIDs) 2308 2309 minerActorID, err := addr.IDFromAddress(rt.Receiver()) 2310 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "runtime provided non-ID receiver address %v", rt.Receiver()) 2311 2312 buf := new(bytes.Buffer) 2313 receiver := rt.Receiver() 2314 err = receiver.MarshalCBOR(buf) 2315 builtin.RequireNoErr(rt, err, exitcode.ErrSerialization, "failed to marshal address for seal verification challenge") 2316 2317 svInfoRandomness := rt.GetRandomnessFromTickets(crypto.DomainSeparationTag_SealRandomness, params.SealRandEpoch, buf.Bytes()) 2318 svInfoInteractiveRandomness := rt.GetRandomnessFromBeacon(crypto.DomainSeparationTag_InteractiveSealChallengeSeed, params.InteractiveEpoch, buf.Bytes()) 2319 2320 return &proof.SealVerifyInfo{ 2321 SealProof: params.RegisteredSealProof, 2322 SectorID: abi.SectorID{ 2323 Miner: abi.ActorID(minerActorID), 2324 Number: params.SectorNumber, 2325 }, 2326 DealIDs: params.DealIDs, 2327 InteractiveRandomness: abi.InteractiveSealRandomness(svInfoInteractiveRandomness), 2328 Proof: params.Proof, 2329 Randomness: abi.SealRandomness(svInfoRandomness), 2330 SealedCID: params.SealedCID, 2331 UnsealedCID: commD, 2332 } 2333 } 2334 2335 // Requests the storage market actor compute the unsealed sector CID from a sector's deals. 2336 func requestUnsealedSectorCID(rt Runtime, proofType abi.RegisteredSealProof, dealIDs []abi.DealID) cid.Cid { 2337 var unsealedCID cbg.CborCid 2338 code := rt.Send( 2339 builtin.StorageMarketActorAddr, 2340 builtin.MethodsMarket.ComputeDataCommitment, 2341 &market.ComputeDataCommitmentParams{ 2342 SectorType: proofType, 2343 DealIDs: dealIDs, 2344 }, 2345 abi.NewTokenAmount(0), 2346 &unsealedCID, 2347 ) 2348 builtin.RequireSuccess(rt, code, "failed request for unsealed sector CID for deals %v", dealIDs) 2349 return cid.Cid(unsealedCID) 2350 } 2351 2352 func requestDealWeights(rt Runtime, sectors []market.SectorDeals) *market.VerifyDealsForActivationReturn { 2353 // Short-circuit if there are no deals in any of the sectors. 2354 dealCount := 0 2355 for _, sector := range sectors { 2356 dealCount += len(sector.DealIDs) 2357 } 2358 if dealCount == 0 { 2359 emptyResult := &market.VerifyDealsForActivationReturn{ 2360 Sectors: make([]market.SectorWeights, len(sectors)), 2361 } 2362 for i := 0; i < len(sectors); i++ { 2363 emptyResult.Sectors[i] = market.SectorWeights{ 2364 DealSpace: 0, 2365 DealWeight: big.Zero(), 2366 VerifiedDealWeight: big.Zero(), 2367 } 2368 } 2369 return emptyResult 2370 } 2371 2372 var dealWeights market.VerifyDealsForActivationReturn 2373 code := rt.Send( 2374 builtin.StorageMarketActorAddr, 2375 builtin.MethodsMarket.VerifyDealsForActivation, 2376 &market.VerifyDealsForActivationParams{ 2377 Sectors: sectors, 2378 }, 2379 abi.NewTokenAmount(0), 2380 &dealWeights, 2381 ) 2382 builtin.RequireSuccess(rt, code, "failed to verify deals and get deal weight") 2383 return &dealWeights 2384 } 2385 2386 // Requests the current epoch target block reward from the reward actor. 2387 // return value includes reward, smoothed estimate of reward, and baseline power 2388 func requestCurrentEpochBlockReward(rt Runtime) reward.ThisEpochRewardReturn { 2389 var ret reward.ThisEpochRewardReturn 2390 code := rt.Send(builtin.RewardActorAddr, builtin.MethodsReward.ThisEpochReward, nil, big.Zero(), &ret) 2391 builtin.RequireSuccess(rt, code, "failed to check epoch baseline power") 2392 return ret 2393 } 2394 2395 // Requests the current network total power and pledge from the power actor. 2396 func requestCurrentTotalPower(rt Runtime) *power.CurrentTotalPowerReturn { 2397 var pwr power.CurrentTotalPowerReturn 2398 code := rt.Send(builtin.StoragePowerActorAddr, builtin.MethodsPower.CurrentTotalPower, nil, big.Zero(), &pwr) 2399 builtin.RequireSuccess(rt, code, "failed to check current power") 2400 return &pwr 2401 } 2402 2403 // Resolves an address to an ID address and verifies that it is address of an account or multisig actor. 2404 func resolveControlAddress(rt Runtime, raw addr.Address) addr.Address { 2405 resolved, ok := rt.ResolveAddress(raw) 2406 if !ok { 2407 rt.Abortf(exitcode.ErrIllegalArgument, "unable to resolve address %v", raw) 2408 } 2409 ownerCode, ok := rt.GetActorCodeCID(resolved) 2410 if !ok { 2411 rt.Abortf(exitcode.ErrIllegalArgument, "no code for address %v", resolved) 2412 } 2413 if !builtin.IsPrincipal(ownerCode) { 2414 rt.Abortf(exitcode.ErrIllegalArgument, "owner actor type must be a principal, was %v", ownerCode) 2415 } 2416 return resolved 2417 } 2418 2419 // Resolves an address to an ID address and verifies that it is address of an account actor with an associated BLS key. 2420 // The worker must be BLS since the worker key will be used alongside a BLS-VRF. 2421 func resolveWorkerAddress(rt Runtime, raw addr.Address) addr.Address { 2422 resolved, ok := rt.ResolveAddress(raw) 2423 if !ok { 2424 rt.Abortf(exitcode.ErrIllegalArgument, "unable to resolve address %v", raw) 2425 } 2426 workerCode, ok := rt.GetActorCodeCID(resolved) 2427 if !ok { 2428 rt.Abortf(exitcode.ErrIllegalArgument, "no code for address %v", resolved) 2429 } 2430 if workerCode != builtin.AccountActorCodeID { 2431 rt.Abortf(exitcode.ErrIllegalArgument, "worker actor type must be an account, was %v", workerCode) 2432 } 2433 2434 if raw.Protocol() != addr.BLS { 2435 var pubkey addr.Address 2436 code := rt.Send(resolved, builtin.MethodsAccount.PubkeyAddress, nil, big.Zero(), &pubkey) 2437 builtin.RequireSuccess(rt, code, "failed to fetch account pubkey from %v", resolved) 2438 if pubkey.Protocol() != addr.BLS { 2439 rt.Abortf(exitcode.ErrIllegalArgument, "worker account %v must have BLS pubkey, was %v", resolved, pubkey.Protocol()) 2440 } 2441 } 2442 return resolved 2443 } 2444 2445 func burnFunds(rt Runtime, amt abi.TokenAmount) { 2446 if amt.GreaterThan(big.Zero()) { 2447 code := rt.Send(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, amt, &builtin.Discard{}) 2448 builtin.RequireSuccess(rt, code, "failed to burn funds") 2449 } 2450 } 2451 2452 func notifyPledgeChanged(rt Runtime, pledgeDelta abi.TokenAmount) { 2453 if !pledgeDelta.IsZero() { 2454 code := rt.Send(builtin.StoragePowerActorAddr, builtin.MethodsPower.UpdatePledgeTotal, &pledgeDelta, big.Zero(), &builtin.Discard{}) 2455 builtin.RequireSuccess(rt, code, "failed to update total pledge") 2456 } 2457 } 2458 2459 // Assigns proving period offset randomly in the range [0, WPoStProvingPeriod) by hashing 2460 // the actor's address and current epoch. 2461 func assignProvingPeriodOffset(myAddr addr.Address, currEpoch abi.ChainEpoch, hash func(data []byte) [32]byte) (abi.ChainEpoch, error) { 2462 offsetSeed := bytes.Buffer{} 2463 err := myAddr.MarshalCBOR(&offsetSeed) 2464 if err != nil { 2465 return 0, fmt.Errorf("failed to serialize address: %w", err) 2466 } 2467 2468 err = binary.Write(&offsetSeed, binary.BigEndian, currEpoch) 2469 if err != nil { 2470 return 0, fmt.Errorf("failed to serialize epoch: %w", err) 2471 } 2472 2473 digest := hash(offsetSeed.Bytes()) 2474 var offset uint64 2475 err = binary.Read(bytes.NewBuffer(digest[:]), binary.BigEndian, &offset) 2476 if err != nil { 2477 return 0, fmt.Errorf("failed to interpret digest: %w", err) 2478 } 2479 2480 offset = offset % uint64(WPoStProvingPeriod) 2481 return abi.ChainEpoch(offset), nil 2482 } 2483 2484 // Computes the epoch at which a proving period should start such that it is greater than the current epoch, and 2485 // has a defined offset from being an exact multiple of WPoStProvingPeriod. 2486 // A miner is exempt from Winow PoSt until the first full proving period starts. 2487 func currentProvingPeriodStart(currEpoch abi.ChainEpoch, offset abi.ChainEpoch) abi.ChainEpoch { 2488 currModulus := currEpoch % WPoStProvingPeriod 2489 var periodProgress abi.ChainEpoch // How far ahead is currEpoch from previous offset boundary. 2490 if currModulus >= offset { 2491 periodProgress = currModulus - offset 2492 } else { 2493 periodProgress = WPoStProvingPeriod - (offset - currModulus) 2494 } 2495 2496 periodStart := currEpoch - periodProgress 2497 return periodStart 2498 } 2499 2500 // Computes the deadline index for the current epoch for a given period start. 2501 // currEpoch must be within the proving period that starts at provingPeriodStart to produce a valid index. 2502 func currentDeadlineIndex(currEpoch abi.ChainEpoch, periodStart abi.ChainEpoch) uint64 { 2503 return uint64((currEpoch - periodStart) / WPoStChallengeWindow) 2504 } 2505 2506 func asMapBySectorNumber(sectors []*SectorOnChainInfo) map[abi.SectorNumber]*SectorOnChainInfo { 2507 m := make(map[abi.SectorNumber]*SectorOnChainInfo, len(sectors)) 2508 for _, s := range sectors { 2509 m[s.SectorNumber] = s 2510 } 2511 return m 2512 } 2513 2514 func replacedSectorParameters(rt Runtime, precommit *SectorPreCommitOnChainInfo, 2515 replacedByNum map[abi.SectorNumber]*SectorOnChainInfo) (pledge abi.TokenAmount, age abi.ChainEpoch, dayReward big.Int) { 2516 if !precommit.Info.ReplaceCapacity { 2517 return big.Zero(), abi.ChainEpoch(0), big.Zero() 2518 } 2519 replaced, ok := replacedByNum[precommit.Info.ReplaceSectorNumber] 2520 if !ok { 2521 rt.Abortf(exitcode.ErrNotFound, "no such sector %v to replace", precommit.Info.ReplaceSectorNumber) 2522 } 2523 // The sector will actually be active for the period between activation and its next proving deadline, 2524 // but this covers the period for which we will be looking to the old sector for termination fees. 2525 return replaced.InitialPledge, 2526 maxEpoch(0, rt.CurrEpoch()-replaced.Activation), 2527 replaced.ExpectedDayReward 2528 } 2529 2530 // Update worker address with pending worker key if exists and delay has passed 2531 func processPendingWorker(info *MinerInfo, rt Runtime, st *State) { 2532 if info.PendingWorkerKey == nil || rt.CurrEpoch() < info.PendingWorkerKey.EffectiveAt { 2533 return 2534 } 2535 2536 info.Worker = info.PendingWorkerKey.NewWorker 2537 info.PendingWorkerKey = nil 2538 2539 err := st.SaveInfo(adt.AsStore(rt), info) 2540 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "could not save miner info") 2541 } 2542 2543 // Computes deadline information for a fault or recovery declaration. 2544 // If the deadline has not yet elapsed, the declaration is taken as being for the current proving period. 2545 // If the deadline has elapsed, it's instead taken as being for the next proving period after the current epoch. 2546 func declarationDeadlineInfo(periodStart abi.ChainEpoch, deadlineIdx uint64, currEpoch abi.ChainEpoch) (*dline.Info, error) { 2547 if deadlineIdx >= WPoStPeriodDeadlines { 2548 return nil, fmt.Errorf("invalid deadline %d, must be < %d", deadlineIdx, WPoStPeriodDeadlines) 2549 } 2550 2551 deadline := NewDeadlineInfo(periodStart, deadlineIdx, currEpoch).NextNotElapsed() 2552 return deadline, nil 2553 } 2554 2555 // Checks that a fault or recovery declaration at a specific deadline is outside the exclusion window for the deadline. 2556 func validateFRDeclarationDeadline(deadline *dline.Info) error { 2557 if deadline.FaultCutoffPassed() { 2558 return fmt.Errorf("late fault or recovery declaration at %v", deadline) 2559 } 2560 return nil 2561 } 2562 2563 // Validates that a partition contains the given sectors. 2564 func validatePartitionContainsSectors(partition *Partition, sectors bitfield.BitField) error { 2565 // Check that the declared sectors are actually assigned to the partition. 2566 contains, err := BitFieldContainsAll(partition.Sectors, sectors) 2567 if err != nil { 2568 return xerrors.Errorf("failed to check sectors: %w", err) 2569 } 2570 if !contains { 2571 return xerrors.Errorf("not all sectors are assigned to the partition") 2572 } 2573 return nil 2574 } 2575 2576 func terminationPenalty(sectorSize abi.SectorSize, currEpoch abi.ChainEpoch, 2577 rewardEstimate, networkQAPowerEstimate smoothing.FilterEstimate, sectors []*SectorOnChainInfo) abi.TokenAmount { 2578 totalFee := big.Zero() 2579 for _, s := range sectors { 2580 sectorPower := QAPowerForSector(sectorSize, s) 2581 fee := PledgePenaltyForTermination(s.ExpectedDayReward, currEpoch-s.Activation, s.ExpectedStoragePledge, 2582 networkQAPowerEstimate, sectorPower, rewardEstimate, s.ReplacedDayReward, s.ReplacedSectorAge) 2583 totalFee = big.Add(fee, totalFee) 2584 } 2585 return totalFee 2586 } 2587 2588 func PowerForSector(sectorSize abi.SectorSize, sector *SectorOnChainInfo) PowerPair { 2589 return PowerPair{ 2590 Raw: big.NewIntUnsigned(uint64(sectorSize)), 2591 QA: QAPowerForSector(sectorSize, sector), 2592 } 2593 } 2594 2595 // Returns the sum of the raw byte and quality-adjusted power for sectors. 2596 func PowerForSectors(ssize abi.SectorSize, sectors []*SectorOnChainInfo) PowerPair { 2597 qa := big.Zero() 2598 for _, s := range sectors { 2599 qa = big.Add(qa, QAPowerForSector(ssize, s)) 2600 } 2601 2602 return PowerPair{ 2603 Raw: big.Mul(big.NewIntUnsigned(uint64(ssize)), big.NewIntUnsigned(uint64(len(sectors)))), 2604 QA: qa, 2605 } 2606 } 2607 2608 func ConsensusFaultActive(info *MinerInfo, currEpoch abi.ChainEpoch) bool { 2609 // For penalization period to last for exactly finality epochs 2610 // consensus faults are active until currEpoch exceeds ConsensusFaultElapsed 2611 return currEpoch <= info.ConsensusFaultElapsed 2612 } 2613 2614 func getMinerInfo(rt Runtime, st *State) *MinerInfo { 2615 info, err := st.GetInfo(adt.AsStore(rt)) 2616 builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "could not read miner info") 2617 return info 2618 } 2619 2620 func min64(a, b uint64) uint64 { 2621 if a < b { 2622 return a 2623 } 2624 return b 2625 } 2626 2627 func max64(a, b uint64) uint64 { 2628 if a > b { 2629 return a 2630 } 2631 return b 2632 } 2633 2634 func minEpoch(a, b abi.ChainEpoch) abi.ChainEpoch { 2635 if a < b { 2636 return a 2637 } 2638 return b 2639 } 2640 2641 func maxEpoch(a, b abi.ChainEpoch) abi.ChainEpoch { 2642 if a > b { 2643 return a 2644 } 2645 return b 2646 } 2647 2648 func checkControlAddresses(rt Runtime, controlAddrs []addr.Address) { 2649 if len(controlAddrs) > MaxControlAddresses { 2650 rt.Abortf(exitcode.ErrIllegalArgument, "control addresses length %d exceeds max control addresses length %d", len(controlAddrs), MaxControlAddresses) 2651 } 2652 } 2653 2654 func checkPeerInfo(rt Runtime, peerID abi.PeerID, multiaddrs []abi.Multiaddrs) { 2655 if len(peerID) > MaxPeerIDLength { 2656 rt.Abortf(exitcode.ErrIllegalArgument, "peer ID size of %d exceeds maximum size of %d", peerID, MaxPeerIDLength) 2657 } 2658 2659 totalSize := 0 2660 for _, ma := range multiaddrs { 2661 if len(ma) == 0 { 2662 rt.Abortf(exitcode.ErrIllegalArgument, "invalid empty multiaddr") 2663 } 2664 totalSize += len(ma) 2665 } 2666 if totalSize > MaxMultiaddrData { 2667 rt.Abortf(exitcode.ErrIllegalArgument, "multiaddr size of %d exceeds maximum of %d", totalSize, MaxMultiaddrData) 2668 } 2669 }