clientstates

package
v0.0.0-...-75360cb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 11, 2023 License: Apache-2.0, MIT Imports: 12 Imported by: 0

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:

https://raw.githubusercontent.com/filecoin-project/go-fil-markets/master/docs/retrievalclient.mmd.svg

Index

Constants

This section is empty.

Variables

View Source
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

ClientFinalityStates are terminal states after which no further events are received

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 IsFinalityState(st fsm.StateKey) bool

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL