refactor(datatransfer): xerrors, cbor-gen, tweaks

Various refactors per PR comments: use xerrors, add context to dt calls, use cbor-gen, move files
into seperate modules, etc
This commit is contained in:
hannahhoward 2019-10-30 20:00:02 -07:00
parent 4e1e43f10f
commit ca5032937c
11 changed files with 319 additions and 260 deletions

View File

@ -851,3 +851,51 @@ func (t *MinerDeal) UnmarshalCBOR(r io.Reader) error {
t.SectorID = uint64(extra)
return nil
}
func (t *StorageDataTransferVoucher) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{129}); err != nil {
return err
}
// t.t.Proposal (cid.Cid)
if err := cbg.WriteCid(w, t.Proposal); err != nil {
return xerrors.Errorf("failed to write cid field t.Proposal: %w", err)
}
return nil
}
func (t *StorageDataTransferVoucher) UnmarshalCBOR(r io.Reader) error {
br := cbg.GetPeeker(r)
maj, extra, err := cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajArray {
return fmt.Errorf("cbor input should be of type array")
}
if extra != 1 {
return fmt.Errorf("cbor input had wrong number of fields")
}
// t.t.Proposal (cid.Cid)
{
c, err := cbg.ReadCid(br)
if err != nil {
return xerrors.Errorf("failed to read cid field t.Proposal: %w", err)
}
t.Proposal = c
}
return nil
}

View File

@ -1,17 +1,28 @@
package deals
import (
"bytes"
"context"
"reflect"
"runtime"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
files "github.com/ipfs/go-ipfs-files"
unixfile "github.com/ipfs/go-unixfs/file"
"github.com/ipld/go-ipld-prime"
"github.com/libp2p/go-libp2p-core/peer"
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/datatransfer"
"github.com/filecoin-project/lotus/lib/cborutil"
"github.com/filecoin-project/lotus/lib/padreader"
"github.com/filecoin-project/lotus/lib/sectorbuilder"
"github.com/filecoin-project/lotus/lib/statestore"
"github.com/filecoin-project/lotus/node/modules/dtypes"
)
func (c *Client) failDeal(id cid.Cid, cerr error) {
@ -98,3 +109,81 @@ func (c *Client) disconnect(deal ClientDeal) error {
delete(c.conns, deal.ProposalCid)
return err
}
var _ datatransfer.RequestValidator = &ClientRequestValidator{}
// ClientRequestValidator validates data transfer requests for the client
// in a storage market
type ClientRequestValidator struct {
deals *statestore.StateStore
}
// RegisterClientValidator is an initialization hook that registers the client
// request validator with the data transfer module as the validator for
// StorageDataTransferVoucher types
func RegisterClientValidator(lc fx.Lifecycle, crv *ClientRequestValidator, dtm datatransfer.ClientDataTransfer) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
return dtm.RegisterVoucherType(reflect.TypeOf(StorageDataTransferVoucher{}), crv)
},
})
}
// NewClientRequestValidator returns a new client request validator for the
// given datastore
func NewClientRequestValidator(ds dtypes.MetadataDS) *ClientRequestValidator {
crv := &ClientRequestValidator{
deals: statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client"))),
}
return crv
}
// ValidatePush validates a push request received from the peer that will send data
// Will always error because clients should not accept push requests from a provider
// in a storage deal (i.e. send data to client).
func (c *ClientRequestValidator) ValidatePush(
sender peer.ID,
voucher datatransfer.Voucher,
baseCid cid.Cid,
Selector ipld.Node) error {
return ErrNoPushAccepted
}
// ValidatePull validates a pull request received from the peer that will receive data
// Will succeed only if:
// - voucher has correct type
// - voucher references an active deal
// - referenced deal matches the receiver (miner)
// - referenced deal matches the given base CID
// - referenced deal is in an acceptable state
func (c *ClientRequestValidator) ValidatePull(
receiver peer.ID,
voucher datatransfer.Voucher,
baseCid cid.Cid,
Selector ipld.Node) error {
dealVoucher, ok := voucher.(*StorageDataTransferVoucher)
if !ok {
return xerrors.Errorf("voucher type %s: %w", voucher.Identifier(), ErrWrongVoucherType)
}
var deal ClientDeal
err := c.deals.Mutate(dealVoucher.Proposal, func(d *ClientDeal) error {
deal = *d
return nil
})
if err != nil {
return xerrors.Errorf("Proposal CID %s: %w", dealVoucher.Proposal.String(), ErrNoDeal)
}
if deal.Miner != receiver {
return xerrors.Errorf("Deal Peer %s, Data Transfer Peer %s: %w", deal.Miner.String(), receiver.String(), ErrWrongPeer)
}
if !bytes.Equal(deal.Proposal.PieceRef, baseCid.Bytes()) {
return xerrors.Errorf("Deal Payload CID %s, Data Transfer CID %s: %w", string(deal.Proposal.PieceRef), baseCid.String(), ErrWrongPiece)
}
for _, state := range AcceptableDealStates {
if deal.State == state {
return nil
}
}
return xerrors.Errorf("Deal State %s: %w", deal.State, ErrInacceptableDealState)
}

View File

@ -212,7 +212,7 @@ func (p *Provider) onUpdated(ctx context.Context, update minerDealUpdate) {
// and update message for the deal -- either moving to staged for a completion
// event or moving to error if a data transfer error occurs
func (p *Provider) onDataTransferEvent(event datatransfer.Event, channelState datatransfer.ChannelState) {
voucher, ok := channelState.Voucher().(StorageDataTransferVoucher)
voucher, ok := channelState.Voucher().(*StorageDataTransferVoucher)
// if this event is for a transfer not related to storage, ignore
if !ok {
return

View File

@ -163,8 +163,9 @@ func (p *Provider) accept(ctx context.Context, deal MinerDeal) (func(*MinerDeal)
// initiate a pull data transfer. This will complete asynchronously and the
// completion of the data transfer will trigger a change in deal state
// (see onDataTransferEvent)
_, err = p.dataTransfer.OpenPullDataChannel(deal.Client,
StorageDataTransferVoucher{Proposal: deal.ProposalCid},
_, err = p.dataTransfer.OpenPullDataChannel(ctx,
deal.Client,
&StorageDataTransferVoucher{Proposal: deal.ProposalCid},
deal.Ref,
allSelector,
)

View File

@ -1,17 +1,28 @@
package deals
import (
"bytes"
"context"
"reflect"
"runtime"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/datatransfer"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/ipld/go-ipld-prime"
"go.uber.org/fx"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/address"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/cborutil"
"github.com/filecoin-project/lotus/lib/statestore"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
inet "github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
"golang.org/x/xerrors"
)
@ -125,3 +136,81 @@ func (p *Provider) getWorker(miner address.Address) (address.Address, error) {
return address.NewFromBytes(r.Return)
}
var _ datatransfer.RequestValidator = &ProviderRequestValidator{}
// ProviderRequestValidator validates data transfer requests for the provider
// in a storage market
type ProviderRequestValidator struct {
deals *statestore.StateStore
}
// RegisterProviderValidator is an initialization hook that registers the provider
// request validator with the data transfer module as the validator for
// StorageDataTransferVoucher types
func RegisterProviderValidator(lc fx.Lifecycle, mrv *ProviderRequestValidator, dtm datatransfer.ProviderDataTransfer) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
return dtm.RegisterVoucherType(reflect.TypeOf(StorageDataTransferVoucher{}), mrv)
},
})
}
// NewProviderRequestValidator returns a new client request validator for the
// given datastore
func NewProviderRequestValidator(ds dtypes.MetadataDS) *ProviderRequestValidator {
return &ProviderRequestValidator{
deals: statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client"))),
}
}
// ValidatePush validates a push request received from the peer that will send data
// Will succeed only if:
// - voucher has correct type
// - voucher references an active deal
// - referenced deal matches the client
// - referenced deal matches the given base CID
// - referenced deal is in an acceptable state
func (m *ProviderRequestValidator) ValidatePush(
sender peer.ID,
voucher datatransfer.Voucher,
baseCid cid.Cid,
Selector ipld.Node) error {
dealVoucher, ok := voucher.(*StorageDataTransferVoucher)
if !ok {
return xerrors.Errorf("voucher type %s: %w", voucher.Identifier(), ErrWrongVoucherType)
}
var deal MinerDeal
err := m.deals.Mutate(dealVoucher.Proposal, func(d *MinerDeal) error {
deal = *d
return nil
})
if err != nil {
return xerrors.Errorf("Proposal CID %s: %w", dealVoucher.Proposal.String(), ErrNoDeal)
}
if deal.Client != sender {
return xerrors.Errorf("Deal Peer %s, Data Transfer Peer %s: %w", deal.Client.String(), sender.String(), ErrWrongPeer)
}
if !bytes.Equal(deal.Proposal.PieceRef, baseCid.Bytes()) {
return xerrors.Errorf("Deal Payload CID %s, Data Transfer CID %s: %w", string(deal.Proposal.PieceRef), baseCid.String(), ErrWrongPiece)
}
for _, state := range AcceptableDealStates {
if deal.State == state {
return nil
}
}
return xerrors.Errorf("Deal State %s: %w", deal.State, ErrInacceptableDealState)
}
// ValidatePull validates a pull request received from the peer that will receive data.
// Will always error because providers should not accept pull requests from a client
// in a storage deal (i.e. send data to client).
func (m *ProviderRequestValidator) ValidatePull(
receiver peer.ID,
voucher datatransfer.Voucher,
baseCid cid.Cid,
Selector ipld.Node) error {
return ErrNoPullAccepted
}

View File

@ -1,230 +0,0 @@
package deals
import (
"bytes"
"context"
"errors"
"reflect"
"github.com/ipfs/go-cid"
datastore "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
"github.com/ipld/go-ipld-prime"
"github.com/libp2p/go-libp2p-core/peer"
"go.uber.org/fx"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/datatransfer"
"github.com/filecoin-project/lotus/lib/statestore"
"github.com/filecoin-project/lotus/node/modules/dtypes"
)
var (
// ErrWrongVoucherType means the voucher was not the correct type can validate against
ErrWrongVoucherType = errors.New("cannot validate voucher type")
// ErrNoPushAccepted just means clients do not accept pushes for storage deals
ErrNoPushAccepted = errors.New("Client should not receive data for a storage deal")
// ErrNoPullAccepted just means providers do not accept pulls for storage deals
ErrNoPullAccepted = errors.New("Provider should not send data for a storage deal")
// ErrNoDeal means no active deal was found for this vouchers proposal cid
ErrNoDeal = errors.New("No deal found for this proposal")
// ErrWrongPeer means that the other peer for this data transfer request does not match
// the other peer for the deal
ErrWrongPeer = errors.New("Data Transfer peer id and Deal peer id do not match")
// ErrWrongPiece means that the pieceref for this data transfer request does not match
// the one specified in the deal
ErrWrongPiece = errors.New("Base CID for deal does not match CID for piece")
// ErrInacceptableDealState means the deal for this transfer is not in a deal state
// where transfer can be performed
ErrInacceptableDealState = errors.New("Deal is not a in a state where deals are accepted")
// AcceptableDealStates are the states in which it would make sense to actually start a data transfer
AcceptableDealStates = []api.DealState{api.DealAccepted, api.DealUnknown}
)
// StorageDataTransferVoucher is the voucher type for data transfers
// used by the storage market
type StorageDataTransferVoucher struct {
Proposal cid.Cid
}
// ToBytes converts the StorageDataTransferVoucher to raw bytes
func (dv StorageDataTransferVoucher) ToBytes() []byte {
return dv.Proposal.Bytes()
}
// FromBytes converts the StorageDataTransferVoucher to raw bytes
func (dv StorageDataTransferVoucher) FromBytes(raw []byte) (datatransfer.Voucher, error) {
c, err := cid.Cast(raw)
if err != nil {
return nil, err
}
return StorageDataTransferVoucher{c}, nil
}
// Identifier is the unique string identifier for a StorageDataTransferVoucher
func (dv StorageDataTransferVoucher) Identifier() string {
return "StorageDataTransferVoucher"
}
var _ datatransfer.RequestValidator = &ClientRequestValidator{}
// ClientRequestValidator validates data transfer requests for the client
// in a storage market
type ClientRequestValidator struct {
deals *statestore.StateStore
}
// RegisterClientValidator is an initialization hook that registers the client
// request validator with the data transfer module as the validator for
// StorageDataTransferVoucher types
func RegisterClientValidator(lc fx.Lifecycle, crv *ClientRequestValidator, dtm datatransfer.ClientDataTransfer) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
return dtm.RegisterVoucherType(reflect.TypeOf(StorageDataTransferVoucher{}), crv)
},
})
}
// NewClientRequestValidator returns a new client request validator for the
// given datastore
func NewClientRequestValidator(ds dtypes.MetadataDS) *ClientRequestValidator {
crv := &ClientRequestValidator{
deals: statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client"))),
}
return crv
}
// ValidatePush validates a push request received from the peer that will send data
// Will always error because clients should not accept push requests from a provider
// in a storage deal (i.e. send data to client).
func (c *ClientRequestValidator) ValidatePush(
sender peer.ID,
voucher datatransfer.Voucher,
baseCid cid.Cid,
Selector ipld.Node) error {
return ErrNoPushAccepted
}
// ValidatePull validates a pull request received from the peer that will receive data
// Will succeed only if:
// - voucher has correct type
// - voucher references an active deal
// - referenced deal matches the receiver (miner)
// - referenced deal matches the given base CID
// - referenced deal is in an acceptable state
func (c *ClientRequestValidator) ValidatePull(
receiver peer.ID,
voucher datatransfer.Voucher,
baseCid cid.Cid,
Selector ipld.Node) error {
dealVoucher, ok := voucher.(StorageDataTransferVoucher)
if !ok {
return ErrWrongVoucherType
}
var deal ClientDeal
err := c.deals.Mutate(dealVoucher.Proposal, func(d *ClientDeal) error {
deal = *d
return nil
})
if err != nil {
return ErrNoDeal
}
if deal.Miner != receiver {
return ErrWrongPeer
}
if !bytes.Equal(deal.Proposal.PieceRef, baseCid.Bytes()) {
return ErrWrongPiece
}
for _, state := range AcceptableDealStates {
if deal.State == state {
return nil
}
}
return ErrInacceptableDealState
}
var _ datatransfer.RequestValidator = &ProviderRequestValidator{}
// ProviderRequestValidator validates data transfer requests for the provider
// in a storage market
type ProviderRequestValidator struct {
deals *statestore.StateStore
}
// RegisterProviderValidator is an initialization hook that registers the provider
// request validator with the data transfer module as the validator for
// StorageDataTransferVoucher types
func RegisterProviderValidator(lc fx.Lifecycle, mrv *ProviderRequestValidator, dtm datatransfer.ProviderDataTransfer) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
return dtm.RegisterVoucherType(reflect.TypeOf(StorageDataTransferVoucher{}), mrv)
},
})
}
// NewProviderRequestValidator returns a new client request validator for the
// given datastore
func NewProviderRequestValidator(ds dtypes.MetadataDS) *ProviderRequestValidator {
return &ProviderRequestValidator{
deals: statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client"))),
}
}
// ValidatePush validates a push request received from the peer that will send data
// Will succeed only if:
// - voucher has correct type
// - voucher references an active deal
// - referenced deal matches the client
// - referenced deal matches the given base CID
// - referenced deal is in an acceptable state
func (m *ProviderRequestValidator) ValidatePush(
sender peer.ID,
voucher datatransfer.Voucher,
baseCid cid.Cid,
Selector ipld.Node) error {
dealVoucher, ok := voucher.(StorageDataTransferVoucher)
if !ok {
return ErrWrongVoucherType
}
var deal MinerDeal
err := m.deals.Mutate(dealVoucher.Proposal, func(d *MinerDeal) error {
deal = *d
return nil
})
if err != nil {
return ErrNoDeal
}
if deal.Client != sender {
return ErrWrongPeer
}
if !bytes.Equal(deal.Proposal.PieceRef, baseCid.Bytes()) {
return ErrWrongPiece
}
for _, state := range AcceptableDealStates {
if deal.State == state {
return nil
}
}
return ErrInacceptableDealState
}
// ValidatePull validates a pull request received from the peer that will receive data.
// Will always error because providers should not accept pull requests from a client
// in a storage deal (i.e. send data to client).
func (m *ProviderRequestValidator) ValidatePull(
receiver peer.ID,
voucher datatransfer.Voucher,
baseCid cid.Cid,
Selector ipld.Node) error {
return ErrNoPullAccepted
}

View File

@ -11,13 +11,13 @@ import (
dss "github.com/ipfs/go-datastore/sync"
blocksutil "github.com/ipfs/go-ipfs-blocksutil"
"github.com/libp2p/go-libp2p-core/peer"
xerrors "golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/address"
"github.com/filecoin-project/lotus/chain/deals"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/datatransfer"
"github.com/filecoin-project/lotus/lib/cborrpc"
"github.com/filecoin-project/lotus/lib/statestore"
)
@ -27,12 +27,12 @@ var blockGenerator = blocksutil.NewBlockGenerator()
type wrongDTType struct {
}
func (wrongDTType) ToBytes() []byte {
return []byte{}
func (wrongDTType) ToBytes() ([]byte, error) {
return []byte{}, nil
}
func (wrongDTType) FromBytes([]byte) (datatransfer.Voucher, error) {
return nil, fmt.Errorf("not implemented")
func (wrongDTType) FromBytes([]byte) error {
return fmt.Errorf("not implemented")
}
func (wrongDTType) Identifier() string {
@ -106,7 +106,7 @@ func TestClientRequestValidation(t *testing.T) {
minerId := peer.ID("fakepeerid")
block := blockGenerator.Next()
t.Run("ValidatePush fails", func(t *testing.T) {
if crv.ValidatePush(minerId, wrongDTType{}, block.Cid(), nil) != deals.ErrNoPushAccepted {
if !xerrors.Is(crv.ValidatePush(minerId, wrongDTType{}, block.Cid(), nil), deals.ErrNoPushAccepted) {
t.Fatal("Push should fail for the client request validator for storage deals")
}
})
@ -123,7 +123,7 @@ func TestClientRequestValidation(t *testing.T) {
if err != nil {
t.Fatal("unable to construct piece cid")
}
if crv.ValidatePull(minerId, deals.StorageDataTransferVoucher{proposalNd.Cid()}, pieceRef, nil) != deals.ErrNoDeal {
if !xerrors.Is(crv.ValidatePull(minerId, &deals.StorageDataTransferVoucher{proposalNd.Cid()}, pieceRef, nil), deals.ErrNoDeal) {
t.Fatal("Pull should fail if there is no deal stored")
}
})
@ -140,7 +140,7 @@ func TestClientRequestValidation(t *testing.T) {
if err != nil {
t.Fatal("unable to construct piece cid")
}
if crv.ValidatePull(minerId, deals.StorageDataTransferVoucher{clientDeal.ProposalCid}, pieceRef, nil) != deals.ErrWrongPeer {
if !xerrors.Is(crv.ValidatePull(minerId, &deals.StorageDataTransferVoucher{clientDeal.ProposalCid}, pieceRef, nil), deals.ErrWrongPeer) {
t.Fatal("Pull should fail if miner address is incorrect")
}
})
@ -152,7 +152,7 @@ func TestClientRequestValidation(t *testing.T) {
if err := state.Begin(clientDeal.ProposalCid, &clientDeal); err != nil {
t.Fatal("deal tracking failed")
}
if crv.ValidatePull(minerId, deals.StorageDataTransferVoucher{clientDeal.ProposalCid}, blockGenerator.Next().Cid(), nil) != deals.ErrWrongPiece {
if !xerrors.Is(crv.ValidatePull(minerId, &deals.StorageDataTransferVoucher{clientDeal.ProposalCid}, blockGenerator.Next().Cid(), nil), deals.ErrWrongPiece) {
t.Fatal("Pull should fail if piece ref is incorrect")
}
})
@ -168,7 +168,7 @@ func TestClientRequestValidation(t *testing.T) {
if err != nil {
t.Fatal("unable to construct piece cid")
}
if crv.ValidatePull(minerId, deals.StorageDataTransferVoucher{clientDeal.ProposalCid}, pieceRef, nil) != deals.ErrInacceptableDealState {
if !xerrors.Is(crv.ValidatePull(minerId, &deals.StorageDataTransferVoucher{clientDeal.ProposalCid}, pieceRef, nil), deals.ErrInacceptableDealState) {
t.Fatal("Pull should fail if deal is in a state that cannot be data transferred")
}
})
@ -184,7 +184,7 @@ func TestClientRequestValidation(t *testing.T) {
if err != nil {
t.Fatal("unable to construct piece cid")
}
if crv.ValidatePull(minerId, deals.StorageDataTransferVoucher{clientDeal.ProposalCid}, pieceRef, nil) != nil {
if crv.ValidatePull(minerId, &deals.StorageDataTransferVoucher{clientDeal.ProposalCid}, pieceRef, nil) != nil {
t.Fatal("Pull should should succeed when all parameters are correct")
}
})
@ -198,7 +198,7 @@ func TestProviderRequestValidation(t *testing.T) {
clientID := peer.ID("fakepeerid")
block := blockGenerator.Next()
t.Run("ValidatePull fails", func(t *testing.T) {
if mrv.ValidatePull(clientID, wrongDTType{}, block.Cid(), nil) != deals.ErrNoPullAccepted {
if !xerrors.Is(mrv.ValidatePull(clientID, wrongDTType{}, block.Cid(), nil), deals.ErrNoPullAccepted) {
t.Fatal("Pull should fail for the provider request validator for storage deals")
}
})
@ -216,7 +216,7 @@ func TestProviderRequestValidation(t *testing.T) {
if err != nil {
t.Fatal("unable to construct piece cid")
}
if mrv.ValidatePush(clientID, deals.StorageDataTransferVoucher{proposalNd.Cid()}, pieceRef, nil) != deals.ErrNoDeal {
if !xerrors.Is(mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{proposalNd.Cid()}, pieceRef, nil), deals.ErrNoDeal) {
t.Fatal("Push should fail if there is no deal stored")
}
})
@ -233,7 +233,7 @@ func TestProviderRequestValidation(t *testing.T) {
if err != nil {
t.Fatal("unable to construct piece cid")
}
if mrv.ValidatePush(clientID, deals.StorageDataTransferVoucher{minerDeal.ProposalCid}, pieceRef, nil) != deals.ErrWrongPeer {
if !xerrors.Is(mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{minerDeal.ProposalCid}, pieceRef, nil), deals.ErrWrongPeer) {
t.Fatal("Push should fail if miner address is incorrect")
}
})
@ -245,7 +245,7 @@ func TestProviderRequestValidation(t *testing.T) {
if err := state.Begin(minerDeal.ProposalCid, &minerDeal); err != nil {
t.Fatal("deal tracking failed")
}
if mrv.ValidatePush(clientID, deals.StorageDataTransferVoucher{minerDeal.ProposalCid}, blockGenerator.Next().Cid(), nil) != deals.ErrWrongPiece {
if !xerrors.Is(mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{minerDeal.ProposalCid}, blockGenerator.Next().Cid(), nil), deals.ErrWrongPiece) {
t.Fatal("Push should fail if piece ref is incorrect")
}
})
@ -261,7 +261,7 @@ func TestProviderRequestValidation(t *testing.T) {
if err != nil {
t.Fatal("unable to construct piece cid")
}
if mrv.ValidatePush(clientID, deals.StorageDataTransferVoucher{minerDeal.ProposalCid}, pieceRef, nil) != deals.ErrInacceptableDealState {
if !xerrors.Is(mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{minerDeal.ProposalCid}, pieceRef, nil), deals.ErrInacceptableDealState) {
t.Fatal("Push should fail if deal is in a state that cannot be data transferred")
}
})
@ -277,7 +277,7 @@ func TestProviderRequestValidation(t *testing.T) {
if err != nil {
t.Fatal("unable to construct piece cid")
}
if mrv.ValidatePush(clientID, deals.StorageDataTransferVoucher{minerDeal.ProposalCid}, pieceRef, nil) != nil {
if mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{minerDeal.ProposalCid}, pieceRef, nil) != nil {
t.Fatal("Push should should succeed when all parameters are correct")
}
})

View File

@ -1,6 +1,9 @@
package deals
import (
"bytes"
"errors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/address"
@ -9,6 +12,35 @@ import (
"github.com/ipfs/go-cid"
)
var (
// ErrWrongVoucherType means the voucher was not the correct type can validate against
ErrWrongVoucherType = errors.New("cannot validate voucher type")
// ErrNoPushAccepted just means clients do not accept pushes for storage deals
ErrNoPushAccepted = errors.New("Client should not receive data for a storage deal")
// ErrNoPullAccepted just means providers do not accept pulls for storage deals
ErrNoPullAccepted = errors.New("Provider should not send data for a storage deal")
// ErrNoDeal means no active deal was found for this vouchers proposal cid
ErrNoDeal = errors.New("No deal found for this proposal")
// ErrWrongPeer means that the other peer for this data transfer request does not match
// the other peer for the deal
ErrWrongPeer = errors.New("Data Transfer peer id and Deal peer id do not match")
// ErrWrongPiece means that the pieceref for this data transfer request does not match
// the one specified in the deal
ErrWrongPiece = errors.New("Base CID for deal does not match CID for piece")
// ErrInacceptableDealState means the deal for this transfer is not in a deal state
// where transfer can be performed
ErrInacceptableDealState = errors.New("Deal is not a in a state where deals are accepted")
// AcceptableDealStates are the states in which it would make sense to actually start a data transfer
AcceptableDealStates = []api.DealState{api.DealAccepted, api.DealUnknown}
)
const DealProtocolID = "/fil/storage/mk/1.0.1"
const AskProtocolID = "/fil/storage/ask/1.0.1"
@ -53,3 +85,30 @@ type AskRequest struct {
type AskResponse struct {
Ask *types.SignedStorageAsk
}
// StorageDataTransferVoucher is the voucher type for data transfers
// used by the storage market
type StorageDataTransferVoucher struct {
Proposal cid.Cid
}
// ToBytes converts the StorageDataTransferVoucher to raw bytes
func (dv *StorageDataTransferVoucher) ToBytes() ([]byte, error) {
var buf bytes.Buffer
err := dv.MarshalCBOR(&buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// FromBytes converts the StorageDataTransferVoucher to raw bytes
func (dv *StorageDataTransferVoucher) FromBytes(raw []byte) error {
r := bytes.NewReader(raw)
return dv.UnmarshalCBOR(r)
}
// Identifier is the unique string identifier for a StorageDataTransferVoucher
func (dv *StorageDataTransferVoucher) Identifier() string {
return "StorageDataTransferVoucher"
}

View File

@ -2,15 +2,14 @@ package datatransfer
import (
"context"
"fmt"
"reflect"
"github.com/ipfs/go-cid"
ipldformat "github.com/ipfs/go-ipld-format"
"github.com/ipfs/go-merkledag"
ipld "github.com/ipld/go-ipld-prime"
"github.com/libp2p/go-libp2p-core/peer"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/node/modules/dtypes"
)
@ -51,15 +50,17 @@ func (impl *dagserviceImpl) RegisterVoucherType(voucherType reflect.Type, valida
// open a data transfer that will send data to the recipient peer and
// open a data transfer that will send data to the recipient peer and
// transfer parts of the piece that match the selector
func (impl *dagserviceImpl) OpenPushDataChannel(to peer.ID, voucher Voucher, baseCid cid.Cid, Selector ipld.Node) (ChannelID, error) {
return ChannelID{}, fmt.Errorf("not implemented")
func (impl *dagserviceImpl) OpenPushDataChannel(ctx context.Context, to peer.ID, voucher Voucher, baseCid cid.Cid, Selector ipld.Node) (ChannelID, error) {
return ChannelID{}, xerrors.Errorf("not implemented")
}
// open a data transfer that will request data from the sending peer and
// transfer parts of the piece that match the selector
func (impl *dagserviceImpl) OpenPullDataChannel(to peer.ID, voucher Voucher, baseCid cid.Cid, Selector ipld.Node) (ChannelID, error) {
func (impl *dagserviceImpl) OpenPullDataChannel(ctx context.Context, to peer.ID, voucher Voucher, baseCid cid.Cid, Selector ipld.Node) (ChannelID, error) {
ctx, cancel := context.WithCancel(ctx)
go func() {
err := merkledag.FetchGraph(context.TODO(), baseCid, impl.dag)
defer cancel()
err := merkledag.FetchGraph(ctx, baseCid, impl.dag)
var event Event
if err != nil {
event = Error

View File

@ -1,6 +1,7 @@
package datatransfer
import (
"context"
"reflect"
"github.com/ipfs/go-cid"
@ -14,9 +15,9 @@ import (
// from bytes, and has a string identifier type
type Voucher interface {
// ToBytes converts the Voucher to raw bytes
ToBytes() []byte
ToBytes() ([]byte, error)
// FromBytes reads a Voucher from raw bytes
FromBytes([]byte) (Voucher, error)
FromBytes([]byte) error
// Identifier is a unique string identifier for this voucher type
Identifier() string
}
@ -152,11 +153,11 @@ type Manager interface {
// open a data transfer that will send data to the recipient peer and
// open a data transfer that will send data to the recipient peer and
// transfer parts of the piece that match the selector
OpenPushDataChannel(to peer.ID, voucher Voucher, baseCid cid.Cid, selector ipld.Node) (ChannelID, error)
OpenPushDataChannel(ctx context.Context, to peer.ID, voucher Voucher, baseCid cid.Cid, selector ipld.Node) (ChannelID, error)
// open a data transfer that will request data from the sending peer and
// transfer parts of the piece that match the selector
OpenPullDataChannel(to peer.ID, voucher Voucher, baseCid cid.Cid, selector ipld.Node) (ChannelID, error)
OpenPullDataChannel(ctx context.Context, to peer.ID, voucher Voucher, baseCid cid.Cid, selector ipld.Node) (ChannelID, error)
// close an open channel (effectively a cancel)
CloseDataTransferChannel(x ChannelID)

View File

@ -143,6 +143,7 @@ func main() {
deals.ClientDealProposal{},
deals.ClientDeal{},
deals.MinerDeal{},
deals.StorageDataTransferVoucher{},
)
if err != nil {
fmt.Println(err)