Skip to content

Add optional signature on query-ask v2 #691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/retrieval.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func NewRetrievalClient() (*RetrievalClient, error) {
}

// Query sends a retrieval query v2 to another peer
func (c *RetrievalClient) Query(ctx context.Context, providerID peer.ID, query types.Query) (*types.QueryResponse, error) {
func (c *RetrievalClient) Query(ctx context.Context, providerID peer.ID, query types.SignedQuery) (*types.QueryResponse, error) {
// Send the deal proposal to the provider
return c.queryClient.SendQuery(ctx, providerID, query)
}
33 changes: 31 additions & 2 deletions cmd/boost/retrieve_piece_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import (
"github.com/filecoin-project/boost/retrievalmarket/lp2pimpl"
"github.com/filecoin-project/boost/retrievalmarket/types"
"github.com/filecoin-project/go-address"
lapi "github.com/filecoin-project/lotus/api"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/ipfs/go-cid"
"github.com/ipld/go-ipld-prime/codec/dagcbor"
"github.com/urfave/cli/v2"
)

Expand All @@ -28,6 +30,11 @@ var retrievePieceCmd = &cli.Command{
Usage: "",
Required: true,
},
&cli.StringFlag{
Name: "wallet",
Usage: "wallet to identify the client of a potential deal",
Required: false,
},
},
Before: before,
Action: func(cctx *cli.Context) error {
Expand Down Expand Up @@ -59,16 +66,38 @@ var retrievePieceCmd = &cli.Command{
return err
}

walletAddr, err := n.GetProvidedOrDefaultWallet(ctx, cctx.String("wallet"))
if err != nil {
return err
}

log.Debugw("found storage provider", "id", addrInfo.ID, "multiaddrs", addrInfo.Addrs, "addr", maddr)

if err := n.Host.Connect(ctx, *addrInfo); err != nil {
return fmt.Errorf("failed to connect to peer %s: %w", addrInfo.ID, err)
}

dc := lp2pimpl.NewQueryClient(n.Host)
resp, err := dc.SendQuery(ctx, addrInfo.ID, types.Query{
query := types.Query{
PieceCID: &pieceCID,
}

buf, err := types.BindnodeRegistry.TypeToBytes(query, dagcbor.Encode)
if err != nil {
return err
}

sig, err := n.Wallet.WalletSign(ctx, walletAddr, buf, lapi.MsgMeta{})
if err != nil {
return err
}

dc := lp2pimpl.NewQueryClient(n.Host)
resp, err := dc.SendQuery(ctx, addrInfo.ID, types.SignedQuery{
Query: query,
ClientAddress: &walletAddr,
ClientSignature: sig,
})

if err != nil {
return fmt.Errorf("send deal status request failed: %w", err)
}
Expand Down
1 change: 1 addition & 0 deletions node/modules/storageminer.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ func NewRetrievalMarketProvider(provAddr address.Address, cfg *config.Boost) fun
pieceStore,
dagStore,
askStore,
&signatureVerifier{a},
retrievalmarket.RetrievalPricingFunc(pricingFnc),
)
}
Expand Down
6 changes: 3 additions & 3 deletions retrievalmarket/lp2pimpl/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func NewQueryClient(h host.Host, options ...QueryClientOption) *QueryClient {
}

// SendQuery sends a retrieval query over a libp2p stream to the peer
func (c *QueryClient) SendQuery(ctx context.Context, id peer.ID, query types.Query) (*types.QueryResponse, error) {
func (c *QueryClient) SendQuery(ctx context.Context, id peer.ID, query types.SignedQuery) (*types.QueryResponse, error) {
log.Debugw("send query", "pieceCID", query.PieceCID, "payloadCID", query.PayloadCID, "provider-peer", id)

// Create a libp2p stream to the provider
Expand Down Expand Up @@ -125,12 +125,12 @@ func (p *QueryProvider) handleNewQueryStream(s network.Stream) {
defer s.SetReadDeadline(time.Time{}) // nolint

// Read the query from the stream
queryi, err := types.BindnodeRegistry.TypeFromReader(s, (*types.Query)(nil), dagcbor.Decode)
queryi, err := types.BindnodeRegistry.TypeFromReader(s, (*types.SignedQuery)(nil), dagcbor.Decode)
if err != nil {
log.Warnw("reading query from stream", "err", err)
return
}
query := queryi.(*types.Query)
query := queryi.(*types.SignedQuery)

// run the query to generate a response
queryResponse := p.prov.ExecuteQuery(query, s.Conn().RemotePeer())
Expand Down
38 changes: 36 additions & 2 deletions retrievalmarket/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-fil-markets/piecestore"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/shared"
"github.com/filecoin-project/go-fil-markets/stores"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api/v1api"
ctypes "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/markets/dagstore"
"github.com/hashicorp/go-multierror"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
"github.com/ipld/go-ipld-prime/codec/dagcbor"
peer "github.com/libp2p/go-libp2p-core/peer"
)

Expand All @@ -32,6 +35,11 @@ type Config struct {
// RetrievalPricingFunc is a custom function that sets retrieval pricing
type RetrievalPricingFunc func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error)

// SignatureVerifier is just wrapper for verifying signatures against arbitrary byte data
type SignatureVerifier interface {
VerifySignature(ctx context.Context, sig crypto.Signature, addr address.Address, input []byte, encodedTs shared.TipSetToken) (bool, error)
}

// Provider is the boost implementation of the retrieval provider, which currently
// only implements the QueryAsk v2 protocol
type Provider struct {
Expand All @@ -44,6 +52,7 @@ type Provider struct {
pieceStore piecestore.PieceStore
sa dagstore.SectorAccessor
askStore retrievalmarket.AskStore
signatureVerifier SignatureVerifier
retrievalPricingFunc RetrievalPricingFunc
}

Expand All @@ -55,6 +64,7 @@ func NewProvider(config Config,
pieceStore piecestore.PieceStore,
dagStore stores.DAGStoreWrapper,
askStore retrievalmarket.AskStore,
signatureVerifier SignatureVerifier,
retrievalPricingFunc RetrievalPricingFunc,
) (*Provider, error) {

Expand All @@ -71,6 +81,7 @@ func NewProvider(config Config,
retrievalPricingFunc: retrievalPricingFunc,
dagStore: dagStore,
askStore: askStore,
signatureVerifier: signatureVerifier,
fullnodeAPI: fullnodeAPI,
ctx: ctx,
cancel: cancel,
Expand All @@ -83,12 +94,35 @@ func (p *Provider) Stop() {
}

// ExecuteQuery generates a query response for the queryAsk v2 protocol
func (p *Provider) ExecuteQuery(q *types.Query, remote peer.ID) *types.QueryResponse {
func (p *Provider) ExecuteQuery(q *types.SignedQuery, remote peer.ID) *types.QueryResponse {

answer := types.QueryResponse{
Status: types.QueryResponseUnavailable,
}

// if address is present, verify signature
if q.ClientAddress != nil {
input, err := types.BindnodeRegistry.TypeToBytes(&q.Query, dagcbor.Encode)
if err != nil {
answer.Status = types.QueryResponseError
answer.Error = fmt.Sprintf("error writing query to bytes: %s", err)
return &answer
}

valid, err := p.signatureVerifier.VerifySignature(p.ctx, *q.ClientSignature, *q.ClientAddress, input, ctypes.EmptyTSK.Bytes())
if err != nil {
answer.Status = types.QueryResponseError
answer.Error = fmt.Sprintf("error while attempting to verify signature: %s", err)
return &answer
}

if !valid {
answer.Status = types.QueryResponseError
answer.Error = "signature invalid"
return &answer
}
}

if q.PayloadCID != nil {

// payload present, process as payload query
Expand All @@ -115,7 +149,7 @@ func (p *Provider) ExecuteQuery(q *types.Query, remote peer.ID) *types.QueryResp
}

// graphsync is always available for payloads, so assemble response -- we'll need
graphsyncFilecoinV1Response, err := p.graphsyncQueryResponse(q, remote, pieceInfo, isUnsealed)
graphsyncFilecoinV1Response, err := p.graphsyncQueryResponse(&q.Query, remote, pieceInfo, isUnsealed)
if err != nil {
answer.Status = types.QueryResponseError
answer.Error = err.Error()
Expand Down
Loading