clientstates

package
v1.2.12 Latest Latest
Warning

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

Go to latest
Published: Apr 8, 2021 License: Apache-2.0, MIT Imports: 11 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/chenjianmei111/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.DealStatusErrored).
		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).
		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.DealStatusRejected).
		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.DealStatusDealNotFound).
		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.DealStatusPaymentChannelAllocatingLane).
		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).
		From(rm.DealStatusPaymentChannelCreating).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.DealStatusErrored).
		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.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).To(rm.DealStatusFundsNeeded).
		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.DealStatusFundsNeeded).ToJustRecord().
		From(rm.DealStatusFundsNeededLastPayment).To(rm.DealStatusSendFundsLastPayment).
		From(rm.DealStatusClientWaitingForLastBlocks).To(rm.DealStatusCompleted).
		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(paymentChannelCreationStates...).ToJustRecord().
		Action(recordReceived),

	fsm.Event(rm.ClientEventSendFunds).
		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.DealStatusErrored).
		Action(func(deal *rm.ClientDealState, err error) error {
			deal.Message = xerrors.Errorf("writing deal payment: %w", err).Error()
			return nil
		}),
	fsm.Event(rm.ClientEventPaymentSent).
		From(rm.DealStatusSendFunds).To(rm.DealStatusOngoing).
		From(rm.DealStatusSendFundsLastPayment).To(rm.DealStatusFinalizing).
		Action(func(deal *rm.ClientDealState) error {

			deal.FundsSpent = big.Add(deal.FundsSpent, deal.PaymentRequested)

			paymentForUnsealing := big.Min(deal.PaymentRequested, big.Sub(deal.UnsealPrice, deal.UnsealFundsPaid))

			bytesPaidFor := big.Div(big.Sub(deal.PaymentRequested, paymentForUnsealing), deal.PricePerByte).Uint64()
			if bytesPaidFor >= deal.CurrentInterval {
				deal.CurrentInterval += deal.DealProposal.PaymentIntervalIncrease
			}
			deal.BytesPaidFor += bytesPaidFor
			deal.UnsealFundsPaid = big.Add(deal.UnsealFundsPaid, paymentForUnsealing)
			deal.PaymentRequested = abi.NewTokenAmount(0)
			return nil
		}),

	fsm.Event(rm.ClientEventComplete).
		From(rm.DealStatusOngoing).To(rm.DealStatusCheckComplete).
		From(rm.DealStatusBlocksComplete).To(rm.DealStatusCheckComplete).
		From(rm.DealStatusFinalizing).To(rm.DealStatusCompleted),
	fsm.Event(rm.ClientEventCompleteVerified).
		From(rm.DealStatusCheckComplete).To(rm.DealStatusCompleted),
	fsm.Event(rm.ClientEventEarlyTermination).
		From(rm.DealStatusCheckComplete).To(rm.DealStatusErrored).
		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),

	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 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
}

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