2019-08-01 14:19:53 +00:00
package modules
import (
2020-11-11 05:06:50 +00:00
"bytes"
2019-08-01 14:19:53 +00:00
"context"
2020-11-16 18:56:53 +00:00
"os"
"path/filepath"
2020-07-08 10:52:37 +00:00
"time"
2019-11-11 20:25:19 +00:00
2020-03-18 19:43:06 +00:00
"go.uber.org/fx"
2021-03-22 11:08:05 +00:00
"golang.org/x/xerrors"
2020-03-18 19:43:06 +00:00
2021-03-22 11:08:05 +00:00
"github.com/filecoin-project/go-data-transfer/channelmonitor"
2020-07-08 08:35:50 +00:00
dtimpl "github.com/filecoin-project/go-data-transfer/impl"
dtnet "github.com/filecoin-project/go-data-transfer/network"
dtgstransport "github.com/filecoin-project/go-data-transfer/transport/graphsync"
2020-09-29 11:53:30 +00:00
"github.com/filecoin-project/go-fil-markets/discovery"
discoveryimpl "github.com/filecoin-project/go-fil-markets/discovery/impl"
2021-07-09 08:59:10 +00:00
"github.com/filecoin-project/go-fil-markets/filestore"
2020-01-10 17:13:12 +00:00
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
retrievalimpl "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl"
2020-01-24 20:19:52 +00:00
rmnet "github.com/filecoin-project/go-fil-markets/retrievalmarket/network"
2019-12-17 10:46:39 +00:00
"github.com/filecoin-project/go-fil-markets/storagemarket"
2020-01-14 01:24:04 +00:00
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
2020-03-18 18:57:22 +00:00
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
2020-02-06 02:43:37 +00:00
smnet "github.com/filecoin-project/go-fil-markets/storagemarket/network"
2021-03-22 11:08:05 +00:00
"github.com/filecoin-project/go-state-types/abi"
2019-08-01 14:19:53 +00:00
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
2020-07-23 02:05:11 +00:00
"github.com/libp2p/go-libp2p-core/host"
2019-08-01 14:19:53 +00:00
2021-01-29 20:01:00 +00:00
"github.com/filecoin-project/lotus/blockstore"
2020-11-11 05:06:50 +00:00
"github.com/filecoin-project/lotus/chain/market"
2020-09-14 15:20:01 +00:00
"github.com/filecoin-project/lotus/journal"
2020-09-14 18:01:35 +00:00
"github.com/filecoin-project/lotus/markets"
2020-07-31 01:27:42 +00:00
marketevents "github.com/filecoin-project/lotus/markets/loggers"
2020-01-14 01:24:04 +00:00
"github.com/filecoin-project/lotus/markets/retrievaladapter"
2020-03-18 18:57:22 +00:00
"github.com/filecoin-project/lotus/node/impl/full"
2019-12-10 04:19:59 +00:00
payapi "github.com/filecoin-project/lotus/node/impl/paych"
2019-10-18 04:47:41 +00:00
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/repo"
2020-07-06 23:39:30 +00:00
"github.com/filecoin-project/lotus/node/repo/importmgr"
2021-07-09 08:59:10 +00:00
"github.com/filecoin-project/lotus/node/repo/retrievalstoremgr"
2019-08-01 14:19:53 +00:00
)
2020-11-11 05:06:50 +00:00
func HandleMigrateClientFunds ( lc fx . Lifecycle , ds dtypes . MetadataDS , wallet full . WalletAPI , fundMgr * market . FundManager ) {
lc . Append ( fx . Hook {
OnStart : func ( ctx context . Context ) error {
addr , err := wallet . WalletDefaultAddress ( ctx )
// nothing to be done if there is no default address
if err != nil {
return nil
}
b , err := ds . Get ( datastore . NewKey ( "/marketfunds/client" ) )
if err != nil {
if xerrors . Is ( err , datastore . ErrNotFound ) {
return nil
}
2021-02-11 11:00:26 +00:00
log . Errorf ( "client funds migration - getting datastore value: %v" , err )
2020-11-12 16:15:05 +00:00
return nil
2020-11-11 05:06:50 +00:00
}
var value abi . TokenAmount
if err = value . UnmarshalCBOR ( bytes . NewReader ( b ) ) ; err != nil {
2021-02-11 11:00:26 +00:00
log . Errorf ( "client funds migration - unmarshalling datastore value: %v" , err )
2020-11-12 16:15:05 +00:00
return nil
2020-11-11 05:06:50 +00:00
}
_ , err = fundMgr . Reserve ( ctx , addr , addr , value )
if err != nil {
2021-02-11 11:00:26 +00:00
log . Errorf ( "client funds migration - reserving funds (wallet %s, addr %s, funds %d): %v" ,
2020-11-12 16:15:05 +00:00
addr , addr , value , err )
return nil
2020-11-11 05:06:50 +00:00
}
return ds . Delete ( datastore . NewKey ( "/marketfunds/client" ) )
} ,
} )
}
2021-07-03 07:25:20 +00:00
func ClientImportMgr ( ds dtypes . MetadataDS , r repo . LockedRepo ) dtypes . ClientImportMgr {
return importmgr . New ( namespace . Wrap ( ds , datastore . NewKey ( "/client" ) ) , r . Path ( ) )
2019-08-01 14:19:53 +00:00
}
2021-07-03 07:25:20 +00:00
// TODO Ge this to work when we work on IPFS integration. What we effectively need here is a cross-shard/cross-CAR files index for the Storage client's imports.
2020-07-07 08:52:19 +00:00
func ClientBlockstore ( imgr dtypes . ClientImportMgr ) dtypes . ClientBlockstore {
2020-07-28 06:13:10 +00:00
// in most cases this is now unused in normal operations -- however, it's important to preserve for the IPFS use case
2021-07-03 07:25:20 +00:00
return blockstore . WrapIDStore ( blockstore . FromDatastore ( datastore . NewMapDatastore ( ) ) )
2019-08-26 13:45:36 +00:00
}
2019-08-06 22:04:21 +00:00
2019-11-11 20:25:19 +00:00
// RegisterClientValidator is an initialization hook that registers the client
// request validator with the data transfer module as the validator for
// StorageDataTransferVoucher types
2020-05-20 22:46:44 +00:00
func RegisterClientValidator ( crv dtypes . ClientRequestValidator , dtm dtypes . ClientDataTransfer ) {
if err := dtm . RegisterVoucherType ( & requestvalidation . StorageDataTransferVoucher { } , ( * requestvalidation . UnifiedRequestValidator ) ( crv ) ) ; err != nil {
2019-12-05 05:02:14 +00:00
panic ( err )
}
2019-11-11 20:25:19 +00:00
}
2020-03-18 00:25:12 +00:00
// NewClientGraphsyncDataTransfer returns a data transfer manager that just
2019-11-11 20:51:28 +00:00
// uses the clients's Client DAG service for transfers
2020-11-16 18:56:53 +00:00
func NewClientGraphsyncDataTransfer ( lc fx . Lifecycle , h host . Host , gs dtypes . Graphsync , ds dtypes . MetadataDS , r repo . LockedRepo ) ( dtypes . ClientDataTransfer , error ) {
2020-12-16 15:48:56 +00:00
// go-data-transfer protocol retries:
// 1s, 5s, 25s, 2m5s, 5m x 11 ~= 1 hour
dtRetryParams := dtnet . RetryParameters ( time . Second , 5 * time . Minute , 15 , 5 )
net := dtnet . NewFromLibp2pHost ( h , dtRetryParams )
2020-07-08 08:35:50 +00:00
dtDs := namespace . Wrap ( ds , datastore . NewKey ( "/datatransfer/client/transfers" ) )
transport := dtgstransport . NewTransport ( h . ID ( ) , gs )
2020-11-16 18:56:53 +00:00
err := os . MkdirAll ( filepath . Join ( r . Path ( ) , "data-transfer" ) , 0755 ) //nolint: gosec
if err != nil && ! os . IsExist ( err ) {
return nil , err
}
2021-03-22 11:08:05 +00:00
// data-transfer push / pull channel restart configuration:
dtRestartConfig := dtimpl . ChannelRestartConfig ( channelmonitor . Config {
2021-06-01 16:16:12 +00:00
// Disable Accept and Complete timeouts until this issue is resolved:
// https://github.com/filecoin-project/lotus/issues/6343#
// Wait for the other side to respond to an Open channel message
AcceptTimeout : 0 ,
// Wait for the other side to send a Complete message once all
// data has been sent / received
CompleteTimeout : 0 ,
2021-04-23 13:32:50 +00:00
// When an error occurs, wait a little while until all related errors
// have fired before sending a restart message
RestartDebounce : 10 * time . Second ,
2021-03-22 11:08:05 +00:00
// After sending a restart, wait for at least 1 minute before sending another
RestartBackoff : time . Minute ,
// After trying to restart 3 times, give up and fail the transfer
MaxConsecutiveRestarts : 3 ,
} )
2021-03-23 12:28:30 +00:00
dt , err := dtimpl . NewDataTransfer ( dtDs , filepath . Join ( r . Path ( ) , "data-transfer" ) , net , transport , dtRestartConfig )
2020-07-08 08:35:50 +00:00
if err != nil {
return nil , err
}
2020-10-14 02:50:13 +00:00
dt . OnReady ( marketevents . ReadyLogger ( "client data transfer" ) )
2020-07-08 08:35:50 +00:00
lc . Append ( fx . Hook {
OnStart : func ( ctx context . Context ) error {
2020-11-23 09:31:39 +00:00
dt . SubscribeToEvents ( marketevents . DataTransferLogger )
2020-07-08 08:35:50 +00:00
return dt . Start ( ctx )
} ,
2020-08-29 01:03:27 +00:00
OnStop : func ( ctx context . Context ) error {
return dt . Stop ( ctx )
2020-07-08 08:35:50 +00:00
} ,
} )
return dt , nil
2019-11-11 20:51:28 +00:00
}
2020-03-18 18:57:22 +00:00
// NewClientDatastore creates a datastore for the client to store its deals
func NewClientDatastore ( ds dtypes . MetadataDS ) dtypes . ClientDatastore {
return namespace . Wrap ( ds , datastore . NewKey ( "/deals/client" ) )
2019-11-11 20:51:28 +00:00
}
2021-07-03 04:40:56 +00:00
func StorageClient ( lc fx . Lifecycle , h host . Host , dataTransfer dtypes . ClientDataTransfer , discovery * discoveryimpl . Local , deals dtypes . ClientDatastore , scn storagemarket . StorageClientNode , j journal . Journal ) ( storagemarket . StorageClient , error ) {
2020-12-16 15:48:56 +00:00
// go-fil-markets protocol retries:
2020-12-17 10:00:03 +00:00
// 1s, 5s, 25s, 2m5s, 5m x 11 ~= 1 hour
marketsRetryParams := smnet . RetryParameters ( time . Second , 5 * time . Minute , 15 , 5 )
2020-12-16 15:48:56 +00:00
net := smnet . NewFromLibp2pHost ( h , marketsRetryParams )
2021-07-01 16:34:51 +00:00
c , err := storageimpl . NewClient ( net , dataTransfer , discovery , deals , scn , storageimpl . DealPollingInterval ( time . Second ) )
2020-05-08 21:11:40 +00:00
if err != nil {
return nil , err
}
2020-09-29 11:53:30 +00:00
c . OnReady ( marketevents . ReadyLogger ( "storage client" ) )
2020-05-08 21:11:40 +00:00
lc . Append ( fx . Hook {
OnStart : func ( ctx context . Context ) error {
2020-07-31 01:27:42 +00:00
c . SubscribeToEvents ( marketevents . StorageClientLogger )
2020-09-14 15:20:01 +00:00
2020-10-09 19:52:04 +00:00
evtType := j . RegisterEventType ( "markets/storage/client" , "state_change" )
c . SubscribeToEvents ( markets . StorageClientJournaler ( j , evtType ) )
2020-09-14 15:20:01 +00:00
2020-06-15 22:43:47 +00:00
return c . Start ( ctx )
2020-05-08 21:11:40 +00:00
} ,
OnStop : func ( context . Context ) error {
2020-08-20 04:49:10 +00:00
return c . Stop ( )
2020-05-08 21:11:40 +00:00
} ,
} )
return c , nil
2019-12-17 10:46:39 +00:00
}
2019-12-10 04:19:59 +00:00
// RetrievalClient creates a new retrieval client attached to the client blockstore
2021-07-03 04:40:56 +00:00
func RetrievalClient ( lc fx . Lifecycle , h host . Host , r repo . LockedRepo , dt dtypes . ClientDataTransfer , payAPI payapi . PaychAPI , resolver discovery . PeerResolver ,
ds dtypes . MetadataDS , chainAPI full . ChainAPI , stateAPI full . StateAPI , j journal . Journal ) ( retrievalmarket . RetrievalClient , error ) {
2021-07-09 08:59:10 +00:00
carStore , err := getRetrievalCarStore ( r . Path ( ) )
if err != nil {
return nil , err
}
2020-09-04 05:34:59 +00:00
adapter := retrievaladapter . NewRetrievalClientNode ( payAPI , chainAPI , stateAPI )
2020-01-24 20:19:52 +00:00
network := rmnet . NewFromLibp2pHost ( h )
2021-07-03 04:40:56 +00:00
client , err := retrievalimpl . NewClient ( network ,
2021-07-09 08:59:10 +00:00
carStore , dt , adapter , resolver , namespace . Wrap ( ds , datastore . NewKey ( "/retrievals/client" ) ) )
2020-07-31 01:27:42 +00:00
if err != nil {
return nil , err
}
2020-09-29 11:53:30 +00:00
client . OnReady ( marketevents . ReadyLogger ( "retrieval client" ) )
2020-07-31 01:27:42 +00:00
lc . Append ( fx . Hook {
OnStart : func ( ctx context . Context ) error {
client . SubscribeToEvents ( marketevents . RetrievalClientLogger )
2020-09-14 15:20:01 +00:00
2020-10-09 19:52:04 +00:00
evtType := j . RegisterEventType ( "markets/retrieval/client" , "state_change" )
client . SubscribeToEvents ( markets . RetrievalClientJournaler ( j , evtType ) )
2020-09-14 15:20:01 +00:00
2020-09-29 11:53:30 +00:00
return client . Start ( ctx )
2020-07-31 01:27:42 +00:00
} ,
} )
return client , nil
2019-12-10 04:19:59 +00:00
}
2021-07-07 05:41:29 +00:00
2021-07-09 08:59:10 +00:00
func getRetrievalCarStore ( path string ) ( filestore . CarFileStore , error ) {
carStorePath := filepath . Join ( path , "retrieval-cars" )
err := os . Mkdir ( carStorePath , os . ModePerm )
if err != nil {
return nil , xerrors . Errorf ( "could not create directory %s: %w" , carStorePath , err )
}
return filestore . NewLocalCarStore ( path )
}
2021-07-07 05:41:29 +00:00
// ClientBlockstoreRetrievalStoreManager is the default version of the RetrievalStoreManager that runs on multistore
2021-07-07 08:40:59 +00:00
func ClientBlockstoreRetrievalStoreManager ( isIpfsRetrieval bool ) func ( bs dtypes . ClientBlockstore ) ( dtypes . ClientRetrievalStoreManager , error ) {
return func ( bs dtypes . ClientBlockstore ) ( dtypes . ClientRetrievalStoreManager , error ) {
return retrievalstoremgr . NewBlockstoreRetrievalStoreManager ( bs , isIpfsRetrieval ) , nil
}
2021-07-07 05:41:29 +00:00
}