client cli: Interactive deal command
This commit is contained in:
parent
21d15d3151
commit
b6c9169a87
@ -227,6 +227,8 @@ type FullNode interface {
|
|||||||
ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error)
|
ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error)
|
||||||
// ClientGenCar generates a CAR file for the specified file.
|
// ClientGenCar generates a CAR file for the specified file.
|
||||||
ClientGenCar(ctx context.Context, ref FileRef, outpath string) error
|
ClientGenCar(ctx context.Context, ref FileRef, outpath string) error
|
||||||
|
// ClientDealSize calculates real deal data size
|
||||||
|
ClientDealSize(ctx context.Context, root cid.Cid) (DataSize, error)
|
||||||
|
|
||||||
// ClientUnimport removes references to the specified file from filestore
|
// ClientUnimport removes references to the specified file from filestore
|
||||||
//ClientUnimport(path string)
|
//ClientUnimport(path string)
|
||||||
@ -666,6 +668,11 @@ type BlockTemplate struct {
|
|||||||
WinningPoStProof []abi.PoStProof
|
WinningPoStProof []abi.PoStProof
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DataSize struct {
|
||||||
|
PayloadSize int64
|
||||||
|
PieceSize abi.PaddedPieceSize
|
||||||
|
}
|
||||||
|
|
||||||
type CommPRet struct {
|
type CommPRet struct {
|
||||||
Root cid.Cid
|
Root cid.Cid
|
||||||
Size abi.UnpaddedPieceSize
|
Size abi.UnpaddedPieceSize
|
||||||
|
@ -129,6 +129,7 @@ type FullNodeStruct struct {
|
|||||||
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
|
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"`
|
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"`
|
ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"`
|
||||||
|
ClientDealSize func(ctx context.Context, root cid.Cid) (api.DataSize, error) `perm:"read"`
|
||||||
|
|
||||||
StateNetworkName func(context.Context) (dtypes.NetworkName, error) `perm:"read"`
|
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"`
|
StateMinerSectors func(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||||
@ -418,6 +419,10 @@ func (c *FullNodeStruct) ClientGenCar(ctx context.Context, ref api.FileRef, outp
|
|||||||
return c.Internal.ClientGenCar(ctx, ref, outpath)
|
return c.Internal.ClientGenCar(ctx, ref, outpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FullNodeStruct) ClientDealSize(ctx context.Context, root cid.Cid) (api.DataSize, error) {
|
||||||
|
return c.Internal.ClientDealSize(ctx, root)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *FullNodeStruct) GasEstimateGasPrice(ctx context.Context, nblocksincl uint64,
|
func (c *FullNodeStruct) GasEstimateGasPrice(ctx context.Context, nblocksincl uint64,
|
||||||
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||||
return c.Internal.GasEstimateGasPrice(ctx, nblocksincl, sender, gaslimit, tsk)
|
return c.Internal.GasEstimateGasPrice(ctx, nblocksincl, sender, gaslimit, tsk)
|
||||||
|
200
cli/client.go
200
cli/client.go
@ -3,13 +3,14 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/filecoin-project/lotus/build"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/go-units"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
"github.com/ipfs/go-cidutil/cidenc"
|
"github.com/ipfs/go-cidutil/cidenc"
|
||||||
@ -22,10 +23,12 @@ import (
|
|||||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||||
"github.com/filecoin-project/go-multistore"
|
"github.com/filecoin-project/go-multistore"
|
||||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||||
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
lapi "github.com/filecoin-project/lotus/api"
|
lapi "github.com/filecoin-project/lotus/api"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -314,6 +317,10 @@ var clientDealCmd = &cli.Command{
|
|||||||
&CidBaseFlag,
|
&CidBaseFlag,
|
||||||
},
|
},
|
||||||
Action: func(cctx *cli.Context) error {
|
Action: func(cctx *cli.Context) error {
|
||||||
|
if !cctx.Args().Present() {
|
||||||
|
return interactiveDeal(cctx)
|
||||||
|
}
|
||||||
|
|
||||||
api, closer, err := GetFullNodeAPI(cctx)
|
api, closer, err := GetFullNodeAPI(cctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -435,6 +442,197 @@ var clientDealCmd = &cli.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func interactiveDeal(cctx *cli.Context) error {
|
||||||
|
api, closer, err := GetFullNodeAPI(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer closer()
|
||||||
|
ctx := ReqContext(cctx)
|
||||||
|
|
||||||
|
state := "import"
|
||||||
|
|
||||||
|
var data cid.Cid
|
||||||
|
var days int
|
||||||
|
var maddr address.Address
|
||||||
|
var ask storagemarket.StorageAsk
|
||||||
|
var epochPrice big.Int
|
||||||
|
var epochs abi.ChainEpoch
|
||||||
|
|
||||||
|
var a address.Address
|
||||||
|
if from := cctx.String("from"); from != "" {
|
||||||
|
faddr, err := address.NewFromString(from)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to parse 'from' address: %w", err)
|
||||||
|
}
|
||||||
|
a = faddr
|
||||||
|
} else {
|
||||||
|
def, err := api.WalletDefaultAddress(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a = def
|
||||||
|
}
|
||||||
|
|
||||||
|
printErr := func(err error) {
|
||||||
|
fmt.Printf("%s %s\n", color.RedString("Error:"), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
// TODO: better exit handling
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case "import":
|
||||||
|
fmt.Print("Data CID (from " + color.YellowString("lotus client import") + "): ")
|
||||||
|
|
||||||
|
var cidStr string
|
||||||
|
_, err := fmt.Scan(&cidStr)
|
||||||
|
if err != nil {
|
||||||
|
printErr(xerrors.Errorf("reading cid string: %w", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err = cid.Parse(cidStr)
|
||||||
|
if err != nil {
|
||||||
|
printErr(xerrors.Errorf("parsing cid string: %w", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
state = "duration"
|
||||||
|
case "duration":
|
||||||
|
fmt.Print("Deal duration (days): ")
|
||||||
|
|
||||||
|
_, err := fmt.Scan(&days)
|
||||||
|
if err != nil {
|
||||||
|
printErr(xerrors.Errorf("parsing duration: %w", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
state = "miner"
|
||||||
|
case "miner":
|
||||||
|
fmt.Print("Miner Address (t0..): ")
|
||||||
|
var maddrStr string
|
||||||
|
|
||||||
|
_, err := fmt.Scan(&maddrStr)
|
||||||
|
if err != nil {
|
||||||
|
printErr(xerrors.Errorf("reading miner address: %w", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
maddr, err = address.NewFromString(maddrStr)
|
||||||
|
if err != nil {
|
||||||
|
printErr(xerrors.Errorf("parsing miner address: %w", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
state = "query"
|
||||||
|
case "query":
|
||||||
|
color.Blue(".. querying miner ask")
|
||||||
|
|
||||||
|
mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK)
|
||||||
|
if err != nil {
|
||||||
|
printErr(xerrors.Errorf("failed to get peerID for miner: %w", err))
|
||||||
|
state = "miner"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := api.ClientQueryAsk(ctx, mi.PeerId, maddr)
|
||||||
|
if err != nil {
|
||||||
|
printErr(xerrors.Errorf("failed to query ask: %w", err))
|
||||||
|
state = "miner"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ask = *a.Ask
|
||||||
|
|
||||||
|
// TODO: run more validation
|
||||||
|
state = "confirm"
|
||||||
|
case "confirm":
|
||||||
|
fromBal, err := api.WalletBalance(ctx, a)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("checking from address balance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
color.Blue(".. calculating data size\n")
|
||||||
|
ds, err := api.ClientDealSize(ctx, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dur := 24 * time.Hour * time.Duration(days)
|
||||||
|
|
||||||
|
epochs = abi.ChainEpoch(dur / (time.Duration(build.BlockDelaySecs) * time.Second))
|
||||||
|
// TODO: do some more or epochs math (round to miner PP, deal start buffer)
|
||||||
|
|
||||||
|
gib := types.NewInt(1 << 30)
|
||||||
|
|
||||||
|
// TODO: price is based on PaddedPieceSize, right?
|
||||||
|
epochPrice = types.BigDiv(types.BigMul(ask.Price, types.NewInt(uint64(ds.PieceSize))), gib)
|
||||||
|
totalPrice := types.BigMul(epochPrice, types.NewInt(uint64(epochs)))
|
||||||
|
|
||||||
|
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)
|
||||||
|
fmt.Printf("Total price: ~%s (%s per epoch)\n", types.FIL(totalPrice), types.FIL(epochPrice))
|
||||||
|
|
||||||
|
state = "accept"
|
||||||
|
case "accept":
|
||||||
|
fmt.Print("\nAccept (yes/no): ")
|
||||||
|
|
||||||
|
var yn string
|
||||||
|
_, err := fmt.Scan(&yn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if yn == "no" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if yn != "yes" {
|
||||||
|
fmt.Println("Type in full 'yes' or 'no'")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
state = "execute"
|
||||||
|
case "execute":
|
||||||
|
color.Blue(".. executing")
|
||||||
|
proposal, err := api.ClientStartDeal(ctx, &lapi.StartDealParams{
|
||||||
|
Data: &storagemarket.DataRef{
|
||||||
|
TransferType: storagemarket.TTGraphsync,
|
||||||
|
Root: data,
|
||||||
|
},
|
||||||
|
Wallet: a,
|
||||||
|
Miner: maddr,
|
||||||
|
EpochPrice: epochPrice,
|
||||||
|
MinBlocksDuration: uint64(epochs),
|
||||||
|
DealStartEpoch: abi.ChainEpoch(cctx.Int64("start-epoch")),
|
||||||
|
FastRetrieval: cctx.Bool("fast-retrieval"),
|
||||||
|
VerifiedDeal: false, // TODO: Allow setting
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder, err := GetCidEncoder(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\nDeal CID:", color.GreenString(encoder.Encode(*proposal)))
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return xerrors.Errorf("unknown state: %s", state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var clientFindCmd = &cli.Command{
|
var clientFindCmd = &cli.Command{
|
||||||
Name: "find",
|
Name: "find",
|
||||||
Usage: "Find data in the network",
|
Usage: "Find data in the network",
|
||||||
|
1
go.mod
1
go.mod
@ -28,6 +28,7 @@ require (
|
|||||||
github.com/filecoin-project/go-fil-markets v0.5.3-0.20200730194453-26fac2c92927
|
github.com/filecoin-project/go-fil-markets v0.5.3-0.20200730194453-26fac2c92927
|
||||||
github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24
|
github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24
|
||||||
github.com/filecoin-project/go-multistore v0.0.2
|
github.com/filecoin-project/go-multistore v0.0.2
|
||||||
|
github.com/filecoin-project/go-padreader v0.0.0-20200210211231-548257017ca6
|
||||||
github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261
|
github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261
|
||||||
github.com/filecoin-project/go-statestore v0.1.0
|
github.com/filecoin-project/go-statestore v0.1.0
|
||||||
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
|
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
|
||||||
|
@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/filecoin-project/go-fil-markets/shared"
|
"github.com/filecoin-project/go-fil-markets/shared"
|
||||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||||
"github.com/filecoin-project/go-multistore"
|
"github.com/filecoin-project/go-multistore"
|
||||||
|
"github.com/filecoin-project/go-padreader"
|
||||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||||
@ -69,7 +70,7 @@ type API struct {
|
|||||||
|
|
||||||
Imports dtypes.ClientImportMgr
|
Imports dtypes.ClientImportMgr
|
||||||
|
|
||||||
RetBstore dtypes.ClientBlockstore // TODO: try to remove
|
CombinedBstore dtypes.ClientBlockstore // TODO: try to remove
|
||||||
}
|
}
|
||||||
|
|
||||||
func calcDealExpiration(minDuration uint64, md *miner.DeadlineInfo, startEpoch abi.ChainEpoch) abi.ChainEpoch {
|
func calcDealExpiration(minDuration uint64, md *miner.DeadlineInfo, startEpoch abi.ChainEpoch) abi.ChainEpoch {
|
||||||
@ -554,6 +555,31 @@ func (a *API) ClientCalcCommP(ctx context.Context, inpath string, miner address.
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type lenWriter int64
|
||||||
|
|
||||||
|
func (w *lenWriter) Write(p []byte) (n int, err error) {
|
||||||
|
*w += lenWriter(len(p))
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) ClientDealSize(ctx context.Context, root cid.Cid) (api.DataSize, error) {
|
||||||
|
dag := merkledag.NewDAGService(blockservice.New(a.CombinedBstore, offline.Exchange(a.CombinedBstore)))
|
||||||
|
|
||||||
|
w := lenWriter(0)
|
||||||
|
|
||||||
|
err := car.WriteCar(ctx, dag, []cid.Cid{root}, &w)
|
||||||
|
if err != nil {
|
||||||
|
return api.DataSize{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
up := padreader.PaddedSize(uint64(w))
|
||||||
|
|
||||||
|
return api.DataSize{
|
||||||
|
PayloadSize: int64(w),
|
||||||
|
PieceSize: up.Padded(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *API) ClientGenCar(ctx context.Context, ref api.FileRef, outputPath string) error {
|
func (a *API) ClientGenCar(ctx context.Context, ref api.FileRef, outputPath string) error {
|
||||||
id, st, err := a.imgr().NewStore()
|
id, st, err := a.imgr().NewStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,7 +2,6 @@ package modules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/funds"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-multistore"
|
"github.com/filecoin-project/go-multistore"
|
||||||
@ -19,6 +18,7 @@ import (
|
|||||||
rmnet "github.com/filecoin-project/go-fil-markets/retrievalmarket/network"
|
rmnet "github.com/filecoin-project/go-fil-markets/retrievalmarket/network"
|
||||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||||
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
|
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
|
||||||
|
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/funds"
|
||||||
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
|
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
|
||||||
smnet "github.com/filecoin-project/go-fil-markets/storagemarket/network"
|
smnet "github.com/filecoin-project/go-fil-markets/storagemarket/network"
|
||||||
"github.com/filecoin-project/go-statestore"
|
"github.com/filecoin-project/go-statestore"
|
||||||
@ -64,7 +64,7 @@ func ClientImportMgr(mds dtypes.ClientMultiDstore, ds dtypes.MetadataDS) dtypes.
|
|||||||
|
|
||||||
func ClientBlockstore(imgr dtypes.ClientImportMgr) dtypes.ClientBlockstore {
|
func ClientBlockstore(imgr dtypes.ClientImportMgr) dtypes.ClientBlockstore {
|
||||||
// in most cases this is now unused in normal operations -- however, it's important to preserve for the IPFS use case
|
// in most cases this is now unused in normal operations -- however, it's important to preserve for the IPFS use case
|
||||||
return blockstore.NewTemporary()
|
return blockstore.WrapIDStore(imgr.Blockstore)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterClientValidator is an initialization hook that registers the client
|
// RegisterClientValidator is an initialization hook that registers the client
|
||||||
|
Loading…
Reference in New Issue
Block a user