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