Merge pull request #5961 from filecoin-project/feat/stateless-offline-dealflow
Introduce stateless offline dealflow, bypassing the FSM/deallists
This commit is contained in:
commit
9a6e601754
@ -323,6 +323,8 @@ type FullNode interface {
|
|||||||
ClientRemoveImport(ctx context.Context, importID multistore.StoreID) error //perm:admin
|
ClientRemoveImport(ctx context.Context, importID multistore.StoreID) error //perm:admin
|
||||||
// ClientStartDeal proposes a deal with a miner.
|
// ClientStartDeal proposes a deal with a miner.
|
||||||
ClientStartDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error) //perm:admin
|
ClientStartDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error) //perm:admin
|
||||||
|
// ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking.
|
||||||
|
ClientStatelessDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error) //perm:write
|
||||||
// ClientGetDealInfo returns the latest information about a given deal.
|
// ClientGetDealInfo returns the latest information about a given deal.
|
||||||
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error) //perm:read
|
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error) //perm:read
|
||||||
// ClientListDeals returns information about the deals made by the local client.
|
// ClientListDeals returns information about the deals made by the local client.
|
||||||
|
@ -771,6 +771,21 @@ func (mr *MockFullNodeMockRecorder) ClientStartDeal(arg0, arg1 interface{}) *gom
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientStartDeal", reflect.TypeOf((*MockFullNode)(nil).ClientStartDeal), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientStartDeal", reflect.TypeOf((*MockFullNode)(nil).ClientStartDeal), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientStatelessDeal mocks base method
|
||||||
|
func (m *MockFullNode) ClientStatelessDeal(arg0 context.Context, arg1 *api.StartDealParams) (*cid.Cid, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ClientStatelessDeal", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*cid.Cid)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientStatelessDeal indicates an expected call of ClientStatelessDeal
|
||||||
|
func (mr *MockFullNodeMockRecorder) ClientStatelessDeal(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientStatelessDeal", reflect.TypeOf((*MockFullNode)(nil).ClientStatelessDeal), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// Closing mocks base method
|
// Closing mocks base method
|
||||||
func (m *MockFullNode) Closing(arg0 context.Context) (<-chan struct{}, error) {
|
func (m *MockFullNode) Closing(arg0 context.Context) (<-chan struct{}, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
@ -205,6 +205,8 @@ type FullNodeStruct struct {
|
|||||||
|
|
||||||
ClientStartDeal func(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) `perm:"admin"`
|
ClientStartDeal func(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) `perm:"admin"`
|
||||||
|
|
||||||
|
ClientStatelessDeal func(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) `perm:"write"`
|
||||||
|
|
||||||
CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"`
|
CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"`
|
||||||
|
|
||||||
GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||||
@ -1395,6 +1397,14 @@ func (s *FullNodeStub) ClientStartDeal(p0 context.Context, p1 *StartDealParams)
|
|||||||
return nil, xerrors.New("method not supported")
|
return nil, xerrors.New("method not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *FullNodeStruct) ClientStatelessDeal(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) {
|
||||||
|
return s.Internal.ClientStatelessDeal(p0, p1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FullNodeStub) ClientStatelessDeal(p0 context.Context, p1 *StartDealParams) (*cid.Cid, error) {
|
||||||
|
return nil, xerrors.New("method not supported")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *FullNodeStruct) CreateBackup(p0 context.Context, p1 string) error {
|
func (s *FullNodeStruct) CreateBackup(p0 context.Context, p1 string) error {
|
||||||
return s.Internal.CreateBackup(p0, p1)
|
return s.Internal.CreateBackup(p0, p1)
|
||||||
}
|
}
|
||||||
|
@ -304,6 +304,8 @@ type FullNode interface {
|
|||||||
ClientRemoveImport(ctx context.Context, importID multistore.StoreID) error //perm:admin
|
ClientRemoveImport(ctx context.Context, importID multistore.StoreID) error //perm:admin
|
||||||
// ClientStartDeal proposes a deal with a miner.
|
// ClientStartDeal proposes a deal with a miner.
|
||||||
ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) //perm:admin
|
ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) //perm:admin
|
||||||
|
// ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking.
|
||||||
|
ClientStatelessDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) //perm:write
|
||||||
// ClientGetDealInfo returns the latest information about a given deal.
|
// ClientGetDealInfo returns the latest information about a given deal.
|
||||||
ClientGetDealInfo(context.Context, cid.Cid) (*api.DealInfo, error) //perm:read
|
ClientGetDealInfo(context.Context, cid.Cid) (*api.DealInfo, error) //perm:read
|
||||||
// ClientListDeals returns information about the deals made by the local client.
|
// ClientListDeals returns information about the deals made by the local client.
|
||||||
|
@ -123,6 +123,8 @@ type FullNodeStruct struct {
|
|||||||
|
|
||||||
ClientStartDeal func(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
|
ClientStartDeal func(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
|
||||||
|
|
||||||
|
ClientStatelessDeal func(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) `perm:"write"`
|
||||||
|
|
||||||
CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"`
|
CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"`
|
||||||
|
|
||||||
GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||||
@ -818,6 +820,14 @@ func (s *FullNodeStub) ClientStartDeal(p0 context.Context, p1 *api.StartDealPara
|
|||||||
return nil, xerrors.New("method not supported")
|
return nil, xerrors.New("method not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *FullNodeStruct) ClientStatelessDeal(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) {
|
||||||
|
return s.Internal.ClientStatelessDeal(p0, p1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FullNodeStub) ClientStatelessDeal(p0 context.Context, p1 *api.StartDealParams) (*cid.Cid, error) {
|
||||||
|
return nil, xerrors.New("method not supported")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *FullNodeStruct) CreateBackup(p0 context.Context, p1 string) error {
|
func (s *FullNodeStruct) CreateBackup(p0 context.Context, p1 string) error {
|
||||||
return s.Internal.CreateBackup(p0, p1)
|
return s.Internal.CreateBackup(p0, p1)
|
||||||
}
|
}
|
||||||
|
@ -771,6 +771,21 @@ func (mr *MockFullNodeMockRecorder) ClientStartDeal(arg0, arg1 interface{}) *gom
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientStartDeal", reflect.TypeOf((*MockFullNode)(nil).ClientStartDeal), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientStartDeal", reflect.TypeOf((*MockFullNode)(nil).ClientStartDeal), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientStatelessDeal mocks base method
|
||||||
|
func (m *MockFullNode) ClientStatelessDeal(arg0 context.Context, arg1 *api.StartDealParams) (*cid.Cid, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ClientStatelessDeal", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*cid.Cid)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientStatelessDeal indicates an expected call of ClientStatelessDeal
|
||||||
|
func (mr *MockFullNodeMockRecorder) ClientStatelessDeal(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientStatelessDeal", reflect.TypeOf((*MockFullNode)(nil).ClientStatelessDeal), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// Closing mocks base method
|
// Closing mocks base method
|
||||||
func (m *MockFullNode) Closing(arg0 context.Context) (<-chan struct{}, error) {
|
func (m *MockFullNode) Closing(arg0 context.Context) (<-chan struct{}, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -322,6 +322,10 @@ The minimum value is 518400 (6 months).`,
|
|||||||
Name: "manual-piece-size",
|
Name: "manual-piece-size",
|
||||||
Usage: "if manually specifying piece cid, used to specify size (dataCid must be to a car file)",
|
Usage: "if manually specifying piece cid, used to specify size (dataCid must be to a car file)",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "manual-stateless-deal",
|
||||||
|
Usage: "instructs the node to send an offline deal without registering it with the deallist/fsm",
|
||||||
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "from",
|
Name: "from",
|
||||||
Usage: "specify address to fund the deal with",
|
Usage: "specify address to fund the deal with",
|
||||||
@ -461,7 +465,7 @@ The minimum value is 518400 (6 months).`,
|
|||||||
isVerified = verifiedDealParam
|
isVerified = verifiedDealParam
|
||||||
}
|
}
|
||||||
|
|
||||||
proposal, err := api.ClientStartDeal(ctx, &lapi.StartDealParams{
|
sdParams := &lapi.StartDealParams{
|
||||||
Data: ref,
|
Data: ref,
|
||||||
Wallet: a,
|
Wallet: a,
|
||||||
Miner: miner,
|
Miner: miner,
|
||||||
@ -471,7 +475,18 @@ The minimum value is 518400 (6 months).`,
|
|||||||
FastRetrieval: cctx.Bool("fast-retrieval"),
|
FastRetrieval: cctx.Bool("fast-retrieval"),
|
||||||
VerifiedDeal: isVerified,
|
VerifiedDeal: isVerified,
|
||||||
ProviderCollateral: provCol,
|
ProviderCollateral: provCol,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
var proposal *cid.Cid
|
||||||
|
if cctx.Bool("manual-stateless-deal") {
|
||||||
|
if ref.TransferType != storagemarket.TTManual || price.Int64() != 0 {
|
||||||
|
return xerrors.New("when manual-stateless-deal is enabled, you must also provide a 'price' of 0 and specify 'manual-piece-cid' and 'manual-piece-size'")
|
||||||
|
}
|
||||||
|
proposal, err = api.ClientStatelessDeal(ctx, sdParams)
|
||||||
|
} else {
|
||||||
|
proposal, err = api.ClientStartDeal(ctx, sdParams)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
* [ClientRetrieveTryRestartInsufficientFunds](#ClientRetrieveTryRestartInsufficientFunds)
|
* [ClientRetrieveTryRestartInsufficientFunds](#ClientRetrieveTryRestartInsufficientFunds)
|
||||||
* [ClientRetrieveWithEvents](#ClientRetrieveWithEvents)
|
* [ClientRetrieveWithEvents](#ClientRetrieveWithEvents)
|
||||||
* [ClientStartDeal](#ClientStartDeal)
|
* [ClientStartDeal](#ClientStartDeal)
|
||||||
|
* [ClientStatelessDeal](#ClientStatelessDeal)
|
||||||
* [Create](#Create)
|
* [Create](#Create)
|
||||||
* [CreateBackup](#CreateBackup)
|
* [CreateBackup](#CreateBackup)
|
||||||
* [Gas](#Gas)
|
* [Gas](#Gas)
|
||||||
@ -1501,6 +1502,39 @@ Inputs:
|
|||||||
|
|
||||||
Response: `null`
|
Response: `null`
|
||||||
|
|
||||||
|
### ClientStatelessDeal
|
||||||
|
ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking.
|
||||||
|
|
||||||
|
|
||||||
|
Perms: write
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Data": {
|
||||||
|
"TransferType": "string value",
|
||||||
|
"Root": {
|
||||||
|
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||||
|
},
|
||||||
|
"PieceCid": null,
|
||||||
|
"PieceSize": 1024,
|
||||||
|
"RawBlockSize": 42
|
||||||
|
},
|
||||||
|
"Wallet": "f01234",
|
||||||
|
"Miner": "f01234",
|
||||||
|
"EpochPrice": "0",
|
||||||
|
"MinBlocksDuration": 42,
|
||||||
|
"ProviderCollateral": "0",
|
||||||
|
"DealStartEpoch": 10101,
|
||||||
|
"FastRetrieval": true,
|
||||||
|
"VerifiedDeal": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Response: `null`
|
||||||
|
|
||||||
## Create
|
## Create
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
* [ClientRetrieveTryRestartInsufficientFunds](#ClientRetrieveTryRestartInsufficientFunds)
|
* [ClientRetrieveTryRestartInsufficientFunds](#ClientRetrieveTryRestartInsufficientFunds)
|
||||||
* [ClientRetrieveWithEvents](#ClientRetrieveWithEvents)
|
* [ClientRetrieveWithEvents](#ClientRetrieveWithEvents)
|
||||||
* [ClientStartDeal](#ClientStartDeal)
|
* [ClientStartDeal](#ClientStartDeal)
|
||||||
|
* [ClientStatelessDeal](#ClientStatelessDeal)
|
||||||
* [Create](#Create)
|
* [Create](#Create)
|
||||||
* [CreateBackup](#CreateBackup)
|
* [CreateBackup](#CreateBackup)
|
||||||
* [Gas](#Gas)
|
* [Gas](#Gas)
|
||||||
@ -1503,6 +1504,39 @@ Inputs:
|
|||||||
|
|
||||||
Response: `null`
|
Response: `null`
|
||||||
|
|
||||||
|
### ClientStatelessDeal
|
||||||
|
ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking.
|
||||||
|
|
||||||
|
|
||||||
|
Perms: write
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Data": {
|
||||||
|
"TransferType": "string value",
|
||||||
|
"Root": {
|
||||||
|
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||||
|
},
|
||||||
|
"PieceCid": null,
|
||||||
|
"PieceSize": 1024,
|
||||||
|
"RawBlockSize": 42
|
||||||
|
},
|
||||||
|
"Wallet": "f01234",
|
||||||
|
"Miner": "f01234",
|
||||||
|
"EpochPrice": "0",
|
||||||
|
"MinBlocksDuration": 42,
|
||||||
|
"ProviderCollateral": "0",
|
||||||
|
"DealStartEpoch": 10101,
|
||||||
|
"FastRetrieval": true,
|
||||||
|
"VerifiedDeal": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Response: `null`
|
||||||
|
|
||||||
## Create
|
## Create
|
||||||
|
|
||||||
|
|
||||||
|
@ -544,6 +544,7 @@ The minimum value is 518400 (6 months).
|
|||||||
OPTIONS:
|
OPTIONS:
|
||||||
--manual-piece-cid value manually specify piece commitment for data (dataCid must be to a car file)
|
--manual-piece-cid value manually specify piece commitment for data (dataCid must be to a car file)
|
||||||
--manual-piece-size value if manually specifying piece cid, used to specify size (dataCid must be to a car file) (default: 0)
|
--manual-piece-size value if manually specifying piece cid, used to specify size (dataCid must be to a car file) (default: 0)
|
||||||
|
--manual-stateless-deal instructs the node to send an offline deal without registering it with the deallist/fsm (default: false)
|
||||||
--from value specify address to fund the deal with
|
--from value specify address to fund the deal with
|
||||||
--start-epoch value specify the epoch that the deal should start at (default: -1)
|
--start-epoch value specify the epoch that the deal should start at (default: -1)
|
||||||
--fast-retrieval indicates that data should be available for fast retrieval (default: true)
|
--fast-retrieval indicates that data should be available for fast retrieval (default: true)
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||||
|
|
||||||
@ -31,10 +32,12 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime/traversal/selector/builder"
|
"github.com/ipld/go-ipld-prime/traversal/selector/builder"
|
||||||
"github.com/libp2p/go-libp2p-core/host"
|
"github.com/libp2p/go-libp2p-core/host"
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
"github.com/multiformats/go-multibase"
|
||||||
mh "github.com/multiformats/go-multihash"
|
mh "github.com/multiformats/go-multihash"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
|
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||||
"github.com/filecoin-project/go-commp-utils/ffiwrapper"
|
"github.com/filecoin-project/go-commp-utils/ffiwrapper"
|
||||||
"github.com/filecoin-project/go-commp-utils/writer"
|
"github.com/filecoin-project/go-commp-utils/writer"
|
||||||
datatransfer "github.com/filecoin-project/go-data-transfer"
|
datatransfer "github.com/filecoin-project/go-data-transfer"
|
||||||
@ -43,8 +46,10 @@ import (
|
|||||||
rm "github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
rm "github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||||
"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-fil-markets/storagemarket/network"
|
||||||
"github.com/filecoin-project/go-multistore"
|
"github.com/filecoin-project/go-multistore"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/specs-actors/v3/actors/builtin/market"
|
||||||
|
|
||||||
marketevents "github.com/filecoin-project/lotus/markets/loggers"
|
marketevents "github.com/filecoin-project/lotus/markets/loggers"
|
||||||
|
|
||||||
@ -99,8 +104,23 @@ func (a *API) imgr() *importmgr.Mgr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) {
|
func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) {
|
||||||
|
return a.dealStarter(ctx, params, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) ClientStatelessDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) {
|
||||||
|
return a.dealStarter(ctx, params, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) dealStarter(ctx context.Context, params *api.StartDealParams, isStateless bool) (*cid.Cid, error) {
|
||||||
var storeID *multistore.StoreID
|
var storeID *multistore.StoreID
|
||||||
if params.Data.TransferType == storagemarket.TTGraphsync {
|
if isStateless {
|
||||||
|
if params.Data.TransferType != storagemarket.TTManual {
|
||||||
|
return nil, xerrors.Errorf("invalid transfer type %s for stateless storage deal", params.Data.TransferType)
|
||||||
|
}
|
||||||
|
if !params.EpochPrice.IsZero() {
|
||||||
|
return nil, xerrors.New("stateless storage deals can only be initiated with storage price of 0")
|
||||||
|
}
|
||||||
|
} else if params.Data.TransferType == storagemarket.TTGraphsync {
|
||||||
importIDs := a.imgr().List()
|
importIDs := a.imgr().List()
|
||||||
for _, importID := range importIDs {
|
for _, importID := range importIDs {
|
||||||
info, err := a.imgr().Info(importID)
|
info, err := a.imgr().Info(importID)
|
||||||
@ -148,8 +168,6 @@ func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams)
|
|||||||
return nil, xerrors.New("data doesn't fit in a sector")
|
return nil, xerrors.New("data doesn't fit in a sector")
|
||||||
}
|
}
|
||||||
|
|
||||||
providerInfo := utils.NewStorageProviderInfo(params.Miner, mi.Worker, mi.SectorSize, *mi.PeerId, mi.Multiaddrs)
|
|
||||||
|
|
||||||
dealStart := params.DealStartEpoch
|
dealStart := params.DealStartEpoch
|
||||||
if dealStart <= 0 { // unset, or explicitly 'epoch undefined'
|
if dealStart <= 0 { // unset, or explicitly 'epoch undefined'
|
||||||
ts, err := a.ChainHead(ctx)
|
ts, err := a.ChainHead(ctx)
|
||||||
@ -171,6 +189,10 @@ func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams)
|
|||||||
return nil, xerrors.Errorf("failed to get seal proof type: %w", err)
|
return nil, xerrors.Errorf("failed to get seal proof type: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// regular flow
|
||||||
|
if !isStateless {
|
||||||
|
providerInfo := utils.NewStorageProviderInfo(params.Miner, mi.Worker, mi.SectorSize, *mi.PeerId, mi.Multiaddrs)
|
||||||
|
|
||||||
result, err := a.SMDealClient.ProposeStorageDeal(ctx, storagemarket.ProposeStorageDealParams{
|
result, err := a.SMDealClient.ProposeStorageDeal(ctx, storagemarket.ProposeStorageDealParams{
|
||||||
Addr: params.Wallet,
|
Addr: params.Wallet,
|
||||||
Info: &providerInfo,
|
Info: &providerInfo,
|
||||||
@ -190,6 +212,89 @@ func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &result.ProposalCid, nil
|
return &result.ProposalCid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// stateless flow from here to the end
|
||||||
|
//
|
||||||
|
|
||||||
|
dealProposal := &market.DealProposal{
|
||||||
|
PieceCID: *params.Data.PieceCid,
|
||||||
|
PieceSize: params.Data.PieceSize.Padded(),
|
||||||
|
Client: walletKey,
|
||||||
|
Provider: params.Miner,
|
||||||
|
Label: params.Data.Root.Encode(multibase.MustNewEncoder('u')),
|
||||||
|
StartEpoch: dealStart,
|
||||||
|
EndEpoch: calcDealExpiration(params.MinBlocksDuration, md, dealStart),
|
||||||
|
StoragePricePerEpoch: big.Zero(),
|
||||||
|
ProviderCollateral: params.ProviderCollateral,
|
||||||
|
ClientCollateral: big.Zero(),
|
||||||
|
VerifiedDeal: params.VerifiedDeal,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dealProposal.ProviderCollateral.IsZero() {
|
||||||
|
networkCollateral, err := a.StateDealProviderCollateralBounds(ctx, params.Data.PieceSize.Padded(), params.VerifiedDeal, types.EmptyTSK)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to determine minimum provider collateral: %w", err)
|
||||||
|
}
|
||||||
|
dealProposal.ProviderCollateral = networkCollateral.Min
|
||||||
|
}
|
||||||
|
|
||||||
|
dealProposalSerialized, err := cborutil.Dump(dealProposal)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to serialize deal proposal: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dealProposalSig, err := a.WalletSign(ctx, walletKey, dealProposalSerialized)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to sign proposal : %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dealProposalSigned := &market.ClientDealProposal{
|
||||||
|
Proposal: *dealProposal,
|
||||||
|
ClientSignature: *dealProposalSig,
|
||||||
|
}
|
||||||
|
dStream, err := network.NewFromLibp2pHost(a.Host,
|
||||||
|
// params duplicated from .../node/modules/client.go
|
||||||
|
// https://github.com/filecoin-project/lotus/pull/5961#discussion_r629768011
|
||||||
|
network.RetryParameters(time.Second, 5*time.Minute, 15, 5),
|
||||||
|
).NewDealStream(ctx, *mi.PeerId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("opening dealstream to %s/%s failed: %w", params.Miner, *mi.PeerId, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = dStream.WriteDealProposal(network.Proposal{
|
||||||
|
FastRetrieval: true,
|
||||||
|
DealProposal: dealProposalSigned,
|
||||||
|
Piece: &storagemarket.DataRef{
|
||||||
|
TransferType: storagemarket.TTManual,
|
||||||
|
Root: params.Data.Root,
|
||||||
|
PieceCid: params.Data.PieceCid,
|
||||||
|
PieceSize: params.Data.PieceSize,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return nil, xerrors.Errorf("sending deal proposal failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, _, err := dStream.ReadDealResponse()
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("reading proposal response failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dealProposalIpld, err := cborutil.AsIpld(dealProposalSigned)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("serializing proposal node failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dealProposalIpld.Cid().Equals(resp.Response.Proposal) {
|
||||||
|
return nil, xerrors.Errorf("provider returned proposal cid %s but we expected %s", resp.Response.Proposal, dealProposalIpld.Cid())
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Response.State != storagemarket.StorageDealWaitingForData {
|
||||||
|
return nil, xerrors.Errorf("provider returned unexpected state %d for proposal %s, with message: %s", resp.Response.State, resp.Response.Proposal, resp.Response.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp.Response.Proposal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) ClientListDeals(ctx context.Context) ([]api.DealInfo, error) {
|
func (a *API) ClientListDeals(ctx context.Context) ([]api.DealInfo, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user