lotus/cli/deal/showdeals.go

272 lines
6.7 KiB
Go
Raw Normal View History

2021-03-04 15:56:49 +00:00
package deal
import (
"context"
"fmt"
"io"
"time"
tm "github.com/buger/goterm"
"github.com/fatih/color"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-state-types/abi"
lapi "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/lib/tablewriter"
"github.com/ipfs/go-cid"
term "github.com/nsf/termbox-go"
)
var mockDealInfos []lapi.DealInfo
func init() {
dummyCid, err := cid.Parse("bafkqaaa")
if err != nil {
panic(err)
}
mockDealStages := storagemarket.DealStages{
At: time.Now(),
Stages: []storagemarket.DealStage{{
Name: "Reserving Funds",
ExpectedDuration: "5 epochs",
StartedAt: time.Now().Add(-5 * time.Minute),
Logs: []string{
"Sending AddBalance message for 0.3FIL",
"Waiting 5 epochs (2:30) for confirmation",
"Funds for deal reserved successfully",
},
}, {
Name: "Sending Deal Proposal to Provider",
ExpectedDuration: "5 epochs",
StartedAt: time.Now().Add(-2 * time.Minute),
Logs: []string{
"Proposal: 512MB for 300 days @ 0.1FIL / GB / epoch",
"Deal proposal accepted by provider",
"Funds for deal reserved successfully",
},
}, {
Name: "Sending deal data to Provider",
ExpectedDuration: "13 minutes",
StartedAt: time.Now().Add(-53 * time.Second),
Logs: []string{
"Progress: 4:02 254MB / 1024MB (1MB / sec)",
"Connection to Provider f01234 disconnected, retrying in 8s",
},
}, {
Name: "Waiting for deal to be published by Provider",
ExpectedDuration: "several hours",
}, {
Name: "Waiting for pre-commit message from Provider",
ExpectedDuration: "several hours",
}, {
Name: "Waiting for prove-commit message from Provider",
ExpectedDuration: "several hours",
}},
}
mockDealInfos = []lapi.DealInfo{{
State: 3,
Message: "Reserving Client Funds",
DealStages: mockDealStages,
Provider: address.TestAddress,
DataRef: nil,
PieceCID: dummyCid,
Size: 512 * 1024 * 1024,
PricePerEpoch: abi.NewTokenAmount(10),
Duration: 300,
DealID: 10,
CreationTime: time.Now().Add(-5 * time.Second),
Verified: false,
TransferChannelID: nil,
DataTransfer: nil,
}, {
State: 5,
Message: "Publishing Deal",
DealStages: mockDealStages,
Provider: address.TestAddress,
DataRef: nil,
PieceCID: dummyCid,
Size: 482 * 1024 * 1024,
PricePerEpoch: abi.NewTokenAmount(12),
Duration: 323,
DealID: 14,
CreationTime: time.Now().Add(-23 * time.Minute),
Verified: false,
TransferChannelID: nil,
DataTransfer: nil,
}, {
State: 7,
Message: "Waiting for Pre-Commit",
DealStages: mockDealStages,
Provider: address.TestAddress,
DataRef: nil,
PieceCID: dummyCid,
Size: 2 * 1024 * 1024 * 1024,
PricePerEpoch: abi.NewTokenAmount(8),
Duration: 298,
DealID: 8,
CreationTime: time.Now().Add(-3 * time.Hour),
Verified: false,
TransferChannelID: nil,
DataTransfer: nil,
}, {
State: 2,
Message: "Transferring Data",
DealStages: mockDealStages,
Provider: address.TestAddress,
DataRef: nil,
PieceCID: dummyCid,
Size: 23 * 1024 * 1024,
PricePerEpoch: abi.NewTokenAmount(11),
Duration: 328,
DealID: 3,
CreationTime: time.Now().Add(-49 * time.Hour),
Verified: false,
TransferChannelID: nil,
DataTransfer: nil,
}}
}
func ShowDealsCmd(ctx context.Context, api lapi.FullNode) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
//localDeals, err := api.ClientListDeals(ctx)
//if err != nil {
// return err
//}
localDeals := mockDealInfos
return showDealsUX(ctx, localDeals)
}
func showDealsUX(ctx context.Context, deals []lapi.DealInfo) error {
err := term.Init()
if err != nil {
return err
}
defer term.Close()
renderer := dealRenderer{out: tm.Screen}
dealIdx := -1
state := "main"
highlighted := -1
for {
if err := ctx.Err(); err != nil {
return err
}
switch state {
case "main":
renderMain := func(hlite int) error {
tm.Clear()
tm.MoveCursor(1, 1)
err := renderer.renderDeals(deals, hlite)
if err != nil {
return err
}
tm.Flush()
return nil
}
err := renderMain(highlighted)
if err != nil {
return err
}
switch ev := term.PollEvent(); ev.Type {
case term.EventKey:
switch {
case ev.Ch == 'q', ev.Key == term.KeyEsc:
return nil
case ev.Key == term.KeyArrowUp:
term.Sync()
if highlighted > 0 {
highlighted--
}
case ev.Key == term.KeyArrowDown:
term.Sync()
highlighted++
case ev.Key == term.KeyEnter:
term.Sync()
dealIdx = highlighted
state = "deal"
}
case term.EventError:
return ev.Err
}
case "deal":
tm.Clear()
tm.MoveCursor(1, 1)
renderer.renderDeal(deals[dealIdx])
tm.Flush()
switch ev := term.PollEvent(); ev.Type {
case term.EventKey:
if ev.Ch == 'q' || ev.Key == term.KeyEsc || ev.Key == term.KeyEnter || ev.Key == term.KeyArrowLeft {
term.Sync()
state = "main"
}
case term.EventError:
return ev.Err
}
}
}
}
type dealRenderer struct {
out io.Writer
}
func (r *dealRenderer) renderDeals(deals []lapi.DealInfo, highlighted int) error {
tw := tablewriter.New(
tablewriter.Col(""),
tablewriter.Col("Created"),
tablewriter.Col("Provider"),
tablewriter.Col("Size"),
tablewriter.Col("State"),
)
for i, di := range deals {
lineNum := fmt.Sprintf("%d", i+1)
cols := map[string]interface{}{
"": lineNum,
"Created": time.Since(di.CreationTime).Round(time.Second),
"Provider": di.Provider,
"Size": di.Size,
"State": di.Message,
}
if i == highlighted {
for k, v := range cols {
cols[k] = color.YellowString(fmt.Sprint(v))
}
}
tw.Write(cols)
}
return tw.Flush(r.out)
}
func (r *dealRenderer) renderDeal(di lapi.DealInfo) error {
_, err := fmt.Fprintf(r.out, "Deal %d\n", di.DealID)
if err != nil {
return err
}
for _, stg := range di.DealStages.Stages {
msg := fmt.Sprintf("%s (%s)", stg.Name, stg.ExpectedDuration)
if stg.StartedAt.IsZero() {
msg = color.YellowString(msg)
}
_, err := fmt.Fprintf(r.out, "%s\n", msg)
if err != nil {
return err
}
for _, l := range stg.Logs {
_, err = fmt.Fprintf(r.out, " %s\n", l)
if err != nil {
return err
}
}
}
return nil
}