diff --git a/docs/retrievalclient.mmd b/docs/retrievalclient.mmd index d7ff4442..5462d1ed 100644 --- a/docs/retrievalclient.mmd +++ b/docs/retrievalclient.mmd @@ -25,6 +25,7 @@ stateDiagram-v2 state "DealStatusRetryLegacy" as 27 state "DealStatusWaitForAcceptanceLegacy" as 28 state "DealStatusWaitingForLastBlocks" as 29 + state "DealStatusPaymentChannelAddingInitialFunds" as 30 0 : On entry runs ProposeDeal 4 : On entry runs WaitPaymentChannelReady 5 : On entry runs WaitPaymentChannelReady @@ -40,6 +41,7 @@ stateDiagram-v2 24 : On entry runs AllocateLane 25 : On entry runs CancelDeal 27 : On entry runs ProposeDeal + 30 : On entry runs WaitPaymentChannelReady [*] --> 0 note right of 0 The following events are not shown cause they can trigger from any state. @@ -65,11 +67,12 @@ stateDiagram-v2 6 --> 8 : ClientEventPaymentChannelErrored 6 --> 13 : ClientEventPaymentChannelSkip 6 --> 4 : ClientEventPaymentChannelCreateInitiated - 6 --> 24 : ClientEventPaymentChannelAddingFunds + 6 --> 30 : ClientEventPaymentChannelAddingFunds 22 --> 5 : ClientEventPaymentChannelAddingFunds 4 --> 24 : ClientEventPaymentChannelReady 5 --> 13 : ClientEventPaymentChannelReady 22 --> 13 : ClientEventPaymentChannelReady + 30 --> 24 : ClientEventPaymentChannelReady 24 --> 8 : ClientEventAllocateLaneErrored 24 --> 13 : ClientEventLaneAllocated 10 --> 14 : ClientEventLastPaymentRequested diff --git a/docs/retrievalclient.mmd.png b/docs/retrievalclient.mmd.png index a16ffed6..8659dc5c 100644 Binary files a/docs/retrievalclient.mmd.png and b/docs/retrievalclient.mmd.png differ diff --git a/docs/retrievalclient.mmd.svg b/docs/retrievalclient.mmd.svg index f0d29fc2..aa4ee8be 100644 --- a/docs/retrievalclient.mmd.svg +++ b/docs/retrievalclient.mmd.svg @@ -1,6 +1,6 @@ -ClientEventOpenClientEventDealProposedClientEventDealProposedClientEventDealRejectedClientEventDealRejectedClientEventDealNotFoundClientEventDealNotFoundClientEventDealAcceptedClientEventDealAcceptedClientEventPaymentChannelErroredClientEventPaymentChannelErroredClientEventPaymentChannelErroredClientEventPaymentChannelSkipClientEventPaymentChannelCreateInitiatedClientEventPaymentChannelAddingFundsClientEventPaymentChannelAddingFundsClientEventPaymentChannelReadyClientEventPaymentChannelReadyClientEventPaymentChannelReadyClientEventPaymentChannelReadyClientEventAllocateLaneErroredClientEventLaneAllocatedClientEventLastPaymentRequestedClientEventLastPaymentRequestedClientEventLastPaymentRequestedClientEventLastPaymentRequestedClientEventPaymentRequestedClientEventPaymentRequestedClientEventPaymentRequestedClientEventUnsealPaymentRequestedClientEventUnsealPaymentRequestedClientEventAllBlocksReceivedClientEventAllBlocksReceivedClientEventAllBlocksReceivedClientEventAllBlocksReceivedClientEventBlocksReceivedClientEventBlocksReceivedClientEventBlocksReceivedClientEventBlocksReceivedClientEventBlocksReceivedClientEventSendFundsClientEventSendFundsClientEventFundsExpendedClientEventBadPaymentRequestedClientEventBadPaymentRequestedClientEventCreateVoucherFailedClientEventCreateVoucherFailedClientEventVoucherShortfallClientEventVoucherShortfallClientEventPaymentSentClientEventPaymentSentClientEventCompleteClientEventCompleteClientEventCompleteClientEventCompleteVerifiedClientEventEarlyTerminationClientEventWaitForLastBlocksClientEventCancelCompleteClientEventCancelCompleteClientEventRecheckFundsDealStatusNewOn entry runs ProposeDealDealStatusWaitForAcceptanceDealStatusPaymentChannelCreatingOn entry runs WaitPaymentChannelReadyDealStatusPaymentChannelAddingFundsOn entry runs WaitPaymentChannelReadyDealStatusAcceptedOn entry runs SetupPaymentChannelStartDealStatusFailingOn entry runs CancelDealDealStatusRejectedDealStatusFundsNeededOn entry runs ProcessPaymentRequestedDealStatusSendFundsOn entry runs SendFundsDealStatusSendFundsLastPaymentOn entry runs SendFundsDealStatusOngoingOn entry runs OngoingDealStatusFundsNeededLastPaymentOn entry runs ProcessPaymentRequestedDealStatusCompletedDealStatusDealNotFoundDealStatusErroredDealStatusBlocksCompleteDealStatusFinalizingDealStatusCheckCompleteOn entry runs CheckCompleteDealStatusCheckFundsOn entry runs CheckFundsDealStatusInsufficientFundsDealStatusPaymentChannelAllocatingLaneOn entry runs AllocateLaneDealStatusCancellingOn entry runs CancelDealDealStatusCancelledDealStatusRetryLegacyOn entry runs ProposeDealDealStatusWaitForAcceptanceLegacyDealStatusWaitingForLastBlocksDealStatusPaymentChannelAddingInitialFundsOn entry runs WaitPaymentChannelReadyThe following events are not shown cause they can trigger from any state.ClientEventWriteDealProposalErrored - transitions state to DealStatusErroredClientEventUnknownResponseReceived - transitions state to DealStatusFailingClientEventDataTransferError - transitions state to DealStatusErroredClientEventWriteDealPaymentErrored - transitions state to DealStatusErroredClientEventProviderCancelled - transitions state to DealStatusCancellingClientEventCancel - transitions state to DealStatusCancellingThe following events only record in this state.ClientEventLastPaymentRequestedClientEventPaymentRequestedClientEventAllBlocksReceivedClientEventBlocksReceivedThe following events only record in this state.ClientEventLastPaymentRequestedClientEventPaymentRequestedClientEventAllBlocksReceivedClientEventBlocksReceivedThe following events only record in this state.ClientEventLastPaymentRequestedClientEventPaymentRequestedClientEventAllBlocksReceivedClientEventBlocksReceivedThe following events only record in this state.ClientEventProviderCancelledThe following events only record in this state.ClientEventAllBlocksReceivedThe following events only record in this state.ClientEventAllBlocksReceivedThe following events only record in this state.ClientEventLastPaymentRequestedClientEventPaymentRequestedClientEventAllBlocksReceivedClientEventBlocksReceivedThe following events only record in this state.ClientEventProviderCancelledThe following events only record in this state.ClientEventLastPaymentRequestedClientEventPaymentRequestedClientEventAllBlocksReceivedClientEventBlocksReceived \ No newline at end of file diff --git a/retrievalmarket/dealstatus.go b/retrievalmarket/dealstatus.go index 2885954f..c73e76a0 100644 --- a/retrievalmarket/dealstatus.go +++ b/retrievalmarket/dealstatus.go @@ -113,38 +113,44 @@ const ( // the deal price per byte is zero (if it's not zero the provider asks // for final payment after sending the last blocks). DealStatusClientWaitingForLastBlocks + + // DealStatusPaymentChannelAddingInitialFunds means that a payment channel + // exists from an earlier deal between client and provider, but we need + // to add funds to the channel for this particular deal + DealStatusPaymentChannelAddingInitialFunds ) // DealStatuses maps deal status to a human readable representation var DealStatuses = map[DealStatus]string{ - DealStatusNew: "DealStatusNew", - DealStatusUnsealing: "DealStatusUnsealing", - DealStatusUnsealed: "DealStatusUnsealed", - DealStatusWaitForAcceptance: "DealStatusWaitForAcceptance", - DealStatusPaymentChannelCreating: "DealStatusPaymentChannelCreating", - DealStatusPaymentChannelAddingFunds: "DealStatusPaymentChannelAddingFunds", - DealStatusAccepted: "DealStatusAccepted", - DealStatusFundsNeededUnseal: "DealStatusFundsNeededUnseal", - DealStatusFailing: "DealStatusFailing", - DealStatusRejected: "DealStatusRejected", - DealStatusFundsNeeded: "DealStatusFundsNeeded", - DealStatusSendFunds: "DealStatusSendFunds", - DealStatusSendFundsLastPayment: "DealStatusSendFundsLastPayment", - DealStatusOngoing: "DealStatusOngoing", - DealStatusFundsNeededLastPayment: "DealStatusFundsNeededLastPayment", - DealStatusCompleted: "DealStatusCompleted", - DealStatusDealNotFound: "DealStatusDealNotFound", - DealStatusErrored: "DealStatusErrored", - DealStatusBlocksComplete: "DealStatusBlocksComplete", - DealStatusFinalizing: "DealStatusFinalizing", - DealStatusCompleting: "DealStatusCompleting", - DealStatusCheckComplete: "DealStatusCheckComplete", - DealStatusCheckFunds: "DealStatusCheckFunds", - DealStatusInsufficientFunds: "DealStatusInsufficientFunds", - DealStatusPaymentChannelAllocatingLane: "DealStatusPaymentChannelAllocatingLane", - DealStatusCancelling: "DealStatusCancelling", - DealStatusCancelled: "DealStatusCancelled", - DealStatusRetryLegacy: "DealStatusRetryLegacy", - DealStatusWaitForAcceptanceLegacy: "DealStatusWaitForAcceptanceLegacy", - DealStatusClientWaitingForLastBlocks: "DealStatusWaitingForLastBlocks", + DealStatusNew: "DealStatusNew", + DealStatusUnsealing: "DealStatusUnsealing", + DealStatusUnsealed: "DealStatusUnsealed", + DealStatusWaitForAcceptance: "DealStatusWaitForAcceptance", + DealStatusPaymentChannelCreating: "DealStatusPaymentChannelCreating", + DealStatusPaymentChannelAddingFunds: "DealStatusPaymentChannelAddingFunds", + DealStatusAccepted: "DealStatusAccepted", + DealStatusFundsNeededUnseal: "DealStatusFundsNeededUnseal", + DealStatusFailing: "DealStatusFailing", + DealStatusRejected: "DealStatusRejected", + DealStatusFundsNeeded: "DealStatusFundsNeeded", + DealStatusSendFunds: "DealStatusSendFunds", + DealStatusSendFundsLastPayment: "DealStatusSendFundsLastPayment", + DealStatusOngoing: "DealStatusOngoing", + DealStatusFundsNeededLastPayment: "DealStatusFundsNeededLastPayment", + DealStatusCompleted: "DealStatusCompleted", + DealStatusDealNotFound: "DealStatusDealNotFound", + DealStatusErrored: "DealStatusErrored", + DealStatusBlocksComplete: "DealStatusBlocksComplete", + DealStatusFinalizing: "DealStatusFinalizing", + DealStatusCompleting: "DealStatusCompleting", + DealStatusCheckComplete: "DealStatusCheckComplete", + DealStatusCheckFunds: "DealStatusCheckFunds", + DealStatusInsufficientFunds: "DealStatusInsufficientFunds", + DealStatusPaymentChannelAllocatingLane: "DealStatusPaymentChannelAllocatingLane", + DealStatusCancelling: "DealStatusCancelling", + DealStatusCancelled: "DealStatusCancelled", + DealStatusRetryLegacy: "DealStatusRetryLegacy", + DealStatusWaitForAcceptanceLegacy: "DealStatusWaitForAcceptanceLegacy", + DealStatusClientWaitingForLastBlocks: "DealStatusWaitingForLastBlocks", + DealStatusPaymentChannelAddingInitialFunds: "DealStatusPaymentChannelAddingInitialFunds", } diff --git a/retrievalmarket/impl/clientstates/client_fsm.go b/retrievalmarket/impl/clientstates/client_fsm.go index 5d70b271..b5a8877f 100644 --- a/retrievalmarket/impl/clientstates/client_fsm.go +++ b/retrievalmarket/impl/clientstates/client_fsm.go @@ -92,8 +92,13 @@ var ClientEvents = fsm.Events{ return nil }), + // Client is adding funds to payment channel fsm.Event(rm.ClientEventPaymentChannelAddingFunds). - FromMany(rm.DealStatusAccepted).To(rm.DealStatusPaymentChannelAllocatingLane). + // If the deal has just been accepted, we are adding the initial funds + // to the payment channel + FromMany(rm.DealStatusAccepted).To(rm.DealStatusPaymentChannelAddingInitialFunds). + // If the deal was already ongoing, and ran out of funds, we are + // topping up funds in the payment channel FromMany(rm.DealStatusCheckFunds).To(rm.DealStatusPaymentChannelAddingFunds). Action(func(deal *rm.ClientDealState, msgCID cid.Cid, payCh address.Address) error { deal.WaitMsgCID = &msgCID @@ -105,8 +110,18 @@ var ClientEvents = fsm.Events{ return nil }), + // The payment channel add funds message has landed on chain fsm.Event(rm.ClientEventPaymentChannelReady). + // If the payment channel between client and provider was being created + // for the first time, or if the payment channel had already been + // created for an earlier deal but the initial funding for this deal + // was being added, then we still need to allocate a payment channel + // lane From(rm.DealStatusPaymentChannelCreating).To(rm.DealStatusPaymentChannelAllocatingLane). + From(rm.DealStatusPaymentChannelAddingInitialFunds).To(rm.DealStatusPaymentChannelAllocatingLane). + // If the payment channel ran out of funds and needed to be topped up, + // then the payment channel lane already exists so just move straight + // to the ongoing state From(rm.DealStatusPaymentChannelAddingFunds).To(rm.DealStatusOngoing). From(rm.DealStatusCheckFunds).To(rm.DealStatusOngoing). Action(func(deal *rm.ClientDealState, payCh address.Address) error { @@ -318,19 +333,20 @@ var ClientFinalityStates = []fsm.StateKey{ // ClientStateEntryFuncs are the handlers for different states in a retrieval client var ClientStateEntryFuncs = fsm.StateEntryFuncs{ - rm.DealStatusNew: ProposeDeal, - rm.DealStatusRetryLegacy: ProposeDeal, - rm.DealStatusAccepted: SetupPaymentChannelStart, - rm.DealStatusPaymentChannelCreating: 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.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, } diff --git a/retrievalmarket/impl/clientstates/client_states_test.go b/retrievalmarket/impl/clientstates/client_states_test.go index ccf86128..a258eca7 100644 --- a/retrievalmarket/impl/clientstates/client_states_test.go +++ b/retrievalmarket/impl/clientstates/client_states_test.go @@ -93,6 +93,7 @@ func TestProposeDeal(t *testing.T) { require.Equal(t, dealState.Status, retrievalmarket.DealStatusErrored) }) } + func TestSetupPaymentChannel(t *testing.T) { ctx := context.Background() expectedPayCh := address.TestAddress2 @@ -131,7 +132,7 @@ func TestSetupPaymentChannel(t *testing.T) { runSetupPaymentChannel(t, envParams, dealState) require.Empty(t, dealState.Message) require.Equal(t, envParams.AddFundsCID, *dealState.WaitMsgCID) - require.Equal(t, retrievalmarket.DealStatusPaymentChannelAllocatingLane, dealState.Status) + require.Equal(t, retrievalmarket.DealStatusPaymentChannelAddingInitialFunds, dealState.Status) require.Equal(t, expectedPayCh, dealState.PaymentInfo.PayCh) }) diff --git a/retrievalmarket/storage_retrieval_integration_test.go b/retrievalmarket/storage_retrieval_integration_test.go index 53f3ea63..49e03122 100644 --- a/retrievalmarket/storage_retrieval_integration_test.go +++ b/retrievalmarket/storage_retrieval_integration_test.go @@ -54,7 +54,7 @@ func TestStorageRetrieval(t *testing.T) { _ = sh.Client.SubscribeToEvents(clientSubscriber) // set ask price where we'll accept any price - err := sh.Provider.SetAsk(big.NewInt(0), big.NewInt(0), 50_000) + err := sh.Provider.SetAsk(big.NewInt(0), big.NewInt(0), 50000) assert.NoError(t, err) result := sh.ProposeStorageDeal(t, &storagemarket.DataRef{TransferType: storagemarket.TTGraphsync, Root: sh.PayloadCid}, false, false)