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 ¶
func AllocateLane(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
AllocateLane allocates a lane for this retrieval operation
func CancelDeal ¶
func CancelDeal(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error
CancelDeal clears a deal that went wrong for an unknown reason
func CheckComplete ¶
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 ¶
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 ¶
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 ¶
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 ¶
func Ongoing ¶
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 ¶
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 ¶
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, legacy bool) (datatransfer.ChannelID, error) SendDataTransferVoucher(context.Context, datatransfer.ChannelID, *rm.DealPayment, bool) 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