Documentation ¶
Overview ¶
Package clientstates contains state machine logic relating to the `RetrievalClient`.
client_fsm.go is where the state transitions are defined, and the default handlers for each new state are defined.
client_states.go contains state handler functions.
The following diagram illustrates the operation of the client state machine. This diagram is auto-generated from current code and should remain up to date over time:
Index ¶
- Variables
- func AllocateLane(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func CancelDeal(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func CheckComplete(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func CheckFunds(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func FailsafeFinalizeBlockstore(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func FinalizeBlockstore(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func IsFinalityState(st fsm.StateKey) bool
- func Ongoing(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func ProcessPaymentRequested(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func ProposeDeal(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func SendFunds(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func SetupPaymentChannelStart(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- func WaitPaymentChannelReady(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
- type ClientDealEnvironment
Constants ¶
This section is empty.
Variables ¶
var ClientEvents = fsm.Events{ fsm.Event(rm.ClientEventOpen). From(rm.DealStatusNew).ToNoChange(), fsm.Event(rm.ClientEventWriteDealProposalErrored). FromAny().To(rm.DealStatusErroring). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = xerrors.Errorf("proposing deal: %w", err).Error() return nil }), fsm.Event(rm.ClientEventDealProposed). From(rm.DealStatusNew).To(rm.DealStatusWaitForAcceptance). From(rm.DealStatusRetryLegacy).To(rm.DealStatusWaitForAcceptanceLegacy). From(rm.DealStatusCancelling).ToJustRecord(). Action(func(deal *rm.ClientDealState, channelID datatransfer.ChannelID) error { deal.ChannelID = &channelID deal.Message = "" return nil }), fsm.Event(rm.ClientEventDealRejected). From(rm.DealStatusWaitForAcceptance).To(rm.DealStatusRetryLegacy). From(rm.DealStatusWaitForAcceptanceLegacy).To(rm.DealStatusRejecting). Action(func(deal *rm.ClientDealState, message string) error { deal.Message = fmt.Sprintf("deal rejected: %s", message) deal.LegacyProtocol = true return nil }), fsm.Event(rm.ClientEventDealNotFound). FromMany(rm.DealStatusWaitForAcceptance, rm.DealStatusWaitForAcceptanceLegacy).To(rm.DealStatusDealNotFoundCleanup). Action(func(deal *rm.ClientDealState, message string) error { deal.Message = fmt.Sprintf("deal not found: %s", message) return nil }), fsm.Event(rm.ClientEventDealAccepted). FromMany(rm.DealStatusWaitForAcceptance, rm.DealStatusWaitForAcceptanceLegacy).To(rm.DealStatusAccepted), fsm.Event(rm.ClientEventUnknownResponseReceived). FromAny().To(rm.DealStatusFailing). Action(func(deal *rm.ClientDealState, status rm.DealStatus) error { deal.Message = fmt.Sprintf("Unexpected deal response status: %s", rm.DealStatuses[status]) return nil }), fsm.Event(rm.ClientEventPaymentChannelErrored). FromMany(rm.DealStatusAccepted, rm.DealStatusPaymentChannelCreating, rm.DealStatusPaymentChannelAddingFunds).To(rm.DealStatusFailing). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = xerrors.Errorf("error from payment channel: %w", err).Error() return nil }), fsm.Event(rm.ClientEventPaymentChannelSkip). From(rm.DealStatusAccepted).To(rm.DealStatusOngoing), fsm.Event(rm.ClientEventPaymentChannelCreateInitiated). From(rm.DealStatusAccepted).To(rm.DealStatusPaymentChannelCreating). Action(func(deal *rm.ClientDealState, msgCID cid.Cid) error { deal.WaitMsgCID = &msgCID return nil }), fsm.Event(rm.ClientEventPaymentChannelAddingFunds). FromMany(rm.DealStatusAccepted).To(rm.DealStatusPaymentChannelAddingInitialFunds). FromMany(rm.DealStatusCheckFunds).To(rm.DealStatusPaymentChannelAddingFunds). Action(func(deal *rm.ClientDealState, msgCID cid.Cid, payCh address.Address) error { deal.WaitMsgCID = &msgCID if deal.PaymentInfo == nil { deal.PaymentInfo = &rm.PaymentInfo{ PayCh: payCh, } } return nil }), fsm.Event(rm.ClientEventPaymentChannelReady). FromMany(rm.DealStatusPaymentChannelCreating, rm.DealStatusPaymentChannelAddingInitialFunds, rm.DealStatusAccepted).To(rm.DealStatusPaymentChannelAllocatingLane). From(rm.DealStatusPaymentChannelAddingFunds).To(rm.DealStatusOngoing). From(rm.DealStatusCheckFunds).To(rm.DealStatusOngoing). Action(func(deal *rm.ClientDealState, payCh address.Address) error { if deal.PaymentInfo == nil { deal.PaymentInfo = &rm.PaymentInfo{ PayCh: payCh, } } deal.WaitMsgCID = nil deal.Message = "" return nil }), fsm.Event(rm.ClientEventAllocateLaneErrored). FromMany(rm.DealStatusPaymentChannelAllocatingLane). To(rm.DealStatusFailing). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = xerrors.Errorf("allocating payment lane: %w", err).Error() return nil }), fsm.Event(rm.ClientEventLaneAllocated). From(rm.DealStatusPaymentChannelAllocatingLane).To(rm.DealStatusOngoing). Action(func(deal *rm.ClientDealState, lane uint64) error { deal.PaymentInfo.Lane = lane return nil }), fsm.Event(rm.ClientEventDataTransferError). FromAny().To(rm.DealStatusErroring). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = fmt.Sprintf("error generated by data transfer: %s", err.Error()) return nil }), fsm.Event(rm.ClientEventLastPaymentRequested). FromMany( rm.DealStatusOngoing, rm.DealStatusFundsNeededLastPayment, rm.DealStatusFundsNeeded).To(rm.DealStatusFundsNeededLastPayment). From(rm.DealStatusSendFunds).To(rm.DealStatusOngoing). From(rm.DealStatusCheckComplete).ToNoChange(). From(rm.DealStatusBlocksComplete).To(rm.DealStatusSendFundsLastPayment). FromMany( paymentChannelCreationStates...).ToJustRecord(). Action(func(deal *rm.ClientDealState, paymentOwed abi.TokenAmount) error { deal.PaymentRequested = big.Add(deal.PaymentRequested, paymentOwed) deal.LastPaymentRequested = true return nil }), fsm.Event(rm.ClientEventPaymentRequested). FromMany( rm.DealStatusOngoing, rm.DealStatusBlocksComplete, rm.DealStatusFundsNeeded, rm.DealStatusFundsNeededLastPayment).To(rm.DealStatusFundsNeeded). From(rm.DealStatusSendFunds).To(rm.DealStatusOngoing). From(rm.DealStatusCheckComplete).ToNoChange(). FromMany( paymentChannelCreationStates...).ToJustRecord(). Action(func(deal *rm.ClientDealState, paymentOwed abi.TokenAmount) error { deal.PaymentRequested = big.Add(deal.PaymentRequested, paymentOwed) return nil }), fsm.Event(rm.ClientEventUnsealPaymentRequested). FromMany(rm.DealStatusWaitForAcceptance, rm.DealStatusWaitForAcceptanceLegacy).To(rm.DealStatusAccepted). Action(func(deal *rm.ClientDealState, paymentOwed abi.TokenAmount) error { deal.PaymentRequested = big.Add(deal.PaymentRequested, paymentOwed) return nil }), fsm.Event(rm.ClientEventAllBlocksReceived). FromMany( rm.DealStatusOngoing, rm.DealStatusBlocksComplete, ).To(rm.DealStatusBlocksComplete). FromMany(paymentChannelCreationStates...).ToJustRecord(). FromMany(rm.DealStatusSendFunds, rm.DealStatusSendFundsLastPayment).To(rm.DealStatusOngoing). From(rm.DealStatusFundsNeeded).ToNoChange(). From(rm.DealStatusFundsNeededLastPayment).To(rm.DealStatusSendFundsLastPayment). From(rm.DealStatusClientWaitingForLastBlocks).To(rm.DealStatusFinalizingBlockstore). From(rm.DealStatusCheckComplete).To(rm.DealStatusFinalizingBlockstore). Action(func(deal *rm.ClientDealState) error { deal.AllBlocksReceived = true return nil }), fsm.Event(rm.ClientEventBlocksReceived). FromMany(rm.DealStatusOngoing, rm.DealStatusFundsNeeded, rm.DealStatusFundsNeededLastPayment, rm.DealStatusCheckComplete, rm.DealStatusClientWaitingForLastBlocks).ToNoChange(). FromMany(rm.DealStatusSendFunds, rm.DealStatusSendFundsLastPayment).To(rm.DealStatusOngoing). FromMany(paymentChannelCreationStates...).ToJustRecord(). Action(recordReceived), fsm.Event(rm.ClientEventSendFunds). FromMany(rm.DealStatusSendFunds, rm.DealStatusSendFundsLastPayment).To(rm.DealStatusOngoing). From(rm.DealStatusFundsNeeded).To(rm.DealStatusSendFunds). From(rm.DealStatusFundsNeededLastPayment).To(rm.DealStatusSendFundsLastPayment), fsm.Event(rm.ClientEventFundsExpended). FromMany(rm.DealStatusCheckFunds).To(rm.DealStatusInsufficientFunds). Action(func(deal *rm.ClientDealState, shortfall abi.TokenAmount) error { deal.Message = fmt.Sprintf("not enough current or pending funds in payment channel, shortfall of %s", shortfall.String()) return nil }), fsm.Event(rm.ClientEventBadPaymentRequested). FromMany(rm.DealStatusSendFunds, rm.DealStatusSendFundsLastPayment).To(rm.DealStatusFailing). Action(func(deal *rm.ClientDealState, message string) error { deal.Message = message return nil }), fsm.Event(rm.ClientEventCreateVoucherFailed). FromMany(rm.DealStatusSendFunds, rm.DealStatusSendFundsLastPayment).To(rm.DealStatusFailing). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = xerrors.Errorf("creating payment voucher: %w", err).Error() return nil }), fsm.Event(rm.ClientEventVoucherShortfall). FromMany(rm.DealStatusSendFunds, rm.DealStatusSendFundsLastPayment).To(rm.DealStatusCheckFunds). Action(func(deal *rm.ClientDealState, shortfall abi.TokenAmount) error { return nil }), fsm.Event(rm.ClientEventWriteDealPaymentErrored). FromAny().To(rm.DealStatusErroring). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = xerrors.Errorf("writing deal payment: %w", err).Error() return nil }), fsm.Event(rm.ClientEventPaymentNotSent). From(rm.DealStatusOngoing).ToJustRecord(). From(rm.DealStatusSendFunds).To(rm.DealStatusOngoing). From(rm.DealStatusSendFundsLastPayment).To(rm.DealStatusFinalizing), fsm.Event(rm.ClientEventPaymentSent). From(rm.DealStatusOngoing).ToJustRecord(). From(rm.DealStatusBlocksComplete).To(rm.DealStatusCheckComplete). From(rm.DealStatusCheckComplete).ToNoChange(). FromMany( rm.DealStatusFundsNeeded, rm.DealStatusFundsNeededLastPayment, rm.DealStatusSendFunds).To(rm.DealStatusOngoing). From(rm.DealStatusSendFundsLastPayment).To(rm.DealStatusFinalizing). Action(func(deal *rm.ClientDealState, voucherAmt abi.TokenAmount) error { sentAmt := big.Sub(voucherAmt, deal.FundsSpent) deal.PaymentRequested = big.Sub(deal.PaymentRequested, sentAmt) deal.FundsSpent = voucherAmt if deal.UnsealPrice.GreaterThanEqual(deal.FundsSpent) { deal.UnsealFundsPaid = deal.FundsSpent return nil } deal.UnsealFundsPaid = deal.UnsealPrice if deal.PricePerByte.IsZero() { return nil } paidSoFarForTransfer := big.Sub(deal.FundsSpent, deal.UnsealFundsPaid) deal.BytesPaidFor = big.Div(paidSoFarForTransfer, deal.PricePerByte).Uint64() if deal.BytesPaidFor >= deal.CurrentInterval { deal.CurrentInterval = deal.NextInterval() } return nil }), fsm.Event(rm.ClientEventComplete). FromMany( rm.DealStatusSendFunds, rm.DealStatusSendFundsLastPayment, rm.DealStatusFundsNeeded, rm.DealStatusFundsNeededLastPayment).To(rm.DealStatusCheckComplete). From(rm.DealStatusOngoing).To(rm.DealStatusCheckComplete). From(rm.DealStatusBlocksComplete).To(rm.DealStatusCheckComplete). From(rm.DealStatusFinalizing).To(rm.DealStatusFinalizingBlockstore), fsm.Event(rm.ClientEventCompleteVerified). From(rm.DealStatusCheckComplete).To(rm.DealStatusFinalizingBlockstore), fsm.Event(rm.ClientEventEarlyTermination). From(rm.DealStatusCheckComplete).To(rm.DealStatusErroring). Action(func(deal *rm.ClientDealState) error { deal.Message = "Provider sent complete status without sending all data" return nil }), fsm.Event(rm.ClientEventWaitForLastBlocks). From(rm.DealStatusCheckComplete).To(rm.DealStatusClientWaitingForLastBlocks). FromMany(rm.DealStatusFinalizingBlockstore, rm.DealStatusCompleted).ToJustRecord(), fsm.Event(rm.ClientEventBlockstoreFinalized). From(rm.DealStatusFinalizingBlockstore).To(rm.DealStatusCompleted). From(rm.DealStatusErroring).To(rm.DealStatusErrored). From(rm.DealStatusRejecting).To(rm.DealStatusRejected). From(rm.DealStatusDealNotFoundCleanup).To(rm.DealStatusDealNotFound), fsm.Event(rm.ClientEventFinalizeBlockstoreErrored). From(rm.DealStatusFinalizingBlockstore).To(rm.DealStatusErrored). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = xerrors.Errorf("finalizing blockstore: %w", err).Error() return nil }), fsm.Event(rm.ClientEventCancelComplete). From(rm.DealStatusFailing).To(rm.DealStatusErrored). From(rm.DealStatusCancelling).To(rm.DealStatusCancelled), fsm.Event(rm.ClientEventProviderCancelled). From(rm.DealStatusFailing).ToJustRecord(). From(rm.DealStatusCancelling).ToJustRecord(). FromAny().To(rm.DealStatusCancelling).Action( func(deal *rm.ClientDealState) error { if deal.Status != rm.DealStatusFailing && deal.Status != rm.DealStatusCancelling { deal.Message = "Provider cancelled retrieval" } return nil }, ), fsm.Event(rm.ClientEventCancel).FromAny().To(rm.DealStatusCancelling).Action(func(deal *rm.ClientDealState) error { deal.Message = "Client cancelled retrieval" return nil }), fsm.Event(rm.ClientEventRecheckFunds).From(rm.DealStatusInsufficientFunds).To(rm.DealStatusCheckFunds), }
ClientEvents are the events that can happen in a retrieval client
var ClientFinalityStates = []fsm.StateKey{ rm.DealStatusErrored, rm.DealStatusCompleted, rm.DealStatusCancelled, rm.DealStatusRejected, rm.DealStatusDealNotFound, }
ClientFinalityStates are terminal states after which no further events are received
var ClientStateEntryFuncs = fsm.StateEntryFuncs{ rm.DealStatusNew: ProposeDeal, rm.DealStatusRetryLegacy: ProposeDeal, rm.DealStatusAccepted: SetupPaymentChannelStart, rm.DealStatusPaymentChannelCreating: WaitPaymentChannelReady, rm.DealStatusPaymentChannelAddingInitialFunds: WaitPaymentChannelReady, rm.DealStatusPaymentChannelAllocatingLane: AllocateLane, rm.DealStatusOngoing: Ongoing, rm.DealStatusFundsNeeded: ProcessPaymentRequested, rm.DealStatusFundsNeededLastPayment: ProcessPaymentRequested, rm.DealStatusSendFunds: SendFunds, rm.DealStatusSendFundsLastPayment: SendFunds, rm.DealStatusCheckFunds: CheckFunds, rm.DealStatusPaymentChannelAddingFunds: WaitPaymentChannelReady, rm.DealStatusFailing: CancelDeal, rm.DealStatusCancelling: CancelDeal, rm.DealStatusCheckComplete: CheckComplete, rm.DealStatusFinalizingBlockstore: FinalizeBlockstore, rm.DealStatusErroring: FailsafeFinalizeBlockstore, rm.DealStatusRejecting: FailsafeFinalizeBlockstore, rm.DealStatusDealNotFoundCleanup: FailsafeFinalizeBlockstore, }
ClientStateEntryFuncs are the handlers for different states in a retrieval client
Functions ¶
func AllocateLane ¶ added in v0.6.0
func AllocateLane(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
AllocateLane allocates a lane for this retrieval operation
func CancelDeal ¶ added in v0.5.0
func CancelDeal(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
CancelDeal clears a deal that went wrong for an unknown reason
func CheckComplete ¶ added in v0.5.9
func CheckComplete(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
CheckComplete verifies that a provider that completed without a last payment requested did in fact send us all the data
func CheckFunds ¶ added in v0.6.0
func CheckFunds(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) 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 FailsafeFinalizeBlockstore ¶ added in v1.8.0
func FailsafeFinalizeBlockstore(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
FailsafeFinalizeBlockstore is called when there is a termination state because of some irregularity (eg deal not found). It attempts to clean up the blockstore, but even if there's an error it always fires a blockstore finalized event so that we still end up in the appropriate termination state.
func FinalizeBlockstore ¶ added in v1.8.0
func FinalizeBlockstore(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
FinalizeBlockstore is called once all blocks have been received and the blockstore needs to be finalized before completing the deal
func IsFinalityState ¶ added in v1.2.0
func Ongoing ¶ added in v0.5.0
func Ongoing(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) 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 ClientDealEnvironment, deal rm.ClientDealState) error
ProcessPaymentRequested processes a request for payment from the provider
func ProposeDeal ¶
func ProposeDeal(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
ProposeDeal sends the proposal to the other party
func SendFunds ¶ added in v0.5.0
func SendFunds(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
SendFunds sends the next amount requested by the provider
func SetupPaymentChannelStart ¶
func SetupPaymentChannelStart(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
SetupPaymentChannelStart initiates setting up a payment channel for a deal
func WaitPaymentChannelReady ¶ added in v0.6.0
func WaitPaymentChannelReady(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
WaitPaymentChannelReady waits for a pending operation on a payment channel -- either creating or depositing funds
Types ¶
type ClientDealEnvironment ¶
type ClientDealEnvironment interface { // Node returns the node interface for this deal Node() rm.RetrievalClientNode OpenDataTransfer(ctx context.Context, to peer.ID, proposal *rm.DealProposal) (datatransfer.ChannelID, error) SendDataTransferVoucher(context.Context, datatransfer.ChannelID, *rm.DealPayment) error CloseDataTransfer(context.Context, datatransfer.ChannelID) error FinalizeBlockstore(context.Context, rm.DealID) error }
ClientDealEnvironment is a bridge to the environment a client deal is executing in. It provides access to relevant functionality on the retrieval client