diff --git a/shared_testutil/generators.go b/shared_testutil/generators.go index 7007a8df..92df574d 100644 --- a/shared_testutil/generators.go +++ b/shared_testutil/generators.go @@ -162,6 +162,7 @@ func MakeTestClientDeal(state storagemarket.StorageDealStatus, clientDealProposa Miner: p, MinerWorker: address.TestAddress2, DataRef: MakeTestDataRef(manualXfer), + DealStages: storagemarket.NewDealStages(), }, nil } diff --git a/storagemarket/dealstatus.go b/storagemarket/dealstatus.go index dc10de7a..8aa0b1fc 100644 --- a/storagemarket/dealstatus.go +++ b/storagemarket/dealstatus.go @@ -141,3 +141,70 @@ var DealStates = map[StorageDealStatus]string{ StorageDealClientTransferRestart: "StorageDealClientTransferRestart", StorageDealProviderTransferAwaitRestart: "StorageDealProviderTransferAwaitRestart", } + +// DealStatesDescriptions maps StorageDealStatus codes to string description for better UX +var DealStatesDescriptions = map[StorageDealStatus]string{ + StorageDealUnknown: "Unknown", + StorageDealProposalNotFound: "Proposal not found", + StorageDealProposalRejected: "Proposal rejected", + StorageDealProposalAccepted: "Proposal accepted", + StorageDealAcceptWait: "AcceptWait", + StorageDealStartDataTransfer: "Starting data transfer", + StorageDealStaged: "Staged", + StorageDealAwaitingPreCommit: "Awaiting a PreCommit message on chain", + StorageDealSealing: "Sealing", + StorageDealActive: "Active", + StorageDealExpired: "Expired", + StorageDealSlashed: "Slashed", + StorageDealRejecting: "Rejecting", + StorageDealFailing: "Failing", + StorageDealFundsReserved: "FundsReserved", + StorageDealCheckForAcceptance: "Checking for deal acceptance", + StorageDealValidating: "Validating", + StorageDealTransferring: "Transferring", + StorageDealWaitingForData: "Waiting for data", + StorageDealVerifyData: "Verifying data", + StorageDealReserveProviderFunds: "Reserving provider funds", + StorageDealReserveClientFunds: "Reserving client funds", + StorageDealProviderFunding: "Provider funding", + StorageDealClientFunding: "Client funding", + StorageDealPublish: "Publish", + StorageDealPublishing: "Publishing", + StorageDealError: "Error", + StorageDealFinalizing: "Finalizing", + StorageDealClientTransferRestart: "Client transfer restart", + StorageDealProviderTransferAwaitRestart: "ProviderTransferAwaitRestart", +} + +var DealStatesDurations = map[StorageDealStatus]string{ + StorageDealUnknown: "", + StorageDealProposalNotFound: "", + StorageDealProposalRejected: "", + StorageDealProposalAccepted: "a few minutes", + StorageDealAcceptWait: "a few minutes", + StorageDealStartDataTransfer: "a few minutes", + StorageDealStaged: "a few minutes", + StorageDealAwaitingPreCommit: "a few minutes", + StorageDealSealing: "a few hours", + StorageDealActive: "", + StorageDealExpired: "", + StorageDealSlashed: "", + StorageDealRejecting: "", + StorageDealFailing: "", + StorageDealFundsReserved: "a few minutes", + StorageDealCheckForAcceptance: "a few minutes", + StorageDealValidating: "a few minutes", + StorageDealTransferring: "a few minutes", + StorageDealWaitingForData: "a few minutes", + StorageDealVerifyData: "a few minutes", + StorageDealReserveProviderFunds: "a few minutes", + StorageDealReserveClientFunds: "a few minutes", + StorageDealProviderFunding: "a few minutes", + StorageDealClientFunding: "a few minutes", + StorageDealPublish: "a few minutes", + StorageDealPublishing: "a few minutes", + StorageDealError: "", + StorageDealFinalizing: "a few minutes", + StorageDealClientTransferRestart: "depending on data size, anywhere between a few minutes to a few hours", + StorageDealProviderTransferAwaitRestart: "a few minutes", +} diff --git a/storagemarket/impl/client.go b/storagemarket/impl/client.go index c3ff4e6c..da2b45e2 100644 --- a/storagemarket/impl/client.go +++ b/storagemarket/impl/client.go @@ -388,6 +388,7 @@ func (c *Client) ProposeStorageDeal(ctx context.Context, params storagemarket.Pr DataRef: params.Data, FastRetrieval: params.FastRetrieval, StoreID: params.StoreID, + DealStages: storagemarket.NewDealStages(), CreationTime: curTime(), } diff --git a/storagemarket/impl/clientstates/client_fsm.go b/storagemarket/impl/clientstates/client_fsm.go index f7ed90d9..7a37213c 100644 --- a/storagemarket/impl/clientstates/client_fsm.go +++ b/storagemarket/impl/clientstates/client_fsm.go @@ -22,12 +22,14 @@ var ClientEvents = fsm.Events{ From(storagemarket.StorageDealReserveClientFunds).To(storagemarket.StorageDealClientFunding). Action(func(deal *storagemarket.ClientDeal, mcid cid.Cid) error { deal.AddFundsCid = &mcid + deal.AddLog("reserving funds for storage deal, message cid: <%s>", mcid) return nil }), fsm.Event(storagemarket.ClientEventReserveFundsFailed). FromMany(storagemarket.StorageDealClientFunding, storagemarket.StorageDealReserveClientFunds).To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("adding market funds failed: %w", err).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventFundsReserved). @@ -38,12 +40,14 @@ var ClientEvents = fsm.Events{ } else { deal.FundsReserved = big.Add(deal.FundsReserved, fundsReserved) } + deal.AddLog("funds reserved, amount <%s>", fundsReserved) return nil }), fsm.Event(storagemarket.ClientEventFundsReleased). FromMany(storagemarket.StorageDealProposalAccepted, storagemarket.StorageDealFailing).ToJustRecord(). Action(func(deal *storagemarket.ClientDeal, fundsReleased abi.TokenAmount) error { deal.FundsReserved = big.Subtract(deal.FundsReserved, fundsReleased) + deal.AddLog("funds released, amount <%s>", fundsReleased) return nil }), fsm.Event(storagemarket.ClientEventFundingComplete). @@ -52,27 +56,34 @@ var ClientEvents = fsm.Events{ From(storagemarket.StorageDealFundsReserved).To(storagemarket.StorageDealError). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("sending proposal to storage provider failed: %w", err).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventReadResponseFailed). From(storagemarket.StorageDealFundsReserved).To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal, err error) error { - deal.Message = xerrors.Errorf("error reading Response message: %w", err).Error() + deal.Message = xerrors.Errorf("error reading Response message from provider: %w", err).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventResponseVerificationFailed). From(storagemarket.StorageDealFundsReserved).To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal) error { deal.Message = "unable to verify signature on deal response" + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventInitiateDataTransfer). - From(storagemarket.StorageDealFundsReserved).To(storagemarket.StorageDealStartDataTransfer), - + From(storagemarket.StorageDealFundsReserved).To(storagemarket.StorageDealStartDataTransfer). + Action(func(deal *storagemarket.ClientDeal) error { + deal.AddLog("opening data transfer to storage provider") + return nil + }), fsm.Event(storagemarket.ClientEventUnexpectedDealState). From(storagemarket.StorageDealFundsReserved).To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal, status storagemarket.StorageDealStatus, providerMessage string) error { deal.Message = xerrors.Errorf("unexpected deal status while waiting for data request: %d (%s). Provider message: %s", status, storagemarket.DealStates[status], providerMessage).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventDataTransferFailed). @@ -80,6 +91,7 @@ var ClientEvents = fsm.Events{ To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("failed to complete data transfer: %w", err).Error() + deal.AddLog(deal.Message) return nil }), @@ -87,6 +99,7 @@ var ClientEvents = fsm.Events{ To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("failed to restart data transfer: %w", err).Error() + deal.AddLog(deal.Message) return nil }), @@ -94,6 +107,7 @@ var ClientEvents = fsm.Events{ FromMany(storagemarket.StorageDealStartDataTransfer).To(storagemarket.StorageDealTransferring). Action(func(deal *storagemarket.ClientDeal, channelId datatransfer.ChannelID) error { deal.TransferChannelID = &channelId + deal.AddLog("data transfer initiated on channel id <%s>", channelId) return nil }), @@ -103,6 +117,7 @@ var ClientEvents = fsm.Events{ Action(func(deal *storagemarket.ClientDeal, channelId datatransfer.ChannelID) error { deal.TransferChannelID = &channelId deal.Message = "" + deal.AddLog("data transfer restarted on channel id <%s>", channelId) return nil }), @@ -111,6 +126,7 @@ var ClientEvents = fsm.Events{ To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("could not complete data transfer, could not connect to provider %s", deal.Miner).Error() + deal.AddLog(deal.Message) return nil }), @@ -123,6 +139,7 @@ var ClientEvents = fsm.Events{ To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal) error { deal.Message = "data transfer cancelled" + deal.AddLog(deal.Message) return nil }), @@ -137,18 +154,32 @@ var ClientEvents = fsm.Events{ deal.PollErrorCount++ } deal.Message = fmt.Sprintf("Provider state: %s", storagemarket.DealStates[providerState]) + switch storagemarket.DealStates[providerState] { + case "StorageDealVerifyData": + deal.AddLog("provider is verifying the data") + case "StorageDealPublish": + deal.AddLog("waiting for provider to publish the deal on-chain") // TODO: is that right? + case "StorageDealPublishing": + deal.AddLog("provider has submitted the deal on-chain and is waiting for confirmation") // TODO: is that right? + case "StorageDealProviderFunding": + deal.AddLog("waiting for provider to lock collateral on-chain") // TODO: is that right? + default: + deal.AddLog(deal.Message) + } return nil }), fsm.Event(storagemarket.ClientEventResponseDealDidNotMatch). From(storagemarket.StorageDealCheckForAcceptance).To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal, responseCid cid.Cid, proposalCid cid.Cid) error { deal.Message = xerrors.Errorf("miner responded to a wrong proposal: %s != %s", responseCid, proposalCid).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventDealRejected). From(storagemarket.StorageDealCheckForAcceptance).To(storagemarket.StorageDealFailing). Action(func(deal *storagemarket.ClientDeal, state storagemarket.StorageDealStatus, reason string) error { deal.Message = xerrors.Errorf("deal failed: (State=%d) %s", state, reason).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventDealAccepted). @@ -156,51 +187,63 @@ var ClientEvents = fsm.Events{ Action(func(deal *storagemarket.ClientDeal, publishMessage *cid.Cid) error { deal.PublishMessage = publishMessage deal.Message = "" + deal.AddLog("deal has been accepted by storage provider") return nil }), fsm.Event(storagemarket.ClientEventStreamCloseError). FromAny().To(storagemarket.StorageDealError). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("error attempting to close stream: %w", err).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventDealPublishFailed). From(storagemarket.StorageDealProposalAccepted).To(storagemarket.StorageDealError). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("error validating deal published: %w", err).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventDealPublished). From(storagemarket.StorageDealProposalAccepted).To(storagemarket.StorageDealAwaitingPreCommit). Action(func(deal *storagemarket.ClientDeal, dealID abi.DealID) error { deal.DealID = dealID + deal.AddLog("") return nil }), fsm.Event(storagemarket.ClientEventDealPrecommitFailed). From(storagemarket.StorageDealAwaitingPreCommit).To(storagemarket.StorageDealError). Action(func(deal *storagemarket.ClientDeal, err error) error { - deal.Message = xerrors.Errorf("error awaiting deal pre-commit: %w", err).Error() + deal.Message = xerrors.Errorf("error waiting for deal pre-commit message to appear on chain: %w", err).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventDealPrecommitted). From(storagemarket.StorageDealAwaitingPreCommit).To(storagemarket.StorageDealSealing). Action(func(deal *storagemarket.ClientDeal, sectorNumber abi.SectorNumber) error { deal.SectorNumber = sectorNumber + deal.AddLog("deal pre-commit message has landed on chain") return nil }), fsm.Event(storagemarket.ClientEventDealActivationFailed). From(storagemarket.StorageDealSealing).To(storagemarket.StorageDealError). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("error in deal activation: %w", err).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventDealActivated). FromMany(storagemarket.StorageDealAwaitingPreCommit, storagemarket.StorageDealSealing). - To(storagemarket.StorageDealActive), + To(storagemarket.StorageDealActive). + Action(func(deal *storagemarket.ClientDeal) error { + deal.AddLog("deal activated") + return nil + }), fsm.Event(storagemarket.ClientEventDealSlashed). From(storagemarket.StorageDealActive).To(storagemarket.StorageDealSlashed). Action(func(deal *storagemarket.ClientDeal, slashEpoch abi.ChainEpoch) error { deal.SlashEpoch = slashEpoch + deal.AddLog("deal slashed at epoch <%d>", slashEpoch) return nil }), fsm.Event(storagemarket.ClientEventDealExpired). @@ -209,10 +252,15 @@ var ClientEvents = fsm.Events{ From(storagemarket.StorageDealActive).To(storagemarket.StorageDealError). Action(func(deal *storagemarket.ClientDeal, err error) error { deal.Message = xerrors.Errorf("error waiting for deal completion: %w", err).Error() + deal.AddLog(deal.Message) return nil }), fsm.Event(storagemarket.ClientEventFailed). - From(storagemarket.StorageDealFailing).To(storagemarket.StorageDealError), + From(storagemarket.StorageDealFailing).To(storagemarket.StorageDealError). + Action(func(deal *storagemarket.ClientDeal) error { + deal.AddLog("") + return nil + }), fsm.Event(storagemarket.ClientEventRestart).From(storagemarket.StorageDealTransferring).To(storagemarket.StorageDealClientTransferRestart). FromAny().ToNoChange(), } diff --git a/storagemarket/impl/clientstates/client_states_test.go b/storagemarket/impl/clientstates/client_states_test.go index ec27d848..3459e34e 100644 --- a/storagemarket/impl/clientstates/client_states_test.go +++ b/storagemarket/impl/clientstates/client_states_test.go @@ -153,7 +153,7 @@ func TestProposeDeal(t *testing.T) { }, inspector: func(deal storagemarket.ClientDeal, env *fakeEnvironment) { tut.AssertDealState(t, storagemarket.StorageDealFailing, deal.State) - assert.Equal(t, "error reading Response message: read response failed", deal.Message) + assert.Equal(t, "error reading Response message from provider: read response failed", deal.Message) }, }) }) @@ -437,7 +437,7 @@ func TestVerifyDealPreCommitted(t *testing.T) { nodeParams: nodeParams{DealPreCommittedSyncError: errors.New("Something went wrong")}, inspector: func(deal storagemarket.ClientDeal, env *fakeEnvironment) { tut.AssertDealState(t, storagemarket.StorageDealError, deal.State) - assert.Equal(t, "error awaiting deal pre-commit: Something went wrong", deal.Message) + assert.Equal(t, "error waiting for deal pre-commit message to appear on chain: Something went wrong", deal.Message) }, }) }) @@ -446,7 +446,7 @@ func TestVerifyDealPreCommitted(t *testing.T) { nodeParams: nodeParams{DealPreCommittedAsyncError: errors.New("Something went wrong later")}, inspector: func(deal storagemarket.ClientDeal, env *fakeEnvironment) { tut.AssertDealState(t, storagemarket.StorageDealError, deal.State) - assert.Equal(t, "error awaiting deal pre-commit: Something went wrong later", deal.Message) + assert.Equal(t, "error waiting for deal pre-commit message to appear on chain: Something went wrong later", deal.Message) }, }) }) diff --git a/storagemarket/types.go b/storagemarket/types.go index ed642c13..eef6c190 100644 --- a/storagemarket/types.go +++ b/storagemarket/types.go @@ -1,7 +1,11 @@ package storagemarket import ( + "fmt" + "time" + "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" "github.com/libp2p/go-libp2p-core/peer" ma "github.com/multiformats/go-multiaddr" cbg "github.com/whyrusleeping/cbor-gen" @@ -16,7 +20,9 @@ import ( "github.com/filecoin-project/go-fil-markets/filestore" ) -//go:generate cbor-gen-for --map-encoding ClientDeal MinerDeal Balance SignedStorageAsk StorageAsk DataRef ProviderDealState +var log = logging.Logger("storagemrkt") + +//go:generate cbor-gen-for --map-encoding ClientDeal MinerDeal Balance SignedStorageAsk StorageAsk DataRef ProviderDealState DealStages DealStage Log // DealProtocolID is the ID for the libp2p protocol for proposing storage deals. const OldDealProtocolID = "/fil/storage/mk/1.0.1" @@ -108,6 +114,118 @@ type MinerDeal struct { SectorNumber abi.SectorNumber } +// NewDealStages creates a new DealStages object ready to be used. +// EXPERIMENTAL; subject to change. +func NewDealStages() *DealStages { + return &DealStages{} +} + +// DealStages captures a timeline of the progress of a deal, grouped by stages. +// EXPERIMENTAL; subject to change. +type DealStages struct { + // Stages contains an entry for every stage that the deal has gone through. + // Each stage then contains logs. + Stages []*DealStage +} + +// DealStages captures data about the execution of a deal stage. +// EXPERIMENTAL; subject to change. +type DealStage struct { + // Human-readable fields. + // TODO: these _will_ need to be converted to canonical representations, so + // they are machine readable. + Name string + Description string + ExpectedDuration string + + // Timestamps. + // TODO: may be worth adding an exit timestamp. It _could_ be inferred from + // the start of the next stage, or from the timestamp of the last log line + // if this is a terminal stage. But that's non-determistic and it relies on + // assumptions. + CreatedTime cbg.CborTime + UpdatedTime cbg.CborTime + + // Logs contains a detailed timeline of events that occurred inside + // this stage. + Logs []*Log +} + +// Log represents a point-in-time event that occurred inside a deal stage. +// EXPERIMENTAL; subject to change. +type Log struct { + // Log is a human readable message. + // + // TODO: this _may_ need to be converted to a canonical data model so it + // is machine-readable. + Log string + + UpdatedTime cbg.CborTime +} + +// GetStage returns the DealStage object for a named stage, or nil if not found. +// +// TODO: the input should be a strongly-typed enum instead of a free-form string. +// TODO: drop Get from GetStage to make this code more idiomatic. Return a +// second ok boolean to make it even more idiomatic. +// EXPERIMENTAL; subject to change. +func (ds *DealStages) GetStage(stage string) *DealStage { + if ds == nil { + return nil + } + + for _, s := range ds.Stages { + if s.Name == stage { + return s + } + } + + return nil +} + +// AddStageLog adds a log to the specified stage, creating the stage if it +// doesn't exist yet. +// EXPERIMENTAL; subject to change. +func (ds *DealStages) AddStageLog(stage, description, expectedDuration, msg string) { + if ds == nil { + return + } + + log.Debugf("adding log for stage <%s> msg <%s>", stage, msg) + + now := curTime() + st := ds.GetStage(stage) + if st == nil { + st = &DealStage{ + CreatedTime: now, + } + ds.Stages = append(ds.Stages, st) + } + + st.Name = stage + st.Description = description + st.ExpectedDuration = expectedDuration + st.UpdatedTime = now + if msg != "" && (len(st.Logs) == 0 || st.Logs[len(st.Logs)-1].Log != msg) { + // only add the log if it's not a duplicate. + st.Logs = append(st.Logs, &Log{msg, now}) + } +} + +// AddLog adds a log inside the DealStages object of the deal. +// EXPERIMENTAL; subject to change. +func (d *ClientDeal) AddLog(msg string, a ...interface{}) { + if len(a) > 0 { + msg = fmt.Sprintf(msg, a...) + } + + stage := DealStates[d.State] + description := DealStatesDescriptions[d.State] + expectedDuration := DealStatesDurations[d.State] + + d.DealStages.AddStageLog(stage, description, expectedDuration, msg) +} + // ClientDeal is the local state tracked for a deal by a StorageClient type ClientDeal struct { market.ClientDealProposal @@ -119,6 +237,7 @@ type ClientDeal struct { DealID abi.DealID DataRef *DataRef Message string + DealStages *DealStages PublishMessage *cid.Cid SlashEpoch abi.ChainEpoch PollRetryCount uint64 @@ -192,3 +311,8 @@ type ProviderDealState struct { DealID abi.DealID FastRetrieval bool } + +func curTime() cbg.CborTime { + now := time.Now() + return cbg.CborTime(time.Unix(0, now.UnixNano()).UTC()) +} diff --git a/storagemarket/types_cbor_gen.go b/storagemarket/types_cbor_gen.go index 7181ce1f..c9b7b29b 100644 --- a/storagemarket/types_cbor_gen.go +++ b/storagemarket/types_cbor_gen.go @@ -28,7 +28,7 @@ func (t *ClientDeal) MarshalCBOR(w io.Writer) error { _, err := w.Write(cbg.CborNull) return err } - if _, err := w.Write([]byte{179}); err != nil { + if _, err := w.Write([]byte{180}); err != nil { return err } @@ -198,6 +198,22 @@ func (t *ClientDeal) MarshalCBOR(w io.Writer) error { return err } + // t.DealStages (storagemarket.DealStages) (struct) + if len("DealStages") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"DealStages\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("DealStages"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("DealStages")); err != nil { + return err + } + + if err := t.DealStages.MarshalCBOR(w); err != nil { + return err + } + // t.PublishMessage (cid.Cid) (struct) if len("PublishMessage") > cbg.MaxLength { return xerrors.Errorf("Value in field \"PublishMessage\" was too long") @@ -540,6 +556,26 @@ func (t *ClientDeal) UnmarshalCBOR(r io.Reader) error { t.Message = string(sval) } + // t.DealStages (storagemarket.DealStages) (struct) + case "DealStages": + + { + + b, err := br.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := br.UnreadByte(); err != nil { + return err + } + t.DealStages = new(DealStages) + if err := t.DealStages.UnmarshalCBOR(br); err != nil { + return xerrors.Errorf("unmarshaling t.DealStages pointer: %w", err) + } + } + + } // t.PublishMessage (cid.Cid) (struct) case "PublishMessage": @@ -2616,3 +2652,490 @@ func (t *ProviderDealState) UnmarshalCBOR(r io.Reader) error { return nil } +func (t *DealStages) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{161}); err != nil { + return err + } + + scratch := make([]byte, 9) + + // t.Stages ([]*storagemarket.DealStage) (slice) + if len("Stages") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"Stages\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Stages"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("Stages")); err != nil { + return err + } + + if len(t.Stages) > cbg.MaxLength { + return xerrors.Errorf("Slice value in field t.Stages was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Stages))); err != nil { + return err + } + for _, v := range t.Stages { + if err := v.MarshalCBOR(w); err != nil { + return err + } + } + return nil +} + +func (t *DealStages) UnmarshalCBOR(r io.Reader) error { + *t = DealStages{} + + br := cbg.GetPeeker(r) + scratch := make([]byte, 8) + + maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) + if err != nil { + return err + } + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("DealStages: map struct too large (%d)", extra) + } + + var name string + n := extra + + for i := uint64(0); i < n; i++ { + + { + sval, err := cbg.ReadStringBuf(br, scratch) + if err != nil { + return err + } + + name = string(sval) + } + + switch name { + // t.Stages ([]*storagemarket.DealStage) (slice) + case "Stages": + + maj, extra, err = cbg.CborReadHeaderBuf(br, scratch) + if err != nil { + return err + } + + if extra > cbg.MaxLength { + return fmt.Errorf("t.Stages: array too large (%d)", extra) + } + + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + + if extra > 0 { + t.Stages = make([]*DealStage, extra) + } + + for i := 0; i < int(extra); i++ { + + var v DealStage + if err := v.UnmarshalCBOR(br); err != nil { + return err + } + + t.Stages[i] = &v + } + + default: + // Field doesn't exist on this type, so ignore it + cbg.ScanForLinks(r, func(cid.Cid) {}) + } + } + + return nil +} +func (t *DealStage) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{166}); err != nil { + return err + } + + scratch := make([]byte, 9) + + // t.Name (string) (string) + if len("Name") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"Name\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Name"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("Name")); err != nil { + return err + } + + if len(t.Name) > cbg.MaxLength { + return xerrors.Errorf("Value in field t.Name was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.Name))); err != nil { + return err + } + if _, err := io.WriteString(w, string(t.Name)); err != nil { + return err + } + + // t.Description (string) (string) + if len("Description") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"Description\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Description"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("Description")); err != nil { + return err + } + + if len(t.Description) > cbg.MaxLength { + return xerrors.Errorf("Value in field t.Description was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.Description))); err != nil { + return err + } + if _, err := io.WriteString(w, string(t.Description)); err != nil { + return err + } + + // t.ExpectedDuration (string) (string) + if len("ExpectedDuration") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"ExpectedDuration\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("ExpectedDuration"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("ExpectedDuration")); err != nil { + return err + } + + if len(t.ExpectedDuration) > cbg.MaxLength { + return xerrors.Errorf("Value in field t.ExpectedDuration was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.ExpectedDuration))); err != nil { + return err + } + if _, err := io.WriteString(w, string(t.ExpectedDuration)); err != nil { + return err + } + + // t.CreatedTime (typegen.CborTime) (struct) + if len("CreatedTime") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"CreatedTime\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("CreatedTime"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("CreatedTime")); err != nil { + return err + } + + if err := t.CreatedTime.MarshalCBOR(w); err != nil { + return err + } + + // t.UpdatedTime (typegen.CborTime) (struct) + if len("UpdatedTime") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"UpdatedTime\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("UpdatedTime"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("UpdatedTime")); err != nil { + return err + } + + if err := t.UpdatedTime.MarshalCBOR(w); err != nil { + return err + } + + // t.Logs ([]*storagemarket.Log) (slice) + if len("Logs") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"Logs\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Logs"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("Logs")); err != nil { + return err + } + + if len(t.Logs) > cbg.MaxLength { + return xerrors.Errorf("Slice value in field t.Logs was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Logs))); err != nil { + return err + } + for _, v := range t.Logs { + if err := v.MarshalCBOR(w); err != nil { + return err + } + } + return nil +} + +func (t *DealStage) UnmarshalCBOR(r io.Reader) error { + *t = DealStage{} + + br := cbg.GetPeeker(r) + scratch := make([]byte, 8) + + maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) + if err != nil { + return err + } + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("DealStage: map struct too large (%d)", extra) + } + + var name string + n := extra + + for i := uint64(0); i < n; i++ { + + { + sval, err := cbg.ReadStringBuf(br, scratch) + if err != nil { + return err + } + + name = string(sval) + } + + switch name { + // t.Name (string) (string) + case "Name": + + { + sval, err := cbg.ReadStringBuf(br, scratch) + if err != nil { + return err + } + + t.Name = string(sval) + } + // t.Description (string) (string) + case "Description": + + { + sval, err := cbg.ReadStringBuf(br, scratch) + if err != nil { + return err + } + + t.Description = string(sval) + } + // t.ExpectedDuration (string) (string) + case "ExpectedDuration": + + { + sval, err := cbg.ReadStringBuf(br, scratch) + if err != nil { + return err + } + + t.ExpectedDuration = string(sval) + } + // t.CreatedTime (typegen.CborTime) (struct) + case "CreatedTime": + + { + + if err := t.CreatedTime.UnmarshalCBOR(br); err != nil { + return xerrors.Errorf("unmarshaling t.CreatedTime: %w", err) + } + + } + // t.UpdatedTime (typegen.CborTime) (struct) + case "UpdatedTime": + + { + + if err := t.UpdatedTime.UnmarshalCBOR(br); err != nil { + return xerrors.Errorf("unmarshaling t.UpdatedTime: %w", err) + } + + } + // t.Logs ([]*storagemarket.Log) (slice) + case "Logs": + + maj, extra, err = cbg.CborReadHeaderBuf(br, scratch) + if err != nil { + return err + } + + if extra > cbg.MaxLength { + return fmt.Errorf("t.Logs: array too large (%d)", extra) + } + + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + + if extra > 0 { + t.Logs = make([]*Log, extra) + } + + for i := 0; i < int(extra); i++ { + + var v Log + if err := v.UnmarshalCBOR(br); err != nil { + return err + } + + t.Logs[i] = &v + } + + default: + // Field doesn't exist on this type, so ignore it + cbg.ScanForLinks(r, func(cid.Cid) {}) + } + } + + return nil +} +func (t *Log) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{162}); err != nil { + return err + } + + scratch := make([]byte, 9) + + // t.Log (string) (string) + if len("Log") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"Log\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Log"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("Log")); err != nil { + return err + } + + if len(t.Log) > cbg.MaxLength { + return xerrors.Errorf("Value in field t.Log was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.Log))); err != nil { + return err + } + if _, err := io.WriteString(w, string(t.Log)); err != nil { + return err + } + + // t.UpdatedTime (typegen.CborTime) (struct) + if len("UpdatedTime") > cbg.MaxLength { + return xerrors.Errorf("Value in field \"UpdatedTime\" was too long") + } + + if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("UpdatedTime"))); err != nil { + return err + } + if _, err := io.WriteString(w, string("UpdatedTime")); err != nil { + return err + } + + if err := t.UpdatedTime.MarshalCBOR(w); err != nil { + return err + } + return nil +} + +func (t *Log) UnmarshalCBOR(r io.Reader) error { + *t = Log{} + + br := cbg.GetPeeker(r) + scratch := make([]byte, 8) + + maj, extra, err := cbg.CborReadHeaderBuf(br, scratch) + if err != nil { + return err + } + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("Log: map struct too large (%d)", extra) + } + + var name string + n := extra + + for i := uint64(0); i < n; i++ { + + { + sval, err := cbg.ReadStringBuf(br, scratch) + if err != nil { + return err + } + + name = string(sval) + } + + switch name { + // t.Log (string) (string) + case "Log": + + { + sval, err := cbg.ReadStringBuf(br, scratch) + if err != nil { + return err + } + + t.Log = string(sval) + } + // t.UpdatedTime (typegen.CborTime) (struct) + case "UpdatedTime": + + { + + if err := t.UpdatedTime.UnmarshalCBOR(br); err != nil { + return xerrors.Errorf("unmarshaling t.UpdatedTime: %w", err) + } + + } + + default: + // Field doesn't exist on this type, so ignore it + cbg.ScanForLinks(r, func(cid.Cid) {}) + } + } + + return nil +} diff --git a/storagemarket/types_test.go b/storagemarket/types_test.go new file mode 100644 index 00000000..8b5df7df --- /dev/null +++ b/storagemarket/types_test.go @@ -0,0 +1,13 @@ +package storagemarket_test + +import ( + "testing" + + "github.com/filecoin-project/go-fil-markets/storagemarket" +) + +func TestDealStagesNil(t *testing.T) { + var ds *storagemarket.DealStages + ds.GetStage("none") // no panic. + ds.AddStageLog("MyStage", "desc", "duration", "msg") // no panic. +}