on chain deals: More actor methods
This commit is contained in:
		
							parent
							
								
									c41dd4efeb
								
							
						
					
					
						commit
						6bade2eb23
					
				| @ -213,6 +213,14 @@ type CommitSectorParams struct { | ||||
| 	Proof     []byte | ||||
| } | ||||
| 
 | ||||
| type OnChainSealVerifyInfo struct { | ||||
| 	SealedCID    cid.Cid // CommR .. TODO: spec says cid, but it feela weird
 | ||||
| 	Epoch        uint64 | ||||
| 	Proof        []byte | ||||
| 	DealIDs      []uint64 | ||||
| 	SectorNumber uint64 | ||||
| } | ||||
| 
 | ||||
| func (sma StorageMinerActor) CommitSector(act *types.Actor, vmctx types.VMContext, params *CommitSectorParams) ([]byte, ActorError) { | ||||
| 	ctx := context.TODO() | ||||
| 	oldstate, self, err := loadState(vmctx) | ||||
|  | ||||
| @ -6,7 +6,9 @@ import ( | ||||
| 	"github.com/filecoin-project/go-amt-ipld" | ||||
| 	"github.com/ipfs/go-cid" | ||||
| 	"github.com/ipfs/go-hamt-ipld" | ||||
| 	cbg "github.com/whyrusleeping/cbor-gen" | ||||
| 
 | ||||
| 	"github.com/filecoin-project/go-lotus/build" | ||||
| 	"github.com/filecoin-project/go-lotus/chain/actors/aerrors" | ||||
| 	"github.com/filecoin-project/go-lotus/chain/address" | ||||
| 	"github.com/filecoin-project/go-lotus/chain/types" | ||||
| @ -25,9 +27,10 @@ type smaMethods struct { | ||||
| 	ProcessStorageDealsPayment   uint64 | ||||
| 	SlashStorageDealCollateral   uint64 | ||||
| 	GetLastExpirationFromDealIDs uint64 | ||||
| 	ActivateStorageDeals         uint64 | ||||
| } | ||||
| 
 | ||||
| var SMAMethods = smaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} | ||||
| var SMAMethods = smaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} | ||||
| 
 | ||||
| func (sma StorageMarketActor) Exports() []interface{} { | ||||
| 	return []interface{}{ | ||||
| @ -40,6 +43,7 @@ func (sma StorageMarketActor) Exports() []interface{} { | ||||
| 		// 8: sma.ProcessStorageDealsPayment,
 | ||||
| 		// 9: sma.SlashStorageDealCollateral,
 | ||||
| 		// 10: sma.GetLastExpirationFromDealIDs,
 | ||||
| 		11: sma.ActivateStorageDeals, // TODO: move under PublishStorageDeals after specs team approves
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -49,9 +53,10 @@ type StorageParticipantBalance struct { | ||||
| } | ||||
| 
 | ||||
| type StorageMarketState struct { | ||||
| 	Balances   cid.Cid // hamt<StorageParticipantBalance>
 | ||||
| 	Deals      cid.Cid // amt
 | ||||
| 	NextDealID uint64  // TODO: amt.LastIndex()
 | ||||
| 	Balances cid.Cid // hamt<addr, StorageParticipantBalance>
 | ||||
| 	Deals    cid.Cid // amt<StorageDeal>
 | ||||
| 
 | ||||
| 	NextDealID uint64 // TODO: amt.LastIndex()
 | ||||
| } | ||||
| 
 | ||||
| type StorageDealProposal struct { | ||||
| @ -71,6 +76,11 @@ type StorageDeal struct { | ||||
| 	CounterSignature types.Signature | ||||
| } | ||||
| 
 | ||||
| type OnChainDeal struct { | ||||
| 	Deal            StorageDeal | ||||
| 	ActivationEpoch uint64 // 0 = inactive
 | ||||
| } | ||||
| 
 | ||||
| type WithdrawBalanceParams struct { | ||||
| 	Balance types.BigInt | ||||
| } | ||||
| @ -209,6 +219,9 @@ type PublishStorageDealResponse struct { | ||||
| 	DealIDs []uint64 | ||||
| } | ||||
| 
 | ||||
| // TODO: spec says 'call by CommitSector in StorageMiningSubsystem', and then
 | ||||
| //  says that this should be called before CommitSector. For now assuming that
 | ||||
| //  they meant 2 separate methods, See 'ActivateStorageDeals' below
 | ||||
| func (sma StorageMarketActor) PublishStorageDeals(act *types.Actor, vmctx types.VMContext, params *PublishStorageDealsParams) ([]byte, ActorError) { | ||||
| 	var self StorageMarketState | ||||
| 	old := vmctx.Storage().GetHead() | ||||
| @ -233,7 +246,7 @@ func (sma StorageMarketActor) PublishStorageDeals(act *types.Actor, vmctx types. | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		err := deals.Set(self.NextDealID, deal) | ||||
| 		err := deals.Set(self.NextDealID, OnChainDeal{Deal: deal}) | ||||
| 		if err != nil { | ||||
| 			return nil, aerrors.HandleExternalError(err, "setting deal in deal AMT") | ||||
| 		} | ||||
| @ -275,6 +288,9 @@ func (self *StorageMarketState) validateDeal(vmctx types.VMContext, deal Storage | ||||
| 	if vmctx.BlockHeight() >= deal.Proposal.DealExpiration { | ||||
| 		return aerrors.New(2, "deal proposal already expired") | ||||
| 	} | ||||
| 	if deal.Proposal.ProposalExpiration > deal.Proposal.DealExpiration { | ||||
| 		return aerrors.New(3, "ProposalExpiration > DealExpiration") | ||||
| 	} | ||||
| 
 | ||||
| 	var proposalBuf bytes.Buffer | ||||
| 	err := deal.Proposal.MarshalCBOR(&proposalBuf) | ||||
| @ -300,7 +316,7 @@ func (self *StorageMarketState) validateDeal(vmctx types.VMContext, deal Storage | ||||
| 
 | ||||
| 	// TODO: maybe this is actually fine
 | ||||
| 	if vmctx.Message().From != deal.Proposal.Provider && vmctx.Message().From != deal.Proposal.Client { | ||||
| 		return aerrors.New(3, "message not sent by deal participant") | ||||
| 		return aerrors.New(4, "message not sent by deal participant") | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO: REVIEW: Do we want to check if provider exists in the power actor?
 | ||||
| @ -317,14 +333,14 @@ func (self *StorageMarketState) validateDeal(vmctx types.VMContext, deal Storage | ||||
| 	providerBalance := b[1] | ||||
| 
 | ||||
| 	if clientBalance.Available.LessThan(deal.Proposal.StoragePrice) { | ||||
| 		return aerrors.Newf(4, "client doesn't have enough available funds to cover StoragePrice; %d < %d", clientBalance.Available, deal.Proposal.StoragePrice) | ||||
| 		return aerrors.Newf(5, "client doesn't have enough available funds to cover StoragePrice; %d < %d", clientBalance.Available, deal.Proposal.StoragePrice) | ||||
| 	} | ||||
| 
 | ||||
| 	clientBalance = lockFunds(clientBalance, deal.Proposal.StoragePrice) | ||||
| 
 | ||||
| 	// TODO: REVIEW: Not clear who pays for this
 | ||||
| 	if providerBalance.Available.LessThan(deal.Proposal.StorageCollateral) { | ||||
| 		return aerrors.Newf(5, "provider doesn't have enough available funds to cover StorageCollateral; %d < %d", providerBalance.Available, deal.Proposal.StorageCollateral) | ||||
| 		return aerrors.Newf(6, "provider doesn't have enough available funds to cover StorageCollateral; %d < %d", providerBalance.Available, deal.Proposal.StorageCollateral) | ||||
| 	} | ||||
| 
 | ||||
| 	providerBalance = lockFunds(providerBalance, deal.Proposal.StorageCollateral) | ||||
| @ -344,12 +360,161 @@ func (self *StorageMarketState) validateDeal(vmctx types.VMContext, deal Storage | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type ActivateStorageDealsParams struct { | ||||
| 	Deals []uint64 | ||||
| } | ||||
| 
 | ||||
| func (sma StorageMarketActor) ActivateStorageDeals(act *types.Actor, vmctx types.VMContext, params *ActivateStorageDealsParams) ([]byte, ActorError) { | ||||
| 	var self StorageMarketState | ||||
| 	old := vmctx.Storage().GetHead() | ||||
| 	if err := vmctx.Storage().Get(old, &self); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals) | ||||
| 	if err != nil { | ||||
| 		// TODO: kind of annoying that this can be caused by gas, otherwise could be fatal
 | ||||
| 		return nil, aerrors.HandleExternalError(err, "loading deals amt") | ||||
| 	} | ||||
| 
 | ||||
| 	for _, deal := range params.Deals { | ||||
| 		var dealInfo OnChainDeal | ||||
| 		if err := deals.Get(deal, &dealInfo); err != nil { | ||||
| 			return nil, aerrors.HandleExternalError(err, "getting del info failed") | ||||
| 		} | ||||
| 
 | ||||
| 		if vmctx.Message().From != dealInfo.Deal.Proposal.Provider { | ||||
| 			return nil, aerrors.New(1, "ActivateStorageDeals can only be called by deal provider") | ||||
| 		} | ||||
| 
 | ||||
| 		if vmctx.BlockHeight() > dealInfo.Deal.Proposal.ProposalExpiration { | ||||
| 			return nil, aerrors.New(2, "deal cannot be activated: proposal expired") | ||||
| 		} | ||||
| 
 | ||||
| 		if dealInfo.ActivationEpoch > 0 { | ||||
| 			// this probably can't happen in practice
 | ||||
| 			return nil, aerrors.New(3, "deal already active") | ||||
| 		} | ||||
| 
 | ||||
| 		dealInfo.ActivationEpoch = vmctx.BlockHeight() | ||||
| 
 | ||||
| 		if err := deals.Set(deal, dealInfo); err != nil { | ||||
| 			return nil, aerrors.HandleExternalError(err, "setting deal info in AMT failed") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	dealsCid, err := deals.Flush() | ||||
| 	if err != nil { | ||||
| 		return nil, aerrors.HandleExternalError(err, "saving deals AMT") | ||||
| 	} | ||||
| 
 | ||||
| 	self.Deals = dealsCid | ||||
| 
 | ||||
| 	nroot, err := vmctx.Storage().Put(&self) | ||||
| 	if err != nil { | ||||
| 		return nil, aerrors.HandleExternalError(err, "storing state failed") | ||||
| 	} | ||||
| 
 | ||||
| 	aerr := vmctx.Storage().Commit(old, nroot) | ||||
| 	if aerr != nil { | ||||
| 		return nil, aerr | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, nil | ||||
| } | ||||
| 
 | ||||
| type ProcessStorageDealsPaymentParams struct { | ||||
| 	DealIDs []uint64 | ||||
| } | ||||
| 
 | ||||
| func (sma StorageMarketActor) ProcessStorageDealsPayment(act *types.Actor, vmctx types.VMContext, params *ProcessStorageDealsPaymentParams) ([]byte, ActorError) { | ||||
| 	var self StorageMarketState | ||||
| 	old := vmctx.Storage().GetHead() | ||||
| 	if err := vmctx.Storage().Get(old, &self); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals) | ||||
| 	if err != nil { | ||||
| 		// TODO: kind of annoying that this can be caused by gas, otherwise could be fatal
 | ||||
| 		return nil, aerrors.HandleExternalError(err, "loading deals amt") | ||||
| 	} | ||||
| 
 | ||||
| 	for _, deal := range params.DealIDs { | ||||
| 		var dealInfo OnChainDeal | ||||
| 		if err := deals.Get(deal, &dealInfo); err != nil { | ||||
| 			return nil, aerrors.HandleExternalError(err, "getting del info failed") | ||||
| 		} | ||||
| 
 | ||||
| 		encoded, err := CreateExecParams(StorageMinerCodeCid, &IsMinerParam{ | ||||
| 			Addr: vmctx.Message().From, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		ret, err := vmctx.Send(StoragePowerAddress, SPAMethods.IsMiner, types.NewInt(0), encoded) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		if bytes.Equal(ret, cbg.CborBoolTrue) { | ||||
| 			return nil, aerrors.New(1, "ProcessStorageDealsPayment can only be called by storage miner actors") | ||||
| 		} | ||||
| 
 | ||||
| 		if vmctx.BlockHeight() > dealInfo.Deal.Proposal.ProposalExpiration { | ||||
| 			// TODO: ???
 | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 
 | ||||
| 		// TODO: clients probably want this to be fixed
 | ||||
| 		dealDuration := dealInfo.Deal.Proposal.DealExpiration - dealInfo.ActivationEpoch | ||||
| 
 | ||||
| 		// todo: check math (written on a plane, also tired)
 | ||||
| 		// TODO: division is hard, this more than likely has some off-by-one issue
 | ||||
| 		toPay := types.BigDiv(types.BigMul(dealInfo.Deal.Proposal.StoragePrice, types.NewInt(build.ProvingPeriodDuration)), types.NewInt(dealDuration)) | ||||
| 
 | ||||
| 		b, bnd, err := getMarketBalances(vmctx, self.Balances, []address.Address{ | ||||
| 			dealInfo.Deal.Proposal.Client, | ||||
| 			dealInfo.Deal.Proposal.Provider, | ||||
| 		}) | ||||
| 		clientBal := b[0] | ||||
| 		providerBal := b[1] | ||||
| 
 | ||||
| 		clientBal.Locked, providerBal.Available = transferFunds(clientBal.Locked, providerBal.Available, toPay) | ||||
| 
 | ||||
| 		// TODO: call set once
 | ||||
| 		bcid, aerr := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{ | ||||
| 			dealInfo.Deal.Proposal.Client:   clientBal, | ||||
| 			dealInfo.Deal.Proposal.Provider: providerBal, | ||||
| 		}) | ||||
| 		if aerr != nil { | ||||
| 			return nil, aerr | ||||
| 		} | ||||
| 
 | ||||
| 		self.Balances = bcid | ||||
| 	} | ||||
| 
 | ||||
| 	nroot, err := vmctx.Storage().Put(&self) | ||||
| 	if err != nil { | ||||
| 		return nil, aerrors.HandleExternalError(err, "storing state failed") | ||||
| 	} | ||||
| 
 | ||||
| 	aerr := vmctx.Storage().Commit(old, nroot) | ||||
| 	if aerr != nil { | ||||
| 		return nil, aerr | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, nil | ||||
| } | ||||
| 
 | ||||
| func lockFunds(p StorageParticipantBalance, amt types.BigInt) StorageParticipantBalance { | ||||
| 	p.Available, p.Locked = transferFunds(p.Available, p.Locked, amt) | ||||
| 	return p | ||||
| } | ||||
| 
 | ||||
| func transferFunds(from, to, amt types.BigInt) (types.BigInt, types.BigInt) { | ||||
| 	// TODO: some asserts
 | ||||
| 	return types.BigSub(from, amt), types.BigAdd(to, amt) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -3401,3 +3401,141 @@ func (t *PublishStorageDealResponse) UnmarshalCBOR(r io.Reader) error { | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (t *ActivateStorageDealsParams) MarshalCBOR(w io.Writer) error { | ||||
| 	if t == nil { | ||||
| 		_, err := w.Write(cbg.CborNull) | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err := w.Write([]byte{129}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// t.t.Deals ([]uint64)
 | ||||
| 	if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Deals)))); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, v := range t.Deals { | ||||
| 		if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, v); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (t *ActivateStorageDealsParams) UnmarshalCBOR(r io.Reader) error { | ||||
| 	br := cbg.GetPeeker(r) | ||||
| 
 | ||||
| 	maj, extra, err := cbg.CborReadHeader(br) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if maj != cbg.MajArray { | ||||
| 		return fmt.Errorf("cbor input should be of type array") | ||||
| 	} | ||||
| 
 | ||||
| 	if extra != 1 { | ||||
| 		return fmt.Errorf("cbor input had wrong number of fields") | ||||
| 	} | ||||
| 
 | ||||
| 	// t.t.Deals ([]uint64)
 | ||||
| 
 | ||||
| 	maj, extra, err = cbg.CborReadHeader(br) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if extra > 8192 { | ||||
| 		return fmt.Errorf("t.Deals: array too large (%d)", extra) | ||||
| 	} | ||||
| 
 | ||||
| 	if maj != cbg.MajArray { | ||||
| 		return fmt.Errorf("expected cbor array") | ||||
| 	} | ||||
| 	if extra > 0 { | ||||
| 		t.Deals = make([]uint64, extra) | ||||
| 	} | ||||
| 	for i := 0; i < int(extra); i++ { | ||||
| 
 | ||||
| 		maj, val, err := cbg.CborReadHeader(br) | ||||
| 		if err != nil { | ||||
| 			return xerrors.Errorf("failed to read uint64 for t.Deals slice: %w", err) | ||||
| 		} | ||||
| 
 | ||||
| 		if maj != cbg.MajUnsignedInt { | ||||
| 			return xerrors.Errorf("value read for array t.Deals was not a uint, instead got %d", maj) | ||||
| 		} | ||||
| 
 | ||||
| 		t.Deals[i] = val | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (t *ProcessStorageDealsPaymentParams) MarshalCBOR(w io.Writer) error { | ||||
| 	if t == nil { | ||||
| 		_, err := w.Write(cbg.CborNull) | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err := w.Write([]byte{129}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// t.t.DealIDs ([]uint64)
 | ||||
| 	if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.DealIDs)))); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, v := range t.DealIDs { | ||||
| 		if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, v); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (t *ProcessStorageDealsPaymentParams) UnmarshalCBOR(r io.Reader) error { | ||||
| 	br := cbg.GetPeeker(r) | ||||
| 
 | ||||
| 	maj, extra, err := cbg.CborReadHeader(br) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if maj != cbg.MajArray { | ||||
| 		return fmt.Errorf("cbor input should be of type array") | ||||
| 	} | ||||
| 
 | ||||
| 	if extra != 1 { | ||||
| 		return fmt.Errorf("cbor input had wrong number of fields") | ||||
| 	} | ||||
| 
 | ||||
| 	// t.t.DealIDs ([]uint64)
 | ||||
| 
 | ||||
| 	maj, extra, err = cbg.CborReadHeader(br) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if extra > 8192 { | ||||
| 		return fmt.Errorf("t.DealIDs: array too large (%d)", extra) | ||||
| 	} | ||||
| 
 | ||||
| 	if maj != cbg.MajArray { | ||||
| 		return fmt.Errorf("expected cbor array") | ||||
| 	} | ||||
| 	if extra > 0 { | ||||
| 		t.DealIDs = make([]uint64, extra) | ||||
| 	} | ||||
| 	for i := 0; i < int(extra); i++ { | ||||
| 
 | ||||
| 		maj, val, err := cbg.CborReadHeader(br) | ||||
| 		if err != nil { | ||||
| 			return xerrors.Errorf("failed to read uint64 for t.DealIDs slice: %w", err) | ||||
| 		} | ||||
| 
 | ||||
| 		if maj != cbg.MajUnsignedInt { | ||||
| 			return xerrors.Errorf("value read for array t.DealIDs was not a uint, instead got %d", maj) | ||||
| 		} | ||||
| 
 | ||||
| 		t.DealIDs[i] = val | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -82,6 +82,8 @@ func main() { | ||||
| 		actors.StorageDeal{}, | ||||
| 		actors.PublishStorageDealsParams{}, | ||||
| 		actors.PublishStorageDealResponse{}, | ||||
| 		actors.ActivateStorageDealsParams{}, | ||||
| 		actors.ProcessStorageDealsPaymentParams{}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user