Skip to content

Commit

Permalink
Merge pull request #4497 from filecoin-project/feat/lite-market-clien…
Browse files Browse the repository at this point in the history
…t-cli

lotus-lite: CLI tests for `lotus client` commands
  • Loading branch information
magik6k committed Oct 23, 2020
2 parents 92942d4 + 906286f commit 1ec8252
Show file tree
Hide file tree
Showing 15 changed files with 378 additions and 159 deletions.
1 change: 1 addition & 0 deletions api/api_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ type GatewayAPI interface {
StateMarketStorageDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*MarketDeal, error)
StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (miner.MinerInfo, error)
StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error)
StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*MsgLookup, error)
}
5 changes: 5 additions & 0 deletions api/apistruct/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ type GatewayStruct struct {
StateMarketBalance func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MarketBalance, error)
StateMarketStorageDeal func(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*api.MarketDeal, error)
StateNetworkVersion func(ctx context.Context, tsk types.TipSetKey) (stnetwork.Version, error)
StateVerifiedClientStatus func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
StateWaitMsg func(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
}
}
Expand Down Expand Up @@ -1547,6 +1548,10 @@ func (g GatewayStruct) StateNetworkVersion(ctx context.Context, tsk types.TipSet
return g.Internal.StateNetworkVersion(ctx, tsk)
}

func (g GatewayStruct) StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error) {
return g.Internal.StateVerifiedClientStatus(ctx, addr, tsk)
}

func (g GatewayStruct) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) {
return g.Internal.StateWaitMsg(ctx, msg, confidence)
}
Expand Down
38 changes: 23 additions & 15 deletions api/test/deals.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,21 +106,7 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
}

func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode, miner TestStorageNode, carExport, fastRet bool) {
data := make([]byte, 1600)
rand.New(rand.NewSource(int64(rseed))).Read(data)

dir, err := ioutil.TempDir(os.TempDir(), "test-make-deal-")
if err != nil {
t.Fatal(err)
}

path := filepath.Join(dir, "sourcefile.dat")
err = ioutil.WriteFile(path, data, 0644)
if err != nil {
t.Fatal(err)
}

res, err := client.ClientImport(ctx, api.FileRef{Path: path})
res, data, err := CreateClientFile(ctx, client, rseed)
if err != nil {
t.Fatal(err)
}
Expand All @@ -141,6 +127,28 @@ func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode,
testRetrieval(t, ctx, client, fcid, &info.PieceCID, carExport, data)
}

func CreateClientFile(ctx context.Context, client api.FullNode, rseed int) (*api.ImportRes, []byte, error) {
data := make([]byte, 1600)
rand.New(rand.NewSource(int64(rseed))).Read(data)

dir, err := ioutil.TempDir(os.TempDir(), "test-make-deal-")
if err != nil {
return nil, nil, err
}

path := filepath.Join(dir, "sourcefile.dat")
err = ioutil.WriteFile(path, data, 0644)
if err != nil {
return nil, nil, err
}

res, err := client.ClientImport(ctx, api.FileRef{Path: path})
if err != nil {
return nil, nil, err
}
return res, data, nil
}

func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {

ctx := context.Background()
Expand Down
74 changes: 39 additions & 35 deletions cli/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ var clientDealCmd = &cli.Command{
}
defer closer()
ctx := ReqContext(cctx)
afmt := NewAppFmt(cctx.App)

if cctx.NArg() != 4 {
return xerrors.New("expected 4 args: dataCid, miner, price, duration")
Expand Down Expand Up @@ -462,7 +463,7 @@ var clientDealCmd = &cli.Command{
return err
}

fmt.Println(encoder.Encode(*proposal))
afmt.Println(encoder.Encode(*proposal))

return nil
},
Expand All @@ -477,6 +478,7 @@ func interactiveDeal(cctx *cli.Context) error {
ctx := ReqContext(cctx)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
afmt := NewAppFmt(cctx.App)

state := "import"
gib := types.NewInt(1 << 30)
Expand Down Expand Up @@ -517,10 +519,10 @@ func interactiveDeal(cctx *cli.Context) error {
}

printErr := func(err error) {
fmt.Printf("%s %s\n", color.RedString("Error:"), err.Error())
afmt.Printf("%s %s\n", color.RedString("Error:"), err.Error())
}

cs := readline.NewCancelableStdin(os.Stdin)
cs := readline.NewCancelableStdin(afmt.Stdin)
go func() {
<-ctx.Done()
cs.Close() // nolint:errcheck
Expand All @@ -537,7 +539,7 @@ uiLoop:

switch state {
case "import":
fmt.Print("Data CID (from " + color.YellowString("lotus client import") + "): ")
afmt.Print("Data CID (from " + color.YellowString("lotus client import") + "): ")

_cidStr, _, err := rl.ReadLine()
cidStr := string(_cidStr)
Expand All @@ -560,7 +562,7 @@ uiLoop:

state = "duration"
case "duration":
fmt.Print("Deal duration (days): ")
afmt.Print("Deal duration (days): ")

_daystr, _, err := rl.ReadLine()
daystr := string(_daystr)
Expand Down Expand Up @@ -605,7 +607,7 @@ uiLoop:
continue
}

fmt.Print("\nMake this a verified deal? (yes/no): ")
afmt.Print("\nMake this a verified deal? (yes/no): ")

_yn, _, err := rl.ReadLine()
yn := string(_yn)
Expand All @@ -619,13 +621,13 @@ uiLoop:
case "no":
verified = false
default:
fmt.Println("Type in full 'yes' or 'no'")
afmt.Println("Type in full 'yes' or 'no'")
continue
}

state = "miner"
case "miner":
fmt.Print("Miner Addresses (f0.. f0..), none to find: ")
afmt.Print("Miner Addresses (f0.. f0..), none to find: ")

_maddrsStr, _, err := rl.ReadLine()
maddrsStr := string(_maddrsStr)
Expand Down Expand Up @@ -664,11 +666,11 @@ uiLoop:
candidateAsks = append(candidateAsks, ask)
}

fmt.Printf("Found %d candidate asks\n", len(candidateAsks))
afmt.Printf("Found %d candidate asks\n", len(candidateAsks))
state = "find-budget"
case "find-budget":
fmt.Printf("Proposing from %s, Current Balance: %s\n", a, types.FIL(fromBal))
fmt.Print("Maximum budget (FIL): ") // TODO: Propose some default somehow?
afmt.Printf("Proposing from %s, Current Balance: %s\n", a, types.FIL(fromBal))
afmt.Print("Maximum budget (FIL): ") // TODO: Propose some default somehow?

_budgetStr, _, err := rl.ReadLine()
budgetStr := string(_budgetStr)
Expand Down Expand Up @@ -698,10 +700,10 @@ uiLoop:
}
}
candidateAsks = goodAsks
fmt.Printf("%d asks within budget\n", len(candidateAsks))
afmt.Printf("%d asks within budget\n", len(candidateAsks))
state = "find-count"
case "find-count":
fmt.Print("Deals to make (1): ")
afmt.Print("Deals to make (1): ")
dealcStr, _, err := rl.ReadLine()
if err != nil {
printErr(xerrors.Errorf("reading deal count: %w", err))
Expand Down Expand Up @@ -780,12 +782,12 @@ uiLoop:
case "confirm":
// TODO: do some more or epochs math (round to miner PP, deal start buffer)

fmt.Printf("-----\n")
fmt.Printf("Proposing from %s\n", a)
fmt.Printf("\tBalance: %s\n", types.FIL(fromBal))
fmt.Printf("\n")
fmt.Printf("Piece size: %s (Payload size: %s)\n", units.BytesSize(float64(ds.PieceSize)), units.BytesSize(float64(ds.PayloadSize)))
fmt.Printf("Duration: %s\n", dur)
afmt.Printf("-----\n")
afmt.Printf("Proposing from %s\n", a)
afmt.Printf("\tBalance: %s\n", types.FIL(fromBal))
afmt.Printf("\n")
afmt.Printf("Piece size: %s (Payload size: %s)\n", units.BytesSize(float64(ds.PieceSize)), units.BytesSize(float64(ds.PayloadSize)))
afmt.Printf("Duration: %s\n", dur)

pricePerGib := big.Zero()
for _, a := range ask {
Expand All @@ -804,20 +806,20 @@ uiLoop:

if len(ask) > 1 {
totalPrice := types.BigMul(epochPrice, types.NewInt(uint64(epochs)))
fmt.Printf("Miner %s (Power:%s) price: ~%s (%s per epoch)\n", color.YellowString(a.Miner.String()), color.GreenString(types.SizeStr(mpow.MinerPower.QualityAdjPower)), color.BlueString(types.FIL(totalPrice).String()), types.FIL(epochPrice))
afmt.Printf("Miner %s (Power:%s) price: ~%s (%s per epoch)\n", color.YellowString(a.Miner.String()), color.GreenString(types.SizeStr(mpow.MinerPower.QualityAdjPower)), color.BlueString(types.FIL(totalPrice).String()), types.FIL(epochPrice))
}
}

// TODO: price is based on PaddedPieceSize, right?
epochPrice := types.BigDiv(types.BigMul(pricePerGib, types.NewInt(uint64(ds.PieceSize))), gib)
totalPrice := types.BigMul(epochPrice, types.NewInt(uint64(epochs)))

fmt.Printf("Total price: ~%s (%s per epoch)\n", color.CyanString(types.FIL(totalPrice).String()), types.FIL(epochPrice))
fmt.Printf("Verified: %v\n", verified)
afmt.Printf("Total price: ~%s (%s per epoch)\n", color.CyanString(types.FIL(totalPrice).String()), types.FIL(epochPrice))
afmt.Printf("Verified: %v\n", verified)

state = "accept"
case "accept":
fmt.Print("\nAccept (yes/no): ")
afmt.Print("\nAccept (yes/no): ")

_yn, _, err := rl.ReadLine()
yn := string(_yn)
Expand All @@ -830,7 +832,7 @@ uiLoop:
}

if yn != "yes" {
fmt.Println("Type in full 'yes' or 'no'")
afmt.Println("Type in full 'yes' or 'no'")
continue
}

Expand Down Expand Up @@ -861,7 +863,7 @@ uiLoop:
return err
}

fmt.Printf("Deal (%s) CID: %s\n", maddr, color.GreenString(encoder.Encode(*proposal)))
afmt.Printf("Deal (%s) CID: %s\n", maddr, color.GreenString(encoder.Encode(*proposal)))
}

return nil
Expand Down Expand Up @@ -975,6 +977,7 @@ var clientRetrieveCmd = &cli.Command{
}
defer closer()
ctx := ReqContext(cctx)
afmt := NewAppFmt(cctx.App)

var payer address.Address
if cctx.String("from") != "" {
Expand Down Expand Up @@ -1083,14 +1086,14 @@ var clientRetrieveCmd = &cli.Command{
select {
case evt, ok := <-updates:
if ok {
fmt.Printf("> Recv: %s, Paid %s, %s (%s)\n",
afmt.Printf("> Recv: %s, Paid %s, %s (%s)\n",
types.SizeStr(types.NewInt(evt.BytesReceived)),
types.FIL(evt.FundsSpent),
retrievalmarket.ClientEvents[evt.Event],
retrievalmarket.DealStatuses[evt.Status],
)
} else {
fmt.Println("Success")
afmt.Println("Success")
return nil
}

Expand Down Expand Up @@ -1269,8 +1272,9 @@ var clientQueryAskCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
if cctx.NArg() != 1 {
fmt.Println("Usage: query-ask [minerAddress]")
afmt.Println("Usage: query-ask [minerAddress]")
return nil
}

Expand Down Expand Up @@ -1311,23 +1315,23 @@ var clientQueryAskCmd = &cli.Command{
return err
}

fmt.Printf("Ask: %s\n", maddr)
fmt.Printf("Price per GiB: %s\n", types.FIL(ask.Price))
fmt.Printf("Verified Price per GiB: %s\n", types.FIL(ask.VerifiedPrice))
fmt.Printf("Max Piece size: %s\n", types.SizeStr(types.NewInt(uint64(ask.MaxPieceSize))))
afmt.Printf("Ask: %s\n", maddr)
afmt.Printf("Price per GiB: %s\n", types.FIL(ask.Price))
afmt.Printf("Verified Price per GiB: %s\n", types.FIL(ask.VerifiedPrice))
afmt.Printf("Max Piece size: %s\n", types.SizeStr(types.NewInt(uint64(ask.MaxPieceSize))))

size := cctx.Int64("size")
if size == 0 {
return nil
}
perEpoch := types.BigDiv(types.BigMul(ask.Price, types.NewInt(uint64(size))), types.NewInt(1<<30))
fmt.Printf("Price per Block: %s\n", types.FIL(perEpoch))
afmt.Printf("Price per Block: %s\n", types.FIL(perEpoch))

duration := cctx.Int64("duration")
if duration == 0 {
return nil
}
fmt.Printf("Total Price: %s\n", types.FIL(types.BigMul(perEpoch, types.NewInt(uint64(duration)))))
afmt.Printf("Total Price: %s\n", types.FIL(types.BigMul(perEpoch, types.NewInt(uint64(duration)))))

return nil
},
Expand Down Expand Up @@ -1410,7 +1414,7 @@ var clientListDeals = &cli.Command{
}
}

return outputStorageDeals(ctx, os.Stdout, api, localDeals, cctx.Bool("verbose"), cctx.Bool("color"), showFailed)
return outputStorageDeals(ctx, cctx.App.Writer, api, localDeals, cctx.Bool("verbose"), cctx.Bool("color"), showFailed)
},
}

Expand Down
22 changes: 22 additions & 0 deletions cli/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cli

import (
"context"
"os"
"testing"
"time"

clitest "github.com/filecoin-project/lotus/cli/test"
)

// TestClient does a basic test to exercise the client CLI
// commands
func TestClient(t *testing.T) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
clitest.QuietMiningLogs()

blocktime := 5 * time.Millisecond
ctx := context.Background()
clientNode, _ := clitest.StartOneNodeOneMiner(ctx, t, blocktime)
clitest.RunClientTest(t, Commands, clientNode)
}
Loading

0 comments on commit 1ec8252

Please sign in to comment.