actors: implement sma.PublishStorageDeals

This commit is contained in:
Łukasz Magiera 2019-10-19 06:54:22 +02:00
parent fcf928ec76
commit 8638cd25f5
3 changed files with 557 additions and 15 deletions

View File

@ -1,6 +1,9 @@
package actors
import (
"bytes"
"github.com/filecoin-project/go-amt-ipld"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-hamt-ipld"
@ -28,10 +31,10 @@ var SMAMethods = smaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
func (sma StorageMarketActor) Exports() []interface{} {
return []interface{}{
// 2: sma.WithdrawBalance,
// 3: sma.AddBalance,
2: sma.WithdrawBalance,
3: sma.AddBalance,
// 4: sma.CheckLockedBalance,
// 5: sma.PublishStorageDeals,
5: sma.PublishStorageDeals,
// 6: sma.HandleCronAction,
// 7: sma.SettleExpiredDeals,
// 8: sma.ProcessStorageDealsPayment,
@ -46,8 +49,26 @@ type StorageParticipantBalance struct {
}
type StorageMarketState struct {
Balances cid.Cid // hamt
Balances cid.Cid // hamt<StorageParticipantBalance>
Deals cid.Cid // amt
NextDealID uint64 // TODO: amt.LastIndex()
}
type StorageDealProposal struct {
PieceRef cid.Cid // can this mess anything up? Should this just be cid bytes
PieceSize uint64
Client address.Address
Provider address.Address
ProposalExpiration uint64
DealExpiration uint64
StoragePrice types.BigInt
StorageCollateral types.BigInt
ProposerSignature types.Signature
}
type StorageDeal struct {
Proposal StorageDealProposal
CounterSignature types.Signature
}
type WithdrawBalanceParams struct {
@ -61,7 +82,7 @@ func (sma StorageMarketActor) WithdrawBalance(act *types.Actor, vmctx types.VMCo
return nil, err
}
b, bnd, err := sma.getBalances(vmctx, self.Balances, []address.Address{vmctx.Message().From})
b, bnd, err := getMarketBalances(vmctx, self.Balances, []address.Address{vmctx.Message().From})
if err != nil {
return nil, aerrors.Wrap(err, "could not get balance")
}
@ -79,7 +100,7 @@ func (sma StorageMarketActor) WithdrawBalance(act *types.Actor, vmctx types.VMCo
return nil, aerrors.Wrap(err, "sending funds failed")
}
bcid, err := sma.setBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
bcid, err := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
vmctx.Message().From: balance,
})
if err != nil {
@ -103,7 +124,7 @@ func (sma StorageMarketActor) AddBalance(act *types.Actor, vmctx types.VMContext
return nil, err
}
b, bnd, err := sma.getBalances(vmctx, self.Balances, []address.Address{vmctx.Message().From})
b, bnd, err := getMarketBalances(vmctx, self.Balances, []address.Address{vmctx.Message().From})
if err != nil {
return nil, aerrors.Wrap(err, "could not get balance")
}
@ -112,7 +133,7 @@ func (sma StorageMarketActor) AddBalance(act *types.Actor, vmctx types.VMContext
balance.Available = types.BigAdd(balance.Available, vmctx.Message().Value)
bcid, err := sma.setBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
bcid, err := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
vmctx.Message().From: balance,
})
if err != nil {
@ -129,7 +150,7 @@ func (sma StorageMarketActor) AddBalance(act *types.Actor, vmctx types.VMContext
return nil, vmctx.Storage().Commit(old, nroot)
}
func (sma StorageMarketActor) setBalances(vmctx types.VMContext, nd *hamt.Node, set map[address.Address]StorageParticipantBalance) (cid.Cid, ActorError) {
func setMarketBalances(vmctx types.VMContext, nd *hamt.Node, set map[address.Address]StorageParticipantBalance) (cid.Cid, ActorError) {
for addr, b := range set {
if err := nd.Set(vmctx.Context(), string(addr.Bytes()), b); err != nil {
return cid.Undef, aerrors.HandleExternalError(err, "setting new balance")
@ -146,7 +167,7 @@ func (sma StorageMarketActor) setBalances(vmctx types.VMContext, nd *hamt.Node,
return c, nil
}
func (sma StorageMarketActor) getBalances(vmctx types.VMContext, rcid cid.Cid, addrs []address.Address) ([]StorageParticipantBalance, *hamt.Node, ActorError) {
func getMarketBalances(vmctx types.VMContext, rcid cid.Cid, addrs []address.Address) ([]StorageParticipantBalance, *hamt.Node, ActorError) {
nd, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), rcid)
if err != nil {
return nil, nil, aerrors.HandleExternalError(err, "failed to load miner set")
@ -178,11 +199,161 @@ func (sma StorageMarketActor) getBalances(vmctx types.VMContext, rcid cid.Cid, a
func (sma StorageMarketActor) CheckLockedBalance(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
}
*/
func (sma StorageMarketActor) PublishStorageDeals(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
type PublishStorageDealsParams struct {
Deals []StorageDeal
}
type PublishStorageDealResponse struct {
DealIDs []uint64
}
func (sma StorageMarketActor) PublishStorageDeals(act *types.Actor, vmctx types.VMContext, params *PublishStorageDealsParams) ([]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")
}
// todo: handle duplicate deals
out := PublishStorageDealResponse{
DealIDs: make([]uint64, len(params.Deals)),
}
for i, deal := range params.Deals {
if err := self.validateDeal(vmctx, deal); err != nil {
return nil, err
}
err := deals.Set(self.NextDealID, deal)
if err != nil {
return nil, aerrors.HandleExternalError(err, "setting deal in deal AMT")
}
out.DealIDs[i] = self.NextDealID
self.NextDealID++
}
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
}
var outBuf bytes.Buffer
if err := out.MarshalCBOR(&outBuf); err != nil {
return nil, aerrors.HandleExternalError(err, "serialising output")
}
return outBuf.Bytes(), nil
}
func (self *StorageMarketState) validateDeal(vmctx types.VMContext, deal StorageDeal) aerrors.ActorError {
// REVIEW: just > ?
if vmctx.BlockHeight() >= deal.Proposal.ProposalExpiration {
return aerrors.New(1, "deal proposal already expired")
}
if vmctx.BlockHeight() >= deal.Proposal.DealExpiration {
return aerrors.New(2, "deal proposal already expired")
}
var proposalBuf bytes.Buffer
err := deal.Proposal.MarshalCBOR(&proposalBuf)
if err != nil {
return aerrors.HandleExternalError(err, "serializing deal proposal failed")
}
err = deal.Proposal.ProposerSignature.Verify(deal.Proposal.Client, proposalBuf.Bytes())
if err != nil {
return aerrors.HandleExternalError(err, "verifying proposer signature")
}
var dealBuf bytes.Buffer
err = deal.MarshalCBOR(&dealBuf)
if err != nil {
return aerrors.HandleExternalError(err, "serializing deal failed")
}
err = deal.CounterSignature.Verify(deal.Proposal.Provider, dealBuf.Bytes())
if err != nil {
return aerrors.HandleExternalError(err, "verifying provider signature")
}
// 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")
}
// TODO: REVIEW: Do we want to check if provider exists in the power actor?
// TODO: do some caching (changes gas so needs to be in spec too)
b, bnd, aerr := getMarketBalances(vmctx, self.Balances, []address.Address{
deal.Proposal.Client,
deal.Proposal.Provider,
})
if aerr != nil {
return aerrors.Wrap(aerr, "getting client, and provider balances")
}
clientBalance := b[0]
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)
}
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)
}
providerBalance = lockFunds(providerBalance, deal.Proposal.StorageCollateral)
// TODO: piece checks (e.g. size > sectorSize)?
bcid, aerr := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
deal.Proposal.Client: clientBalance,
deal.Proposal.Provider: providerBalance,
})
if aerr != nil {
return aerr
}
self.Balances = bcid
return 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) {
return types.BigSub(from, amt), types.BigAdd(to, amt)
}
/*
func (sma StorageMarketActor) HandleCronAction(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
}

View File

@ -2931,7 +2931,7 @@ func (t *StorageMarketState) MarshalCBOR(w io.Writer) error {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{130}); err != nil {
if _, err := w.Write([]byte{131}); err != nil {
return err
}
@ -2947,6 +2947,10 @@ func (t *StorageMarketState) MarshalCBOR(w io.Writer) error {
return xerrors.Errorf("failed to write cid field t.Deals: %w", err)
}
// t.t.NextDealID (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.NextDealID)); err != nil {
return err
}
return nil
}
@ -2961,7 +2965,7 @@ func (t *StorageMarketState) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("cbor input should be of type array")
}
if extra != 2 {
if extra != 3 {
return fmt.Errorf("cbor input had wrong number of fields")
}
@ -2989,6 +2993,16 @@ func (t *StorageMarketState) UnmarshalCBOR(r io.Reader) error {
t.Deals = c
}
// t.t.NextDealID (uint64)
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.NextDealID = extra
return nil
}
@ -3034,3 +3048,356 @@ func (t *WithdrawBalanceParams) UnmarshalCBOR(r io.Reader) error {
}
return nil
}
func (t *StorageDealProposal) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{137}); err != nil {
return err
}
// t.t.PieceRef (cid.Cid)
if err := cbg.WriteCid(w, t.PieceRef); err != nil {
return xerrors.Errorf("failed to write cid field t.PieceRef: %w", err)
}
// t.t.PieceSize (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.PieceSize)); err != nil {
return err
}
// t.t.Client (address.Address)
if err := t.Client.MarshalCBOR(w); err != nil {
return err
}
// t.t.Provider (address.Address)
if err := t.Provider.MarshalCBOR(w); err != nil {
return err
}
// t.t.ProposalExpiration (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.ProposalExpiration)); err != nil {
return err
}
// t.t.DealExpiration (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.DealExpiration)); err != nil {
return err
}
// t.t.StoragePrice (types.BigInt)
if err := t.StoragePrice.MarshalCBOR(w); err != nil {
return err
}
// t.t.StorageCollateral (types.BigInt)
if err := t.StorageCollateral.MarshalCBOR(w); err != nil {
return err
}
// t.t.ProposerSignature (types.Signature)
if err := t.ProposerSignature.MarshalCBOR(w); err != nil {
return err
}
return nil
}
func (t *StorageDealProposal) 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 != 9 {
return fmt.Errorf("cbor input had wrong number of fields")
}
// t.t.PieceRef (cid.Cid)
{
c, err := cbg.ReadCid(br)
if err != nil {
return xerrors.Errorf("failed to read cid field t.PieceRef: %w", err)
}
t.PieceRef = c
}
// t.t.PieceSize (uint64)
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.PieceSize = extra
// t.t.Client (address.Address)
{
if err := t.Client.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.t.Provider (address.Address)
{
if err := t.Provider.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.t.ProposalExpiration (uint64)
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.ProposalExpiration = extra
// t.t.DealExpiration (uint64)
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.DealExpiration = extra
// t.t.StoragePrice (types.BigInt)
{
if err := t.StoragePrice.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.t.StorageCollateral (types.BigInt)
{
if err := t.StorageCollateral.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.t.ProposerSignature (types.Signature)
{
if err := t.ProposerSignature.UnmarshalCBOR(br); err != nil {
return err
}
}
return nil
}
func (t *StorageDeal) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{130}); err != nil {
return err
}
// t.t.Proposal (actors.StorageDealProposal)
if err := t.Proposal.MarshalCBOR(w); err != nil {
return err
}
// t.t.CounterSignature (types.Signature)
if err := t.CounterSignature.MarshalCBOR(w); err != nil {
return err
}
return nil
}
func (t *StorageDeal) 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 != 2 {
return fmt.Errorf("cbor input had wrong number of fields")
}
// t.t.Proposal (actors.StorageDealProposal)
{
if err := t.Proposal.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.t.CounterSignature (types.Signature)
{
if err := t.CounterSignature.UnmarshalCBOR(br); err != nil {
return err
}
}
return nil
}
func (t *PublishStorageDealsParams) 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 ([]actors.StorageDeal)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Deals)))); err != nil {
return err
}
for _, v := range t.Deals {
if err := v.MarshalCBOR(w); err != nil {
return err
}
}
return nil
}
func (t *PublishStorageDealsParams) 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 ([]actors.StorageDeal)
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([]StorageDeal, extra)
}
for i := 0; i < int(extra); i++ {
var v StorageDeal
if err := v.UnmarshalCBOR(br); err != nil {
return err
}
t.Deals[i] = v
}
return nil
}
func (t *PublishStorageDealResponse) 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 *PublishStorageDealResponse) 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

@ -78,6 +78,10 @@ func main() {
actors.StorageParticipantBalance{},
actors.StorageMarketState{},
actors.WithdrawBalanceParams{},
actors.StorageDealProposal{},
actors.StorageDeal{},
actors.PublishStorageDealsParams{},
actors.PublishStorageDealResponse{},
)
if err != nil {
fmt.Println(err)