diff --git a/docs/storageclient.mmd b/docs/storageclient.mmd index 50a2727d..f0318045 100644 --- a/docs/storageclient.mmd +++ b/docs/storageclient.mmd @@ -70,7 +70,7 @@ stateDiagram-v2 note left of 11 : The following events only record in this state.

ClientEventFundsReleased - note left of 17 : The following events only record in this state.

ClientEventDataTransferRestarted + note left of 17 : The following events only record in this state.

ClientEventDataTransferRestarted
ClientEventDataTransferStalled note left of 21 : The following events only record in this state.

ClientEventFundsReserved diff --git a/docs/storageprovider.mmd b/docs/storageprovider.mmd index f9c49c0f..33352a59 100644 --- a/docs/storageprovider.mmd +++ b/docs/storageprovider.mmd @@ -89,7 +89,7 @@ stateDiagram-v2 note left of 11 : The following events only record in this state.

ProviderEventFundsReleased - note left of 17 : The following events only record in this state.

ProviderEventDataTransferRestarted + note left of 17 : The following events only record in this state.

ProviderEventDataTransferRestarted
ProviderEventDataTransferStalled note left of 20 : The following events only record in this state.

ProviderEventFundsReserved diff --git a/docs/storageprovider.mmd.png b/docs/storageprovider.mmd.png index f4a71dff..1e202335 100644 Binary files a/docs/storageprovider.mmd.png and b/docs/storageprovider.mmd.png differ diff --git a/docs/storageprovider.mmd.svg b/docs/storageprovider.mmd.svg index 823ec608..95d91cbf 100644 --- a/docs/storageprovider.mmd.svg +++ b/docs/storageprovider.mmd.svg @@ -1,6 +1,6 @@ -ProviderEventOpenProviderEventDealRejectedProviderEventDealRejectedProviderEventDealRejectedProviderEventRejectionSentProviderEventDealDecidingProviderEventDataRequestedProviderEventDataTransferFailedProviderEventDataTransferInitiatedProviderEventDataTransferRestartFailedProviderEventDataTransferRestartedProviderEventDataTransferRestartedProviderEventDataTransferCompletedProviderEventDataVerificationFailedProviderEventVerifiedDataProviderEventVerifiedDataProviderEventFundingInitiatedProviderEventFundedProviderEventFundedProviderEventDealPublishInitiatedProviderEventDealPublishErrorProviderEventSendResponseFailedProviderEventSendResponseFailedProviderEventDealPublishedProviderEventFileStoreErroredProviderEventFileStoreErroredProviderEventFileStoreErroredProviderEventMultistoreErroredProviderEventDealHandoffFailedProviderEventDealHandedOffProviderEventDealActivationFailedProviderEventDealActivatedProviderEventCleanupFinishedProviderEventDealSlashedProviderEventDealExpiredProviderEventDealCompletionFailedProviderEventFailedProviderEventRestartProviderEventRestartProviderEventRestartProviderEventRestartProviderEventTrackFundsFailedStorageDealUnknownStorageDealStagedOn entry runs HandoffDealStorageDealSealingOn entry runs VerifyDealActivatedStorageDealFinalizingOn entry runs CleanupDealStorageDealActiveOn entry runs WaitForDealCompletionStorageDealExpiredStorageDealSlashedStorageDealRejectingOn entry runs RejectDealStorageDealFailingOn entry runs FailDealStorageDealValidatingOn entry runs ValidateDealProposalStorageDealAcceptWaitOn entry runs DecideOnProposalStorageDealTransferringStorageDealWaitingForDataStorageDealVerifyDataOn entry runs VerifyDataStorageDealEnsureProviderFundsOn entry runs EnsureProviderFundsStorageDealProviderFundingOn entry runs WaitForFundingStorageDealPublishOn entry runs PublishDealStorageDealPublishingOn entry runs WaitForPublishStorageDealErrorStorageDealProviderTransferRestartOn entry runs RestartDataTransferThe following events are not shown cause they can trigger from any state.ProviderEventNodeErrored - transitions state to StorageDealFailingProviderEventRestart - does not transition stateThe following events only record in this state.ProviderEventPieceStoreErroredThe following events only record in this state.ProviderEventFundsReleasedThe following events only record in this state.ProviderEventDataTransferRestartedProviderEventDataTransferStalledThe following events only record in this state.ProviderEventFundsReservedThe following events only record in this state.ProviderEventFundsReleased \ No newline at end of file diff --git a/go.mod b/go.mod index ee7679eb..b101dcf4 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/filecoin-project/filecoin-ffi v0.30.4-0.20200716204036-cddc56607e1d github.com/filecoin-project/go-address v0.0.3 github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 - github.com/filecoin-project/go-data-transfer v0.6.8-0.20201012061237-d181ba9cacad + github.com/filecoin-project/go-data-transfer v0.6.8-0.20201013092017-eee8d1cde5d5 github.com/filecoin-project/go-ds-versioning v0.1.0 github.com/filecoin-project/go-multistore v0.0.3 github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 @@ -24,7 +24,7 @@ require ( github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834 github.com/ipfs/go-cid v0.0.7 github.com/ipfs/go-datastore v0.4.5 - github.com/ipfs/go-graphsync v0.2.1 + github.com/ipfs/go-graphsync v0.2.1-0.20201013053840-5d8ea8076a2c github.com/ipfs/go-ipfs-blockstore v1.0.1 github.com/ipfs/go-ipfs-blocksutil v0.0.1 github.com/ipfs/go-ipfs-chunker v0.0.5 diff --git a/go.sum b/go.sum index bd66d855..700cd15d 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,8 @@ github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:a github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ= -github.com/filecoin-project/go-data-transfer v0.6.8-0.20201012061237-d181ba9cacad h1:yO7pQMXjM1i6IGkYOx4Q3v3hXCGc4Agn0bapsr/twcU= -github.com/filecoin-project/go-data-transfer v0.6.8-0.20201012061237-d181ba9cacad/go.mod h1:QvXh2HwVSuKAmIkqZGuqW7VBwO1Yj1F03WdrM76mJ/s= +github.com/filecoin-project/go-data-transfer v0.6.8-0.20201013092017-eee8d1cde5d5 h1:kgmVHDP+rqcPqytpC215MzRRXrW9FqTfn0VFWhB2oog= +github.com/filecoin-project/go-data-transfer v0.6.8-0.20201013092017-eee8d1cde5d5/go.mod h1:1zaap881YZLMeIxhvLM0zA9qfUkh3bjOLSRaOoO9WRM= github.com/filecoin-project/go-ds-versioning v0.1.0 h1:y/X6UksYTsK8TLCI7rttCKEvl8btmWxyFMEeeWGUxIQ= github.com/filecoin-project/go-ds-versioning v0.1.0/go.mod h1:mp16rb4i2QPmxBnmanUx8i/XANp+PFCCJWiAb+VW4/s= github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f h1:GxJzR3oRIMTPtpZ0b7QF8FKPK6/iPAc7trhlL5k/g+s= @@ -278,8 +278,8 @@ github.com/ipfs/go-filestore v1.0.0 h1:QR7ekKH+q2AGiWDc7W2Q0qHuYSRZGUJqUn0GsegEP github.com/ipfs/go-filestore v1.0.0/go.mod h1:/XOCuNtIe2f1YPbiXdYvD0BKLA0JR1MgPiFOdcuu9SM= github.com/ipfs/go-graphsync v0.1.0 h1:RjLk7ha1tJtDXktqoxOjhvx4lDuzzIU+xQ+PEi74r3s= github.com/ipfs/go-graphsync v0.1.0/go.mod h1:jMXfqIEDFukLPZHqDPp8tJMbHO9Rmeb9CEGevngQbmE= -github.com/ipfs/go-graphsync v0.2.1 h1:MdehhqBSuTI2LARfKLkpYnt0mUrqHs/mtuDnESXHBfU= -github.com/ipfs/go-graphsync v0.2.1/go.mod h1:gEBvJUNelzMkaRPJTpg/jaKN4AQW/7wDWu0K92D8o10= +github.com/ipfs/go-graphsync v0.2.1-0.20201013053840-5d8ea8076a2c h1:De/AZGvRa3WMyw5zdMMhcvRcho46BVo+C0NRud+T4io= +github.com/ipfs/go-graphsync v0.2.1-0.20201013053840-5d8ea8076a2c/go.mod h1:gEBvJUNelzMkaRPJTpg/jaKN4AQW/7wDWu0K92D8o10= github.com/ipfs/go-hamt-ipld v0.1.1 h1:0IQdvwnAAUKmDE+PMJa5y1QiwOPHpI9+eAbQEEEYthk= github.com/ipfs/go-hamt-ipld v0.1.1/go.mod h1:1EZCr2v0jlCnhpa+aZ0JZYp8Tt2w16+JJOAVz17YcDk= github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= diff --git a/retrievalmarket/impl/dtutils/dtutils.go b/retrievalmarket/impl/dtutils/dtutils.go index 57fb8362..9327e28a 100644 --- a/retrievalmarket/impl/dtutils/dtutils.go +++ b/retrievalmarket/impl/dtutils/dtutils.go @@ -31,6 +31,8 @@ func providerEvent(event datatransfer.Event, channelState datatransfer.ChannelSt switch event.Code { case datatransfer.Accept: return rm.ProviderEventDealAccepted, []interface{}{channelState.ChannelID()} + case datatransfer.Disconnected: + return rm.ProviderEventDataTransferError, []interface{}{fmt.Errorf("deal data transfer stalled (peer hungup)")} case datatransfer.Error: return rm.ProviderEventDataTransferError, []interface{}{fmt.Errorf("deal data transfer failed: %s", event.Message)} case datatransfer.Cancel: @@ -112,6 +114,8 @@ func clientEvent(event datatransfer.Event, channelState datatransfer.ChannelStat } return clientEventForResponse(response) + case datatransfer.Disconnected: + return rm.ClientEventDataTransferError, []interface{}{fmt.Errorf("deal data transfer stalled (peer hungup)")} case datatransfer.Error: if channelState.Message() == datatransfer.ErrRejected.Error() { return rm.ClientEventDealRejected, []interface{}{"rejected for unknown reasons"} diff --git a/retrievalmarket/impl/dtutils/dtutils_test.go b/retrievalmarket/impl/dtutils/dtutils_test.go index 235e4449..ad3c1e42 100644 --- a/retrievalmarket/impl/dtutils/dtutils_test.go +++ b/retrievalmarket/impl/dtutils/dtutils_test.go @@ -90,6 +90,20 @@ func TestProviderDataTransferSubscriber(t *testing.T) { expectedEvent: rm.ProviderEventDataTransferError, expectedArgs: []interface{}{errors.New("deal data transfer failed: something went wrong")}, }, + "disconnected": { + code: datatransfer.Disconnected, + message: "something went wrong", + state: shared_testutil.TestChannelParams{ + IsPull: true, + TransferID: transferID, + Sender: testPeers[0], + Recipient: testPeers[1], + Vouchers: []datatransfer.Voucher{&dealProposal}, + Status: datatransfer.Ongoing}, + expectedID: rm.ProviderDealIdentifier{DealID: dealProposal.ID, Receiver: testPeers[1]}, + expectedEvent: rm.ProviderEventDataTransferError, + expectedArgs: []interface{}{errors.New("deal data transfer stalled (peer hungup)")}, + }, "completed": { code: datatransfer.ResumeResponder, state: shared_testutil.TestChannelParams{ @@ -287,6 +301,16 @@ func TestClientDataTransferSubscriber(t *testing.T) { expectedEvent: rm.ClientEventDataTransferError, expectedArgs: []interface{}{errors.New("deal data transfer failed: something went wrong")}, }, + "disconnected": { + code: datatransfer.Disconnected, + message: "something went wrong", + state: shared_testutil.TestChannelParams{ + Vouchers: []datatransfer.Voucher{&dealProposal}, + Status: datatransfer.Ongoing}, + expectedID: dealProposal.ID, + expectedEvent: rm.ClientEventDataTransferError, + expectedArgs: []interface{}{errors.New("deal data transfer stalled (peer hungup)")}, + }, "error, response rejected": { code: datatransfer.Error, message: datatransfer.ErrRejected.Error(), diff --git a/storagemarket/events.go b/storagemarket/events.go index 619c8f8c..391ecbe5 100644 --- a/storagemarket/events.go +++ b/storagemarket/events.go @@ -93,6 +93,9 @@ const ( // ClientEventRestart is used to resume the deal after a state machine shutdown ClientEventRestart + + // ClientEventDataTransferStalled happens when the clients data transfer experiences a disconnect + ClientEventDataTransferStalled ) // ClientEvents maps client event codes to string names @@ -127,6 +130,7 @@ var ClientEvents = map[ClientEvent]string{ ClientEventRestart: "ClientEventRestart", ClientEventDataTransferRestarted: "ClientEventDataTransferRestarted", ClientEventDataTransferRestartFailed: "ClientEventDataTransferRestartFailed", + ClientEventDataTransferStalled: "ClientEventDataTransferStalled", } // ProviderEvent is an event that happens in the provider's deal state machine @@ -253,6 +257,9 @@ const ( // ProviderEventDataTransferRestartFailed means a data transfer that was restarted by the provider failed ProviderEventDataTransferRestartFailed + + // ProviderEventDataTransferStalled happens when the providers data transfer experiences a disconnect + ProviderEventDataTransferStalled ) // ProviderEvents maps provider event codes to string names @@ -295,4 +302,5 @@ var ProviderEvents = map[ProviderEvent]string{ ProviderEventRestart: "ProviderEventRestart", ProviderEventDataTransferRestarted: "ProviderEventDataTransferRestarted", ProviderEventDataTransferRestartFailed: "ProviderEventDataTransferRestartFailed", + ProviderEventDataTransferStalled: "ProviderEventDataTransferStalled", } diff --git a/storagemarket/impl/clientstates/client_fsm.go b/storagemarket/impl/clientstates/client_fsm.go index e820bfbc..6dd04fee 100644 --- a/storagemarket/impl/clientstates/client_fsm.go +++ b/storagemarket/impl/clientstates/client_fsm.go @@ -100,9 +100,16 @@ var ClientEvents = fsm.Events{ From(storagemarket.StorageDealTransferring).ToJustRecord(). Action(func(deal *storagemarket.ClientDeal, channelId datatransfer.ChannelID) error { deal.TransferChannelID = &channelId + deal.Message = "" return nil }), + fsm.Event(storagemarket.ClientEventDataTransferStalled). + From(storagemarket.StorageDealTransferring).ToJustRecord().Action(func(deal *storagemarket.ClientDeal) error { + deal.Message = "data transfer appears to be stalled. attempt restart" + return nil + }), + fsm.Event(storagemarket.ClientEventDataTransferComplete). FromMany(storagemarket.StorageDealTransferring, storagemarket.StorageDealStartDataTransfer).To(storagemarket.StorageDealCheckForAcceptance), fsm.Event(storagemarket.ClientEventWaitForDealState). diff --git a/storagemarket/impl/dtutils/dtutils.go b/storagemarket/impl/dtutils/dtutils.go index 39093afe..1c923f59 100644 --- a/storagemarket/impl/dtutils/dtutils.go +++ b/storagemarket/impl/dtutils/dtutils.go @@ -51,6 +51,11 @@ func ProviderDataTransferSubscriber(deals EventReceiver) datatransfer.Subscriber if err != nil { log.Errorf("processing dt event: %w", err) } + case datatransfer.Disconnected: + err := deals.Send(voucher.Proposal, storagemarket.ProviderEventDataTransferStalled) + if err != nil { + log.Errorf("processing dt event: %w", err) + } case datatransfer.Open: err := deals.Send(voucher.Proposal, storagemarket.ProviderEventDataTransferInitiated, channelState.ChannelID()) if err != nil { @@ -92,6 +97,11 @@ func ClientDataTransferSubscriber(deals EventReceiver) datatransfer.Subscriber { if err != nil { log.Errorf("processing dt event: %w", err) } + case datatransfer.Disconnected: + err := deals.Send(voucher.Proposal, storagemarket.ClientEventDataTransferStalled) + if err != nil { + log.Errorf("processing dt event: %w", err) + } case datatransfer.Accept: err := deals.Send(voucher.Proposal, storagemarket.ClientEventDataTransferInitiated, channelState.ChannelID()) if err != nil { diff --git a/storagemarket/impl/dtutils/dtutils_test.go b/storagemarket/impl/dtutils/dtutils_test.go index 1d6c17c0..c70f7e1e 100644 --- a/storagemarket/impl/dtutils/dtutils_test.go +++ b/storagemarket/impl/dtutils/dtutils_test.go @@ -62,6 +62,16 @@ func TestProviderDataTransferSubscriber(t *testing.T) { expectedEvent: storagemarket.ProviderEventDataTransferRestarted, expectedArgs: []interface{}{datatransfer.ChannelID{Initiator: init, Responder: resp, ID: tid}}, }, + "disconnected event": { + code: datatransfer.Disconnected, + status: datatransfer.Ongoing, + called: true, + voucher: &requestvalidation.StorageDataTransferVoucher{ + Proposal: expectedProposalCID, + }, + expectedID: expectedProposalCID, + expectedEvent: storagemarket.ProviderEventDataTransferStalled, + }, "completion status": { code: datatransfer.Complete, status: datatransfer.Completed, @@ -164,6 +174,16 @@ func TestClientDataTransferSubscriber(t *testing.T) { expectedEvent: storagemarket.ClientEventDataTransferRestarted, expectedArgs: []interface{}{datatransfer.ChannelID{Initiator: init, Responder: resp, ID: tid}}, }, + "disconnected event": { + code: datatransfer.Disconnected, + status: datatransfer.Ongoing, + called: true, + voucher: &requestvalidation.StorageDataTransferVoucher{ + Proposal: expectedProposalCID, + }, + expectedID: expectedProposalCID, + expectedEvent: storagemarket.ClientEventDataTransferStalled, + }, "accept event": { code: datatransfer.Accept, status: datatransfer.Requested, diff --git a/storagemarket/impl/providerstates/provider_fsm.go b/storagemarket/impl/providerstates/provider_fsm.go index e3aa4024..449e015c 100644 --- a/storagemarket/impl/providerstates/provider_fsm.go +++ b/storagemarket/impl/providerstates/provider_fsm.go @@ -58,9 +58,16 @@ var ProviderEvents = fsm.Events{ From(storagemarket.StorageDealTransferring).ToJustRecord(). Action(func(deal *storagemarket.MinerDeal, channelId datatransfer.ChannelID) error { deal.TransferChannelId = &channelId + deal.Message = "" return nil }), + fsm.Event(storagemarket.ProviderEventDataTransferStalled). + From(storagemarket.StorageDealTransferring).ToJustRecord().Action(func(deal *storagemarket.MinerDeal) error { + deal.Message = "data transfer appears to be stalled. attempt restart" + return nil + }), + fsm.Event(storagemarket.ProviderEventDataTransferCompleted). From(storagemarket.StorageDealTransferring).To(storagemarket.StorageDealVerifyData), fsm.Event(storagemarket.ProviderEventDataVerificationFailed).