-
Notifications
You must be signed in to change notification settings - Fork 58
/
dtutils.go
149 lines (134 loc) · 5.76 KB
/
dtutils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Package dtutils provides event listeners for the client and provider to
// listen for events on the data transfer module and dispatch FSM events based on them
package dtutils
import (
"fmt"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
"github.com/ipld/go-ipld-prime"
datatransfer "github.com/filecoin-project/go-data-transfer"
"github.com/filecoin-project/go-multistore"
"github.com/filecoin-project/go-statemachine/fsm"
"github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
)
var log = logging.Logger("storagemarket_impl")
// EventReceiver is any thing that can receive FSM events
type EventReceiver interface {
Send(id interface{}, name fsm.EventName, args ...interface{}) (err error)
}
// ProviderDataTransferSubscriber is the function called when an event occurs in a data
// transfer received by a provider -- it reads the voucher to verify this event occurred
// in a storage market deal, then, based on the data transfer event that occurred, it generates
// and update message for the deal -- either moving to staged for a completion
// event or moving to error if a data transfer error occurs
func ProviderDataTransferSubscriber(deals EventReceiver) datatransfer.Subscriber {
return func(event datatransfer.Event, channelState datatransfer.ChannelState) {
voucher, ok := channelState.Voucher().(*requestvalidation.StorageDataTransferVoucher)
// if this event is for a transfer not related to storage, ignore
if !ok {
return
}
if channelState.Status() == datatransfer.Completed {
err := deals.Send(voucher.Proposal, storagemarket.ProviderEventDataTransferCompleted)
if err != nil {
log.Errorf("processing dt event: %w", err)
}
}
// Translate from data transfer events to provider FSM events
// Note: We ignore data transfer progress events (they do not affect deal state)
err := func() error {
switch event.Code {
case datatransfer.Cancel:
return deals.Send(voucher.Proposal, storagemarket.ProviderEventDataTransferCancelled)
case datatransfer.Restart:
return deals.Send(voucher.Proposal, storagemarket.ProviderEventDataTransferRestarted, channelState.ChannelID())
case datatransfer.Disconnected:
return deals.Send(voucher.Proposal, storagemarket.ProviderEventDataTransferStalled)
case datatransfer.Open:
return deals.Send(voucher.Proposal, storagemarket.ProviderEventDataTransferInitiated, channelState.ChannelID())
case datatransfer.Error:
return deals.Send(voucher.Proposal, storagemarket.ProviderEventDataTransferFailed, fmt.Errorf("deal data transfer failed: %s", event.Message))
default:
return nil
}
}()
if err != nil {
log.Errorf("processing dt event: %w", err)
}
}
}
// ClientDataTransferSubscriber is the function called when an event occurs in a data
// transfer initiated on the client -- it reads the voucher to verify this even occurred
// in a storage market deal, then, based on the data transfer event that occurred, it dispatches
// an event to the appropriate state machine
func ClientDataTransferSubscriber(deals EventReceiver) datatransfer.Subscriber {
return func(event datatransfer.Event, channelState datatransfer.ChannelState) {
voucher, ok := channelState.Voucher().(*requestvalidation.StorageDataTransferVoucher)
// if this event is for a transfer not related to storage, ignore
if !ok {
return
}
if channelState.Status() == datatransfer.Completed {
err := deals.Send(voucher.Proposal, storagemarket.ClientEventDataTransferComplete)
if err != nil {
log.Errorf("processing dt event: %w", err)
}
}
// Translate from data transfer events to client FSM events
// Note: We ignore data transfer progress events (they do not affect deal state)
err := func() error {
switch event.Code {
case datatransfer.Cancel:
return deals.Send(voucher.Proposal, storagemarket.ClientEventDataTransferCancelled)
case datatransfer.Restart:
return deals.Send(voucher.Proposal, storagemarket.ClientEventDataTransferRestarted, channelState.ChannelID())
case datatransfer.Disconnected:
return deals.Send(voucher.Proposal, storagemarket.ClientEventDataTransferStalled)
case datatransfer.Accept:
return deals.Send(voucher.Proposal, storagemarket.ClientEventDataTransferInitiated, channelState.ChannelID())
case datatransfer.Error:
return deals.Send(voucher.Proposal, storagemarket.ClientEventDataTransferFailed, fmt.Errorf("deal data transfer failed: %s", event.Message))
default:
return nil
}
}()
if err != nil {
log.Errorf("processing dt event: %w", err)
}
}
}
// StoreGetter retrieves the store for a given proposal cid
type StoreGetter interface {
Get(proposalCid cid.Cid) (*multistore.Store, error)
}
// StoreConfigurableTransport defines the methods needed to
// configure a data transfer transport use a unique store for a given request
type StoreConfigurableTransport interface {
UseStore(datatransfer.ChannelID, ipld.Loader, ipld.Storer) error
}
// TransportConfigurer configurers the graphsync transport to use a custom blockstore per deal
func TransportConfigurer(storeGetter StoreGetter) datatransfer.TransportConfigurer {
return func(channelID datatransfer.ChannelID, voucher datatransfer.Voucher, transport datatransfer.Transport) {
storageVoucher, ok := voucher.(*requestvalidation.StorageDataTransferVoucher)
if !ok {
return
}
gsTransport, ok := transport.(StoreConfigurableTransport)
if !ok {
return
}
store, err := storeGetter.Get(storageVoucher.Proposal)
if err != nil {
log.Errorf("attempting to configure data store: %w", err)
return
}
if store == nil {
return
}
err = gsTransport.UseStore(channelID, store.Loader, store.Storer)
if err != nil {
log.Errorf("attempting to configure data store: %w", err)
}
}
}