lotus/cli/client.go

367 lines
7.5 KiB
Go
Raw Normal View History

2019-07-12 10:17:16 +00:00
package cli
import (
"fmt"
2019-11-08 01:45:45 +00:00
"os"
2019-10-23 07:05:22 +00:00
"path/filepath"
2019-08-02 14:09:54 +00:00
"strconv"
2019-11-08 01:45:45 +00:00
"text/tabwriter"
2019-07-16 16:07:08 +00:00
2019-08-02 14:09:54 +00:00
"github.com/ipfs/go-cid"
2019-09-13 21:00:36 +00:00
"github.com/libp2p/go-libp2p-core/peer"
2019-08-02 14:09:54 +00:00
"golang.org/x/xerrors"
2019-07-12 10:17:16 +00:00
"gopkg.in/urfave/cli.v2"
2019-08-02 14:09:54 +00:00
"github.com/filecoin-project/go-address"
2019-11-08 01:45:45 +00:00
lapi "github.com/filecoin-project/lotus/api"
actors "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
2019-07-12 10:17:16 +00:00
)
var clientCmd = &cli.Command{
Name: "client",
Usage: "Make deals, store data, retrieve data",
Subcommands: []*cli.Command{
clientImportCmd,
2019-07-12 10:44:01 +00:00
clientLocalCmd,
2019-08-02 14:09:54 +00:00
clientDealCmd,
2019-08-26 13:45:36 +00:00
clientFindCmd,
clientRetrieveCmd,
2019-09-13 21:00:36 +00:00
clientQueryAskCmd,
2019-11-08 01:45:45 +00:00
clientListDeals,
2019-07-12 10:17:16 +00:00
},
}
var clientImportCmd = &cli.Command{
2019-07-12 10:44:01 +00:00
Name: "import",
Usage: "Import data",
2019-07-12 10:17:16 +00:00
Action: func(cctx *cli.Context) error {
2019-10-03 18:12:30 +00:00
api, closer, err := GetFullNodeAPI(cctx)
2019-07-12 10:17:16 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer()
2019-07-18 23:16:23 +00:00
ctx := ReqContext(cctx)
2019-10-23 07:05:22 +00:00
absPath, err := filepath.Abs(cctx.Args().First())
if err != nil {
return err
}
2019-07-12 10:17:16 +00:00
2019-10-23 07:05:22 +00:00
c, err := api.ClientImport(ctx, absPath)
2019-07-12 10:17:16 +00:00
if err != nil {
return err
}
fmt.Println(c.String())
return nil
},
2019-07-12 10:44:01 +00:00
}
var clientLocalCmd = &cli.Command{
Name: "local",
Usage: "List locally imported data",
Action: func(cctx *cli.Context) error {
2019-10-03 18:12:30 +00:00
api, closer, err := GetFullNodeAPI(cctx)
2019-07-12 10:44:01 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer()
2019-07-18 23:16:23 +00:00
ctx := ReqContext(cctx)
2019-07-12 10:44:01 +00:00
list, err := api.ClientListImports(ctx)
if err != nil {
return err
}
for _, v := range list {
fmt.Printf("%s %s %d %s\n", v.Key, v.FilePath, v.Size, v.Status)
}
return nil
},
}
2019-08-02 14:09:54 +00:00
var clientDealCmd = &cli.Command{
Name: "deal",
Usage: "Initialize storage deal with a miner",
Action: func(cctx *cli.Context) error {
2019-10-03 18:12:30 +00:00
api, closer, err := GetFullNodeAPI(cctx)
2019-08-02 14:09:54 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer()
2019-08-02 14:09:54 +00:00
ctx := ReqContext(cctx)
2019-08-07 20:06:10 +00:00
if cctx.NArg() != 4 {
return xerrors.New("expected 4 args: dataCid, miner, price, duration")
2019-08-02 14:09:54 +00:00
}
// [data, miner, dur]
data, err := cid.Parse(cctx.Args().Get(0))
if err != nil {
return err
}
miner, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
2019-10-29 12:02:13 +00:00
price, err := types.ParseFIL(cctx.Args().Get(2))
2019-08-02 14:09:54 +00:00
if err != nil {
return err
}
2019-08-07 20:06:10 +00:00
dur, err := strconv.ParseInt(cctx.Args().Get(3), 10, 32)
if err != nil {
return err
}
a, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
proposal, err := api.ClientStartDeal(ctx, data, a, miner, types.BigInt(price), uint64(dur))
2019-08-07 20:06:10 +00:00
if err != nil {
return err
}
fmt.Println(proposal)
return nil
2019-08-02 14:09:54 +00:00
},
}
2019-08-26 13:45:36 +00:00
var clientFindCmd = &cli.Command{
Name: "find",
Usage: "find data in the network",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
2019-08-28 23:01:28 +00:00
fmt.Println("Usage: find [CID]")
2019-08-26 13:45:36 +00:00
return nil
}
file, err := cid.Parse(cctx.Args().First())
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
api, closer, err := GetFullNodeAPI(cctx)
2019-08-26 13:45:36 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer()
2019-08-26 13:45:36 +00:00
ctx := ReqContext(cctx)
// Check if we already have this data locally
has, err := api.ClientHasLocal(ctx, file)
if err != nil {
return err
}
if has {
fmt.Println("LOCAL")
}
offers, err := api.ClientFindData(ctx, file)
if err != nil {
return err
}
for _, offer := range offers {
if offer.Err != "" {
fmt.Printf("ERR %s@%s: %s\n", offer.Miner, offer.MinerPeerID, offer.Err)
continue
}
2019-10-29 12:02:13 +00:00
fmt.Printf("RETRIEVAL %s@%s-%sfil-%db\n", offer.Miner, offer.MinerPeerID, types.FIL(offer.MinPrice), offer.Size)
2019-08-26 13:45:36 +00:00
}
return nil
},
}
var clientRetrieveCmd = &cli.Command{
Name: "retrieve",
Usage: "retrieve data from network",
2019-09-17 07:55:33 +00:00
Flags: []cli.Flag{
&cli.StringFlag{
Name: "address",
Usage: "address to use for transactions",
},
},
2019-08-26 13:45:36 +00:00
Action: func(cctx *cli.Context) error {
2019-09-17 07:55:33 +00:00
if cctx.NArg() != 2 {
fmt.Println("Usage: retrieve [CID] [outfile]")
2019-08-26 13:45:36 +00:00
return nil
}
2019-10-03 18:12:30 +00:00
api, closer, err := GetFullNodeAPI(cctx)
2019-09-16 13:46:05 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer()
2019-09-17 07:55:33 +00:00
ctx := ReqContext(cctx)
2019-09-16 13:46:05 +00:00
2019-09-17 07:55:33 +00:00
var payer address.Address
if cctx.String("address") != "" {
payer, err = address.NewFromString(cctx.String("address"))
} else {
payer, err = api.WalletDefaultAddress(ctx)
}
2019-08-26 13:45:36 +00:00
if err != nil {
return err
}
2019-09-17 07:55:33 +00:00
file, err := cid.Parse(cctx.Args().Get(0))
2019-08-26 13:45:36 +00:00
if err != nil {
return err
}
// Check if we already have this data locally
2019-08-27 18:45:21 +00:00
/*has, err := api.ClientHasLocal(ctx, file)
2019-08-26 13:45:36 +00:00
if err != nil {
return err
}
if has {
fmt.Println("Success: Already in local storage")
return nil
2019-08-27 22:10:23 +00:00
}*/ // TODO: fix
2019-08-26 13:45:36 +00:00
2019-08-27 18:45:21 +00:00
offers, err := api.ClientFindData(ctx, file)
2019-08-26 13:45:36 +00:00
if err != nil {
return err
}
2019-08-27 18:45:21 +00:00
// TODO: parse offer strings from `client find`, make this smarter
2019-08-26 13:45:36 +00:00
2019-11-20 08:47:24 +00:00
if len(offers) < 1 {
fmt.Println("Failed to find file")
return nil
}
2019-09-16 13:46:05 +00:00
2019-12-01 21:52:24 +00:00
if err := api.ClientRetrieve(ctx, offers[0].Order(payer), cctx.Args().Get(1)); err != nil {
2019-12-11 13:59:15 +00:00
return xerrors.Errorf("Retrieval Failed: %w", err)
2019-08-27 18:45:21 +00:00
}
2019-11-04 19:03:11 +00:00
fmt.Println("Success")
return nil
2019-08-26 13:45:36 +00:00
},
}
2019-09-13 21:00:36 +00:00
var clientQueryAskCmd = &cli.Command{
Name: "query-ask",
Usage: "find a miners ask",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "peerid",
Usage: "specify peer ID of node to make query against",
},
2019-10-12 07:21:28 +00:00
&cli.Int64Flag{
Name: "size",
Usage: "data size in bytes",
},
&cli.Int64Flag{
Name: "duration",
Usage: "deal duration",
},
2019-09-13 21:00:36 +00:00
},
Action: func(cctx *cli.Context) error {
if cctx.NArg() != 1 {
fmt.Println("Usage: query-ask [address]")
return nil
}
maddr, err := address.NewFromString(cctx.Args().First())
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
api, closer, err := GetFullNodeAPI(cctx)
2019-09-13 21:00:36 +00:00
if err != nil {
return err
}
2019-10-03 18:12:30 +00:00
defer closer()
2019-09-13 21:00:36 +00:00
ctx := ReqContext(cctx)
var pid peer.ID
if pidstr := cctx.String("peerid"); pidstr != "" {
p, err := peer.IDFromString(pidstr)
if err != nil {
return err
}
pid = p
} else {
ret, err := api.StateCall(ctx, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetPeerID,
}, types.EmptyTSK)
2019-09-13 21:00:36 +00:00
if err != nil {
return xerrors.Errorf("failed to get peerID for miner: %w", err)
}
if ret.ExitCode != 0 {
return fmt.Errorf("call to GetPeerID was unsuccesful (exit code %d)", ret.ExitCode)
}
if peer.ID(ret.Return) == peer.ID("SETME") {
return fmt.Errorf("the miner hasn't initialized yet")
}
2019-09-13 21:00:36 +00:00
p, err := peer.IDFromBytes(ret.Return)
if err != nil {
return err
}
pid = p
}
ask, err := api.ClientQueryAsk(ctx, pid, maddr)
if err != nil {
return err
}
fmt.Printf("Ask: %s\n", maddr)
fmt.Printf("Price per GiB: %s\n", types.FIL(ask.Ask.Price))
2019-10-12 07:21:28 +00:00
size := cctx.Int64("size")
if size == 0 {
return nil
}
2019-10-29 10:19:39 +00:00
perEpoch := types.BigDiv(types.BigMul(ask.Ask.Price, types.NewInt(uint64(size))), types.NewInt(1<<30))
2019-10-29 12:02:13 +00:00
fmt.Printf("Price per Block: %s\n", types.FIL(perEpoch))
2019-10-12 07:21:28 +00:00
duration := cctx.Int64("duration")
if duration == 0 {
return nil
}
2019-10-29 12:02:13 +00:00
fmt.Printf("Total Price: %s\n", types.FIL(types.BigMul(perEpoch, types.NewInt(uint64(duration)))))
2019-10-12 07:21:28 +00:00
2019-09-13 21:00:36 +00:00
return nil
},
}
2019-11-08 01:45:45 +00:00
var clientListDeals = &cli.Command{
Name: "list-deals",
Usage: "List storage market deals",
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
deals, err := api.ClientListDeals(ctx)
if err != nil {
return err
}
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
fmt.Fprintf(w, "DealCid\tProvider\tState\tPieceRef\tSize\tPrice\tDuration\n")
for _, d := range deals {
fmt.Fprintf(w, "%s\t%s\t%s\t%x\t%d\t%s\t%d\n", d.ProposalCid, d.Provider, lapi.DealStates[d.State], d.PieceRef, d.Size, d.PricePerEpoch, d.Duration)
}
return w.Flush()
},
}