2019-08-20 16:48:33 +00:00
|
|
|
package full
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"os"
|
|
|
|
|
2019-09-06 22:39:47 +00:00
|
|
|
"github.com/ipfs/go-blockservice"
|
2019-08-20 16:48:33 +00:00
|
|
|
"github.com/ipfs/go-cid"
|
|
|
|
"github.com/ipfs/go-filestore"
|
|
|
|
chunker "github.com/ipfs/go-ipfs-chunker"
|
2019-09-06 22:39:47 +00:00
|
|
|
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
2019-08-20 16:48:33 +00:00
|
|
|
files "github.com/ipfs/go-ipfs-files"
|
|
|
|
cbor "github.com/ipfs/go-ipld-cbor"
|
|
|
|
ipld "github.com/ipfs/go-ipld-format"
|
2019-09-06 22:39:47 +00:00
|
|
|
"github.com/ipfs/go-merkledag"
|
2019-08-20 16:48:33 +00:00
|
|
|
"github.com/ipfs/go-unixfs/importer/balanced"
|
|
|
|
ihelper "github.com/ipfs/go-unixfs/importer/helpers"
|
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
"go.uber.org/fx"
|
2019-09-06 22:39:47 +00:00
|
|
|
|
|
|
|
"github.com/filecoin-project/go-lotus/api"
|
|
|
|
"github.com/filecoin-project/go-lotus/build"
|
|
|
|
"github.com/filecoin-project/go-lotus/chain/actors"
|
|
|
|
"github.com/filecoin-project/go-lotus/chain/address"
|
|
|
|
"github.com/filecoin-project/go-lotus/chain/deals"
|
|
|
|
"github.com/filecoin-project/go-lotus/chain/store"
|
|
|
|
"github.com/filecoin-project/go-lotus/chain/types"
|
|
|
|
"github.com/filecoin-project/go-lotus/node/modules/dtypes"
|
|
|
|
"github.com/filecoin-project/go-lotus/retrieval"
|
|
|
|
"github.com/filecoin-project/go-lotus/retrieval/discovery"
|
2019-08-20 16:48:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type ClientAPI struct {
|
|
|
|
fx.In
|
|
|
|
|
|
|
|
ChainAPI
|
2019-09-06 06:26:02 +00:00
|
|
|
StateAPI
|
2019-08-20 16:48:33 +00:00
|
|
|
WalletAPI
|
|
|
|
PaychAPI
|
|
|
|
|
2019-08-26 13:45:36 +00:00
|
|
|
DealClient *deals.Client
|
|
|
|
RetDiscovery discovery.PeerResolver
|
|
|
|
Retrieval *retrieval.Client
|
2019-09-06 22:39:47 +00:00
|
|
|
Chain *store.ChainStore
|
2019-08-20 16:48:33 +00:00
|
|
|
|
2019-08-26 13:45:36 +00:00
|
|
|
LocalDAG dtypes.ClientDAG
|
|
|
|
Blockstore dtypes.ClientBlockstore
|
|
|
|
Filestore dtypes.ClientFilestore `optional:"true"`
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ClientAPI) ClientStartDeal(ctx context.Context, data cid.Cid, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) {
|
|
|
|
// TODO: make this a param
|
|
|
|
self, err := a.WalletDefaultAddress(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// get miner peerID
|
|
|
|
msg := &types.Message{
|
|
|
|
To: miner,
|
|
|
|
From: miner,
|
|
|
|
Method: actors.MAMethods.GetPeerID,
|
|
|
|
}
|
|
|
|
|
2019-09-06 06:26:02 +00:00
|
|
|
r, err := a.StateCall(ctx, msg, nil)
|
2019-08-20 16:48:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
pid, err := peer.IDFromBytes(r.Return)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
vd, err := a.DealClient.VerifyParams(ctx, data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
voucherData, err := cbor.DumpObject(vd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// setup payments
|
|
|
|
total := types.BigMul(price, types.NewInt(blocksDuration))
|
|
|
|
|
|
|
|
// TODO: at least ping the miner before creating paych / locking the money
|
|
|
|
paych, paychMsg, err := a.paychCreate(ctx, self, miner, total)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-09-06 22:39:47 +00:00
|
|
|
head := a.Chain.GetHeaviestTipSet()
|
|
|
|
|
|
|
|
voucher := types.SignedVoucher{ // TODO: split into smaller payments
|
|
|
|
TimeLock: head.Height() + blocksDuration,
|
2019-08-20 16:48:33 +00:00
|
|
|
Extra: &types.ModVerifyParams{
|
|
|
|
Actor: miner,
|
|
|
|
Method: actors.MAMethods.PaymentVerifyInclusion,
|
|
|
|
Data: voucherData,
|
|
|
|
},
|
2019-09-06 22:39:47 +00:00
|
|
|
Lane: 0, // TODO: some api to make this easy
|
2019-08-20 16:48:33 +00:00
|
|
|
Amount: total,
|
2019-09-06 22:39:47 +00:00
|
|
|
MinCloseHeight: head.Height() + blocksDuration, // TODO: some way to start this after initial piece inclusion by actor? Using actors.PieceInclVoucherData?
|
2019-08-20 16:48:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sv, err := a.paychVoucherCreate(ctx, paych, voucher)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
proposal := deals.ClientDealProposal{
|
|
|
|
Data: data,
|
|
|
|
TotalPrice: total,
|
|
|
|
Duration: blocksDuration,
|
|
|
|
Payment: actors.PaymentInfo{
|
|
|
|
PayChActor: paych,
|
|
|
|
Payer: self,
|
|
|
|
ChannelMessage: paychMsg,
|
2019-09-06 22:39:47 +00:00
|
|
|
Vouchers: []*types.SignedVoucher{sv},
|
2019-08-20 16:48:33 +00:00
|
|
|
},
|
|
|
|
MinerAddress: miner,
|
|
|
|
ClientAddress: self,
|
|
|
|
MinerID: pid,
|
|
|
|
}
|
|
|
|
|
|
|
|
c, err := a.DealClient.Start(ctx, proposal, vd)
|
|
|
|
// TODO: send updated voucher with PaymentVerifySector for cheaper validation (validate the sector the miner sent us first!)
|
|
|
|
return &c, err
|
|
|
|
}
|
|
|
|
|
2019-08-26 13:45:36 +00:00
|
|
|
func (a *ClientAPI) ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error) {
|
|
|
|
// TODO: check if we have the ENTIRE dag
|
|
|
|
|
|
|
|
offExch := merkledag.NewDAGService(blockservice.New(a.Blockstore, offline.Exchange(a.Blockstore)))
|
|
|
|
_, err := offExch.Get(ctx, root)
|
|
|
|
if err == ipld.ErrNotFound {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2019-08-26 18:23:11 +00:00
|
|
|
func (a *ClientAPI) ClientFindData(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) {
|
2019-08-26 13:45:36 +00:00
|
|
|
peers, err := a.RetDiscovery.GetPeers(root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-08-26 18:23:11 +00:00
|
|
|
out := make([]api.QueryOffer, len(peers))
|
2019-08-26 13:45:36 +00:00
|
|
|
for k, p := range peers {
|
|
|
|
out[k] = a.Retrieval.Query(ctx, p, root)
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2019-08-20 16:48:33 +00:00
|
|
|
func (a *ClientAPI) ClientImport(ctx context.Context, path string) (cid.Cid, error) {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
stat, err := f.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := files.NewReaderPathFile(path, f, stat)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
bufferedDS := ipld.NewBufferedDAG(ctx, a.LocalDAG)
|
|
|
|
|
|
|
|
params := ihelper.DagBuilderParams{
|
2019-08-27 22:10:23 +00:00
|
|
|
Maxlinks: build.UnixfsLinksPerLevel,
|
2019-08-20 16:48:33 +00:00
|
|
|
RawLeaves: true,
|
|
|
|
CidBuilder: nil,
|
|
|
|
Dagserv: bufferedDS,
|
|
|
|
NoCopy: true,
|
|
|
|
}
|
|
|
|
|
2019-08-27 18:45:21 +00:00
|
|
|
db, err := params.New(chunker.NewSizeSplitter(file, int64(build.UnixfsChunkSize)))
|
2019-08-20 16:48:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
nd, err := balanced.Layout(db)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nd.Cid(), bufferedDS.Commit()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ClientAPI) ClientListImports(ctx context.Context) ([]api.Import, error) {
|
|
|
|
if a.Filestore == nil {
|
|
|
|
return nil, errors.New("listing imports is not supported with in-memory dag yet")
|
|
|
|
}
|
|
|
|
next, err := filestore.ListAll(a.Filestore, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: make this less very bad by tracking root cids instead of using ListAll
|
|
|
|
|
|
|
|
out := make([]api.Import, 0)
|
|
|
|
for {
|
|
|
|
r := next()
|
|
|
|
if r == nil {
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
if r.Offset != 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
out = append(out, api.Import{
|
|
|
|
Status: r.Status,
|
|
|
|
Key: r.Key,
|
|
|
|
FilePath: r.FilePath,
|
|
|
|
Size: r.Size,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-08-27 18:45:21 +00:00
|
|
|
|
2019-08-27 22:10:23 +00:00
|
|
|
func (a *ClientAPI) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, path string) error {
|
|
|
|
outFile, err := os.OpenFile(path, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0777)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = a.Retrieval.RetrieveUnixfs(ctx, order.Root, order.Size, order.MinerPeerID, order.Miner, outFile)
|
|
|
|
if err != nil {
|
|
|
|
_ = outFile.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return outFile.Close()
|
2019-08-27 18:45:21 +00:00
|
|
|
}
|