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

     1  package market
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"sort"
     7  
     8  	addr "github.com/filecoin-project/go-address"
     9  	"github.com/filecoin-project/go-state-types/abi"
    10  	"github.com/filecoin-project/go-state-types/big"
    11  	"github.com/filecoin-project/go-state-types/cbor"
    12  	"github.com/filecoin-project/go-state-types/crypto"
    13  	"github.com/filecoin-project/go-state-types/exitcode"
    14  	rtt "github.com/filecoin-project/go-state-types/rt"
    15  	market0 "github.com/filecoin-project/specs-actors/actors/builtin/market"
    16  	"github.com/ipfs/go-cid"
    17  	cbg "github.com/whyrusleeping/cbor-gen"
    18  	"golang.org/x/xerrors"
    19  
    20  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
    21  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/power"
    22  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/reward"
    23  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg"
    24  	"github.com/filecoin-project/specs-actors/v4/actors/runtime"
    25  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    26  )
    27  
    28  type Actor struct{}
    29  
    30  type Runtime = runtime.Runtime
    31  
    32  func (a Actor) Exports() []interface{} {
    33  	return []interface{}{
    34  		builtin.MethodConstructor: a.Constructor,
    35  		2:                         a.AddBalance,
    36  		3:                         a.WithdrawBalance,
    37  		4:                         a.PublishStorageDeals,
    38  		5:                         a.VerifyDealsForActivation,
    39  		6:                         a.ActivateDeals,
    40  		7:                         a.OnMinerSectorsTerminate,
    41  		8:                         a.ComputeDataCommitment,
    42  		9:                         a.CronTick,
    43  	}
    44  }
    45  
    46  func (a Actor) Code() cid.Cid {
    47  	return builtin.StorageMarketActorCodeID
    48  }
    49  
    50  func (a Actor) IsSingleton() bool {
    51  	return true
    52  }
    53  
    54  func (a Actor) State() cbor.Er {
    55  	return new(State)
    56  }
    57  
    58  var _ runtime.VMActor = Actor{}
    59  
    60  ////////////////////////////////////////////////////////////////////////////////
    61  // Actor methods
    62  ////////////////////////////////////////////////////////////////////////////////
    63  
    64  func (a Actor) Constructor(rt Runtime, _ *abi.EmptyValue) *abi.EmptyValue {
    65  	rt.ValidateImmediateCallerIs(builtin.SystemActorAddr)
    66  
    67  	st, err := ConstructState(adt.AsStore(rt))
    68  	builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to create state")
    69  	rt.StateCreate(st)
    70  	return nil
    71  }
    72  
    73  //type WithdrawBalanceParams struct {
    74  //	ProviderOrClientAddress addr.Address
    75  //	Amount                  abi.TokenAmount
    76  //}
    77  type WithdrawBalanceParams = market0.WithdrawBalanceParams
    78  
    79  // Attempt to withdraw the specified amount from the balance held in escrow.
    80  // If less than the specified amount is available, yields the entire available balance.
    81  func (a Actor) WithdrawBalance(rt Runtime, params *WithdrawBalanceParams) *abi.EmptyValue {
    82  	if params.Amount.LessThan(big.Zero()) {
    83  		rt.Abortf(exitcode.ErrIllegalArgument, "negative amount %v", params.Amount)
    84  	}
    85  
    86  	nominal, recipient, approvedCallers := escrowAddress(rt, params.ProviderOrClientAddress)
    87  	// for providers -> only corresponding owner or worker can withdraw
    88  	// for clients -> only the client i.e the recipient can withdraw
    89  	rt.ValidateImmediateCallerIs(approvedCallers...)
    90  
    91  	amountExtracted := abi.NewTokenAmount(0)
    92  	var st State
    93  	rt.StateTransaction(&st, func() {
    94  		msm, err := st.mutator(adt.AsStore(rt)).withEscrowTable(WritePermission).
    95  			withLockedTable(WritePermission).build()
    96  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load state")
    97  
    98  		// The withdrawable amount might be slightly less than nominal
    99  		// depending on whether or not all relevant entries have been processed
   100  		// by cron
   101  		minBalance, err := msm.lockedTable.Get(nominal)
   102  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get locked balance")
   103  
   104  		ex, err := msm.escrowTable.SubtractWithMinimum(nominal, params.Amount, minBalance)
   105  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to subtract from escrow table")
   106  
   107  		err = msm.commitState()
   108  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to flush state")
   109  
   110  		amountExtracted = ex
   111  	})
   112  
   113  	code := rt.Send(recipient, builtin.MethodSend, nil, amountExtracted, &builtin.Discard{})
   114  	builtin.RequireSuccess(rt, code, "failed to send funds")
   115  	return nil
   116  }
   117  
   118  // Deposits the received value into the balance held in escrow.
   119  func (a Actor) AddBalance(rt Runtime, providerOrClientAddress *addr.Address) *abi.EmptyValue {
   120  	msgValue := rt.ValueReceived()
   121  	builtin.RequireParam(rt, msgValue.GreaterThan(big.Zero()), "balance to add must be greater than zero")
   122  
   123  	// only signing parties can add balance for client AND provider.
   124  	rt.ValidateImmediateCallerType(builtin.CallerTypesSignable...)
   125  
   126  	nominal, _, _ := escrowAddress(rt, *providerOrClientAddress)
   127  
   128  	var st State
   129  	rt.StateTransaction(&st, func() {
   130  		msm, err := st.mutator(adt.AsStore(rt)).withEscrowTable(WritePermission).
   131  			withLockedTable(WritePermission).build()
   132  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load state")
   133  
   134  		err = msm.escrowTable.Add(nominal, msgValue)
   135  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add balance to escrow table")
   136  
   137  		err = msm.commitState()
   138  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to flush state")
   139  	})
   140  	return nil
   141  }
   142  
   143  //type PublishStorageDealsParams struct {
   144  //	Deals []ClientDealProposal
   145  //}
   146  type PublishStorageDealsParams = market0.PublishStorageDealsParams
   147  
   148  //type PublishStorageDealsReturn struct {
   149  //	IDs []abi.DealID
   150  //}
   151  type PublishStorageDealsReturn = market0.PublishStorageDealsReturn
   152  
   153  // Publish a new set of storage deals (not yet included in a sector).
   154  func (a Actor) PublishStorageDeals(rt Runtime, params *PublishStorageDealsParams) *PublishStorageDealsReturn {
   155  
   156  	// Deal message must have a From field identical to the provider of all the deals.
   157  	// This allows us to retain and verify only the client's signature in each deal proposal itself.
   158  	rt.ValidateImmediateCallerType(builtin.CallerTypesSignable...)
   159  	if len(params.Deals) == 0 {
   160  		rt.Abortf(exitcode.ErrIllegalArgument, "empty deals parameter")
   161  	}
   162  
   163  	// All deals should have the same provider so get worker once
   164  	providerRaw := params.Deals[0].Proposal.Provider
   165  	provider, ok := rt.ResolveAddress(providerRaw)
   166  	if !ok {
   167  		rt.Abortf(exitcode.ErrNotFound, "failed to resolve provider address %v", providerRaw)
   168  	}
   169  
   170  	codeID, ok := rt.GetActorCodeCID(provider)
   171  	builtin.RequireParam(rt, ok, "no codeId for address %v", provider)
   172  	if !codeID.Equals(builtin.StorageMinerActorCodeID) {
   173  		rt.Abortf(exitcode.ErrIllegalArgument, "deal provider is not a StorageMinerActor")
   174  	}
   175  
   176  	caller := rt.Caller()
   177  	_, worker, controllers := builtin.RequestMinerControlAddrs(rt, provider)
   178  	callerOk := caller == worker
   179  	for _, controller := range controllers {
   180  		if callerOk {
   181  			break
   182  		}
   183  		callerOk = caller == controller
   184  	}
   185  	if !callerOk {
   186  		rt.Abortf(exitcode.ErrForbidden, "caller %v is not worker or control address of provider %v", caller, provider)
   187  	}
   188  
   189  	resolvedAddrs := make(map[addr.Address]addr.Address, len(params.Deals))
   190  	baselinePower := requestCurrentBaselinePower(rt)
   191  	networkRawPower, networkQAPower := requestCurrentNetworkPower(rt)
   192  
   193  	var newDealIds []abi.DealID
   194  	var st State
   195  	rt.StateTransaction(&st, func() {
   196  		msm, err := st.mutator(adt.AsStore(rt)).withPendingProposals(WritePermission).
   197  			withDealProposals(WritePermission).withDealsByEpoch(WritePermission).withEscrowTable(WritePermission).
   198  			withLockedTable(WritePermission).build()
   199  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load state")
   200  
   201  		// All storage dealProposals will be added in an atomic transaction; this operation will be unrolled if any of them fails.
   202  		for di, deal := range params.Deals {
   203  			validateDeal(rt, deal, networkRawPower, networkQAPower, baselinePower)
   204  
   205  			if deal.Proposal.Provider != provider && deal.Proposal.Provider != providerRaw {
   206  				rt.Abortf(exitcode.ErrIllegalArgument, "cannot publish deals from different providers at the same time")
   207  			}
   208  
   209  			client, ok := rt.ResolveAddress(deal.Proposal.Client)
   210  			if !ok {
   211  				rt.Abortf(exitcode.ErrNotFound, "failed to resolve client address %v", deal.Proposal.Client)
   212  			}
   213  			// Normalise provider and client addresses in the proposal stored on chain (after signature verification).
   214  			deal.Proposal.Provider = provider
   215  			resolvedAddrs[deal.Proposal.Client] = client
   216  			deal.Proposal.Client = client
   217  
   218  			err := msm.lockClientAndProviderBalances(&deal.Proposal)
   219  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to lock balance")
   220  
   221  			id := msm.generateStorageDealID()
   222  
   223  			pcid, err := deal.Proposal.Cid()
   224  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed to take cid of proposal %d", di)
   225  
   226  			has, err := msm.pendingDeals.Has(abi.CidKey(pcid))
   227  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to check for existence of deal proposal")
   228  			if has {
   229  				rt.Abortf(exitcode.ErrIllegalArgument, "cannot publish duplicate deals")
   230  			}
   231  
   232  			err = msm.pendingDeals.Put(abi.CidKey(pcid))
   233  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to set pending deal")
   234  
   235  			err = msm.dealProposals.Set(id, &deal.Proposal)
   236  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to set deal")
   237  
   238  			// We should randomize the first epoch for when the deal will be processed so an attacker isn't able to
   239  			// schedule too many deals for the same tick.
   240  			processEpoch, err := genRandNextEpoch(rt.CurrEpoch(), &deal.Proposal, rt.GetRandomnessFromBeacon)
   241  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to generate random process epoch")
   242  
   243  			err = msm.dealsByEpoch.Put(processEpoch, id)
   244  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to set deal ops by epoch")
   245  
   246  			newDealIds = append(newDealIds, id)
   247  		}
   248  
   249  		err = msm.commitState()
   250  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to flush state")
   251  	})
   252  
   253  	for _, deal := range params.Deals {
   254  		// Check VerifiedClient allowed cap and deduct PieceSize from cap.
   255  		// Either the DealSize is within the available DataCap of the VerifiedClient
   256  		// or this message will fail. We do not allow a deal that is partially verified.
   257  		if deal.Proposal.VerifiedDeal {
   258  			resolvedClient, ok := resolvedAddrs[deal.Proposal.Client]
   259  			builtin.RequireParam(rt, ok, "could not get resolvedClient client address")
   260  
   261  			code := rt.Send(
   262  				builtin.VerifiedRegistryActorAddr,
   263  				builtin.MethodsVerifiedRegistry.UseBytes,
   264  				&verifreg.UseBytesParams{
   265  					Address:  resolvedClient,
   266  					DealSize: big.NewIntUnsigned(uint64(deal.Proposal.PieceSize)),
   267  				},
   268  				abi.NewTokenAmount(0),
   269  				&builtin.Discard{},
   270  			)
   271  			builtin.RequireSuccess(rt, code, "failed to add verified deal for client: %v", deal.Proposal.Client)
   272  		}
   273  	}
   274  
   275  	return &PublishStorageDealsReturn{IDs: newDealIds}
   276  }
   277  
   278  // Changed since v2:
   279  // - Array of sectors rather than just one
   280  // - Removed SectorStart (which is unknown at call time)
   281  type VerifyDealsForActivationParams struct {
   282  	Sectors []SectorDeals
   283  }
   284  
   285  type SectorDeals struct {
   286  	SectorExpiry abi.ChainEpoch
   287  	DealIDs      []abi.DealID
   288  }
   289  
   290  // Changed since v2:
   291  // - Array of sectors weights
   292  type VerifyDealsForActivationReturn struct {
   293  	Sectors []SectorWeights
   294  }
   295  
   296  type SectorWeights struct {
   297  	DealSpace          uint64         // Total space in bytes of submitted deals.
   298  	DealWeight         abi.DealWeight // Total space*time of submitted deals.
   299  	VerifiedDealWeight abi.DealWeight // Total space*time of submitted verified deals.
   300  }
   301  
   302  // Computes the weight of deals proposed for inclusion in a number of sectors.
   303  // Deal weight is defined as the sum, over all deals in the set, of the product of deal size and duration.
   304  //
   305  // This method performs some light validation on the way in order to fail early if deals can be
   306  // determined to be invalid for the proposed sector properties.
   307  // Full deal validation is deferred to deal activation since it depends on the activation epoch.
   308  func (a Actor) VerifyDealsForActivation(rt Runtime, params *VerifyDealsForActivationParams) *VerifyDealsForActivationReturn {
   309  	rt.ValidateImmediateCallerType(builtin.StorageMinerActorCodeID)
   310  	minerAddr := rt.Caller()
   311  	currEpoch := rt.CurrEpoch()
   312  
   313  	var st State
   314  	rt.StateReadonly(&st)
   315  	store := adt.AsStore(rt)
   316  
   317  	proposals, err := AsDealProposalArray(store, st.Proposals)
   318  	builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deal proposals")
   319  
   320  	weights := make([]SectorWeights, len(params.Sectors))
   321  	for i, sector := range params.Sectors {
   322  		// Pass the current epoch as the activation epoch for validation.
   323  		// The sector activation epoch isn't yet known, but it's still more helpful to fail now if the deal
   324  		// is so late that a sector activating now couldn't include it.
   325  		dealWeight, verifiedWeight, dealSpace, err := validateAndComputeDealWeight(proposals, sector.DealIDs, minerAddr, sector.SectorExpiry, currEpoch)
   326  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to validate deal proposals for activation")
   327  
   328  		weights[i] = SectorWeights{
   329  			DealSpace:          dealSpace,
   330  			DealWeight:         dealWeight,
   331  			VerifiedDealWeight: verifiedWeight,
   332  		}
   333  	}
   334  
   335  	return &VerifyDealsForActivationReturn{
   336  		Sectors: weights,
   337  	}
   338  }
   339  
   340  //type ActivateDealsParams struct {
   341  //	DealIDs      []abi.DealID
   342  //	SectorExpiry abi.ChainEpoch
   343  //}
   344  type ActivateDealsParams = market0.ActivateDealsParams
   345  
   346  // Verify that a given set of storage deals is valid for a sector currently being ProveCommitted,
   347  // update the market's internal state accordingly.
   348  func (a Actor) ActivateDeals(rt Runtime, params *ActivateDealsParams) *abi.EmptyValue {
   349  	rt.ValidateImmediateCallerType(builtin.StorageMinerActorCodeID)
   350  	minerAddr := rt.Caller()
   351  	currEpoch := rt.CurrEpoch()
   352  
   353  	var st State
   354  	store := adt.AsStore(rt)
   355  
   356  	// Update deal dealStates.
   357  	rt.StateTransaction(&st, func() {
   358  		_, _, _, err := ValidateDealsForActivation(&st, store, params.DealIDs, minerAddr, params.SectorExpiry, currEpoch)
   359  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to validate dealProposals for activation")
   360  
   361  		msm, err := st.mutator(adt.AsStore(rt)).withDealStates(WritePermission).
   362  			withPendingProposals(ReadOnlyPermission).withDealProposals(ReadOnlyPermission).build()
   363  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load state")
   364  
   365  		for _, dealID := range params.DealIDs {
   366  			// This construction could be replaced with a single "update deal state" state method, possibly batched
   367  			// over all deal ids at once.
   368  			_, found, err := msm.dealStates.Get(dealID)
   369  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get state for dealId %d", dealID)
   370  			if found {
   371  				rt.Abortf(exitcode.ErrIllegalArgument, "deal %d already included in another sector", dealID)
   372  			}
   373  
   374  			proposal, err := getDealProposal(msm.dealProposals, dealID)
   375  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get dealId %d", dealID)
   376  
   377  			propc, err := proposal.Cid()
   378  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to calculate proposal CID")
   379  
   380  			has, err := msm.pendingDeals.Has(abi.CidKey(propc))
   381  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get pending proposal %v", propc)
   382  
   383  			if !has {
   384  				rt.Abortf(exitcode.ErrIllegalState, "tried to activate deal that was not in the pending set (%s)", propc)
   385  			}
   386  
   387  			err = msm.dealStates.Set(dealID, &DealState{
   388  				SectorStartEpoch: currEpoch,
   389  				LastUpdatedEpoch: epochUndefined,
   390  				SlashEpoch:       epochUndefined,
   391  			})
   392  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to set deal state %d", dealID)
   393  		}
   394  
   395  		err = msm.commitState()
   396  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to flush state")
   397  	})
   398  
   399  	return nil
   400  }
   401  
   402  //type ComputeDataCommitmentParams struct {
   403  //	DealIDs    []abi.DealID
   404  //	SectorType abi.RegisteredSealProof
   405  //}
   406  type ComputeDataCommitmentParams = market0.ComputeDataCommitmentParams
   407  
   408  func (a Actor) ComputeDataCommitment(rt Runtime, params *ComputeDataCommitmentParams) *cbg.CborCid {
   409  	rt.ValidateImmediateCallerType(builtin.StorageMinerActorCodeID)
   410  
   411  	var st State
   412  	rt.StateReadonly(&st)
   413  	proposals, err := AsDealProposalArray(adt.AsStore(rt), st.Proposals)
   414  	builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deal dealProposals")
   415  
   416  	pieces := make([]abi.PieceInfo, 0)
   417  	for _, dealID := range params.DealIDs {
   418  		deal, err := getDealProposal(proposals, dealID)
   419  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get dealId %d", dealID)
   420  
   421  		pieces = append(pieces, abi.PieceInfo{
   422  			PieceCID: deal.PieceCID,
   423  			Size:     deal.PieceSize,
   424  		})
   425  	}
   426  
   427  	commd, err := rt.ComputeUnsealedSectorCID(params.SectorType, pieces)
   428  	if err != nil {
   429  		rt.Abortf(exitcode.ErrIllegalArgument, "failed to compute unsealed sector CID: %s", err)
   430  	}
   431  
   432  	return (*cbg.CborCid)(&commd)
   433  }
   434  
   435  //type OnMinerSectorsTerminateParams struct {
   436  //	Epoch   abi.ChainEpoch
   437  //	DealIDs []abi.DealID
   438  //}
   439  type OnMinerSectorsTerminateParams = market0.OnMinerSectorsTerminateParams
   440  
   441  // Terminate a set of deals in response to their containing sector being terminated.
   442  // Slash provider collateral, refund client collateral, and refund partial unpaid escrow
   443  // amount to client.
   444  func (a Actor) OnMinerSectorsTerminate(rt Runtime, params *OnMinerSectorsTerminateParams) *abi.EmptyValue {
   445  	rt.ValidateImmediateCallerType(builtin.StorageMinerActorCodeID)
   446  	minerAddr := rt.Caller()
   447  
   448  	var st State
   449  	rt.StateTransaction(&st, func() {
   450  		msm, err := st.mutator(adt.AsStore(rt)).withDealStates(WritePermission).
   451  			withDealProposals(ReadOnlyPermission).build()
   452  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load deal state")
   453  
   454  		for _, dealID := range params.DealIDs {
   455  			deal, found, err := msm.dealProposals.Get(dealID)
   456  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get deal proposal %v", dealID)
   457  			// deal could have terminated and hence deleted before the sector is terminated.
   458  			// we should simply continue instead of aborting execution here if a deal is not found.
   459  			if !found {
   460  				continue
   461  			}
   462  			builtin.RequireState(rt, deal.Provider == minerAddr, "caller %v is not the provider %v of deal %v",
   463  				minerAddr, deal.Provider, dealID)
   464  
   465  			// do not slash expired deals
   466  			if deal.EndEpoch <= params.Epoch {
   467  				continue
   468  			}
   469  
   470  			state, found, err := msm.dealStates.Get(dealID)
   471  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get deal state %v", dealID)
   472  			if !found {
   473  				rt.Abortf(exitcode.ErrIllegalArgument, "no state for deal %v", dealID)
   474  			}
   475  
   476  			// if a deal is already slashed, we don't need to do anything here.
   477  			if state.SlashEpoch != epochUndefined {
   478  				continue
   479  			}
   480  
   481  			// mark the deal for slashing here.
   482  			// actual releasing of locked funds for the client and slashing of provider collateral happens in CronTick.
   483  			state.SlashEpoch = params.Epoch
   484  
   485  			err = msm.dealStates.Set(dealID, state)
   486  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to set deal state %v", dealID)
   487  		}
   488  
   489  		err = msm.commitState()
   490  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to flush state")
   491  	})
   492  	return nil
   493  }
   494  
   495  func (a Actor) CronTick(rt Runtime, _ *abi.EmptyValue) *abi.EmptyValue {
   496  	rt.ValidateImmediateCallerIs(builtin.CronActorAddr)
   497  	amountSlashed := big.Zero()
   498  
   499  	var timedOutVerifiedDeals []*DealProposal
   500  
   501  	var st State
   502  	rt.StateTransaction(&st, func() {
   503  		updatesNeeded := make(map[abi.ChainEpoch][]abi.DealID)
   504  
   505  		msm, err := st.mutator(adt.AsStore(rt)).withDealStates(WritePermission).
   506  			withLockedTable(WritePermission).withEscrowTable(WritePermission).withDealsByEpoch(WritePermission).
   507  			withDealProposals(WritePermission).withPendingProposals(WritePermission).build()
   508  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load state")
   509  
   510  		for i := st.LastCron + 1; i <= rt.CurrEpoch(); i++ {
   511  			err = msm.dealsByEpoch.ForEach(i, func(dealID abi.DealID) error {
   512  				deal, err := getDealProposal(msm.dealProposals, dealID)
   513  				builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get dealId %d", dealID)
   514  
   515  				dcid, err := deal.Cid()
   516  				builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to calculate CID for proposal %v", dealID)
   517  
   518  				state, found, err := msm.dealStates.Get(dealID)
   519  				builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to get deal state")
   520  
   521  				// deal has been published but not activated yet -> terminate it as it has timed out
   522  				if !found {
   523  					// Not yet appeared in proven sector; check for timeout.
   524  					builtin.RequireState(rt, rt.CurrEpoch() >= deal.StartEpoch, "deal %d processed before start epoch %d",
   525  						dealID, deal.StartEpoch)
   526  
   527  					slashed := msm.processDealInitTimedOut(rt, deal)
   528  					if !slashed.IsZero() {
   529  						amountSlashed = big.Add(amountSlashed, slashed)
   530  					}
   531  					if deal.VerifiedDeal {
   532  						timedOutVerifiedDeals = append(timedOutVerifiedDeals, deal)
   533  					}
   534  
   535  					// we should not attempt to delete the DealState because it does NOT exist
   536  					if err := deleteDealProposalAndState(dealID, msm.dealStates, msm.dealProposals, true, false); err != nil {
   537  						builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to delete deal %d", dealID)
   538  					}
   539  
   540  					pdErr := msm.pendingDeals.Delete(abi.CidKey(dcid))
   541  					builtin.RequireNoErr(rt, pdErr, exitcode.ErrIllegalState, "failed to delete pending proposal %v", dcid)
   542  					return nil
   543  				}
   544  
   545  				// if this is the first cron tick for the deal, it should be in the pending state.
   546  				if state.LastUpdatedEpoch == epochUndefined {
   547  					pdErr := msm.pendingDeals.Delete(abi.CidKey(dcid))
   548  					builtin.RequireNoErr(rt, pdErr, exitcode.ErrIllegalState, "failed to delete pending proposal %v", dcid)
   549  				}
   550  
   551  				slashAmount, nextEpoch, removeDeal := msm.updatePendingDealState(rt, state, deal, rt.CurrEpoch())
   552  				builtin.RequireState(rt, slashAmount.GreaterThanEqual(big.Zero()), "computed negative slash amount %v for deal %d", slashAmount, dealID)
   553  
   554  				if removeDeal {
   555  					builtin.RequireState(rt, nextEpoch == epochUndefined, "removed deal %d should have no scheduled epoch (got %d)", dealID, nextEpoch)
   556  
   557  					amountSlashed = big.Add(amountSlashed, slashAmount)
   558  					err := deleteDealProposalAndState(dealID, msm.dealStates, msm.dealProposals, true, true)
   559  					builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to delete deal proposal and states")
   560  				} else {
   561  					builtin.RequireState(rt, nextEpoch > rt.CurrEpoch(), "continuing deal %d next epoch %d should be in future", dealID, nextEpoch)
   562  					builtin.RequireState(rt, slashAmount.IsZero(), "continuing deal %d should not be slashed", dealID)
   563  
   564  					// Update deal's LastUpdatedEpoch in DealStates
   565  					state.LastUpdatedEpoch = rt.CurrEpoch()
   566  					err = msm.dealStates.Set(dealID, state)
   567  					builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to set deal state")
   568  
   569  					updatesNeeded[nextEpoch] = append(updatesNeeded[nextEpoch], dealID)
   570  				}
   571  
   572  				return nil
   573  			})
   574  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to iterate deal ops")
   575  
   576  			err = msm.dealsByEpoch.RemoveAll(i)
   577  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to delete deal ops for epoch %v", i)
   578  		}
   579  
   580  		// Iterate changes in sorted order to ensure that loads/stores
   581  		// are deterministic. Otherwise, we could end up charging an
   582  		// inconsistent amount of gas.
   583  		changedEpochs := make([]abi.ChainEpoch, 0, len(updatesNeeded))
   584  		for epoch := range updatesNeeded { //nolint:nomaprange
   585  			changedEpochs = append(changedEpochs, epoch)
   586  		}
   587  
   588  		sort.Slice(changedEpochs, func(i, j int) bool { return changedEpochs[i] < changedEpochs[j] })
   589  
   590  		for _, epoch := range changedEpochs {
   591  			err = msm.dealsByEpoch.PutMany(epoch, updatesNeeded[epoch])
   592  			builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to reinsert deal IDs for epoch %v", epoch)
   593  		}
   594  
   595  		st.LastCron = rt.CurrEpoch()
   596  
   597  		err = msm.commitState()
   598  		builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to flush state")
   599  	})
   600  
   601  	for _, d := range timedOutVerifiedDeals {
   602  		code := rt.Send(
   603  			builtin.VerifiedRegistryActorAddr,
   604  			builtin.MethodsVerifiedRegistry.RestoreBytes,
   605  			&verifreg.RestoreBytesParams{
   606  				Address:  d.Client,
   607  				DealSize: big.NewIntUnsigned(uint64(d.PieceSize)),
   608  			},
   609  			abi.NewTokenAmount(0),
   610  			&builtin.Discard{},
   611  		)
   612  
   613  		if !code.IsSuccess() {
   614  			rt.Log(rtt.ERROR, "failed to send RestoreBytes call to the VerifReg actor for timed-out verified deal, client: %s, dealSize: %v, "+
   615  				"provider: %v, got code %v", d.Client, d.PieceSize, d.Provider, code)
   616  		}
   617  	}
   618  
   619  	if !amountSlashed.IsZero() {
   620  		e := rt.Send(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, amountSlashed, &builtin.Discard{})
   621  		builtin.RequireSuccess(rt, e, "expected send to burnt funds actor to succeed")
   622  	}
   623  
   624  	return nil
   625  }
   626  
   627  func genRandNextEpoch(currEpoch abi.ChainEpoch, deal *DealProposal, rbF func(crypto.DomainSeparationTag, abi.ChainEpoch, []byte) abi.Randomness) (abi.ChainEpoch, error) {
   628  	buf := bytes.Buffer{}
   629  	if err := deal.MarshalCBOR(&buf); err != nil {
   630  		return epochUndefined, xerrors.Errorf("failed to marshal proposal: %w", err)
   631  	}
   632  
   633  	rb := rbF(crypto.DomainSeparationTag_MarketDealCronSeed, currEpoch-1, buf.Bytes())
   634  
   635  	// generate a random epoch in [baseEpoch, baseEpoch + DealUpdatesInterval)
   636  	offset := binary.BigEndian.Uint64(rb)
   637  
   638  	return deal.StartEpoch + abi.ChainEpoch(offset%uint64(DealUpdatesInterval)), nil
   639  }
   640  
   641  func deleteDealProposalAndState(dealId abi.DealID, states *DealMetaArray, proposals *DealArray, removeProposal bool,
   642  	removeState bool) error {
   643  	if removeProposal {
   644  		if err := proposals.Delete(dealId); err != nil {
   645  			return xerrors.Errorf("failed to delete proposal %d : %w", dealId, err)
   646  		}
   647  	}
   648  
   649  	if removeState {
   650  		if err := states.Delete(dealId); err != nil {
   651  			return xerrors.Errorf("failed to delete deal state: %w", err)
   652  		}
   653  	}
   654  
   655  	return nil
   656  }
   657  
   658  //
   659  // Exported functions
   660  //
   661  
   662  // Validates a collection of deal dealProposals for activation, and returns their combined weight,
   663  // split into regular deal weight and verified deal weight.
   664  func ValidateDealsForActivation(
   665  	st *State, store adt.Store, dealIDs []abi.DealID, minerAddr addr.Address, sectorExpiry, currEpoch abi.ChainEpoch,
   666  ) (big.Int, big.Int, uint64, error) {
   667  	proposals, err := AsDealProposalArray(store, st.Proposals)
   668  	if err != nil {
   669  		return big.Int{}, big.Int{}, 0, xerrors.Errorf("failed to load dealProposals: %w", err)
   670  	}
   671  
   672  	return validateAndComputeDealWeight(proposals, dealIDs, minerAddr, sectorExpiry, currEpoch)
   673  }
   674  
   675  ////////////////////////////////////////////////////////////////////////////////
   676  // Checks
   677  ////////////////////////////////////////////////////////////////////////////////
   678  
   679  func validateAndComputeDealWeight(proposals *DealArray, dealIDs []abi.DealID, minerAddr addr.Address,
   680  	sectorExpiry abi.ChainEpoch, sectorActivation abi.ChainEpoch) (big.Int, big.Int, uint64, error) {
   681  
   682  	seenDealIDs := make(map[abi.DealID]struct{}, len(dealIDs))
   683  	totalDealSpace := uint64(0)
   684  	totalDealSpaceTime := big.Zero()
   685  	totalVerifiedSpaceTime := big.Zero()
   686  	for _, dealID := range dealIDs {
   687  		// Make sure we don't double-count deals.
   688  		if _, seen := seenDealIDs[dealID]; seen {
   689  			return big.Int{}, big.Int{}, 0, exitcode.ErrIllegalArgument.Wrapf("deal ID %d present multiple times", dealID)
   690  		}
   691  		seenDealIDs[dealID] = struct{}{}
   692  
   693  		proposal, found, err := proposals.Get(dealID)
   694  		if err != nil {
   695  			return big.Int{}, big.Int{}, 0, xerrors.Errorf("failed to load deal %d: %w", dealID, err)
   696  		}
   697  		if !found {
   698  			return big.Int{}, big.Int{}, 0, exitcode.ErrNotFound.Wrapf("no such deal %d", dealID)
   699  		}
   700  		if err = validateDealCanActivate(proposal, minerAddr, sectorExpiry, sectorActivation); err != nil {
   701  			return big.Int{}, big.Int{}, 0, xerrors.Errorf("cannot activate deal %d: %w", dealID, err)
   702  		}
   703  
   704  		// Compute deal weight
   705  		totalDealSpace += uint64(proposal.PieceSize)
   706  		dealSpaceTime := DealWeight(proposal)
   707  		if proposal.VerifiedDeal {
   708  			totalVerifiedSpaceTime = big.Add(totalVerifiedSpaceTime, dealSpaceTime)
   709  		} else {
   710  			totalDealSpaceTime = big.Add(totalDealSpaceTime, dealSpaceTime)
   711  		}
   712  	}
   713  	return totalDealSpaceTime, totalVerifiedSpaceTime, totalDealSpace, nil
   714  }
   715  
   716  func validateDealCanActivate(proposal *DealProposal, minerAddr addr.Address, sectorExpiration, sectorActivation abi.ChainEpoch) error {
   717  	if proposal.Provider != minerAddr {
   718  		return exitcode.ErrForbidden.Wrapf("proposal has provider %v, must be %v", proposal.Provider, minerAddr)
   719  	}
   720  	if sectorActivation > proposal.StartEpoch {
   721  		return exitcode.ErrIllegalArgument.Wrapf("proposal start epoch %d has already elapsed at %d", proposal.StartEpoch, sectorActivation)
   722  	}
   723  	if proposal.EndEpoch > sectorExpiration {
   724  		return exitcode.ErrIllegalArgument.Wrapf("proposal expiration %d exceeds sector expiration %d", proposal.EndEpoch, sectorExpiration)
   725  	}
   726  	return nil
   727  }
   728  
   729  func validateDeal(rt Runtime, deal ClientDealProposal, networkRawPower, networkQAPower, baselinePower abi.StoragePower) {
   730  	if err := dealProposalIsInternallyValid(rt, deal); err != nil {
   731  		rt.Abortf(exitcode.ErrIllegalArgument, "Invalid deal proposal: %s", err)
   732  	}
   733  
   734  	proposal := deal.Proposal
   735  
   736  	if len(proposal.Label) > DealMaxLabelSize {
   737  		rt.Abortf(exitcode.ErrIllegalArgument, "deal label can be at most %d bytes, is %d", DealMaxLabelSize, len(proposal.Label))
   738  	}
   739  
   740  	if err := proposal.PieceSize.Validate(); err != nil {
   741  		rt.Abortf(exitcode.ErrIllegalArgument, "proposal piece size is invalid: %v", err)
   742  	}
   743  
   744  	if !proposal.PieceCID.Defined() {
   745  		rt.Abortf(exitcode.ErrIllegalArgument, "proposal PieceCID undefined")
   746  	}
   747  
   748  	if proposal.PieceCID.Prefix() != PieceCIDPrefix {
   749  		rt.Abortf(exitcode.ErrIllegalArgument, "proposal PieceCID had wrong prefix")
   750  	}
   751  
   752  	if proposal.EndEpoch <= proposal.StartEpoch {
   753  		rt.Abortf(exitcode.ErrIllegalArgument, "proposal end before proposal start")
   754  	}
   755  
   756  	if rt.CurrEpoch() > proposal.StartEpoch {
   757  		rt.Abortf(exitcode.ErrIllegalArgument, "Deal start epoch has already elapsed.")
   758  	}
   759  
   760  	minDuration, maxDuration := DealDurationBounds(proposal.PieceSize)
   761  	if proposal.Duration() < minDuration || proposal.Duration() > maxDuration {
   762  		rt.Abortf(exitcode.ErrIllegalArgument, "Deal duration out of bounds.")
   763  	}
   764  
   765  	minPrice, maxPrice := DealPricePerEpochBounds(proposal.PieceSize, proposal.Duration())
   766  	if proposal.StoragePricePerEpoch.LessThan(minPrice) || proposal.StoragePricePerEpoch.GreaterThan(maxPrice) {
   767  		rt.Abortf(exitcode.ErrIllegalArgument, "Storage price out of bounds.")
   768  	}
   769  
   770  	minProviderCollateral, maxProviderCollateral := DealProviderCollateralBounds(proposal.PieceSize, proposal.VerifiedDeal,
   771  		networkRawPower, networkQAPower, baselinePower, rt.TotalFilCircSupply())
   772  	if proposal.ProviderCollateral.LessThan(minProviderCollateral) || proposal.ProviderCollateral.GreaterThan(maxProviderCollateral) {
   773  		rt.Abortf(exitcode.ErrIllegalArgument, "Provider collateral out of bounds.")
   774  	}
   775  
   776  	minClientCollateral, maxClientCollateral := DealClientCollateralBounds(proposal.PieceSize, proposal.Duration())
   777  	if proposal.ClientCollateral.LessThan(minClientCollateral) || proposal.ClientCollateral.GreaterThan(maxClientCollateral) {
   778  		rt.Abortf(exitcode.ErrIllegalArgument, "Client collateral out of bounds.")
   779  	}
   780  }
   781  
   782  //
   783  // Helpers
   784  //
   785  
   786  // Resolves a provider or client address to the canonical form against which a balance should be held, and
   787  // the designated recipient address of withdrawals (which is the same, for simple account parties).
   788  func escrowAddress(rt Runtime, address addr.Address) (nominal addr.Address, recipient addr.Address, approved []addr.Address) {
   789  	// Resolve the provided address to the canonical form against which the balance is held.
   790  	nominal, ok := rt.ResolveAddress(address)
   791  	if !ok {
   792  		rt.Abortf(exitcode.ErrIllegalArgument, "failed to resolve address %v", address)
   793  	}
   794  
   795  	codeID, ok := rt.GetActorCodeCID(nominal)
   796  	if !ok {
   797  		rt.Abortf(exitcode.ErrIllegalArgument, "no code for address %v", nominal)
   798  	}
   799  
   800  	if codeID.Equals(builtin.StorageMinerActorCodeID) {
   801  		// Storage miner actor entry; implied funds recipient is the associated owner address.
   802  		ownerAddr, workerAddr, _ := builtin.RequestMinerControlAddrs(rt, nominal)
   803  		return nominal, ownerAddr, []addr.Address{ownerAddr, workerAddr}
   804  	}
   805  
   806  	return nominal, nominal, []addr.Address{nominal}
   807  }
   808  
   809  func getDealProposal(proposals *DealArray, dealID abi.DealID) (*DealProposal, error) {
   810  	proposal, found, err := proposals.Get(dealID)
   811  	if err != nil {
   812  		return nil, xerrors.Errorf("failed to load proposal: %w", err)
   813  	}
   814  	if !found {
   815  		return nil, exitcode.ErrNotFound.Wrapf("no such deal %d", dealID)
   816  	}
   817  
   818  	return proposal, nil
   819  }
   820  
   821  // Requests the current epoch target block reward from the reward actor.
   822  func requestCurrentBaselinePower(rt Runtime) abi.StoragePower {
   823  	var ret reward.ThisEpochRewardReturn
   824  	code := rt.Send(builtin.RewardActorAddr, builtin.MethodsReward.ThisEpochReward, nil, big.Zero(), &ret)
   825  	builtin.RequireSuccess(rt, code, "failed to check epoch baseline power")
   826  	return ret.ThisEpochBaselinePower
   827  }
   828  
   829  // Requests the current network total power and pledge from the power actor.
   830  func requestCurrentNetworkPower(rt Runtime) (rawPower, qaPower abi.StoragePower) {
   831  	var pwr power.CurrentTotalPowerReturn
   832  	code := rt.Send(builtin.StoragePowerActorAddr, builtin.MethodsPower.CurrentTotalPower, nil, big.Zero(), &pwr)
   833  	builtin.RequireSuccess(rt, code, "failed to check current power")
   834  	return pwr.RawBytePower, pwr.QualityAdjPower
   835  }