diff --git a/cmd/lotus-miner/market.go b/cmd/lotus-miner/market.go index be1c5fcd2..c32f44b6a 100644 --- a/cmd/lotus-miner/market.go +++ b/cmd/lotus-miner/market.go @@ -3,6 +3,7 @@ package main import ( "bufio" "context" + "encoding/json" "errors" "fmt" "io" @@ -28,6 +29,7 @@ import ( "github.com/filecoin-project/go-fil-markets/storagemarket" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" @@ -386,6 +388,11 @@ var dealsListCmd = &cli.Command{ Name: "list", Usage: "List all deals for this miner", Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "format", + Usage: "output format of data, supported: table, json", + Value: "table", + }, &cli.BoolFlag{ Name: "verbose", Aliases: []string{"v"}, @@ -396,63 +403,74 @@ var dealsListCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { - api, closer, err := lcli.GetMarketsAPI(cctx) - if err != nil { - return err + switch cctx.String("format") { + case "table": + return listDealsWithTable(cctx) + case "json": + return listDealsWithJSON(cctx) } - defer closer() - ctx := lcli.DaemonContext(cctx) + return fmt.Errorf("unknown format: %s; use `table` or `json`", cctx.String("format")) + }, +} - deals, err := api.MarketListIncompleteDeals(ctx) +func listDealsWithTable(cctx *cli.Context) error { + api, closer, err := lcli.GetMarketsAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.DaemonContext(cctx) + + deals, err := api.MarketListIncompleteDeals(ctx) + if err != nil { + return err + } + + verbose := cctx.Bool("verbose") + watch := cctx.Bool("watch") + + if watch { + updates, err := api.MarketGetDealUpdates(ctx) if err != nil { return err } - verbose := cctx.Bool("verbose") - watch := cctx.Bool("watch") + for { + tm.Clear() + tm.MoveCursor(1, 1) - if watch { - updates, err := api.MarketGetDealUpdates(ctx) + err = outputStorageDealsTable(tm.Output, deals, verbose) if err != nil { return err } - for { - tm.Clear() - tm.MoveCursor(1, 1) + tm.Flush() - err = outputStorageDeals(tm.Output, deals, verbose) - if err != nil { - return err + select { + case <-ctx.Done(): + return nil + case updated := <-updates: + var found bool + for i, existing := range deals { + if existing.ProposalCid.Equals(updated.ProposalCid) { + deals[i] = updated + found = true + break + } } - - tm.Flush() - - select { - case <-ctx.Done(): - return nil - case updated := <-updates: - var found bool - for i, existing := range deals { - if existing.ProposalCid.Equals(updated.ProposalCid) { - deals[i] = updated - found = true - break - } - } - if !found { - deals = append(deals, updated) - } + if !found { + deals = append(deals, updated) } } } + } - return outputStorageDeals(os.Stdout, deals, verbose) - }, + return outputStorageDealsTable(os.Stdout, deals, verbose) } -func outputStorageDeals(out io.Writer, deals []storagemarket.MinerDeal, verbose bool) error { +func outputStorageDealsTable(out io.Writer, deals []storagemarket.MinerDeal, verbose bool) error { sort.Slice(deals, func(i, j int) bool { return deals[i].CreationTime.Time().Before(deals[j].CreationTime.Time()) }) @@ -891,3 +909,76 @@ var dealsPendingPublish = &cli.Command{ return nil }, } + +func listDealsWithJSON(cctx *cli.Context) error { + node, closer, err := lcli.GetMarketsAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.DaemonContext(cctx) + + deals, err := node.MarketListIncompleteDeals(ctx) + if err != nil { + return err + } + + channels, err := node.MarketListDataTransfers(ctx) + if err != nil { + return err + } + + sort.Slice(deals, func(i, j int) bool { + return deals[i].CreationTime.Time().Before(deals[j].CreationTime.Time()) + }) + + channelsByTransferID := map[datatransfer.TransferID]api.DataTransferChannel{} + for _, c := range channels { + channelsByTransferID[c.TransferID] = c + } + + w := json.NewEncoder(os.Stdout) + + for _, deal := range deals { + val := struct { + DateTime string `json:"datetime"` + VerifiedDeal bool `json:"verified-deal"` + ProposalCID string `json:"proposal-cid"` + DealID abi.DealID `json:"deal-id"` + DealStatus string `json:"deal-status"` + Client string `json:"client"` + PieceSize string `json:"piece-size"` + Price types.FIL `json:"price"` + DurationEpochs abi.ChainEpoch `json:"duration-epochs"` + TransferID *datatransfer.TransferID `json:"transfer-id,omitempty"` + TransferStatus string `json:"transfer-status,omitempty"` + TransferredData string `json:"transferred-data,omitempty"` + }{} + + val.DateTime = deal.CreationTime.Time().Format(time.RFC3339) + val.VerifiedDeal = deal.Proposal.VerifiedDeal + val.ProposalCID = deal.ProposalCid.String() + val.DealID = deal.DealID + val.DealStatus = storagemarket.DealStates[deal.State] + val.Client = deal.Proposal.Client.String() + val.PieceSize = units.BytesSize(float64(deal.Proposal.PieceSize)) + val.Price = types.FIL(types.BigMul(deal.Proposal.StoragePricePerEpoch, types.NewInt(uint64(deal.Proposal.Duration())))) + val.DurationEpochs = deal.Proposal.Duration() + + if deal.TransferChannelId != nil { + if c, ok := channelsByTransferID[deal.TransferChannelId.ID]; ok { + val.TransferID = &c.TransferID + val.TransferStatus = datatransfer.Statuses[c.Status] + val.TransferredData = units.BytesSize(float64(c.Transferred)) + } + } + + err := w.Encode(val) + if err != nil { + return err + } + } + + return nil +} diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 7266a99ab..e9181cdda 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -658,9 +658,10 @@ USAGE: lotus-miner storage-deals list [command options] [arguments...] OPTIONS: - --verbose, -v (default: false) - --watch watch deal updates in real-time, rather than a one time list (default: false) - --help, -h show help (default: false) + --format value output format of data, supported: table, json (default: "table") + --verbose, -v (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --help, -h show help (default: false) ```