From 218d235931c8751f27894cf6808d591fe8af4c0a Mon Sep 17 00:00:00 2001 From: shannonwells Date: Mon, 13 Apr 2020 16:42:38 -0700 Subject: [PATCH] tests for adding funds and wait for payment channel creation, some cleanup --- .../impl/clientstates/client_fsm.go | 12 +- .../impl/clientstates/client_states.go | 9 +- .../impl/clientstates/client_states_test.go | 117 ++++++++++++++++-- .../testnodes/test_retrieval_client_node.go | 54 ++++---- retrievalmarket/types.go | 10 +- 5 files changed, 153 insertions(+), 49 deletions(-) diff --git a/retrievalmarket/impl/clientstates/client_fsm.go b/retrievalmarket/impl/clientstates/client_fsm.go index 36fe923e1..fa21534bf 100644 --- a/retrievalmarket/impl/clientstates/client_fsm.go +++ b/retrievalmarket/impl/clientstates/client_fsm.go @@ -29,7 +29,7 @@ var ClientEvents = fsm.Events{ fsm.Event(rm.ClientEventOpen). From(rm.DealStatusNew).ToNoChange(), fsm.Event(rm.ClientEventPaymentChannelErrored). - From(rm.DealStatusAccepted).To(rm.DealStatusFailed). + From(rm.DealStatusPaymentChannelCreating).To(rm.DealStatusFailed). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = xerrors.Errorf("getting payment channel: %w", err).Error() return nil @@ -41,7 +41,7 @@ var ClientEvents = fsm.Events{ return nil }), fsm.Event(rm.ClientEventPaymentChannelAddingFunds). - From(rm.DealStatusPaymentChannelReady).To(rm.DealStatusPaymentChannelAddingFunds), + FromMany(rm.DealStatusOngoing, rm.DealStatusPaymentChannelReady).To(rm.DealStatusPaymentChannelAddingFunds), fsm.Event(rm.ClientEventPaymentChannelReady). FromMany(rm.DealStatusPaymentChannelCreating, rm.DealStatusPaymentChannelAddingFunds). To(rm.DealStatusPaymentChannelReady). @@ -53,11 +53,17 @@ var ClientEvents = fsm.Events{ return nil }), fsm.Event(rm.ClientEventAllocateLaneErrored). - From(rm.DealStatusAccepted).To(rm.DealStatusFailed). + From(rm.DealStatusPaymentChannelCreating).To(rm.DealStatusFailed). Action(func(deal *rm.ClientDealState, err error) error { deal.Message = xerrors.Errorf("allocating payment lane: %w", err).Error() return nil }), + fsm.Event(rm.ClientEventPaymentChannelAddFundsErrored). + From(rm.DealStatusPaymentChannelAddingFunds).To(rm.DealStatusFailed). + Action(func(deal *rm.ClientDealState, err error) error { + deal.Message = xerrors.Errorf("wait for add funds: %w", err).Error() + return nil + }), fsm.Event(rm.ClientEventWriteDealProposalErrored). FromAny().To(rm.DealStatusErrored). Action(func(deal *rm.ClientDealState, err error) error { diff --git a/retrievalmarket/impl/clientstates/client_states.go b/retrievalmarket/impl/clientstates/client_states.go index 15e18d904..1e627b7e8 100644 --- a/retrievalmarket/impl/clientstates/client_states.go +++ b/retrievalmarket/impl/clientstates/client_states.go @@ -19,7 +19,7 @@ type ClientDealEnvironment interface { ConsumeBlock(context.Context, rm.DealID, rm.Block) (uint64, bool, error) } -// SetupPaymentChannelStart sets up a payment channel for a deal +// SetupPaymentChannelStart initiates setting up a payment channel for a deal func SetupPaymentChannelStart(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error { tok, _, err := environment.Node().GetChainHead(ctx.Context()) if err != nil { @@ -45,7 +45,6 @@ func WaitForPaymentChannelCreate(ctx fsm.Context, environment ClientDealEnvironm if err != nil { return ctx.Trigger(rm.ClientEventPaymentChannelErrored, err) } - // if successful call allocate lane and trigger ClientEventPaymentChannelReady evt lane, err := environment.Node().AllocateLane(paych) if err != nil { @@ -54,15 +53,13 @@ func WaitForPaymentChannelCreate(ctx fsm.Context, environment ClientDealEnvironm return ctx.Trigger(rm.ClientEventPaymentChannelReady, paych, lane) } -// WaitForPaymentChannelAddFunds waits for funds to be added to a payment channel, then +// WaitForPaymentChannelAddFunds waits for funds to be added to an existing payment channel, then // signals that payment channel is ready again func WaitForPaymentChannelAddFunds(ctx fsm.Context, environment ClientDealEnvironment, deal rm.ClientDealState) error { - // call wait for funds to be added func, err := environment.Node().WaitForPaymentChannelAddFunds(*deal.WaitMsgCID) if err != nil { - return ctx.Trigger(rm.ClientEventPaymentChannelErrored, err) + return ctx.Trigger(rm.ClientEventPaymentChannelAddFundsErrored, err) } - // then trigger ClientEventPaymentChannelReady evt return ctx.Trigger(rm.ClientEventPaymentChannelReady, deal.PaymentInfo.PayCh, deal.PaymentInfo.Lane) } diff --git a/retrievalmarket/impl/clientstates/client_states_test.go b/retrievalmarket/impl/clientstates/client_states_test.go index b7c8f60d1..53182e012 100644 --- a/retrievalmarket/impl/clientstates/client_states_test.go +++ b/retrievalmarket/impl/clientstates/client_states_test.go @@ -14,6 +14,7 @@ import ( "github.com/filecoin-project/specs-actors/actors/builtin/paych" "github.com/ipfs/go-cid" mh "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-fil-markets/retrievalmarket" @@ -106,18 +107,110 @@ func TestSetupPaymentChannel(t *testing.T) { require.Equal(t, dealState.Status, retrievalmarket.DealStatusFailed) }) - // TODO: find the right place for this test - //t.Run("when allocate lane fails", func(t *testing.T) { - // dealState := makeDealState(retrievalmarket.ClientEventPaymentChannelAddingFunds) - // envParams := testnodes.TestRetrievalClientNodeParams{ - // PayCh: expectedPayCh, - // Lane: expectedLane, - // LaneError: errors.New("Something went wrong"), - // } - // runSetupPaymentChannel(t, envParams, dealState) - // require.NotEmpty(t, dealState.Message) - // require.Equal(t, dealState.Status, retrievalmarket.DealStatusFailed) - //}) +} + +func TestWaitForPaymentChannelCreate(t *testing.T) { + ctx := context.Background() + ds := testnet.NewTestRetrievalDealStream(testnet.TestDealStreamParams{}) + expectedPayCh := address.TestAddress2 + expectedLane := uint64(10) + eventMachine, err := fsm.NewEventProcessor(retrievalmarket.ClientDealState{}, "Status", clientstates.ClientEvents) + require.NoError(t, err) + runWaitForPaychCreate := func(t *testing.T, + params testnodes.TestRetrievalClientNodeParams, + dealState *retrievalmarket.ClientDealState) { + node := testnodes.NewTestRetrievalClientNode(params) + environment := &fakeEnvironment{node, ds, 0, nil} + fsmCtx := fsmtest.NewTestContext(ctx, eventMachine) + err := clientstates.WaitForPaymentChannelCreate(fsmCtx, environment, *dealState) + require.NoError(t, err) + fsmCtx.ReplayEvents(t, dealState) + } + msgCID := testnet.GenerateCids(1)[0] + + t.Run("it works", func(t *testing.T) { + dealState := makeDealState(retrievalmarket.DealStatusPaymentChannelCreating) + dealState.WaitMsgCID = &msgCID + params := testnodes.TestRetrievalClientNodeParams{ + PayCh: expectedPayCh, + CreatePaychCID: msgCID, + Lane: expectedLane, + } + runWaitForPaychCreate(t, params, dealState) + require.Empty(t, dealState.Message) + assert.Equal(t, dealState.Status, retrievalmarket.DealStatusPaymentChannelReady) + assert.Equal(t, expectedLane, dealState.PaymentInfo.Lane) + }) + t.Run("if Wait fails", func(t *testing.T) { + dealState := makeDealState(retrievalmarket.DealStatusPaymentChannelCreating) + dealState.WaitMsgCID = &msgCID + params := testnodes.TestRetrievalClientNodeParams{ + PayCh: expectedPayCh, + CreatePaychCID: msgCID, + WaitForChCreateErr: errors.New("boom"), + } + runWaitForPaychCreate(t, params, dealState) + assert.Contains(t, dealState.Message, "boom") + assert.Equal(t, dealState.Status, retrievalmarket.DealStatusFailed) + }) + + t.Run("if AllocateLane fails", func(t *testing.T) { + dealState := makeDealState(retrievalmarket.DealStatusPaymentChannelCreating) + dealState.WaitMsgCID = &msgCID + params := testnodes.TestRetrievalClientNodeParams{ + PayCh: expectedPayCh, + CreatePaychCID: msgCID, + LaneError: errors.New("boom"), + } + runWaitForPaychCreate(t, params, dealState) + assert.Contains(t, dealState.Message, "boom") + assert.Equal(t, dealState.Status, retrievalmarket.DealStatusFailed) + }) +} + +func TestWaitForPaymentChannelAddFunds(t *testing.T) { + ctx := context.Background() + ds := testnet.NewTestRetrievalDealStream(testnet.TestDealStreamParams{}) + expectedPayCh := address.TestAddress2 + eventMachine, err := fsm.NewEventProcessor(retrievalmarket.ClientDealState{}, "Status", clientstates.ClientEvents) + require.NoError(t, err) + runWaitForPaychAddFunds := func(t *testing.T, + params testnodes.TestRetrievalClientNodeParams, + dealState *retrievalmarket.ClientDealState) { + node := testnodes.NewTestRetrievalClientNode(params) + environment := &fakeEnvironment{node, ds, 0, nil} + fsmCtx := fsmtest.NewTestContext(ctx, eventMachine) + err := clientstates.WaitForPaymentChannelAddFunds(fsmCtx, environment, *dealState) + require.NoError(t, err) + fsmCtx.ReplayEvents(t, dealState) + } + msgCID := testnet.GenerateCids(1)[0] + + t.Run("it works", func(t *testing.T) { + dealState := makeDealState(retrievalmarket.DealStatusPaymentChannelAddingFunds) + dealState.WaitMsgCID = &msgCID + params := testnodes.TestRetrievalClientNodeParams{ + AddFundsOnly: true, + PayCh: expectedPayCh, + AddFundsCID: msgCID, + } + runWaitForPaychAddFunds(t, params, dealState) + require.Empty(t, dealState.Message) + assert.Equal(t, dealState.Status, retrievalmarket.DealStatusPaymentChannelReady) + }) + t.Run("if Wait fails", func(t *testing.T) { + dealState := makeDealState(retrievalmarket.DealStatusPaymentChannelAddingFunds) + dealState.WaitMsgCID = &msgCID + params := testnodes.TestRetrievalClientNodeParams{ + AddFundsOnly: true, + PayCh: expectedPayCh, + AddFundsCID: msgCID, + WaitForAddFundsErr: errors.New("boom"), + } + runWaitForPaychAddFunds(t, params, dealState) + assert.Contains(t, dealState.Message, "boom") + assert.Equal(t, dealState.Status, retrievalmarket.DealStatusFailed) + }) } func TestProposeDeal(t *testing.T) { diff --git a/retrievalmarket/impl/testnodes/test_retrieval_client_node.go b/retrievalmarket/impl/testnodes/test_retrieval_client_node.go index 069aefed9..06f5032f7 100644 --- a/retrievalmarket/impl/testnodes/test_retrieval_client_node.go +++ b/retrievalmarket/impl/testnodes/test_retrieval_client_node.go @@ -2,6 +2,7 @@ package testnodes import ( "context" + "fmt" "github.com/filecoin-project/go-address" "github.com/filecoin-project/specs-actors/actors/abi" @@ -15,14 +16,14 @@ import ( // TestRetrievalClientNode is a node adapter for a retrieval client whose responses // are stubbed type TestRetrievalClientNode struct { - addFundsOnly bool // set this to true if the payment channel is expected to have been created already - payCh address.Address - payChErr error - createPaychMsgCID, addFundsMsgCID cid.Cid - lane uint64 - laneError error - voucher *paych.SignedVoucher - voucherError error + addFundsOnly bool // set this to true to test adding funds to an existing payment channel + payCh address.Address + payChErr error + createPaychMsgCID, addFundsMsgCID cid.Cid + lane uint64 + laneError error + voucher *paych.SignedVoucher + voucherError, waitCreateErr, waitAddErr error allocateLaneRecorder func(address.Address) createPaymentVoucherRecorder func(voucher *paych.SignedVoucher) @@ -31,27 +32,30 @@ type TestRetrievalClientNode struct { // TestRetrievalClientNodeParams are parameters for initializing a TestRetrievalClientNode type TestRetrievalClientNodeParams struct { - PayCh address.Address - PayChErr error - CreatePaychCID, AddFundsCID cid.Cid - Lane uint64 - LaneError error - Voucher *paych.SignedVoucher - VoucherError error - AllocateLaneRecorder func(address.Address) - PaymentVoucherRecorder func(voucher *paych.SignedVoucher) - PaymentChannelRecorder func(address.Address, address.Address, abi.TokenAmount) - AddFundsOnly bool + PayCh address.Address + PayChErr error + CreatePaychCID, AddFundsCID cid.Cid + Lane uint64 + LaneError error + Voucher *paych.SignedVoucher + VoucherError error + AllocateLaneRecorder func(address.Address) + PaymentVoucherRecorder func(voucher *paych.SignedVoucher) + PaymentChannelRecorder func(address.Address, address.Address, abi.TokenAmount) + AddFundsOnly bool + WaitForAddFundsErr, WaitForChCreateErr error } var _ retrievalmarket.RetrievalClientNode = &TestRetrievalClientNode{} -// NewTestRetrievalClientNode instantiates a new TestRetrievalClientNode based ont he given params +// NewTestRetrievalClientNode initializes a new TestRetrievalClientNode based on the given params func NewTestRetrievalClientNode(params TestRetrievalClientNodeParams) *TestRetrievalClientNode { return &TestRetrievalClientNode{ addFundsOnly: params.AddFundsOnly, payCh: params.PayCh, payChErr: params.PayChErr, + waitCreateErr: params.WaitForChCreateErr, + waitAddErr: params.WaitForAddFundsErr, lane: params.Lane, laneError: params.LaneError, voucher: params.Voucher, @@ -96,9 +100,15 @@ func (trcn *TestRetrievalClientNode) GetChainHead(ctx context.Context) (shared.T return shared.TipSetToken{}, 0, nil } func (trcn *TestRetrievalClientNode) WaitForPaymentChannelAddFunds(messageCID cid.Cid) error { - return nil + if messageCID != trcn.addFundsMsgCID { + return fmt.Errorf("expected messageCID: %s does not match actual: %s", trcn.addFundsMsgCID, messageCID) + } + return trcn.waitAddErr } func (trcn *TestRetrievalClientNode) WaitForPaymentChannelCreation(messageCID cid.Cid) (address.Address, error) { - return trcn.payCh, nil + if messageCID != trcn.createPaychMsgCID { + return address.Undef, fmt.Errorf("expected messageCID: %s does not match actual: %s", trcn.createPaychMsgCID, messageCID) + } + return trcn.payCh, trcn.waitCreateErr } diff --git a/retrievalmarket/types.go b/retrievalmarket/types.go index e8352c12e..ddf1c93e9 100644 --- a/retrievalmarket/types.go +++ b/retrievalmarket/types.go @@ -75,19 +75,17 @@ const ( // successful and we are waiting for it to appear on chain ClientEventPaymentChannelCreateInitiated - // ClientEventPaymentChannelCreated means a payment channel has been created - ClientEventPaymentChannelCreated - // ClientEventPaymentChannelReady means the newly created payment channel is ready for the // deal to begin ClientEventPaymentChannelReady // ClientEventPaymentChannelAddingFunds means the request to add funds to a payment channel was - // successful and we are waiting for the funds to be sent + // sent and we are waiting for the funds to be added to the payment channel ClientEventPaymentChannelAddingFunds - // ClientEventPaymentChannelFundsAdded means funds have been added to the payment channel - ClientEventPaymentChannelFundsAdded + // ClientEventPaymentChannelAddingFunds means that adding funds to the payment channel + // failed + ClientEventPaymentChannelAddFundsErrored // ClientEventWriteDealProposalErrored means a network error writing a deal proposal ClientEventWriteDealProposalErrored