2019-09-16 13:46:05 +00:00
|
|
|
package client
|
2019-08-20 16:48:33 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2020-06-16 20:52:47 +00:00
|
|
|
"fmt"
|
2020-04-30 17:42:16 +00:00
|
|
|
|
2020-04-03 22:17:57 +00:00
|
|
|
"github.com/filecoin-project/go-fil-markets/pieceio"
|
2020-05-05 01:31:56 +00:00
|
|
|
basicnode "github.com/ipld/go-ipld-prime/node/basic"
|
2020-04-03 22:17:57 +00:00
|
|
|
"github.com/ipld/go-ipld-prime/traversal/selector"
|
|
|
|
"github.com/ipld/go-ipld-prime/traversal/selector/builder"
|
|
|
|
|
2019-10-23 09:18:22 +00:00
|
|
|
"io"
|
2019-08-20 16:48:33 +00:00
|
|
|
"os"
|
|
|
|
|
2019-11-05 03:42:13 +00:00
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
2019-09-06 22:39:47 +00:00
|
|
|
"github.com/ipfs/go-blockservice"
|
2019-08-20 16:48:33 +00:00
|
|
|
"github.com/ipfs/go-cid"
|
|
|
|
"github.com/ipfs/go-filestore"
|
|
|
|
chunker "github.com/ipfs/go-ipfs-chunker"
|
2019-09-06 22:39:47 +00:00
|
|
|
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
2019-08-20 16:48:33 +00:00
|
|
|
files "github.com/ipfs/go-ipfs-files"
|
|
|
|
ipld "github.com/ipfs/go-ipld-format"
|
2019-09-06 22:39:47 +00:00
|
|
|
"github.com/ipfs/go-merkledag"
|
2019-12-10 04:19:59 +00:00
|
|
|
unixfile "github.com/ipfs/go-unixfs/file"
|
2019-08-20 16:48:33 +00:00
|
|
|
"github.com/ipfs/go-unixfs/importer/balanced"
|
|
|
|
ihelper "github.com/ipfs/go-unixfs/importer/helpers"
|
2020-05-05 01:31:56 +00:00
|
|
|
"github.com/ipld/go-car"
|
2019-08-20 16:48:33 +00:00
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
"go.uber.org/fx"
|
2019-09-06 22:39:47 +00:00
|
|
|
|
2019-12-19 20:13:17 +00:00
|
|
|
"github.com/filecoin-project/go-address"
|
2020-01-10 17:13:12 +00:00
|
|
|
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
2019-12-17 10:46:39 +00:00
|
|
|
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
2020-04-16 21:43:39 +00:00
|
|
|
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
2020-02-13 00:15:33 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
2020-04-16 21:43:39 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
2020-02-13 00:15:33 +00:00
|
|
|
|
2019-10-18 04:47:41 +00:00
|
|
|
"github.com/filecoin-project/lotus/api"
|
|
|
|
"github.com/filecoin-project/lotus/build"
|
|
|
|
"github.com/filecoin-project/lotus/chain/store"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
2020-01-10 18:21:46 +00:00
|
|
|
"github.com/filecoin-project/lotus/markets/utils"
|
2019-10-18 04:47:41 +00:00
|
|
|
"github.com/filecoin-project/lotus/node/impl/full"
|
|
|
|
"github.com/filecoin-project/lotus/node/impl/paych"
|
|
|
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
2019-08-20 16:48:33 +00:00
|
|
|
)
|
|
|
|
|
2020-02-13 00:15:33 +00:00
|
|
|
const dealStartBuffer abi.ChainEpoch = 10000 // TODO: allow setting
|
2020-02-12 22:32:26 +00:00
|
|
|
|
2019-09-16 13:46:05 +00:00
|
|
|
type API struct {
|
2019-08-20 16:48:33 +00:00
|
|
|
fx.In
|
|
|
|
|
2019-09-16 13:46:05 +00:00
|
|
|
full.ChainAPI
|
|
|
|
full.StateAPI
|
|
|
|
full.WalletAPI
|
|
|
|
paych.PaychAPI
|
2019-08-20 16:48:33 +00:00
|
|
|
|
2019-11-04 19:57:54 +00:00
|
|
|
SMDealClient storagemarket.StorageClient
|
2019-12-10 04:19:59 +00:00
|
|
|
RetDiscovery retrievalmarket.PeerResolver
|
|
|
|
Retrieval retrievalmarket.RetrievalClient
|
2019-09-06 22:39:47 +00:00
|
|
|
Chain *store.ChainStore
|
2019-08-20 16:48:33 +00:00
|
|
|
|
2019-08-26 13:45:36 +00:00
|
|
|
LocalDAG dtypes.ClientDAG
|
|
|
|
Blockstore dtypes.ClientBlockstore
|
|
|
|
Filestore dtypes.ClientFilestore `optional:"true"`
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 17:42:16 +00:00
|
|
|
func calcDealExpiration(minDuration uint64, md *miner.DeadlineInfo, startEpoch abi.ChainEpoch) abi.ChainEpoch {
|
2020-04-21 21:38:26 +00:00
|
|
|
// Make sure we give some time for the miner to seal
|
2020-04-30 17:42:16 +00:00
|
|
|
minExp := startEpoch + abi.ChainEpoch(minDuration)
|
2020-04-21 21:38:26 +00:00
|
|
|
|
|
|
|
// Align on miners ProvingPeriodBoundary
|
2020-04-24 17:12:30 +00:00
|
|
|
return minExp + miner.WPoStProvingPeriod - (minExp % miner.WPoStProvingPeriod) + (md.PeriodStart % miner.WPoStProvingPeriod) - 1
|
2020-04-21 21:38:26 +00:00
|
|
|
}
|
|
|
|
|
2020-03-03 00:36:01 +00:00
|
|
|
func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) {
|
|
|
|
exist, err := a.WalletHas(ctx, params.Wallet)
|
2019-08-20 16:48:33 +00:00
|
|
|
if err != nil {
|
2020-03-03 00:36:01 +00:00
|
|
|
return nil, xerrors.Errorf("failed getting addr from wallet: %w", params.Wallet)
|
2019-12-13 19:15:56 +00:00
|
|
|
}
|
|
|
|
if !exist {
|
|
|
|
return nil, xerrors.Errorf("provided address doesn't exist in wallet")
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 17:36:36 +00:00
|
|
|
mi, err := a.StateMinerInfo(ctx, params.Miner, types.EmptyTSK)
|
2019-08-20 16:48:33 +00:00
|
|
|
if err != nil {
|
2019-11-06 06:26:50 +00:00
|
|
|
return nil, xerrors.Errorf("failed getting peer ID: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-04-24 17:12:30 +00:00
|
|
|
md, err := a.StateMinerProvingDeadline(ctx, params.Miner, types.EmptyTSK)
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed getting peer ID: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-04-16 17:36:36 +00:00
|
|
|
rt, err := ffiwrapper.SealProofTypeFromSectorSize(mi.SectorSize)
|
2020-02-27 21:45:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("bad sector size: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-04-17 16:38:20 +00:00
|
|
|
if uint64(params.Data.PieceSize.Padded()) > uint64(mi.SectorSize) {
|
2020-04-07 18:33:12 +00:00
|
|
|
return nil, xerrors.New("data doesn't fit in a sector")
|
|
|
|
}
|
|
|
|
|
2020-06-05 20:06:11 +00:00
|
|
|
providerInfo := utils.NewStorageProviderInfo(params.Miner, mi.Worker, mi.SectorSize, peer.ID(mi.PeerId))
|
2020-04-30 17:42:16 +00:00
|
|
|
|
|
|
|
dealStart := params.DealStartEpoch
|
|
|
|
if dealStart <= 0 { // unset, or explicitly 'epoch undefined'
|
|
|
|
ts, err := a.ChainHead(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed getting chain height: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dealStart = ts.Height() + dealStartBuffer
|
2020-02-12 22:32:26 +00:00
|
|
|
}
|
2020-03-04 02:19:28 +00:00
|
|
|
|
2019-11-04 19:57:54 +00:00
|
|
|
result, err := a.SMDealClient.ProposeStorageDeal(
|
|
|
|
ctx,
|
2020-03-03 00:36:01 +00:00
|
|
|
params.Wallet,
|
2019-11-04 19:57:54 +00:00
|
|
|
&providerInfo,
|
2020-03-03 00:36:01 +00:00
|
|
|
params.Data,
|
2020-04-30 17:42:16 +00:00
|
|
|
dealStart,
|
|
|
|
calcDealExpiration(params.MinBlocksDuration, md, dealStart),
|
2020-03-03 00:36:01 +00:00
|
|
|
params.EpochPrice,
|
2020-02-27 21:45:31 +00:00
|
|
|
big.Zero(),
|
|
|
|
rt,
|
|
|
|
)
|
2019-08-20 16:48:33 +00:00
|
|
|
|
2019-11-06 06:26:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to start deal: %w", err)
|
|
|
|
}
|
|
|
|
|
2019-11-04 19:57:54 +00:00
|
|
|
return &result.ProposalCid, nil
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
|
2019-09-16 13:46:05 +00:00
|
|
|
func (a *API) ClientListDeals(ctx context.Context) ([]api.DealInfo, error) {
|
2020-04-07 02:17:02 +00:00
|
|
|
deals, err := a.SMDealClient.ListLocalDeals(ctx)
|
2019-09-10 14:13:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
out := make([]api.DealInfo, len(deals))
|
|
|
|
for k, v := range deals {
|
|
|
|
out[k] = api.DealInfo{
|
|
|
|
ProposalCid: v.ProposalCid,
|
|
|
|
State: v.State,
|
2020-03-31 18:53:30 +00:00
|
|
|
Message: v.Message,
|
2020-01-10 18:01:48 +00:00
|
|
|
Provider: v.Proposal.Provider,
|
2019-09-10 14:48:54 +00:00
|
|
|
|
2020-03-31 18:53:30 +00:00
|
|
|
PieceCID: v.Proposal.PieceCID,
|
2020-02-13 00:15:33 +00:00
|
|
|
Size: uint64(v.Proposal.PieceSize.Unpadded()),
|
2019-09-10 14:48:54 +00:00
|
|
|
|
2020-02-12 22:32:26 +00:00
|
|
|
PricePerEpoch: v.Proposal.StoragePricePerEpoch,
|
2020-02-13 00:15:33 +00:00
|
|
|
Duration: uint64(v.Proposal.Duration()),
|
2020-03-25 19:30:59 +00:00
|
|
|
DealID: v.DealID,
|
2019-09-10 14:13:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2019-11-06 19:44:28 +00:00
|
|
|
func (a *API) ClientGetDealInfo(ctx context.Context, d cid.Cid) (*api.DealInfo, error) {
|
2020-04-07 02:17:02 +00:00
|
|
|
v, err := a.SMDealClient.GetLocalDeal(ctx, d)
|
2019-11-06 19:44:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-11-04 19:57:54 +00:00
|
|
|
|
2019-11-06 19:44:28 +00:00
|
|
|
return &api.DealInfo{
|
|
|
|
ProposalCid: v.ProposalCid,
|
|
|
|
State: v.State,
|
2020-03-31 18:53:30 +00:00
|
|
|
Message: v.Message,
|
2020-01-10 18:01:48 +00:00
|
|
|
Provider: v.Proposal.Provider,
|
2020-03-31 18:53:30 +00:00
|
|
|
PieceCID: v.Proposal.PieceCID,
|
2020-02-13 00:15:33 +00:00
|
|
|
Size: uint64(v.Proposal.PieceSize.Unpadded()),
|
2020-02-12 22:32:26 +00:00
|
|
|
PricePerEpoch: v.Proposal.StoragePricePerEpoch,
|
2020-02-13 00:15:33 +00:00
|
|
|
Duration: uint64(v.Proposal.Duration()),
|
2020-03-25 19:30:59 +00:00
|
|
|
DealID: v.DealID,
|
2019-11-06 19:44:28 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-09-16 13:46:05 +00:00
|
|
|
func (a *API) ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error) {
|
2019-08-26 13:45:36 +00:00
|
|
|
// TODO: check if we have the ENTIRE dag
|
|
|
|
|
|
|
|
offExch := merkledag.NewDAGService(blockservice.New(a.Blockstore, offline.Exchange(a.Blockstore)))
|
|
|
|
_, err := offExch.Get(ctx, root)
|
|
|
|
if err == ipld.ErrNotFound {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2019-09-16 13:46:05 +00:00
|
|
|
func (a *API) ClientFindData(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) {
|
2019-08-26 13:45:36 +00:00
|
|
|
peers, err := a.RetDiscovery.GetPeers(root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-08-26 18:23:11 +00:00
|
|
|
out := make([]api.QueryOffer, len(peers))
|
2019-08-26 13:45:36 +00:00
|
|
|
for k, p := range peers {
|
2020-06-16 14:14:49 +00:00
|
|
|
out[k] = a.makeRetrievalQuery(ctx, p, root, retrievalmarket.QueryParams{})
|
2019-08-26 13:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2020-06-16 14:14:49 +00:00
|
|
|
func (a *API) ClientMinerQueryOffer(ctx context.Context, payload cid.Cid, miner address.Address) (api.QueryOffer, error) {
|
2020-06-16 15:22:44 +00:00
|
|
|
mi, err := a.StateMinerInfo(ctx, miner, types.EmptyTSK)
|
|
|
|
if err != nil {
|
|
|
|
return api.QueryOffer{}, err
|
|
|
|
}
|
2020-06-16 14:14:49 +00:00
|
|
|
rp := retrievalmarket.RetrievalPeer{
|
|
|
|
Address: miner,
|
2020-06-16 15:22:44 +00:00
|
|
|
ID: mi.PeerId,
|
2020-06-16 14:14:49 +00:00
|
|
|
}
|
|
|
|
return a.makeRetrievalQuery(ctx, rp, payload, retrievalmarket.QueryParams{}), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *API) makeRetrievalQuery(ctx context.Context, rp retrievalmarket.RetrievalPeer, payload cid.Cid, qp retrievalmarket.QueryParams) api.QueryOffer {
|
|
|
|
queryResponse, err := a.Retrieval.Query(ctx, rp, payload, qp)
|
|
|
|
if err != nil {
|
|
|
|
return api.QueryOffer{Err: err.Error(), Miner: rp.Address, MinerPeerID: rp.ID}
|
|
|
|
}
|
2020-06-16 20:32:03 +00:00
|
|
|
var errStr string
|
|
|
|
switch queryResponse.Status {
|
|
|
|
case retrievalmarket.QueryResponseAvailable:
|
|
|
|
errStr = ""
|
|
|
|
case retrievalmarket.QueryResponseUnavailable:
|
2020-06-16 20:52:47 +00:00
|
|
|
errStr = fmt.Sprintf("retrieval query offer was unavailable: %s", queryResponse.Message)
|
2020-06-16 20:32:03 +00:00
|
|
|
case retrievalmarket.QueryResponseError:
|
2020-06-16 20:52:47 +00:00
|
|
|
errStr = fmt.Sprintf("retrieval query offer errored: %s", queryResponse.Message)
|
2020-06-16 20:32:03 +00:00
|
|
|
}
|
2020-06-16 14:14:49 +00:00
|
|
|
|
|
|
|
return api.QueryOffer{
|
|
|
|
Root: payload,
|
|
|
|
Size: queryResponse.Size,
|
|
|
|
MinPrice: queryResponse.PieceRetrievalPrice(),
|
|
|
|
PaymentInterval: queryResponse.MaxPaymentInterval,
|
|
|
|
PaymentIntervalIncrease: queryResponse.MaxPaymentIntervalIncrease,
|
|
|
|
Miner: queryResponse.PaymentAddress, // TODO: check
|
|
|
|
MinerPeerID: rp.ID,
|
2020-06-16 20:32:03 +00:00
|
|
|
Err: errStr,
|
2020-06-16 14:14:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 04:13:08 +00:00
|
|
|
func (a *API) ClientImport(ctx context.Context, ref api.FileRef) (cid.Cid, error) {
|
|
|
|
|
2019-08-20 16:48:33 +00:00
|
|
|
bufferedDS := ipld.NewBufferedDAG(ctx, a.LocalDAG)
|
2020-04-03 22:17:57 +00:00
|
|
|
nd, err := a.clientImport(ref, bufferedDS)
|
2019-08-20 16:48:33 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
2020-04-03 22:17:57 +00:00
|
|
|
return nd, nil
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
|
2019-10-23 09:18:22 +00:00
|
|
|
func (a *API) ClientImportLocal(ctx context.Context, f io.Reader) (cid.Cid, error) {
|
|
|
|
file := files.NewReaderFile(f)
|
|
|
|
|
|
|
|
bufferedDS := ipld.NewBufferedDAG(ctx, a.LocalDAG)
|
|
|
|
|
|
|
|
params := ihelper.DagBuilderParams{
|
|
|
|
Maxlinks: build.UnixfsLinksPerLevel,
|
|
|
|
RawLeaves: true,
|
|
|
|
CidBuilder: nil,
|
|
|
|
Dagserv: bufferedDS,
|
|
|
|
}
|
|
|
|
|
|
|
|
db, err := params.New(chunker.NewSizeSplitter(file, int64(build.UnixfsChunkSize)))
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
nd, err := balanced.Layout(db)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nd.Cid(), bufferedDS.Commit()
|
|
|
|
}
|
|
|
|
|
2019-09-16 13:46:05 +00:00
|
|
|
func (a *API) ClientListImports(ctx context.Context) ([]api.Import, error) {
|
2019-08-20 16:48:33 +00:00
|
|
|
if a.Filestore == nil {
|
|
|
|
return nil, errors.New("listing imports is not supported with in-memory dag yet")
|
|
|
|
}
|
|
|
|
next, err := filestore.ListAll(a.Filestore, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: make this less very bad by tracking root cids instead of using ListAll
|
|
|
|
|
|
|
|
out := make([]api.Import, 0)
|
2020-03-04 02:31:35 +00:00
|
|
|
lowest := make([]uint64, 0)
|
2019-08-20 16:48:33 +00:00
|
|
|
for {
|
|
|
|
r := next()
|
|
|
|
if r == nil {
|
|
|
|
return out, nil
|
|
|
|
}
|
2020-03-04 02:31:35 +00:00
|
|
|
matched := false
|
|
|
|
for i := range out {
|
|
|
|
if out[i].FilePath == r.FilePath {
|
|
|
|
matched = true
|
|
|
|
if lowest[i] > r.Offset {
|
|
|
|
lowest[i] = r.Offset
|
|
|
|
out[i] = api.Import{
|
|
|
|
Status: r.Status,
|
|
|
|
Key: r.Key,
|
|
|
|
FilePath: r.FilePath,
|
|
|
|
Size: r.Size,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !matched {
|
|
|
|
out = append(out, api.Import{
|
|
|
|
Status: r.Status,
|
|
|
|
Key: r.Key,
|
|
|
|
FilePath: r.FilePath,
|
|
|
|
Size: r.Size,
|
|
|
|
})
|
|
|
|
lowest = append(lowest, r.Offset)
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-27 18:45:21 +00:00
|
|
|
|
2020-05-26 15:36:21 +00:00
|
|
|
func (a *API) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error {
|
2019-09-16 20:11:17 +00:00
|
|
|
if order.MinerPeerID == "" {
|
2020-04-16 17:36:36 +00:00
|
|
|
mi, err := a.StateMinerInfo(ctx, order.Miner, types.EmptyTSK)
|
2019-09-16 20:11:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-05 20:06:11 +00:00
|
|
|
order.MinerPeerID = peer.ID(mi.PeerId)
|
2019-09-16 20:11:17 +00:00
|
|
|
}
|
|
|
|
|
2020-02-28 18:01:43 +00:00
|
|
|
if order.Size == 0 {
|
|
|
|
return xerrors.Errorf("cannot make retrieval deal for zero bytes")
|
|
|
|
}
|
|
|
|
|
2019-12-10 04:19:59 +00:00
|
|
|
retrievalResult := make(chan error, 1)
|
|
|
|
|
|
|
|
unsubscribe := a.Retrieval.SubscribeToEvents(func(event retrievalmarket.ClientEvent, state retrievalmarket.ClientDealState) {
|
2020-01-24 20:19:52 +00:00
|
|
|
if state.PayloadCID.Equals(order.Root) {
|
2020-02-29 02:10:11 +00:00
|
|
|
switch state.Status {
|
|
|
|
case retrievalmarket.DealStatusFailed, retrievalmarket.DealStatusErrored:
|
2020-01-24 22:29:31 +00:00
|
|
|
retrievalResult <- xerrors.Errorf("Retrieval Error: %s", state.Message)
|
2020-02-29 02:10:11 +00:00
|
|
|
case retrievalmarket.DealStatusCompleted:
|
2019-12-10 04:19:59 +00:00
|
|
|
retrievalResult <- nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-02-28 18:01:43 +00:00
|
|
|
ppb := types.BigDiv(order.Total, types.NewInt(order.Size))
|
|
|
|
|
2020-05-20 17:24:42 +00:00
|
|
|
_, err := a.Retrieval.Retrieve(
|
2019-12-17 03:17:46 +00:00
|
|
|
ctx,
|
2020-01-24 20:19:52 +00:00
|
|
|
order.Root,
|
2020-02-28 18:01:43 +00:00
|
|
|
retrievalmarket.NewParamsV0(ppb, order.PaymentInterval, order.PaymentIntervalIncrease),
|
2020-02-12 22:32:26 +00:00
|
|
|
order.Total,
|
2019-12-17 03:17:46 +00:00
|
|
|
order.MinerPeerID,
|
2020-01-10 18:01:48 +00:00
|
|
|
order.Client,
|
|
|
|
order.Miner)
|
2020-05-20 17:24:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("Retrieve failed: %w", err)
|
|
|
|
}
|
2019-12-10 04:19:59 +00:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return xerrors.New("Retrieval Timed Out")
|
|
|
|
case err := <-retrievalResult:
|
|
|
|
if err != nil {
|
2020-02-08 00:18:14 +00:00
|
|
|
return xerrors.Errorf("RetrieveUnixfs: %w", err)
|
2019-12-10 04:19:59 +00:00
|
|
|
}
|
2019-08-27 22:10:23 +00:00
|
|
|
}
|
|
|
|
|
2019-12-10 04:19:59 +00:00
|
|
|
unsubscribe()
|
|
|
|
|
2020-05-26 15:36:21 +00:00
|
|
|
// If ref is nil, it only fetches the data into the configured blockstore.
|
|
|
|
if ref == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-03 04:13:08 +00:00
|
|
|
if ref.IsCAR {
|
2020-03-30 14:08:20 +00:00
|
|
|
f, err := os.OpenFile(ref.Path, os.O_CREATE|os.O_WRONLY, 0644)
|
2020-03-03 04:13:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = car.WriteCar(ctx, a.LocalDAG, []cid.Cid{order.Root}, f)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return f.Close()
|
|
|
|
}
|
|
|
|
|
2019-12-10 04:19:59 +00:00
|
|
|
nd, err := a.LocalDAG.Get(ctx, order.Root)
|
2019-08-27 22:10:23 +00:00
|
|
|
if err != nil {
|
2019-12-10 04:19:59 +00:00
|
|
|
return xerrors.Errorf("ClientRetrieve: %w", err)
|
2019-08-27 22:10:23 +00:00
|
|
|
}
|
2019-12-10 04:19:59 +00:00
|
|
|
file, err := unixfile.NewUnixfsFile(ctx, a.LocalDAG, nd)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("ClientRetrieve: %w", err)
|
|
|
|
}
|
2020-03-03 04:13:08 +00:00
|
|
|
return files.WriteTo(file, ref.Path)
|
2019-08-27 18:45:21 +00:00
|
|
|
}
|
2019-09-13 21:00:36 +00:00
|
|
|
|
2020-02-13 00:15:33 +00:00
|
|
|
func (a *API) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) {
|
2020-01-10 18:21:46 +00:00
|
|
|
info := utils.NewStorageProviderInfo(miner, address.Undef, 0, p)
|
2019-12-17 10:46:39 +00:00
|
|
|
signedAsk, err := a.SMDealClient.GetAsk(ctx, info)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-02-13 00:15:33 +00:00
|
|
|
return signedAsk, nil
|
2019-09-13 21:00:36 +00:00
|
|
|
}
|
2020-04-03 22:17:57 +00:00
|
|
|
|
|
|
|
func (a *API) ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*api.CommPRet, error) {
|
2020-04-17 16:38:20 +00:00
|
|
|
mi, err := a.StateMinerInfo(ctx, miner, types.EmptyTSK)
|
2020-04-03 22:17:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed checking miners sector size: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-04-17 16:38:20 +00:00
|
|
|
rt, err := ffiwrapper.SealProofTypeFromSectorSize(mi.SectorSize)
|
2020-04-03 22:17:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("bad sector size: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
rdr, err := os.Open(inpath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
stat, err := rdr.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
commP, pieceSize, err := pieceio.GeneratePieceCommitment(rt, rdr, uint64(stat.Size()))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("computing commP failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &api.CommPRet{
|
|
|
|
Root: commP,
|
|
|
|
Size: pieceSize,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *API) ClientGenCar(ctx context.Context, ref api.FileRef, outputPath string) error {
|
|
|
|
|
|
|
|
bufferedDS := ipld.NewBufferedDAG(ctx, a.LocalDAG)
|
|
|
|
c, err := a.clientImport(ref, bufferedDS)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-27 20:53:20 +00:00
|
|
|
defer bufferedDS.Remove(ctx, c) //nolint:errcheck
|
2020-05-05 01:31:56 +00:00
|
|
|
ssb := builder.NewSelectorSpecBuilder(basicnode.Style.Any)
|
2020-04-03 22:17:57 +00:00
|
|
|
|
|
|
|
// entire DAG selector
|
|
|
|
allSelector := ssb.ExploreRecursive(selector.RecursionLimitNone(),
|
|
|
|
ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node()
|
|
|
|
|
|
|
|
f, err := os.Create(outputPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
sc := car.NewSelectiveCar(ctx, a.Blockstore, []car.Dag{{Root: c, Selector: allSelector}})
|
|
|
|
if err = sc.Write(f); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-27 23:15:19 +00:00
|
|
|
return f.Close()
|
2020-04-03 22:17:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *API) clientImport(ref api.FileRef, bufferedDS *ipld.BufferedDAG) (cid.Cid, error) {
|
|
|
|
f, err := os.Open(ref.Path)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
stat, err := f.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := files.NewReaderPathFile(ref.Path, f, stat)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if ref.IsCAR {
|
|
|
|
var store car.Store
|
|
|
|
if a.Filestore == nil {
|
|
|
|
store = a.Blockstore
|
|
|
|
} else {
|
|
|
|
store = (*filestore.Filestore)(a.Filestore)
|
|
|
|
}
|
|
|
|
result, err := car.LoadCar(store, file)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result.Roots) != 1 {
|
|
|
|
return cid.Undef, xerrors.New("cannot import car with more than one root")
|
|
|
|
}
|
|
|
|
|
|
|
|
return result.Roots[0], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
params := ihelper.DagBuilderParams{
|
|
|
|
Maxlinks: build.UnixfsLinksPerLevel,
|
|
|
|
RawLeaves: true,
|
|
|
|
CidBuilder: nil,
|
|
|
|
Dagserv: bufferedDS,
|
|
|
|
NoCopy: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
db, err := params.New(chunker.NewSizeSplitter(file, int64(build.UnixfsChunkSize)))
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
nd, err := balanced.Layout(db)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := bufferedDS.Commit(); err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nd.Cid(), nil
|
|
|
|
}
|