diff --git a/api/api_full.go b/api/api_full.go index 61ef1fa1c..573a06859 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -118,6 +118,7 @@ type FullNode interface { ClientListDeals(ctx context.Context) ([]DealInfo, error) ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error) ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error) + ClientMinerQueryOffer(ctx context.Context, root cid.Cid, miner address.Address) (QueryOffer, error) ClientRetrieve(ctx context.Context, order RetrievalOrder, ref *FileRef) error ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error) diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 85ac54c17..18f5a97f6 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -106,17 +106,18 @@ 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) (cid.Cid, error) `perm:"admin"` - ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"` - ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"` - ClientFindData func(ctx context.Context, root 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"` - ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) 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, miner address.Address) (*api.CommPRet, error) `perm:"read"` - ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"` + ClientImport func(ctx context.Context, ref api.FileRef) (cid.Cid, error) `perm:"admin"` + ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"` + ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"` + ClientFindData func(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) `perm:"read"` + ClientMinerQueryOffer func(ctx context.Context, root cid.Cid, miner address.Address) (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"` + ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) 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, miner address.Address) (*api.CommPRet, error) `perm:"read"` + ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"` StateNetworkName func(context.Context) (dtypes.NetworkName, error) `perm:"read"` StateMinerSectors func(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"` @@ -318,6 +319,10 @@ func (c *FullNodeStruct) ClientFindData(ctx context.Context, root cid.Cid) ([]ap return c.Internal.ClientFindData(ctx, root) } +func (c *FullNodeStruct) ClientMinerQueryOffer(ctx context.Context, root cid.Cid, miner address.Address) (api.QueryOffer, error) { + return c.Internal.ClientMinerQueryOffer(ctx, root, miner) +} + func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) { return c.Internal.ClientStartDeal(ctx, params) } diff --git a/cli/client.go b/cli/client.go index 9d4479b62..c692fbe4b 100644 --- a/cli/client.go +++ b/cli/client.go @@ -19,6 +19,7 @@ import ( "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/builtin/market" + "github.com/filecoin-project/lotus/api" lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" ) @@ -391,6 +392,10 @@ var clientRetrieveCmd = &cli.Command{ Name: "car", Usage: "export to a car file instead of a regular file", }, + &cli.StringFlag{ + Name: "miner", + Usage: "miner address for retrieval, if not present it'll use local discovery", + }, }, Action: func(cctx *cli.Context) error { if cctx.NArg() != 2 { @@ -398,7 +403,7 @@ var clientRetrieveCmd = &cli.Command{ return nil } - api, closer, err := GetFullNodeAPI(cctx) + fapi, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } @@ -409,7 +414,7 @@ var clientRetrieveCmd = &cli.Command{ if cctx.String("address") != "" { payer, err = address.NewFromString(cctx.String("address")) } else { - payer, err = api.WalletDefaultAddress(ctx) + payer, err = fapi.WalletDefaultAddress(ctx) } if err != nil { return err @@ -432,23 +437,36 @@ var clientRetrieveCmd = &cli.Command{ return nil }*/ // TODO: fix - offers, err := api.ClientFindData(ctx, file) - if err != nil { - return err - } + var offer api.QueryOffer + minerStrAddr := cctx.String("miner") + if minerStrAddr == "" { // Local discovery + offers, err := fapi.ClientFindData(ctx, file) + if err != nil { + return err + } - // TODO: parse offer strings from `client find`, make this smarter - - if len(offers) < 1 { - fmt.Println("Failed to find file") - return nil + // TODO: parse offer strings from `client find`, make this smarter + if len(offers) < 1 { + fmt.Println("Failed to find file") + return nil + } + offer = offers[0] + } else { // Directed retrieval + minerAddr, err := address.NewFromString(minerStrAddr) + if err != nil { + return err + } + offer, err = fapi.ClientMinerQueryOffer(ctx, file, minerAddr) + if err != nil { + return err + } } ref := &lapi.FileRef{ Path: cctx.Args().Get(1), IsCAR: cctx.Bool("car"), } - if err := api.ClientRetrieve(ctx, offers[0].Order(payer), ref); err != nil { + if err := fapi.ClientRetrieve(ctx, offer.Order(payer), ref); err != nil { return xerrors.Errorf("Retrieval Failed: %w", err) } diff --git a/node/impl/client/client.go b/node/impl/client/client.go index ff974e8b2..3ed90b0fa 100644 --- a/node/impl/client/client.go +++ b/node/impl/client/client.go @@ -201,25 +201,36 @@ func (a *API) ClientFindData(ctx context.Context, root cid.Cid) ([]api.QueryOffe out := make([]api.QueryOffer, len(peers)) for k, p := range peers { - queryResponse, err := a.Retrieval.Query(ctx, p, root, retrievalmarket.QueryParams{}) - if err != nil { - out[k] = api.QueryOffer{Err: err.Error(), Miner: p.Address, MinerPeerID: p.ID} - } else { - out[k] = api.QueryOffer{ - Root: root, - Size: queryResponse.Size, - MinPrice: queryResponse.PieceRetrievalPrice(), - PaymentInterval: queryResponse.MaxPaymentInterval, - PaymentIntervalIncrease: queryResponse.MaxPaymentIntervalIncrease, - Miner: queryResponse.PaymentAddress, // TODO: check - MinerPeerID: p.ID, - } - } + out[k] = a.makeRetrievalQuery(ctx, p, root, retrievalmarket.QueryParams{}) } return out, nil } +func (a *API) ClientMinerQueryOffer(ctx context.Context, payload cid.Cid, miner address.Address) (api.QueryOffer, error) { + rp := retrievalmarket.RetrievalPeer{ + Address: miner, + } + 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} + } + + 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, + } +} + func (a *API) ClientImport(ctx context.Context, ref api.FileRef) (cid.Cid, error) { bufferedDS := ipld.NewBufferedDAG(ctx, a.LocalDAG)