Documentation ¶
Index ¶
- Variables
- func AllocateLane(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
- func CancelDeal(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
- func CheckComplete(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
- func CheckFunds(ctx fsm.Context, env DealEnvironment, ds deal.ClientState) error
- func DataTransferSubscriber(deals EventReceiver, host peer.ID) datatransfer.Subscriber
- func Dispatcher(evt pubsub.Event, subscriberFn pubsub.SubscriberFn) error
- func Ongoing(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
- func ProcessPaymentRequested(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
- func ProposeDeal(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
- func SendFunds(ctx fsm.Context, env DealEnvironment, ds deal.ClientState) error
- func SetupPaymentChannelStart(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
- func WaitPaymentChannelReady(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
- type DealEnvironment
- type Event
- type EventReceiver
- type InternalEvent
- type Subscriber
Constants ¶
This section is empty.
Variables ¶
var Events = map[Event]string{ EventOpen: "ClientEventOpen", EventPaymentChannelErrored: "ClientEventPaymentChannelErrored", EventDealProposed: "ClientEventDealProposed", EventAllocateLaneErrored: "ClientEventAllocateLaneErrored", EventPaymentChannelCreateInitiated: "ClientEventPaymentChannelCreateInitiated", EventPaymentChannelReady: "ClientEventPaymentChannelReady", EventPaymentChannelAddingFunds: "ClientEventPaymentChannelAddingFunds", EventPaymentChannelAddFundsErrored: "ClientEventPaymentChannelAddFundsErrored", EventPaymentChannelSkip: "ClientEventPaymentChannelSkip", EventWriteDealProposalErrored: "ClientEventWriteDealProposalErrored", EventDealRejected: "ClientEventDealRejected", EventDealNotFound: "ClientEventDealNotFound", EventDealAccepted: "ClientEventDealAccepted", EventProviderCancelled: "ClientEventProviderCancelled", EventUnknownResponseReceived: "ClientEventUnknownResponseReceived", EventLastPaymentRequested: "ClientEventLastPaymentRequested", EventAllBlocksReceived: "ClientEventAllBlocksReceived", EventPaymentRequested: "ClientEventPaymentRequested", EventUnsealPaymentRequested: "ClientEventUnsealPaymentRequested", EventBlocksReceived: "ClientEventBlocksReceived", EventSendFunds: "ClientEventSendFunds", EventFundsExpended: "ClientEventFundsExpended", EventBadPaymentRequested: "ClientEventBadPaymentRequested", EventCreateVoucherFailed: "ClientEventCreateVoucherFailed", EventWriteDealPaymentErrored: "ClientEventWriteDealPaymentErrored", EventPaymentSent: "ClientEventPaymentSent", EventPaymentNotSent: "ClientEventPaymentNotSent", EventDataTransferError: "ClientEventDataTransferError", EventComplete: "ClientEventComplete", EventCancelComplete: "ClientEventCancelComplete", EventEarlyTermination: "ClientEventEarlyTermination", EventCompleteVerified: "ClientEventCompleteVerified", EventLaneAllocated: "ClientEventLaneAllocated", EventVoucherShortfall: "ClientEventVoucherShortfall", EventRecheckFunds: "ClientEventRecheckFunds", EventCancel: "ClientEventCancel", EventWaitForLastBlocks: "ClientEventWaitForLastBlocks", EventProviderErrored: "ClientEventProviderErrored", }
Events is a human readable map of client event name -> event description
var FSMEvents = fsm.Events{ fsm.Event(EventOpen). From(deal.StatusNew).ToNoChange(), fsm.Event(EventWriteDealProposalErrored). FromAny().To(deal.StatusErrored). Action(func(ds *deal.ClientState, err error) error { ds.Message = fmt.Errorf("proposing deal: %w", err).Error() return nil }), fsm.Event(EventDealProposed). From(deal.StatusNew).To(deal.StatusWaitForAcceptance). Action(func(ds *deal.ClientState, channelID datatransfer.ChannelID) error { ds.ChannelID = channelID ds.Message = "" return nil }), fsm.Event(EventDealRejected). From(deal.StatusWaitForAcceptance).To(deal.StatusRejected). Action(func(deal *deal.ClientState, message string) error { deal.Message = fmt.Sprintf("deal rejected: %s", message) return nil }), fsm.Event(EventDealNotFound). From(deal.StatusWaitForAcceptance).To(deal.StatusDealNotFound). Action(func(ds *deal.ClientState, message string) error { ds.Message = fmt.Sprintf("deal not found: %s", message) return nil }), fsm.Event(EventDealAccepted). From(deal.StatusWaitForAcceptance).To(deal.StatusAccepted). From(deal.StatusClientWaitingForLastBlocks).ToNoChange(), fsm.Event(EventUnknownResponseReceived). FromAny().To(deal.StatusFailing). Action(func(ds *deal.ClientState, status deal.Status) error { ds.Message = fmt.Sprintf("Unexpected deal response status: %s", deal.Statuses[status]) return nil }), fsm.Event(EventPaymentChannelSkip). From(deal.StatusAccepted).To(deal.StatusOngoing), fsm.Event(EventPaymentChannelErrored). FromMany(deal.StatusAccepted, deal.StatusPaymentChannelCreating, deal.StatusPaymentChannelAddingFunds).To(deal.StatusFailing). Action(func(ds *deal.ClientState, err error) error { ds.Message = fmt.Errorf("error from payment channel: %w", err).Error() return nil }), fsm.Event(EventPaymentChannelCreateInitiated). From(deal.StatusAccepted).To(deal.StatusPaymentChannelCreating). Action(func(ds *deal.ClientState, msgCID cid.Cid) error { ds.WaitMsgCID = &msgCID return nil }), fsm.Event(EventPaymentChannelAddingFunds). From(deal.StatusAccepted).To(deal.StatusPaymentChannelAddingInitialFunds). From(deal.StatusCheckFunds).To(deal.StatusPaymentChannelAddingFunds). Action(func(ds *deal.ClientState, msgCID cid.Cid, payCh address.Address) error { ds.WaitMsgCID = &msgCID if ds.PaymentInfo == nil { ds.PaymentInfo = &deal.PaymentInfo{ PayCh: payCh, } } return nil }), fsm.Event(EventPaymentChannelReady). FromMany(deal.StatusPaymentChannelCreating, deal.StatusPaymentChannelAddingInitialFunds, deal.StatusAccepted). To(deal.StatusPaymentChannelAllocatingLane). From(deal.StatusPaymentChannelAddingFunds).To(deal.StatusOngoing). From(deal.StatusCheckFunds).To(deal.StatusOngoing). Action(func(ds *deal.ClientState, payCh address.Address) error { if ds.PaymentInfo == nil { ds.PaymentInfo = &deal.PaymentInfo{ PayCh: payCh, } } ds.WaitMsgCID = nil ds.Message = "" return nil }), fsm.Event(EventAllocateLaneErrored). FromMany(deal.StatusPaymentChannelAllocatingLane). To(deal.StatusFailing). Action(func(ds *deal.ClientState, err error) error { ds.Message = fmt.Errorf("allocating payment lane: %w", err).Error() return nil }), fsm.Event(EventLaneAllocated). From(deal.StatusPaymentChannelAllocatingLane).To(deal.StatusOngoing). Action(func(ds *deal.ClientState, lane uint64) error { ds.PaymentInfo.Lane = lane return nil }), fsm.Event(EventProviderErrored). FromAny().To(deal.StatusErrored). Action(func(ds *deal.ClientState, msg string) error { ds.Message = msg return nil }), fsm.Event(EventDataTransferError). FromAny().To(deal.StatusErrored). Action(func(ds *deal.ClientState, err error) error { ds.Message = fmt.Sprintf("error generated by data transfer: %s", err.Error()) return nil }), fsm.Event(EventLastPaymentRequested). FromMany( deal.StatusOngoing, deal.StatusFundsNeededLastPayment, deal.StatusFundsNeeded).To(deal.StatusFundsNeededLastPayment). From(deal.StatusSendFunds).To(deal.StatusOngoing). From(deal.StatusCheckComplete).ToNoChange(). From(deal.StatusBlocksComplete).To(deal.StatusSendFundsLastPayment). FromMany( paymentChannelCreationStates...).ToJustRecord(). Action(func(ds *deal.ClientState, paymentOwed abi.TokenAmount) error { ds.PaymentRequested = big.Add(ds.PaymentRequested, paymentOwed) ds.LastPaymentRequested = true return nil }), fsm.Event(EventPaymentRequested). FromMany( deal.StatusOngoing, deal.StatusBlocksComplete, deal.StatusFundsNeededLastPayment, deal.StatusFundsNeeded).To(deal.StatusFundsNeeded). From(deal.StatusSendFunds).To(deal.StatusOngoing). From(deal.StatusCheckComplete).ToNoChange(). FromMany( paymentChannelCreationStates...).ToJustRecord(). Action(func(ds *deal.ClientState, paymentOwed abi.TokenAmount) error { ds.PaymentRequested = big.Add(ds.PaymentRequested, paymentOwed) return nil }), fsm.Event(EventUnsealPaymentRequested). From(deal.StatusWaitForAcceptance).To(deal.StatusAccepted). Action(func(ds *deal.ClientState, paymentOwed abi.TokenAmount) error { ds.PaymentRequested = big.Add(ds.PaymentRequested, paymentOwed) return nil }), fsm.Event(EventAllBlocksReceived). FromMany( deal.StatusOngoing, deal.StatusBlocksComplete, ).To(deal.StatusBlocksComplete). FromMany(paymentChannelCreationStates...).ToJustRecord(). FromMany(deal.StatusSendFunds, deal.StatusSendFundsLastPayment).To(deal.StatusOngoing). From(deal.StatusFundsNeeded).ToNoChange(). From(deal.StatusFundsNeededLastPayment).To(deal.StatusSendFundsLastPayment). FromMany( deal.StatusClientWaitingForLastBlocks, deal.StatusCheckComplete, ).To(deal.StatusCompleted). Action(func(ds *deal.ClientState) error { ds.AllBlocksReceived = true return nil }), fsm.Event(EventBlocksReceived). FromMany(deal.StatusOngoing, deal.StatusFundsNeeded, deal.StatusFundsNeededLastPayment, deal.StatusCheckComplete, deal.StatusClientWaitingForLastBlocks).ToNoChange(). FromMany(deal.StatusSendFunds, deal.StatusSendFundsLastPayment).To(deal.StatusOngoing). FromMany(paymentChannelCreationStates...).ToJustRecord(). Action(recordReceived), fsm.Event(EventSendFunds). FromMany(deal.StatusSendFunds, deal.StatusSendFundsLastPayment).To(deal.StatusOngoing). From(deal.StatusFundsNeeded).To(deal.StatusSendFunds). From(deal.StatusFundsNeededLastPayment).To(deal.StatusSendFundsLastPayment), fsm.Event(EventFundsExpended). From(deal.StatusCheckFunds).To(deal.StatusInsufficientFunds). Action(func(ds *deal.ClientState, shortfall abi.TokenAmount) error { ds.Message = fmt.Sprintf("not enough current or pending funds in payment channel, shortfall of %s", shortfall.String()) ds.VoucherShortfall = shortfall return nil }), fsm.Event(EventBadPaymentRequested). FromMany(deal.StatusSendFunds, deal.StatusSendFundsLastPayment).To(deal.StatusFailing). Action(func(ds *deal.ClientState, message string) error { ds.Message = message return nil }), fsm.Event(EventCreateVoucherFailed). FromMany(deal.StatusSendFunds, deal.StatusSendFundsLastPayment).To(deal.StatusFailing). Action(func(ds *deal.ClientState, err error) error { ds.Message = fmt.Errorf("creating payment voucher: %w", err).Error() return nil }), fsm.Event(EventVoucherShortfall). FromMany(deal.StatusSendFunds, deal.StatusSendFundsLastPayment).To(deal.StatusCheckFunds). Action(func(ds *deal.ClientState, shortfall abi.TokenAmount) error { return nil }), fsm.Event(EventWriteDealPaymentErrored). FromAny().To(deal.StatusErrored). Action(func(ds *deal.ClientState, err error) error { ds.Message = fmt.Errorf("writing deal payment: %w", err).Error() return nil }), fsm.Event(EventPaymentNotSent). From(deal.StatusOngoing).ToJustRecord(). From(deal.StatusSendFunds).To(deal.StatusOngoing). From(deal.StatusSendFundsLastPayment).To(deal.StatusFinalizing), fsm.Event(EventPaymentSent). From(deal.StatusOngoing).ToJustRecord(). From(deal.StatusBlocksComplete).To(deal.StatusCheckComplete). From(deal.StatusCheckComplete).ToNoChange(). FromMany( deal.StatusFundsNeeded, deal.StatusFundsNeededLastPayment, deal.StatusSendFunds).To(deal.StatusOngoing). From(deal.StatusSendFundsLastPayment).To(deal.StatusFinalizing). Action(func(state *deal.ClientState, voucherAmt abi.TokenAmount) error { sentAmt := big.Sub(voucherAmt, state.FundsSpent) state.PaymentRequested = big.Sub(state.PaymentRequested, sentAmt) state.FundsSpent = voucherAmt if state.UnsealPrice.GreaterThanEqual(state.FundsSpent) { state.UnsealFundsPaid = state.FundsSpent return nil } state.UnsealFundsPaid = state.UnsealPrice if state.PricePerByte.IsZero() { return nil } paidSoFarForTransfer := big.Sub(state.FundsSpent, state.UnsealFundsPaid) state.BytesPaidFor = big.Div(paidSoFarForTransfer, state.PricePerByte).Uint64() if state.BytesPaidFor >= state.CurrentInterval { state.CurrentInterval = state.NextInterval() } return nil }), fsm.Event(EventComplete). FromMany( deal.StatusSendFunds, deal.StatusSendFundsLastPayment, deal.StatusFundsNeeded, deal.StatusFundsNeededLastPayment, deal.StatusBlocksComplete, deal.StatusWaitForAcceptance, deal.StatusAccepted, deal.StatusOngoing).To(deal.StatusCheckComplete). From(deal.StatusFinalizing).To(deal.StatusCompleted), fsm.Event(EventCompleteVerified). From(deal.StatusCheckComplete).To(deal.StatusCompleted), fsm.Event(EventEarlyTermination). From(deal.StatusCheckComplete).To(deal.StatusErrored). Action(func(state *deal.ClientState) error { state.Message = "Provider sent complete status without sending all data" return nil }), fsm.Event(EventWaitForLastBlocks). From(deal.StatusCheckComplete).To(deal.StatusClientWaitingForLastBlocks). From(deal.StatusCompleted).ToJustRecord(), fsm.Event(EventCancelComplete). From(deal.StatusFailing).To(deal.StatusErrored). From(deal.StatusCancelling).To(deal.StatusCancelled), fsm.Event(EventProviderCancelled). From(deal.StatusFailing).ToJustRecord(). From(deal.StatusCancelling).ToJustRecord(). FromAny().To(deal.StatusCancelling).Action( func(ds *deal.ClientState) error { if ds.Status != deal.StatusFailing && ds.Status != deal.StatusCancelling { ds.Message = "Provider cancelled retrieval" } return nil }, ), fsm.Event(EventCancel).FromAny().To(deal.StatusCancelling).Action(func(ds *deal.ClientState) error { ds.Message = "Client cancelled retrieval" return nil }), fsm.Event(EventRecheckFunds).From(deal.StatusInsufficientFunds).To(deal.StatusCheckFunds), }
FSMEvents is the state chart defining the events that can happen in a retrieval client it is almost identical to go-fil-markets implementation except we don't support legacy events
var FinalityStates = []fsm.StateKey{ deal.StatusErrored, deal.StatusCompleted, deal.StatusCancelled, deal.StatusRejected, deal.StatusDealNotFound, }
FinalityStates are terminal states after which no further events are received
var StateEntryFuncs = fsm.StateEntryFuncs{ deal.StatusNew: ProposeDeal, deal.StatusAccepted: SetupPaymentChannelStart, deal.StatusPaymentChannelCreating: WaitPaymentChannelReady, deal.StatusPaymentChannelAddingInitialFunds: WaitPaymentChannelReady, deal.StatusPaymentChannelAllocatingLane: AllocateLane, deal.StatusOngoing: Ongoing, deal.StatusFundsNeeded: ProcessPaymentRequested, deal.StatusFundsNeededLastPayment: ProcessPaymentRequested, deal.StatusSendFunds: SendFunds, deal.StatusSendFundsLastPayment: SendFunds, deal.StatusCheckFunds: CheckFunds, deal.StatusPaymentChannelAddingFunds: WaitPaymentChannelReady, deal.StatusFailing: CancelDeal, deal.StatusCancelling: CancelDeal, deal.StatusCheckComplete: CheckComplete, }
StateEntryFuncs are the handlers for different states in a retrieval client
Functions ¶
func AllocateLane ¶
func AllocateLane(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
AllocateLane allocates a lane for this retrieval operation
func CancelDeal ¶
func CancelDeal(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
CancelDeal clears a deal that went wrong for an unknown reason
func CheckComplete ¶
func CheckComplete(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
CheckComplete verifies that a provider that completed without a last payment requested did in fact send us all the data
func CheckFunds ¶
func CheckFunds(ctx fsm.Context, env DealEnvironment, ds deal.ClientState) error
CheckFunds examines current available funds in a payment channel after a voucher shortfall to determine a course of action -- whether it's a good time to try again, wait for pending operations, or we've truly expended all funds and we need to wait for a manual readd
func DataTransferSubscriber ¶
func DataTransferSubscriber(deals EventReceiver, host peer.ID) datatransfer.Subscriber
DataTransferSubscriber is the function called when an event occurs in a data transfer initiated on the client -- it reads the voucher to verify this even occurred in a storage market deal, then, based on the data transfer event that occurred, it dispatches an event to the appropriate state machine
func Dispatcher ¶
func Dispatcher(evt pubsub.Event, subscriberFn pubsub.SubscriberFn) error
Dispatcher casts a pubsub event into a provider event and publishes it to a subscriber
func Ongoing ¶
func Ongoing(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
Ongoing just double checks that we may need to move out of the ongoing state cause a payment was previously requested
func ProcessPaymentRequested ¶
func ProcessPaymentRequested(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
ProcessPaymentRequested processes a request for payment from the provider
func ProposeDeal ¶
func ProposeDeal(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
ProposeDeal sends the proposal to the other party
func SendFunds ¶
func SendFunds(ctx fsm.Context, env DealEnvironment, ds deal.ClientState) error
SendFunds sends the next amount requested by the provider
func SetupPaymentChannelStart ¶
func SetupPaymentChannelStart(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
SetupPaymentChannelStart initiates setting up a payment channel for a deal
func WaitPaymentChannelReady ¶
func WaitPaymentChannelReady(ctx fsm.Context, environment DealEnvironment, ds deal.ClientState) error
WaitPaymentChannelReady waits for a pending operation on a payment channel -- either creating or depositing funds
Types ¶
type DealEnvironment ¶
type DealEnvironment interface { Payments() payments.Manager OpenDataTransfer(ctx context.Context, to peer.ID, proposal *deal.Proposal) (datatransfer.ChannelID, error) SendDataTransferVoucher(context.Context, datatransfer.ChannelID, *deal.Payment) error CloseDataTransfer(context.Context, datatransfer.ChannelID) error }
DealEnvironment is a bridge to the environment a client deal is executing in. It provides access to relevant functionality on the retrieval client
type Event ¶
type Event uint64
Event is an event that occurs in a deal lifecycle on the client
const ( // EventOpen indicates a deal was initiated EventOpen Event = iota // EventWriteDealProposalErrored means a network error writing a deal proposal EventWriteDealProposalErrored // EventDealProposed means a deal was successfully sent to a miner EventDealProposed // EventDealRejected means a deal was rejected by the provider EventDealRejected // EventDealNotFound means a provider could not find a piece for a deal EventDealNotFound // EventDealAccepted means a provider accepted a deal EventDealAccepted // EventProviderCancelled means a provider has sent a message to cancel a deal EventProviderCancelled // EventUnknownResponseReceived means a client received a response it doesn't // understand from the provider EventUnknownResponseReceived // EventPaymentChannelErrored means there was a failure creating a payment channel EventPaymentChannelErrored // EventAllocateLaneErrored means there was a failure creating a lane in a payment channel EventAllocateLaneErrored // EventPaymentChannelCreateInitiated means we are waiting for a message to // create a payment channel to appear on chain EventPaymentChannelCreateInitiated // EventPaymentChannelReady means the newly created payment channel is ready for the // deal to resume EventPaymentChannelReady // EventPaymentChannelSkip means we can skip payment channel because the deal price is 0 EventPaymentChannelSkip // EventPaymentChannelAddingFunds mean we are waiting for funds to be // added to a payment channel EventPaymentChannelAddingFunds // EventPaymentChannelAddFundsErrored means that adding funds to the payment channel // failed EventPaymentChannelAddFundsErrored // EventLastPaymentRequested indicates the provider requested a final payment EventLastPaymentRequested // EventAllBlocksReceived indicates the provider has sent all blocks EventAllBlocksReceived // EventPaymentRequested indicates the provider requested a payment EventPaymentRequested // EventPaymentNotSent indicates that payment was requested, but no // payment was actually due, so a voucher was not sent to the provider EventPaymentNotSent // EventUnsealPaymentRequested indicates the provider requested a payment for unsealing the sector EventUnsealPaymentRequested // EventBlocksReceived indicates the provider has sent blocks EventBlocksReceived // EventSendFunds emits when we reach the threshold to send the next payment EventSendFunds // EventFundsExpended indicates a deal has run out of funds in the payment channel // forcing the client to add more funds to continue the deal EventFundsExpended // when totalFunds is expended // EventBadPaymentRequested indicates the provider asked for funds // in a way that does not match the terms of the deal EventBadPaymentRequested // EventCreateVoucherFailed indicates an error happened creating a payment voucher EventCreateVoucherFailed // EventWriteDealPaymentErrored indicates a network error trying to write a payment EventWriteDealPaymentErrored // EventPaymentSent indicates a payment was sent to the provider EventPaymentSent // EventComplete indicates a deal has completed EventComplete // EventDataTransferError emits when something go wrong at the data transfer level EventDataTransferError // EventCancelComplete happens when a deal cancellation is transmitted to the provider EventCancelComplete // EventEarlyTermination indications a provider send a deal complete without sending all data EventEarlyTermination // EventCompleteVerified means that a provider completed without requesting a final payment but // we verified we received all data EventCompleteVerified // EventLaneAllocated is called when a lane is allocated EventLaneAllocated // EventVoucherShortfall means we tried to create a voucher but did not have enough funds in channel // to create it EventVoucherShortfall // EventRecheckFunds runs when an external caller indicates there may be new funds in a payment channel EventRecheckFunds // EventCancel runs when a user cancels a deal EventCancel // EventWaitForLastBlocks is fired when the provider has told // the client that all blocks were sent for the deal, and the client is // waiting for the last blocks to arrive EventWaitForLastBlocks // EventProviderErrored happens when we receive a status in response voucher // telling us something went wrong on the provider side but they don't know what (500) EventProviderErrored )
type EventReceiver ¶
type EventReceiver interface { Has(id interface{}) (bool, error) // Check if we have any state before sending Send(id interface{}, name fsm.EventName, args ...interface{}) (err error) }
EventReceiver is any thing that can receive FSM events
type InternalEvent ¶
type InternalEvent struct { Evt Event State deal.ClientState }
InternalEvent is an atomic state change in the client
type Subscriber ¶
type Subscriber func(event Event, state deal.ClientState)
Subscriber is a callback that is registered to listen for retrieval events