diff --git a/api/api_full.go b/api/api_full.go index 72bacad32..1cc6837be 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -273,6 +273,9 @@ type FullNode interface { // ClientListTransfers returns the status of all ongoing transfers of data ClientListDataTransfers(ctx context.Context) ([]DataTransferChannel, error) ClientDataTransferUpdates(ctx context.Context) (<-chan DataTransferChannel, error) + // ClientRetrieveTryRestartInsufficientFunds attempts to restart stalled retrievals on a given payment channel + // which are stuck due to insufficient funds + ClientRetrieveTryRestartInsufficientFunds(ctx context.Context, paymentChannel address.Address) error // ClientUnimport removes references to the specified file from filestore //ClientUnimport(path string) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index e4946995d..3cf9a0add 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -137,24 +137,25 @@ type FullNodeStruct struct { WalletImport func(context.Context, *types.KeyInfo) (address.Address, error) `perm:"admin"` WalletDelete func(context.Context, address.Address) error `perm:"write"` - ClientImport func(ctx context.Context, ref api.FileRef) (*api.ImportRes, error) `perm:"admin"` - ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"` - ClientRemoveImport func(ctx context.Context, importID multistore.StoreID) error `perm:"admin"` - ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"` - ClientFindData func(ctx context.Context, root cid.Cid, piece *cid.Cid) ([]api.QueryOffer, error) `perm:"read"` - ClientMinerQueryOffer func(ctx context.Context, miner address.Address, root cid.Cid, piece *cid.Cid) (api.QueryOffer, error) `perm:"read"` - ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"` - ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"` - ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"` - ClientGetDealUpdates func(ctx context.Context) (<-chan api.DealInfo, error) `perm:"read"` - ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error `perm:"admin"` - ClientRetrieveWithEvents func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) (<-chan marketevents.RetrievalEvent, error) `perm:"admin"` - ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"` - ClientCalcCommP func(ctx context.Context, inpath string) (*api.CommPRet, error) `perm:"read"` - ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"` - ClientDealSize func(ctx context.Context, root cid.Cid) (api.DataSize, error) `perm:"read"` - ClientListDataTransfers func(ctx context.Context) ([]api.DataTransferChannel, error) `perm:"write"` - ClientDataTransferUpdates func(ctx context.Context) (<-chan api.DataTransferChannel, error) `perm:"write"` + ClientImport func(ctx context.Context, ref api.FileRef) (*api.ImportRes, error) `perm:"admin"` + ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"` + ClientRemoveImport func(ctx context.Context, importID multistore.StoreID) error `perm:"admin"` + ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"` + ClientFindData func(ctx context.Context, root cid.Cid, piece *cid.Cid) ([]api.QueryOffer, error) `perm:"read"` + ClientMinerQueryOffer func(ctx context.Context, miner address.Address, root cid.Cid, piece *cid.Cid) (api.QueryOffer, error) `perm:"read"` + ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"` + ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"` + ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"` + ClientGetDealUpdates func(ctx context.Context) (<-chan api.DealInfo, error) `perm:"read"` + ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error `perm:"admin"` + ClientRetrieveWithEvents func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) (<-chan marketevents.RetrievalEvent, error) `perm:"admin"` + ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"` + ClientCalcCommP func(ctx context.Context, inpath string) (*api.CommPRet, error) `perm:"read"` + ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"` + ClientDealSize func(ctx context.Context, root cid.Cid) (api.DataSize, error) `perm:"read"` + ClientListDataTransfers func(ctx context.Context) ([]api.DataTransferChannel, error) `perm:"write"` + ClientDataTransferUpdates func(ctx context.Context) (<-chan api.DataTransferChannel, error) `perm:"write"` + ClientRetrieveTryRestartInsufficientFunds func(ctx context.Context, paymentChannel address.Address) error `perm:"write"` StateNetworkName func(context.Context) (dtypes.NetworkName, error) `perm:"read"` StateMinerSectors func(context.Context, address.Address, *bitfield.BitField, bool, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"` @@ -495,6 +496,10 @@ func (c *FullNodeStruct) ClientDataTransferUpdates(ctx context.Context) (<-chan return c.Internal.ClientDataTransferUpdates(ctx) } +func (c *FullNodeStruct) ClientRetrieveTryRestartInsufficientFunds(ctx context.Context, paymentChannel address.Address) error { + return c.Internal.ClientRetrieveTryRestartInsufficientFunds(ctx, paymentChannel) +} + func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) { return c.Internal.GasEstimateGasPremium(ctx, nblocksincl, sender, gaslimit, tsk) } diff --git a/cli/paych.go b/cli/paych.go index 6e2104551..bcd8e33f0 100644 --- a/cli/paych.go +++ b/cli/paych.go @@ -36,6 +36,14 @@ var paychAddFundsCmd = &cli.Command{ Name: "add-funds", Usage: "Add funds to the payment channel between fromAddress and toAddress. Creates the payment channel if it doesn't already exist.", ArgsUsage: "[fromAddress toAddress amount]", + Flags: []cli.Flag{ + + &cli.BoolFlag{ + Name: "restart-retrievals", + Usage: "restart stalled retrieval deals on this payment channel", + Value: true, + }, + }, Action: func(cctx *cli.Context) error { if cctx.Args().Len() != 3 { return ShowHelp(cctx, fmt.Errorf("must pass three arguments: ")) @@ -78,6 +86,10 @@ var paychAddFundsCmd = &cli.Command{ } fmt.Fprintln(cctx.App.Writer, chAddr) + restartRetrievals := cctx.Bool("restart-retrievals") + if restartRetrievals { + return api.ClientRetrieveTryRestartInsufficientFunds(ctx, chAddr) + } return nil }, } diff --git a/go.mod b/go.mod index 6250a7af1..9b0dafa3b 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 github.com/filecoin-project/go-data-transfer v0.6.3 github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f - github.com/filecoin-project/go-fil-markets v0.5.10-0.20200907054005-9945d0d2141a + github.com/filecoin-project/go-fil-markets v0.5.10-0.20200907031006-9d489e10498b github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200822201400-474f4fdccc52 github.com/filecoin-project/go-multistore v0.0.3 github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 diff --git a/go.sum b/go.sum index f0626e8ed..9b370c970 100644 --- a/go.sum +++ b/go.sum @@ -251,8 +251,8 @@ github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f h1 github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ= github.com/filecoin-project/go-fil-markets v0.5.6-0.20200814234959-80b1788108ac/go.mod h1:umicPCaN99ysHTiYOmwhuLxTFbOwcsI+mdw/t96vvM4= github.com/filecoin-project/go-fil-markets v0.5.8/go.mod h1:6ZX1vbZbnukbVQ8tCB/MmEizuW/bmRX7SpGAltU3KVg= -github.com/filecoin-project/go-fil-markets v0.5.10-0.20200907054005-9945d0d2141a h1:SYWurOVYyEqajP3rr20F9UvkIpn6p9ewMk9yOg1kaVM= -github.com/filecoin-project/go-fil-markets v0.5.10-0.20200907054005-9945d0d2141a/go.mod h1:w0wCAf/fT7UfvJAZEMjjCQfsbwvrdjU4sN4QFLWsPrk= +github.com/filecoin-project/go-fil-markets v0.5.10-0.20200907031006-9d489e10498b h1:Xe+ngO0+FV1JESIz9rlyzygwIEnI8M3bDvKqljdIoJA= +github.com/filecoin-project/go-fil-markets v0.5.10-0.20200907031006-9d489e10498b/go.mod h1:w0wCAf/fT7UfvJAZEMjjCQfsbwvrdjU4sN4QFLWsPrk= github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200817153016-2ea5cbaf5ec0/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4= github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200822201400-474f4fdccc52 h1:FXtCp0ybqdQL9knb3OGDpkNTaBbPxgkqPeWKotUwkH0= github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200822201400-474f4fdccc52/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4= diff --git a/markets/retrievaladapter/client.go b/markets/retrievaladapter/client.go index de94f50a9..cdb4caa12 100644 --- a/markets/retrievaladapter/client.go +++ b/markets/retrievaladapter/client.go @@ -3,8 +3,6 @@ package retrievaladapter import ( "context" - "golang.org/x/xerrors" - "github.com/filecoin-project/specs-actors/actors/builtin/paych" "github.com/filecoin-project/go-address" @@ -17,20 +15,18 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/node/impl/full" payapi "github.com/filecoin-project/lotus/node/impl/paych" - "github.com/filecoin-project/lotus/paychmgr" ) type retrievalClientNode struct { chainAPI full.ChainAPI - pmgr *paychmgr.Manager payAPI payapi.PaychAPI stateAPI full.StateAPI } // NewRetrievalClientNode returns a new node adapter for a retrieval client that talks to the // Lotus Node -func NewRetrievalClientNode(pmgr *paychmgr.Manager, payAPI payapi.PaychAPI, chainAPI full.ChainAPI, stateAPI full.StateAPI) retrievalmarket.RetrievalClientNode { - return &retrievalClientNode{pmgr: pmgr, payAPI: payAPI, chainAPI: chainAPI, stateAPI: stateAPI} +func NewRetrievalClientNode(payAPI payapi.PaychAPI, chainAPI full.ChainAPI, stateAPI full.StateAPI) retrievalmarket.RetrievalClientNode { + return &retrievalClientNode{payAPI: payAPI, chainAPI: chainAPI, stateAPI: stateAPI} } // GetOrCreatePaymentChannel sets up a new payment channel if one does not exist @@ -39,14 +35,18 @@ func NewRetrievalClientNode(pmgr *paychmgr.Manager, payAPI payapi.PaychAPI, chai func (rcn *retrievalClientNode) GetOrCreatePaymentChannel(ctx context.Context, clientAddress address.Address, minerAddress address.Address, clientFundsAvailable abi.TokenAmount, tok shared.TipSetToken) (address.Address, cid.Cid, error) { // TODO: respect the provided TipSetToken (a serialized TipSetKey) when // querying the chain - return rcn.pmgr.GetPaych(ctx, clientAddress, minerAddress, clientFundsAvailable) + ci, err := rcn.payAPI.PaychGet(ctx, clientAddress, minerAddress, clientFundsAvailable) + if err != nil { + return address.Undef, cid.Undef, err + } + return ci.Channel, ci.WaitSentinel, nil } // Allocate late creates a lane within a payment channel so that calls to // CreatePaymentVoucher will automatically make vouchers only for the difference // in total -func (rcn *retrievalClientNode) AllocateLane(paymentChannel address.Address) (uint64, error) { - return rcn.pmgr.AllocateLane(paymentChannel) +func (rcn *retrievalClientNode) AllocateLane(ctx context.Context, paymentChannel address.Address) (uint64, error) { + return rcn.payAPI.PaychAllocateLane(ctx, paymentChannel) } // CreatePaymentVoucher creates a new payment voucher in the given lane for a @@ -60,7 +60,7 @@ func (rcn *retrievalClientNode) CreatePaymentVoucher(ctx context.Context, paymen return nil, err } if voucher.Voucher == nil { - return nil, xerrors.Errorf("Could not create voucher - shortfall: %d", voucher.Shortfall) + return nil, retrievalmarket.NewShortfallError(voucher.Shortfall) } return voucher.Voucher, nil } @@ -74,15 +74,29 @@ func (rcn *retrievalClientNode) GetChainHead(ctx context.Context) (shared.TipSet return head.Key().Bytes(), head.Height(), nil } -// WaitForPaymentChannelAddFunds waits messageCID to appear on chain. If it doesn't appear within -// defaultMsgWaitTimeout it returns error -func (rcn *retrievalClientNode) WaitForPaymentChannelAddFunds(messageCID cid.Cid) error { - _, err := rcn.payAPI.PaychMgr.GetPaychWaitReady(context.TODO(), messageCID) - return err +func (rcn *retrievalClientNode) WaitForPaymentChannelReady(ctx context.Context, messageCID cid.Cid) (address.Address, error) { + return rcn.payAPI.PaychGetWaitReady(ctx, messageCID) } -func (rcn *retrievalClientNode) WaitForPaymentChannelCreation(messageCID cid.Cid) (address.Address, error) { - return rcn.payAPI.PaychMgr.GetPaychWaitReady(context.TODO(), messageCID) +func (rcn *retrievalClientNode) CheckAvailableFunds(ctx context.Context, paymentChannel address.Address) (retrievalmarket.ChannelAvailableFunds, error) { + // this doesn't actually work potentially -- the looked up from/to may pull up data for a different payment channel if for some reason the + // given address is settling + ci, err := rcn.payAPI.PaychMgr.GetChannelInfo(paymentChannel) + if err != nil { + return retrievalmarket.ChannelAvailableFunds{}, err + } + // assuming this is outbound... again, this is not a final implementation, pending PaychAvailableFundsByAddress + channelAvailableFunds, err := rcn.payAPI.PaychAvailableFunds(ci.Control, ci.Target) + if err != nil { + return retrievalmarket.ChannelAvailableFunds{}, err + } + return retrievalmarket.ChannelAvailableFunds{ + ConfirmedAmt: channelAvailableFunds.ConfirmedAmt, + PendingAmt: channelAvailableFunds.PendingAmt, + PendingWaitSentinel: channelAvailableFunds.PendingWaitSentinel, + QueuedAmt: channelAvailableFunds.QueuedAmt, + VoucherReedeemedAmt: channelAvailableFunds.VoucherReedeemedAmt, + }, nil } func (rcn *retrievalClientNode) GetKnownAddresses(ctx context.Context, p retrievalmarket.RetrievalPeer, encodedTs shared.TipSetToken) ([]multiaddr.Multiaddr, error) { diff --git a/node/impl/client/client.go b/node/impl/client/client.go index ed1e6cd05..d12a4ae73 100644 --- a/node/impl/client/client.go +++ b/node/impl/client/client.go @@ -847,3 +847,7 @@ func newDealInfo(v storagemarket.ClientDeal) api.DealInfo { CreationTime: v.CreationTime.Time(), } } + +func (a *API) ClientRetrieveTryRestartInsufficientFunds(ctx context.Context, paymentChannel address.Address) error { + return a.Retrieval.TryRestartInsufficientFunds(paymentChannel) +} diff --git a/node/modules/client.go b/node/modules/client.go index bf534350e..63633c0e3 100644 --- a/node/modules/client.go +++ b/node/modules/client.go @@ -35,7 +35,6 @@ import ( "github.com/filecoin-project/lotus/node/repo" "github.com/filecoin-project/lotus/node/repo/importmgr" "github.com/filecoin-project/lotus/node/repo/retrievalstoremgr" - "github.com/filecoin-project/lotus/paychmgr" ) func ClientMultiDatastore(lc fx.Lifecycle, r repo.LockedRepo) (dtypes.ClientMultiDstore, error) { @@ -130,8 +129,8 @@ func StorageClient(lc fx.Lifecycle, h host.Host, ibs dtypes.ClientBlockstore, md } // RetrievalClient creates a new retrieval client attached to the client blockstore -func RetrievalClient(lc fx.Lifecycle, h host.Host, mds dtypes.ClientMultiDstore, dt dtypes.ClientDataTransfer, pmgr *paychmgr.Manager, payAPI payapi.PaychAPI, resolver retrievalmarket.PeerResolver, ds dtypes.MetadataDS, chainAPI full.ChainAPI, stateAPI full.StateAPI) (retrievalmarket.RetrievalClient, error) { - adapter := retrievaladapter.NewRetrievalClientNode(pmgr, payAPI, chainAPI, stateAPI) +func RetrievalClient(lc fx.Lifecycle, h host.Host, mds dtypes.ClientMultiDstore, dt dtypes.ClientDataTransfer, payAPI payapi.PaychAPI, resolver retrievalmarket.PeerResolver, ds dtypes.MetadataDS, chainAPI full.ChainAPI, stateAPI full.StateAPI) (retrievalmarket.RetrievalClient, error) { + adapter := retrievaladapter.NewRetrievalClientNode(payAPI, chainAPI, stateAPI) network := rmnet.NewFromLibp2pHost(h) sc := storedcounter.New(ds, datastore.NewKey("/retr")) client, err := retrievalimpl.NewClient(network, mds, dt, adapter, resolver, namespace.Wrap(ds, datastore.NewKey("/retrievals/client")), sc)