on chain deals: More actor methods

This commit is contained in:
Łukasz Magiera 2019-10-19 12:20:33 +02:00
parent c41dd4efeb
commit 6bade2eb23
4 changed files with 321 additions and 8 deletions

View File

@ -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)

View File

@ -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)
}

View File

@ -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
}

View File

@ -82,6 +82,8 @@ func main() {
actors.StorageDeal{},
actors.PublishStorageDealsParams{},
actors.PublishStorageDealResponse{},
actors.ActivateStorageDealsParams{},
actors.ProcessStorageDealsPaymentParams{},
)
if err != nil {
fmt.Println(err)