diff --git a/retrievalmarket/events.go b/retrievalmarket/events.go index 218d39fdb..07139d88e 100644 --- a/retrievalmarket/events.go +++ b/retrievalmarket/events.go @@ -119,6 +119,10 @@ const ( // the client that all blocks were sent for the deal, and the client is // waiting for the last blocks to arrive ClientEventWaitForLastBlocks + + // ClientEventPaymentChannelSkip is fired when the total deal price is zero + // so there's no need to set up a payment channel + ClientEventPaymentChannelSkip ) // ClientEvents is a human readable map of client event name -> event description diff --git a/retrievalmarket/impl/clientstates/client_fsm.go b/retrievalmarket/impl/clientstates/client_fsm.go index 8de1fd991..5d70b271d 100644 --- a/retrievalmarket/impl/clientstates/client_fsm.go +++ b/retrievalmarket/impl/clientstates/client_fsm.go @@ -80,12 +80,18 @@ var ClientEvents = fsm.Events{ deal.Message = xerrors.Errorf("error from payment channel: %w", err).Error() return nil }), + + // Price of deal is zero so skip creating a payment channel + 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). @@ -98,6 +104,7 @@ var ClientEvents = fsm.Events{ } return nil }), + fsm.Event(rm.ClientEventPaymentChannelReady). From(rm.DealStatusPaymentChannelCreating).To(rm.DealStatusPaymentChannelAllocatingLane). From(rm.DealStatusPaymentChannelAddingFunds).To(rm.DealStatusOngoing). @@ -113,6 +120,7 @@ var ClientEvents = fsm.Events{ deal.Message = "" return nil }), + fsm.Event(rm.ClientEventAllocateLaneErrored). FromMany(rm.DealStatusPaymentChannelAllocatingLane). To(rm.DealStatusFailing). @@ -253,6 +261,7 @@ var ClientEvents = fsm.Events{ // completing deals 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), diff --git a/retrievalmarket/impl/clientstates/client_states.go b/retrievalmarket/impl/clientstates/client_states.go index 5190567af..39915d3fe 100644 --- a/retrievalmarket/impl/clientstates/client_states.go +++ b/retrievalmarket/impl/clientstates/client_states.go @@ -36,6 +36,10 @@ func ProposeDeal(ctx fsm.Context, environment ClientDealEnvironment, deal rm.Cli // SetupPaymentChannelStart initiates setting up a payment channel for a deal func SetupPaymentChannelStart(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error { + // If the total funds required for the deal are zero, skip creating the payment channel + if deal.TotalFunds.IsZero() { + return ctx.Trigger(rm.ClientEventPaymentChannelSkip) + } tok, _, err := environment.Node().GetChainHead(ctx.Context()) if err != nil { diff --git a/retrievalmarket/impl/clientstates/client_states_test.go b/retrievalmarket/impl/clientstates/client_states_test.go index 8a394f526..ccf86128c 100644 --- a/retrievalmarket/impl/clientstates/client_states_test.go +++ b/retrievalmarket/impl/clientstates/client_states_test.go @@ -146,6 +146,14 @@ func TestSetupPaymentChannel(t *testing.T) { require.Equal(t, dealState.Status, retrievalmarket.DealStatusFailing) }) + t.Run("payment channel skip if total funds is zero", func(t *testing.T) { + envParams := testnodes.TestRetrievalClientNodeParams{} + dealState := makeDealState(retrievalmarket.DealStatusAccepted) + dealState.TotalFunds = abi.NewTokenAmount(0) + runSetupPaymentChannel(t, envParams, dealState) + assert.Empty(t, dealState.Message) + assert.Equal(t, dealState.Status, retrievalmarket.DealStatusOngoing) + }) } func TestWaitForPaymentReady(t *testing.T) { diff --git a/retrievalmarket/impl/integration_test.go b/retrievalmarket/impl/integration_test.go index 42fd98230..b169ae3d2 100644 --- a/retrievalmarket/impl/integration_test.go +++ b/retrievalmarket/impl/integration_test.go @@ -514,11 +514,12 @@ CurrentInterval: %d } else if testCase.cancelled { assert.Equal(t, retrievalmarket.DealStatusCancelled, clientDealState.Status) } else { - assert.Equal(t, clientDealState.PaymentInfo.Lane, expectedVoucher.Lane) - require.NotNil(t, createdChan) - require.Equal(t, expectedTotal, createdChan.amt) - require.Equal(t, clientPaymentChannel, *newLaneAddr) if !testCase.zeroPricePerByte { + assert.Equal(t, clientDealState.PaymentInfo.Lane, expectedVoucher.Lane) + require.NotNil(t, createdChan) + require.Equal(t, expectedTotal, createdChan.amt) + require.Equal(t, clientPaymentChannel, *newLaneAddr) + // verify that the voucher was saved/seen by the client with correct values require.NotNil(t, createdVoucher) tut.TestVoucherEquality(t, createdVoucher, expectedVoucher)