From fb28e0a5413f24a619ae78442dce3adccf4a745e Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Thu, 22 Apr 2021 19:24:51 +0530 Subject: [PATCH 01/14] refactor for dynamic pricing --- go.sum | 18 +- .../impl/askstore/askstore_impl.go | 129 --------------- .../impl/askstore/askstore_impl_test.go | 75 --------- retrievalmarket/impl/integration_test.go | 46 ++++-- retrievalmarket/impl/provider.go | 95 ++++++----- retrievalmarket/impl/provider_environments.go | 58 ++++++- retrievalmarket/impl/provider_test.go | 154 ++++++++++++++---- .../impl/providerstates/provider_states.go | 16 ++ .../providerstates/provider_states_test.go | 48 +++++- .../requestvalidation/requestvalidation.go | 28 ++-- .../requestvalidation_test.go | 13 +- .../testnodes/test_retrieval_provider_node.go | 17 ++ retrievalmarket/nodes.go | 4 + retrievalmarket/provider.go | 12 -- .../storage_retrieval_integration_test.go | 23 ++- retrievalmarket/types.go | 8 + 16 files changed, 396 insertions(+), 348 deletions(-) delete mode 100644 retrievalmarket/impl/askstore/askstore_impl.go delete mode 100644 retrievalmarket/impl/askstore/askstore_impl_test.go diff --git a/go.sum b/go.sum index bf4ace72..eb938539 100644 --- a/go.sum +++ b/go.sum @@ -851,14 +851,15 @@ github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -github.com/xlab/c-for-go v0.0.0-20200718154222-87b0065af829 h1:wb7xrDzfkLgPHsSEBm+VSx6aDdi64VtV0xvP0E6j8bk= -github.com/xlab/c-for-go v0.0.0-20200718154222-87b0065af829/go.mod h1:h/1PEBwj7Ym/8kOuMWvO2ujZ6Lt+TMbySEXNhjjR87I= +github.com/xlab/c-for-go v0.0.0-20201112171043-ea6dce5809cb h1:/7/dQyiKnxAOj9L69FhST7uMe17U015XPzX7cy+5ykM= +github.com/xlab/c-for-go v0.0.0-20201112171043-ea6dce5809cb/go.mod h1:pbNsDSxn1ICiNn9Ct4ZGNrwzfkkwYbx/lw8VuyutFIg= github.com/xlab/pkgconfig v0.0.0-20170226114623-cea12a0fd245 h1:Sw125DKxZhPUI4JLlWugkzsrlB50jR9v2khiD9FxuSo= github.com/xlab/pkgconfig v0.0.0-20170226114623-cea12a0fd245/go.mod h1:C+diUUz7pxhNY6KAoLgrTYARGWnt82zWTylZlxT92vk= github.com/xorcare/golden v0.6.0 h1:E8emU8bhyMIEpYmgekkTUaw4vtcrRE+Wa0c5wYIcgXc= github.com/xorcare/golden v0.6.0/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -908,6 +909,7 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -980,8 +982,9 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1062,8 +1065,9 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3 h1:r3P/5xOq/dK1991B65Oy6E1fRF/2d/fSYZJ/fXGVfJc= golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696 h1:Bfazo+enXJET5SbHeh95NtxabJF6fJ9r/jpfRJgd3j4= +golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1160,8 +1164,12 @@ honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXe honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= modernc.org/cc v1.0.0 h1:nPibNuDEx6tvYrUAtvDTTw98rx5juGsa5zuDnKwEEQQ= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0 h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE= +modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/golex v1.0.1 h1:EYKY1a3wStt0RzHaH8mdSRNg78Ub0OHxYfCRWw35YtM= +modernc.org/golex v1.0.1/go.mod h1:QCA53QtsT1NdGkaZZkF5ezFwk4IXh4BGNafAARTC254= +modernc.org/lex v1.0.0/go.mod h1:G6rxMTy3cH2iA0iXL/HRRv4Znu8MK4higxph/lE7ypk= +modernc.org/lexer v1.0.0/go.mod h1:F/Dld0YKYdZCLQ7bD0USbWL4YKCyTDRDHiDTOs0q0vk= modernc.org/mathutil v1.1.1 h1:FeylZSVX8S+58VsyJlkEj2bcpdytmp9MmDKZkKx8OIE= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= diff --git a/retrievalmarket/impl/askstore/askstore_impl.go b/retrievalmarket/impl/askstore/askstore_impl.go deleted file mode 100644 index 857e06b5..00000000 --- a/retrievalmarket/impl/askstore/askstore_impl.go +++ /dev/null @@ -1,129 +0,0 @@ -package askstore - -import ( - "bytes" - "context" - "sync" - - "github.com/ipfs/go-datastore" - "golang.org/x/xerrors" - - cborutil "github.com/filecoin-project/go-cbor-util" - versioning "github.com/filecoin-project/go-ds-versioning/pkg" - versionedds "github.com/filecoin-project/go-ds-versioning/pkg/datastore" - - "github.com/filecoin-project/go-fil-markets/retrievalmarket" - "github.com/filecoin-project/go-fil-markets/retrievalmarket/migrations" -) - -// AskStoreImpl implements AskStore, persisting a retrieval Ask -// to disk. It also maintains a cache of the current Ask in memory -type AskStoreImpl struct { - lk sync.RWMutex - ask *retrievalmarket.Ask - ds datastore.Batching - key datastore.Key -} - -// NewAskStore returns a new instance of AskStoreImpl -// It will initialize a new default ask and store it if one is not set. -// Otherwise it loads the current Ask from disk -func NewAskStore(ds datastore.Batching, key datastore.Key) (*AskStoreImpl, error) { - askMigrations, err := migrations.AskMigrations.Build() - if err != nil { - return nil, err - } - versionedDs, migrateDs := versionedds.NewVersionedDatastore(ds, askMigrations, versioning.VersionKey("1")) - err = migrateDs(context.TODO()) - if err != nil { - return nil, err - } - s := &AskStoreImpl{ - ds: versionedDs, - key: key, - } - - if err := s.tryLoadAsk(); err != nil { - return nil, err - } - - if s.ask == nil { - // for now set a default retrieval ask - defaultAsk := &retrievalmarket.Ask{ - PricePerByte: retrievalmarket.DefaultPricePerByte, - UnsealPrice: retrievalmarket.DefaultUnsealPrice, - PaymentInterval: retrievalmarket.DefaultPaymentInterval, - PaymentIntervalIncrease: retrievalmarket.DefaultPaymentIntervalIncrease, - } - - if err := s.SetAsk(defaultAsk); err != nil { - return nil, xerrors.Errorf("failed setting a default retrieval ask: %w", err) - } - } - return s, nil -} - -// SetAsk stores retrieval provider's ask -func (s *AskStoreImpl) SetAsk(ask *retrievalmarket.Ask) error { - s.lk.Lock() - defer s.lk.Unlock() - - return s.saveAsk(ask) -} - -// GetAsk returns the current retrieval ask, or nil if one does not exist. -func (s *AskStoreImpl) GetAsk() *retrievalmarket.Ask { - s.lk.RLock() - defer s.lk.RUnlock() - if s.ask == nil { - return nil - } - ask := *s.ask - return &ask -} - -func (s *AskStoreImpl) tryLoadAsk() error { - s.lk.Lock() - defer s.lk.Unlock() - - err := s.loadAsk() - - if err != nil { - if xerrors.Is(err, datastore.ErrNotFound) { - // this is expected - return nil - } - return err - } - - return nil -} - -func (s *AskStoreImpl) loadAsk() error { - askb, err := s.ds.Get(s.key) - if err != nil { - return xerrors.Errorf("failed to load most recent retrieval ask from disk: %w", err) - } - - var ask retrievalmarket.Ask - if err := cborutil.ReadCborRPC(bytes.NewReader(askb), &ask); err != nil { - return err - } - - s.ask = &ask - return nil -} - -func (s *AskStoreImpl) saveAsk(a *retrievalmarket.Ask) error { - b, err := cborutil.Dump(a) - if err != nil { - return err - } - - if err := s.ds.Put(s.key, b); err != nil { - return err - } - - s.ask = a - return nil -} diff --git a/retrievalmarket/impl/askstore/askstore_impl_test.go b/retrievalmarket/impl/askstore/askstore_impl_test.go deleted file mode 100644 index 296ab2c8..00000000 --- a/retrievalmarket/impl/askstore/askstore_impl_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package askstore_test - -import ( - "bytes" - "math/rand" - "testing" - - "github.com/ipfs/go-datastore" - dss "github.com/ipfs/go-datastore/sync" - "github.com/stretchr/testify/require" - - "github.com/filecoin-project/go-state-types/abi" - - "github.com/filecoin-project/go-fil-markets/retrievalmarket" - "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/askstore" - "github.com/filecoin-project/go-fil-markets/retrievalmarket/migrations" -) - -func TestAskStoreImpl(t *testing.T) { - ds := dss.MutexWrap(datastore.NewMapDatastore()) - store, err := askstore.NewAskStore(ds, datastore.NewKey("retrieval-ask")) - require.NoError(t, err) - - // A new store returns the default ask - ask := store.GetAsk() - require.NotNil(t, ask) - - require.Equal(t, retrievalmarket.DefaultUnsealPrice, ask.UnsealPrice) - require.Equal(t, retrievalmarket.DefaultPricePerByte, ask.PricePerByte) - require.Equal(t, retrievalmarket.DefaultPaymentInterval, ask.PaymentInterval) - require.Equal(t, retrievalmarket.DefaultPaymentIntervalIncrease, ask.PaymentIntervalIncrease) - - // Store a new ask - newAsk := &retrievalmarket.Ask{ - PricePerByte: abi.NewTokenAmount(123), - UnsealPrice: abi.NewTokenAmount(456), - PaymentInterval: 789, - PaymentIntervalIncrease: 789, - } - err = store.SetAsk(newAsk) - require.NoError(t, err) - - // Fetch new ask - stored := store.GetAsk() - require.Equal(t, newAsk, stored) - - // Construct a new AskStore and make sure it returns the previously-stored ask - newStore, err := askstore.NewAskStore(ds, datastore.NewKey("retrieval-ask")) - require.NoError(t, err) - stored = newStore.GetAsk() - require.Equal(t, newAsk, stored) -} -func TestMigrations(t *testing.T) { - ds := dss.MutexWrap(datastore.NewMapDatastore()) - oldAsk := &migrations.Ask0{ - PricePerByte: abi.NewTokenAmount(rand.Int63()), - UnsealPrice: abi.NewTokenAmount(rand.Int63()), - PaymentInterval: rand.Uint64(), - PaymentIntervalIncrease: rand.Uint64(), - } - buf := new(bytes.Buffer) - err := oldAsk.MarshalCBOR(buf) - require.NoError(t, err) - ds.Put(datastore.NewKey("retrieval-ask"), buf.Bytes()) - newStore, err := askstore.NewAskStore(ds, datastore.NewKey("retrieval-ask")) - require.NoError(t, err) - ask := newStore.GetAsk() - expectedAsk := &retrievalmarket.Ask{ - PricePerByte: oldAsk.PricePerByte, - UnsealPrice: oldAsk.UnsealPrice, - PaymentInterval: oldAsk.PaymentInterval, - PaymentIntervalIncrease: oldAsk.PaymentIntervalIncrease, - } - require.Equal(t, expectedAsk, ask) -} diff --git a/retrievalmarket/impl/integration_test.go b/retrievalmarket/impl/integration_test.go index 482b0a5b..972cba32 100644 --- a/retrievalmarket/impl/integration_test.go +++ b/retrievalmarket/impl/integration_test.go @@ -58,6 +58,10 @@ func TestClientCanMakeQueryToProvider(t *testing.T) { expectedQR.Status = retrievalmarket.QueryResponseUnavailable expectedQR.Size = 0 actualQR, err := client.Query(bgCtx, retrievalPeer, missingPiece, retrievalmarket.QueryParams{}) + actualQR.MaxPaymentInterval = expectedQR.MaxPaymentInterval + actualQR.MinPricePerByte = expectedQR.MinPricePerByte + actualQR.MaxPaymentIntervalIncrease = expectedQR.MaxPaymentIntervalIncrease + actualQR.UnsealPrice = expectedQR.UnsealPrice assert.NoError(t, err) assert.Equal(t, expectedQR, actualQR) }) @@ -68,6 +72,10 @@ func TestClientCanMakeQueryToProvider(t *testing.T) { expectedQR.Message = "get cid info: GetCIDInfo failed" actualQR, err := client.Query(bgCtx, retrievalPeer, unknownPiece, retrievalmarket.QueryParams{}) assert.NoError(t, err) + actualQR.MaxPaymentInterval = expectedQR.MaxPaymentInterval + actualQR.MinPricePerByte = expectedQR.MinPricePerByte + actualQR.MaxPaymentIntervalIncrease = expectedQR.MaxPaymentIntervalIncrease + actualQR.UnsealPrice = expectedQR.UnsealPrice assert.Equal(t, expectedQR, actualQR) }) @@ -148,15 +156,20 @@ func requireSetupTestClientAndProvider(ctx context.Context, t *testing.T, payChA testutil.StartAndWaitForReady(ctx, t, dt2) require.NoError(t, err) providerDs := namespace.Wrap(testData.Ds2, datastore.NewKey("/retrievals/provider")) - provider, err := retrievalimpl.NewProvider(paymentAddress, providerNode, nw2, pieceStore, testData.MultiStore2, dt2, providerDs) + + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} + ask.PaymentInterval = expectedQR.MaxPaymentInterval + ask.PaymentIntervalIncrease = expectedQR.MaxPaymentIntervalIncrease + ask.PricePerByte = expectedQR.MinPricePerByte + ask.UnsealPrice = expectedQR.UnsealPrice + return ask, nil + } + + provider, err := retrievalimpl.NewProvider(paymentAddress, providerNode, nw2, pieceStore, testData.MultiStore2, dt2, providerDs, + priceFunc) require.NoError(t, err) - ask := provider.GetAsk() - ask.PaymentInterval = expectedQR.MaxPaymentInterval - ask.PaymentIntervalIncrease = expectedQR.MaxPaymentIntervalIncrease - ask.PricePerByte = expectedQR.MinPricePerByte - ask.UnsealPrice = expectedQR.UnsealPrice - provider.SetAsk(ask) tut.StartAndWaitForReady(ctx, t, provider) retrievalPeer := retrievalmarket.RetrievalPeer{ Address: paymentAddress, @@ -655,18 +668,21 @@ func setupProvider( if disableNewDeals { opts = append(opts, retrievalimpl.DisableNewDeals()) } + + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} + ask.PaymentInterval = expectedQR.MaxPaymentInterval + ask.PaymentIntervalIncrease = expectedQR.MaxPaymentIntervalIncrease + ask.PricePerByte = expectedQR.MinPricePerByte + ask.UnsealPrice = expectedQR.UnsealPrice + return ask, nil + } + provider, err := retrievalimpl.NewProvider(providerPaymentAddr, providerNode, nw2, - pieceStore, testData.MultiStore2, dt2, providerDs, + pieceStore, testData.MultiStore2, dt2, providerDs, priceFunc, opts...) require.NoError(t, err) - ask := provider.GetAsk() - - ask.PaymentInterval = expectedQR.MaxPaymentInterval - ask.PaymentIntervalIncrease = expectedQR.MaxPaymentIntervalIncrease - ask.PricePerByte = expectedQR.MinPricePerByte - ask.UnsealPrice = expectedQR.UnsealPrice - provider.SetAsk(ask) return provider } diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index 5ea8ae5c..1d61423c 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -7,7 +7,6 @@ import ( "github.com/hannahhoward/go-pubsub" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" - "github.com/ipfs/go-datastore/namespace" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -15,11 +14,11 @@ import ( versioning "github.com/filecoin-project/go-ds-versioning/pkg" versionedfsm "github.com/filecoin-project/go-ds-versioning/pkg/fsm" "github.com/filecoin-project/go-multistore" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-statemachine/fsm" "github.com/filecoin-project/go-fil-markets/piecestore" "github.com/filecoin-project/go-fil-markets/retrievalmarket" - "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/askstore" "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/dtutils" "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/providerstates" "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/requestvalidation" @@ -34,6 +33,8 @@ type RetrievalProviderOption func(p *Provider) // DealDecider is a function that makes a decision about whether to accept a deal type DealDecider func(ctx context.Context, state retrievalmarket.ProviderDealState) (bool, string, error) +type DealPricingFunc func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) + // Provider is the production implementation of the RetrievalProvider interface type Provider struct { multiStore *multistore.MultiStore @@ -49,8 +50,8 @@ type Provider struct { stateMachines fsm.Group migrateStateMachines func(context.Context) error dealDecider DealDecider - askStore retrievalmarket.AskStore disableNewDeals bool + dealPricingFunc DealPricingFunc } type internalProviderEvent struct { @@ -95,29 +96,30 @@ func NewProvider(minerAddress address.Address, multiStore *multistore.MultiStore, dataTransfer datatransfer.Manager, ds datastore.Batching, + dealPricingFunc DealPricingFunc, opts ...RetrievalProviderOption, ) (retrievalmarket.RetrievalProvider, error) { + if dealPricingFunc == nil { + return nil, xerrors.New("dealPricingFunc is nil") + } + p := &Provider{ - multiStore: multiStore, - dataTransfer: dataTransfer, - node: node, - network: network, - minerAddress: minerAddress, - pieceStore: pieceStore, - subscribers: pubsub.New(providerDispatcher), - readySub: pubsub.New(shared.ReadyDispatcher), + multiStore: multiStore, + dataTransfer: dataTransfer, + node: node, + network: network, + minerAddress: minerAddress, + pieceStore: pieceStore, + subscribers: pubsub.New(providerDispatcher), + readySub: pubsub.New(shared.ReadyDispatcher), + dealPricingFunc: dealPricingFunc, } err := shared.MoveKey(ds, "retrieval-ask", "retrieval-ask/latest") if err != nil { return nil, err } - askStore, err := askstore.NewAskStore(namespace.Wrap(ds, datastore.NewKey("retrieval-ask")), datastore.NewKey("latest")) - if err != nil { - return nil, err - } - p.askStore = askStore retrievalMigrations, err := migrations.ProviderMigrations.Build() if err != nil { @@ -227,20 +229,6 @@ func (p *Provider) SubscribeToEvents(subscriber retrievalmarket.ProviderSubscrib return retrievalmarket.Unsubscribe(p.subscribers.Subscribe(subscriber)) } -// GetAsk returns the current deal parameters this provider accepts -func (p *Provider) GetAsk() *retrievalmarket.Ask { - return p.askStore.GetAsk() -} - -// SetAsk sets the deal parameters this provider accepts -func (p *Provider) SetAsk(ask *retrievalmarket.Ask) { - err := p.askStore.SetAsk(ask) - - if err != nil { - log.Warnf("Error setting retrieval ask: %w", err) - } -} - // ListDeals lists all known retrieval deals func (p *Provider) ListDeals() map[retrievalmarket.ProviderDealIdentifier]retrievalmarket.ProviderDealState { var deals []retrievalmarket.ProviderDealState @@ -274,15 +262,11 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { return } - ask := p.GetAsk() - answer := retrievalmarket.QueryResponse{ - Status: retrievalmarket.QueryResponseUnavailable, - PieceCIDFound: retrievalmarket.QueryItemUnavailable, - MinPricePerByte: ask.PricePerByte, - MaxPaymentInterval: ask.PaymentInterval, - MaxPaymentIntervalIncrease: ask.PaymentIntervalIncrease, - UnsealPrice: ask.UnsealPrice, + Status: retrievalmarket.QueryResponseUnavailable, + PieceCIDFound: retrievalmarket.QueryItemUnavailable, + MinPricePerByte: big.Zero(), + UnsealPrice: big.Zero(), } ctx := context.TODO() @@ -305,13 +289,22 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { if query.PieceCID != nil { pieceCID = *query.PieceCID } - pieceInfo, err := getPieceInfoFromCid(p.pieceStore, query.PayloadCID, pieceCID) - + pieceInfo, isUnsealed, err := getPieceInfoFromCid(ctx, p.node, p.pieceStore, query.PayloadCID, pieceCID) if err == nil && len(pieceInfo.Deals) > 0 { answer.Status = retrievalmarket.QueryResponseAvailable - // TODO: get price, look for already unsealed ref to reduce work answer.Size = uint64(pieceInfo.Deals[0].Length) // TODO: verify on intermediate answer.PieceCIDFound = retrievalmarket.QueryItemAvailable + + ask, err := p.GetAsk(ctx, pieceInfo, isUnsealed) + if err != nil { + log.Errorf("Retrieval query: GetAsk: %s", err) + return + } + + answer.MinPricePerByte = ask.PricePerByte + answer.MaxPaymentInterval = ask.PaymentInterval + answer.MaxPaymentIntervalIncrease = ask.PaymentIntervalIncrease + answer.UnsealPrice = ask.UnsealPrice } if err != nil && !xerrors.Is(err, retrievalmarket.ErrNotFound) { @@ -327,6 +320,26 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { } } +func (p *Provider) GetAsk(ctx context.Context, piece piecestore.PieceInfo, isUnsealed bool) (retrievalmarket.Ask, error) { + // TODO What should we do here if we have multiple deals ? + // How to fetch Verified Deal & Fast Retrieval here ? + // What's a correct "DealPricingParam" + tok, _, err := p.node.GetChainHead(ctx) + if err != nil { + return retrievalmarket.Ask{}, xerrors.Errorf("failed to GetChainHead: %s", err) + } + dp, err := p.node.GetDealPricingParams(ctx, piece.Deals[0].DealID, tok) + if err != nil { + return retrievalmarket.Ask{}, xerrors.Errorf("GetDealPricingParams: %s", err) + } + dp.Unsealed = isUnsealed + ask, err := p.dealPricingFunc(ctx, dp) + if err != nil { + return retrievalmarket.Ask{}, xerrors.Errorf("dealPricingFunc: %s", err) + } + return ask, nil +} + // Configure reconfigures a provider after initialization func (p *Provider) Configure(opts ...RetrievalProviderOption) { for _, opt := range opts { diff --git a/retrievalmarket/impl/provider_environments.go b/retrievalmarket/impl/provider_environments.go index 16cde2d8..f5f94286 100644 --- a/retrievalmarket/impl/provider_environments.go +++ b/retrievalmarket/impl/provider_environments.go @@ -30,17 +30,22 @@ type providerValidationEnvironment struct { p *Provider } -func (pve *providerValidationEnvironment) GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, error) { +func (pve *providerValidationEnvironment) GetAsk(ctx context.Context, piece piecestore.PieceInfo, isUnsealed bool) (retrievalmarket.Ask, error) { + return pve.p.GetAsk(ctx, piece, isUnsealed) +} + +func (pve *providerValidationEnvironment) GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, bool, error) { inPieceCid := cid.Undef if pieceCID != nil { inPieceCid = *pieceCID } - return getPieceInfoFromCid(pve.p.pieceStore, c, inPieceCid) + + pi, isUnsealed, err := getPieceInfoFromCid(context.TODO(), pve.p.node, pve.p.pieceStore, c, inPieceCid) + return pi, isUnsealed, err } // CheckDealParams verifies the given deal params are acceptable -func (pve *providerValidationEnvironment) CheckDealParams(pricePerByte abi.TokenAmount, paymentInterval uint64, paymentIntervalIncrease uint64, unsealPrice abi.TokenAmount) error { - ask := pve.p.GetAsk() +func (pve *providerValidationEnvironment) CheckDealParams(ask retrievalmarket.Ask, pricePerByte abi.TokenAmount, paymentInterval uint64, paymentIntervalIncrease uint64, unsealPrice abi.TokenAmount) error { if pricePerByte.LessThan(ask.PricePerByte) { return errors.New("Price per byte too low") } @@ -188,25 +193,60 @@ func (pde *providerDealEnvironment) CloseDataTransfer(ctx context.Context, chid func (pde *providerDealEnvironment) DeleteStore(storeID multistore.StoreID) error { return pde.p.multiStore.Delete(storeID) } -func getPieceInfoFromCid(pieceStore piecestore.PieceStore, payloadCID, pieceCID cid.Cid) (piecestore.PieceInfo, error) { + +func pieceInUnsealedSector(ctx context.Context, n retrievalmarket.RetrievalProviderNode, pieceInfo piecestore.PieceInfo) bool { + for _, di := range pieceInfo.Deals { + isUnsealed, err := n.IsUnsealed(ctx, di.SectorID, di.Offset.Unpadded(), di.Length.Unpadded()) + if err != nil { + continue + } + if isUnsealed { + return true + } + } + + return false +} + +func getPieceInfoFromCid(ctx context.Context, n retrievalmarket.RetrievalProviderNode, pieceStore piecestore.PieceStore, payloadCID, pieceCID cid.Cid) (piecestore.PieceInfo, bool, error) { cidInfo, err := pieceStore.GetCIDInfo(payloadCID) if err != nil { - return piecestore.PieceInfoUndefined, xerrors.Errorf("get cid info: %w", err) + return piecestore.PieceInfoUndefined, false, xerrors.Errorf("get cid info: %w", err) } var lastErr error + var sealedPieceInfo *piecestore.PieceInfo + for _, pieceBlockLocation := range cidInfo.PieceBlockLocations { pieceInfo, err := pieceStore.GetPieceInfo(pieceBlockLocation.PieceCID) if err == nil { - if pieceCID.Equals(cid.Undef) || pieceInfo.PieceCID.Equals(pieceCID) { - return pieceInfo, nil + // if client wants to retrieve the payload from a specific piece, just return that piece. + if !pieceCID.Equals(cid.Undef) && pieceInfo.PieceCID.Equals(pieceCID) { + return pieceInfo, pieceInUnsealedSector(ctx, n, pieceInfo), nil + } + + // if client dosen't have a preference for a particular piece, prefer a piece + // for which an unsealed sector exists. + if pieceCID.Equals(cid.Undef) { + if pieceInUnsealedSector(ctx, n, pieceInfo) { + return pieceInfo, true, nil + } + + if sealedPieceInfo == nil { + sealedPieceInfo = &pieceInfo + } } } lastErr = err } + + if sealedPieceInfo != nil { + return *sealedPieceInfo, false, nil + } + if lastErr == nil { lastErr = xerrors.Errorf("unknown pieceCID %s", pieceCID.String()) } - return piecestore.PieceInfoUndefined, xerrors.Errorf("could not locate piece: %w", lastErr) + return piecestore.PieceInfoUndefined, false, xerrors.Errorf("could not locate piece: %w", lastErr) } var _ dtutils.StoreGetter = &providerStoreGetter{} diff --git a/retrievalmarket/impl/provider_test.go b/retrievalmarket/impl/provider_test.go index 4ece7013..ffa95b61 100644 --- a/retrievalmarket/impl/provider_test.go +++ b/retrievalmarket/impl/provider_test.go @@ -43,13 +43,19 @@ func TestHandleQueryStream(t *testing.T) { payloadCID := tut.GenerateCids(1)[0] expectedPeer := peer.ID("somepeer") expectedSize := uint64(1234) + expectedSize2 := uint64(2234) expectedPieceCID := tut.GenerateCids(1)[0] + expectedPieceCID2 := tut.GenerateCids(1)[0] + expectedCIDInfo := piecestore.CIDInfo{ PieceBlockLocations: []piecestore.PieceBlockLocation{ { PieceCID: expectedPieceCID, }, + { + PieceCID: expectedPieceCID2, + }, }, } expectedPiece := piecestore.PieceInfo{ @@ -59,10 +65,20 @@ func TestHandleQueryStream(t *testing.T) { }, }, } + + expectedPiece2 := piecestore.PieceInfo{ + Deals: []piecestore.DealInfo{ + { + Length: abi.PaddedPieceSize(expectedSize2), + }, + }, + } + expectedAddress := address.TestAddress2 expectedPricePerByte := abi.NewTokenAmount(4321) expectedPaymentInterval := uint64(4567) expectedPaymentIntervalIncrease := uint64(100) + expectedUnsealPrice := abi.NewTokenAmount(100) readWriteQueryStream := func() network.RetrievalQueryStream { qRead, qWrite := tut.QueryReadWriter() @@ -77,21 +93,24 @@ func TestHandleQueryStream(t *testing.T) { return qs } - receiveStreamOnProvider := func(t *testing.T, qs network.RetrievalQueryStream, pieceStore piecestore.PieceStore) { - node := testnodes.NewTestRetrievalProviderNode() + receiveStreamOnProvider := func(t *testing.T, node *testnodes.TestRetrievalProviderNode, qs network.RetrievalQueryStream, pieceStore piecestore.PieceStore) { ds := dss.MutexWrap(datastore.NewMapDatastore()) multiStore, err := multistore.NewMultiDstore(ds) require.NoError(t, err) dt := tut.NewTestDataTransfer() net := tut.NewTestRetrievalMarketNetwork(tut.TestNetworkParams{}) - c, err := retrievalimpl.NewProvider(expectedAddress, node, net, pieceStore, multiStore, dt, ds) - require.NoError(t, err) - ask := c.GetAsk() - ask.PricePerByte = expectedPricePerByte - ask.PaymentInterval = expectedPaymentInterval - ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease - c.SetAsk(ask) + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} + ask.PricePerByte = expectedPricePerByte + ask.PaymentInterval = expectedPaymentInterval + ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease + ask.UnsealPrice = expectedUnsealPrice + return ask, nil + } + + c, err := retrievalimpl.NewProvider(expectedAddress, node, net, pieceStore, multiStore, dt, ds, priceFunc) + require.NoError(t, err) tut.StartAndWaitForReady(ctx, t, c) @@ -99,16 +118,23 @@ func TestHandleQueryStream(t *testing.T) { } testCases := []struct { - name string - query retrievalmarket.Query - expResp retrievalmarket.QueryResponse - expErr string - expFunc func(t *testing.T, pieceStore *tut.TestPieceStore) + name string + query retrievalmarket.Query + expResp retrievalmarket.QueryResponse + expErr string + expFunc func(t *testing.T, pieceStore *tut.TestPieceStore) + nodeFunc func(n *testnodes.TestRetrievalProviderNode) + + expectedPricePerByte abi.TokenAmount + expectedPaymentInterval uint64 + expectedPaymentIntervalIncrease uint64 + expectedUnsealPrice abi.TokenAmount }{ {name: "When PieceCID is not provided and PayloadCID is found", expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID, expectedPiece) + pieceStore.ExpectPiece(expectedPieceCID2, expectedPiece2) }, query: retrievalmarket.Query{PayloadCID: payloadCID}, expResp: retrievalmarket.QueryResponse{ @@ -116,7 +142,34 @@ func TestHandleQueryStream(t *testing.T) { PieceCIDFound: retrievalmarket.QueryItemAvailable, Size: expectedSize, }, + expectedPricePerByte: expectedPricePerByte, + expectedPaymentInterval: expectedPaymentInterval, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedUnsealPrice: expectedUnsealPrice, + }, + + {name: "When PieceCID is not provided, prefer a piece for which an unsealed sector already exists", + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := expectedPiece2.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID, expectedPiece) + pieceStore.ExpectPiece(expectedPieceCID2, expectedPiece2) + }, + query: retrievalmarket.Query{PayloadCID: payloadCID}, + expResp: retrievalmarket.QueryResponse{ + Status: retrievalmarket.QueryResponseAvailable, + PieceCIDFound: retrievalmarket.QueryItemAvailable, + Size: expectedSize2, + }, + expectedPricePerByte: expectedPricePerByte, + expectedPaymentInterval: expectedPaymentInterval, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedUnsealPrice: expectedUnsealPrice, }, + {name: "When PieceCID is provided and both PieceCID and PayloadCID are found", expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { loadPieceCIDS(t, pieceStore, payloadCID, expectedPieceCID) @@ -130,12 +183,18 @@ func TestHandleQueryStream(t *testing.T) { PieceCIDFound: retrievalmarket.QueryItemAvailable, Size: expectedSize, }, + expectedPricePerByte: expectedPricePerByte, + expectedPaymentInterval: expectedPaymentInterval, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedUnsealPrice: expectedUnsealPrice, }, {name: "When QueryParams has PieceCID and is missing", expFunc: func(t *testing.T, ps *tut.TestPieceStore) { loadPieceCIDS(t, ps, payloadCID, cid.Undef) ps.ExpectCID(payloadCID, expectedCIDInfo) ps.ExpectMissingPiece(expectedPieceCID) + ps.ExpectMissingPiece(expectedPieceCID2) + }, query: retrievalmarket.Query{ PayloadCID: payloadCID, @@ -145,6 +204,10 @@ func TestHandleQueryStream(t *testing.T) { Status: retrievalmarket.QueryResponseUnavailable, PieceCIDFound: retrievalmarket.QueryItemUnavailable, }, + expectedPricePerByte: big.Zero(), + expectedPaymentInterval: 0, + expectedPaymentIntervalIncrease: 0, + expectedUnsealPrice: big.Zero(), }, {name: "When CID info not found", expFunc: func(t *testing.T, ps *tut.TestPieceStore) { @@ -158,20 +221,26 @@ func TestHandleQueryStream(t *testing.T) { Status: retrievalmarket.QueryResponseUnavailable, PieceCIDFound: retrievalmarket.QueryItemUnavailable, }, + expectedPricePerByte: big.Zero(), + expectedPaymentInterval: 0, + expectedPaymentIntervalIncrease: 0, + expectedUnsealPrice: big.Zero(), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + node := testnodes.NewTestRetrievalProviderNode() qs := readWriteQueryStream() err := qs.WriteQuery(tc.query) require.NoError(t, err) pieceStore := tut.NewTestPieceStore() - pieceStore.ExpectCID(payloadCID, expectedCIDInfo) - pieceStore.ExpectMissingPiece(expectedPieceCID) + if tc.nodeFunc != nil { + tc.nodeFunc(node) + } tc.expFunc(t, pieceStore) - receiveStreamOnProvider(t, qs, pieceStore) + receiveStreamOnProvider(t, node, qs, pieceStore) actualResp, err := qs.ReadQueryResponse() pieceStore.VerifyExpectations(t) @@ -182,15 +251,17 @@ func TestHandleQueryStream(t *testing.T) { } tc.expResp.PaymentAddress = expectedAddress - tc.expResp.MinPricePerByte = expectedPricePerByte - tc.expResp.MaxPaymentInterval = expectedPaymentInterval - tc.expResp.MaxPaymentIntervalIncrease = expectedPaymentIntervalIncrease - tc.expResp.UnsealPrice = big.Zero() + tc.expResp.MinPricePerByte = tc.expectedPricePerByte + tc.expResp.MaxPaymentInterval = tc.expectedPaymentInterval + tc.expResp.MaxPaymentIntervalIncrease = tc.expectedPaymentIntervalIncrease + tc.expResp.UnsealPrice = tc.expectedUnsealPrice assert.Equal(t, tc.expResp, actualResp) }) } t.Run("error reading piece", func(t *testing.T) { + node := testnodes.NewTestRetrievalProviderNode() + qs := readWriteQueryStream() err := qs.WriteQuery(retrievalmarket.Query{ PayloadCID: payloadCID, @@ -198,7 +269,7 @@ func TestHandleQueryStream(t *testing.T) { require.NoError(t, err) pieceStore := tut.NewTestPieceStore() - receiveStreamOnProvider(t, qs, pieceStore) + receiveStreamOnProvider(t, node, qs, pieceStore) response, err := qs.ReadQueryResponse() require.NoError(t, err) @@ -207,10 +278,11 @@ func TestHandleQueryStream(t *testing.T) { }) t.Run("when ReadDealStatusRequest fails", func(t *testing.T) { + node := testnodes.NewTestRetrievalProviderNode() qs := readWriteQueryStream() pieceStore := tut.NewTestPieceStore() - receiveStreamOnProvider(t, qs, pieceStore) + receiveStreamOnProvider(t, node, qs, pieceStore) response, err := qs.ReadQueryResponse() require.NotNil(t, err) @@ -218,6 +290,7 @@ func TestHandleQueryStream(t *testing.T) { }) t.Run("when WriteDealStatusResponse fails", func(t *testing.T) { + node := testnodes.NewTestRetrievalProviderNode() qRead, qWrite := tut.QueryReadWriter() qs := tut.NewTestRetrievalQueryStream(tut.TestQueryStreamParams{ PeerID: expectedPeer, @@ -232,8 +305,9 @@ func TestHandleQueryStream(t *testing.T) { pieceStore := tut.NewTestPieceStore() pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID, expectedPiece) + pieceStore.ExpectPiece(expectedPieceCID2, expectedPiece2) - receiveStreamOnProvider(t, qs, pieceStore) + receiveStreamOnProvider(t, node, qs, pieceStore) pieceStore.VerifyExpectations(t) }) @@ -245,6 +319,12 @@ func TestProvider_Construct(t *testing.T) { multiStore, err := multistore.NewMultiDstore(ds) require.NoError(t, err) dt := tut.NewTestDataTransfer() + + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} + return ask, nil + } + _, err = retrievalimpl.NewProvider( spect.NewIDAddr(t, 2344), testnodes.NewTestRetrievalProviderNode(), @@ -253,6 +333,7 @@ func TestProvider_Construct(t *testing.T) { multiStore, dt, ds, + priceFunc, ) require.NoError(t, err) require.Len(t, dt.Subscribers, 1) @@ -290,6 +371,12 @@ func TestProviderConfigOpts(t *testing.T) { ds := datastore.NewMapDatastore() multiStore, err := multistore.NewMultiDstore(ds) require.NoError(t, err) + + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} + return ask, nil + } + p, err := retrievalimpl.NewProvider( spect.NewIDAddr(t, 2344), testnodes.NewTestRetrievalProviderNode(), @@ -297,7 +384,7 @@ func TestProviderConfigOpts(t *testing.T) { tut.NewTestPieceStore(), multiStore, tut.NewTestDataTransfer(), - ds, opt1, opt2, + ds, priceFunc, opt1, opt2, ) require.NoError(t, err) assert.NotNil(t, p) @@ -317,7 +404,7 @@ func TestProviderConfigOpts(t *testing.T) { tut.NewTestPieceStore(), multiStore, tut.NewTestDataTransfer(), - ds, ddOpt) + ds, priceFunc, ddOpt) require.NoError(t, err) require.NotNil(t, p) } @@ -464,6 +551,12 @@ func TestProviderMigrations(t *testing.T) { require.NoError(t, err) err = providerDs.Put(datastore.NewKey("retrieval-ask"), askBuf.Bytes()) require.NoError(t, err) + + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} + return ask, nil + } + retrievalProvider, err := retrievalimpl.NewProvider( spect.NewIDAddr(t, 2344), testnodes.NewTestRetrievalProviderNode(), @@ -472,6 +565,7 @@ func TestProviderMigrations(t *testing.T) { multiStore, dt, providerDs, + priceFunc, ) require.NoError(t, err) tut.StartAndWaitForReady(ctx, t, retrievalProvider) @@ -518,12 +612,4 @@ func TestProviderMigrations(t *testing.T) { } require.Equal(t, expectedDeal, deal) } - ask := retrievalProvider.GetAsk() - expectedAsk := &retrievalmarket.Ask{ - PricePerByte: oldAsk.PricePerByte, - UnsealPrice: oldAsk.UnsealPrice, - PaymentInterval: oldAsk.PaymentInterval, - PaymentIntervalIncrease: oldAsk.PaymentIntervalIncrease, - } - require.Equal(t, expectedAsk, ask) } diff --git a/retrievalmarket/impl/providerstates/provider_states.go b/retrievalmarket/impl/providerstates/provider_states.go index 493ab996..2109b9f7 100644 --- a/retrievalmarket/impl/providerstates/provider_states.go +++ b/retrievalmarket/impl/providerstates/provider_states.go @@ -30,7 +30,23 @@ type ProviderDealEnvironment interface { } func firstSuccessfulUnseal(ctx context.Context, node rm.RetrievalProviderNode, pieceInfo piecestore.PieceInfo) (io.ReadCloser, error) { + + // prefer an unsealed sector containing the piece if one exists + for _, deal := range pieceInfo.Deals { + isUnsealed, err := node.IsUnsealed(ctx, deal.SectorID, deal.Offset.Unpadded(), deal.Length.Unpadded()) + if err != nil { + continue + } + if isUnsealed { + reader, err := node.UnsealSector(ctx, deal.SectorID, deal.Offset.Unpadded(), deal.Length.Unpadded()) + if err == nil { + return reader, nil + } + } + } + lastErr := xerrors.New("no sectors found to unseal from") + // if there is no unsealed sector containing the piece, just read the piece from the first sector we are able to unseal. for _, deal := range pieceInfo.Deals { reader, err := node.UnsealSector(ctx, deal.SectorID, deal.Offset.Unpadded(), deal.Length.Unpadded()) if err == nil { diff --git a/retrievalmarket/impl/providerstates/provider_states_test.go b/retrievalmarket/impl/providerstates/provider_states_test.go index fe9404b9..cafcfd11 100644 --- a/retrievalmarket/impl/providerstates/provider_states_test.go +++ b/retrievalmarket/impl/providerstates/provider_states_test.go @@ -47,11 +47,17 @@ func TestUnsealData(t *testing.T) { } pieceCid := testnet.GenerateCids(1)[0] + sectorID := abi.SectorNumber(rand.Uint64()) offset := abi.PaddedPieceSize(rand.Uint64()) length := abi.PaddedPieceSize(rand.Uint64()) + + sectorID2 := abi.SectorNumber(rand.Uint64()) + offset2 := abi.PaddedPieceSize(rand.Uint64()) + length2 := abi.PaddedPieceSize(rand.Uint64()) + data := testnet.RandomBytes(100) - makeDeal := func() *rm.ProviderDealState { + makeDeals := func() *rm.ProviderDealState { return &rm.ProviderDealState{ DealProposal: proposal, Status: rm.DealStatusUnsealing, @@ -64,6 +70,12 @@ func TestUnsealData(t *testing.T) { Offset: offset, Length: length, }, + { + DealID: abi.DealID(rand.Uint64()), + SectorID: sectorID2, + Offset: offset2, + Length: length2, + }, }, }, TotalSent: 0, @@ -71,28 +83,52 @@ func TestUnsealData(t *testing.T) { } } - t.Run("it works", func(t *testing.T) { + t.Run("prefers an already unsealed sector", func(t *testing.T) { + node := testnodes.NewTestRetrievalProviderNode() + node.MarkUnsealed(ctx, sectorID2, offset2.Unpadded(), length2.Unpadded()) + node.ExpectUnseal(sectorID2, offset2.Unpadded(), length2.Unpadded(), data) + + dealState := makeDeals() + setupEnv := func(fe *rmtesting.TestProviderDealEnvironment) {} + runUnsealData(t, node, setupEnv, dealState) + require.Equal(t, dealState.Status, rm.DealStatusUnsealed) + }) + + t.Run("use a non-unsealed sector if there is no unsealed sector", func(t *testing.T) { node := testnodes.NewTestRetrievalProviderNode() node.ExpectUnseal(sectorID, offset.Unpadded(), length.Unpadded(), data) - dealState := makeDeal() + dealState := makeDeals() setupEnv := func(fe *rmtesting.TestProviderDealEnvironment) {} runUnsealData(t, node, setupEnv, dealState) require.Equal(t, dealState.Status, rm.DealStatusUnsealed) }) - t.Run("unseal error", func(t *testing.T) { + t.Run("pick a sector for which unseal does NOT fail", func(t *testing.T) { node := testnodes.NewTestRetrievalProviderNode() node.ExpectFailedUnseal(sectorID, offset.Unpadded(), length.Unpadded()) - dealState := makeDeal() + node.ExpectUnseal(sectorID2, offset2.Unpadded(), length2.Unpadded(), data) + dealState := makeDeals() + setupEnv := func(fe *rmtesting.TestProviderDealEnvironment) {} + runUnsealData(t, node, setupEnv, dealState) + require.Equal(t, dealState.Status, rm.DealStatusUnsealed) + }) + + t.Run("unseal error if all fail", func(t *testing.T) { + node := testnodes.NewTestRetrievalProviderNode() + node.ExpectFailedUnseal(sectorID, offset.Unpadded(), length.Unpadded()) + node.ExpectFailedUnseal(sectorID2, offset2.Unpadded(), length2.Unpadded()) + + dealState := makeDeals() setupEnv := func(fe *rmtesting.TestProviderDealEnvironment) {} runUnsealData(t, node, setupEnv, dealState) require.Equal(t, dealState.Status, rm.DealStatusFailing) require.Equal(t, dealState.Message, "Could not unseal") }) + t.Run("ReadIntoBlockstore error", func(t *testing.T) { node := testnodes.NewTestRetrievalProviderNode() node.ExpectUnseal(sectorID, offset.Unpadded(), length.Unpadded(), data) - dealState := makeDeal() + dealState := makeDeals() setupEnv := func(fe *rmtesting.TestProviderDealEnvironment) { fe.ReadIntoBlockstoreError = errors.New("Something went wrong") } diff --git a/retrievalmarket/impl/requestvalidation/requestvalidation.go b/retrievalmarket/impl/requestvalidation/requestvalidation.go index eee173ce..3c241813 100644 --- a/retrievalmarket/impl/requestvalidation/requestvalidation.go +++ b/retrievalmarket/impl/requestvalidation/requestvalidation.go @@ -31,9 +31,11 @@ func init() { // ValidationEnvironment contains the dependencies needed to validate deals type ValidationEnvironment interface { - GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, error) + GetAsk(ctx context.Context, piece piecestore.PieceInfo, isUnsealed bool) (retrievalmarket.Ask, error) + + GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, bool, error) // CheckDealParams verifies the given deal params are acceptable - CheckDealParams(pricePerByte abi.TokenAmount, paymentInterval uint64, paymentIntervalIncrease uint64, unsealPrice abi.TokenAmount) error + CheckDealParams(ask retrievalmarket.Ask, pricePerByte abi.TokenAmount, paymentInterval uint64, paymentIntervalIncrease uint64, unsealPrice abi.TokenAmount) error // RunDealDecisioningLogic runs custom deal decision logic to decide if a deal is accepted, if present RunDealDecisioningLogic(ctx context.Context, state retrievalmarket.ProviderDealState) (bool, string, error) // StateMachines returns the FSM Group to begin tracking with @@ -137,9 +139,22 @@ func (rv *ProviderRequestValidator) validatePull(receiver peer.ID, proposal *ret } func (rv *ProviderRequestValidator) acceptDeal(deal *retrievalmarket.ProviderDealState) (retrievalmarket.DealStatus, error) { + pieceInfo, isUnsealed, err := rv.env.GetPiece(deal.PayloadCID, deal.PieceCID) + if err != nil { + if err == retrievalmarket.ErrNotFound { + return retrievalmarket.DealStatusDealNotFound, err + } + return retrievalmarket.DealStatusErrored, err + } + + ask, err := rv.env.GetAsk(context.TODO(), pieceInfo, isUnsealed) + if err != nil { + return retrievalmarket.DealStatusErrored, err + } + // check that the deal parameters match our required parameters or // reject outright - err := rv.env.CheckDealParams(deal.PricePerByte, deal.PaymentInterval, deal.PaymentIntervalIncrease, deal.UnsealPrice) + err = rv.env.CheckDealParams(ask, deal.PricePerByte, deal.PaymentInterval, deal.PaymentIntervalIncrease, deal.UnsealPrice) if err != nil { return retrievalmarket.DealStatusRejected, err } @@ -153,13 +168,6 @@ func (rv *ProviderRequestValidator) acceptDeal(deal *retrievalmarket.ProviderDea } // verify we have the piece - pieceInfo, err := rv.env.GetPiece(deal.PayloadCID, deal.PieceCID) - if err != nil { - if err == retrievalmarket.ErrNotFound { - return retrievalmarket.DealStatusDealNotFound, err - } - return retrievalmarket.DealStatusErrored, err - } deal.PieceInfo = &pieceInfo diff --git a/retrievalmarket/impl/requestvalidation/requestvalidation_test.go b/retrievalmarket/impl/requestvalidation/requestvalidation_test.go index 35e3ed04..71a15604 100644 --- a/retrievalmarket/impl/requestvalidation/requestvalidation_test.go +++ b/retrievalmarket/impl/requestvalidation/requestvalidation_test.go @@ -211,6 +211,7 @@ func TestValidatePull(t *testing.T) { } type fakeValidationEnvironment struct { + IsUnsealedPiece bool PieceInfo piecestore.PieceInfo GetPieceErr error CheckDealParamsError error @@ -220,14 +221,20 @@ type fakeValidationEnvironment struct { BeginTrackingError error NextStoreIDValue multistore.StoreID NextStoreIDError error + + Ask retrievalmarket.Ask +} + +func (fve *fakeValidationEnvironment) GetAsk(ctx context.Context, piece piecestore.PieceInfo, isUnsealed bool) (retrievalmarket.Ask, error) { + return fve.Ask, nil } -func (fve *fakeValidationEnvironment) GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, error) { - return fve.PieceInfo, fve.GetPieceErr +func (fve *fakeValidationEnvironment) GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, bool, error) { + return fve.PieceInfo, fve.IsUnsealedPiece, fve.GetPieceErr } // CheckDealParams verifies the given deal params are acceptable -func (fve *fakeValidationEnvironment) CheckDealParams(pricePerByte abi.TokenAmount, paymentInterval uint64, paymentIntervalIncrease uint64, unsealPrice abi.TokenAmount) error { +func (fve *fakeValidationEnvironment) CheckDealParams(ask retrievalmarket.Ask, pricePerByte abi.TokenAmount, paymentInterval uint64, paymentIntervalIncrease uint64, unsealPrice abi.TokenAmount) error { return fve.CheckDealParamsError } diff --git a/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go b/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go index 964e01a8..b6fcae67 100644 --- a/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go +++ b/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go @@ -46,6 +46,8 @@ type TestRetrievalProviderNode struct { received map[sectorKey]struct{} expectedVouchers map[expectedVoucherKey]voucherResult receivedVouchers map[expectedVoucherKey]struct{} + + unsealed map[sectorKey]struct{} } var _ retrievalmarket.RetrievalProviderNode = &TestRetrievalProviderNode{} @@ -58,9 +60,24 @@ func NewTestRetrievalProviderNode() *TestRetrievalProviderNode { received: make(map[sectorKey]struct{}), expectedVouchers: make(map[expectedVoucherKey]voucherResult), receivedVouchers: make(map[expectedVoucherKey]struct{}), + + unsealed: make(map[sectorKey]struct{}), } } +func (trpn *TestRetrievalProviderNode) IsUnsealed(ctx context.Context, sectorID abi.SectorNumber, offset abi.UnpaddedPieceSize, length abi.UnpaddedPieceSize) (bool, error) { + _, ok := trpn.unsealed[sectorKey{sectorID, offset, length}] + return ok, nil +} + +func (trpn *TestRetrievalProviderNode) MarkUnsealed(ctx context.Context, sectorID abi.SectorNumber, offset abi.UnpaddedPieceSize, length abi.UnpaddedPieceSize) { + trpn.unsealed[sectorKey{sectorID, offset, length}] = struct{}{} +} + +func (trpn *TestRetrievalProviderNode) GetDealPricingParams(ctx context.Context, dealID abi.DealID, tok shared.TipSetToken) (retrievalmarket.DealPricingParams, error) { + return retrievalmarket.DealPricingParams{}, nil +} + // StubUnseal stubs a response to attempting to unseal a sector with the given paramters func (trpn *TestRetrievalProviderNode) StubUnseal(sectorID abi.SectorNumber, offset, length abi.UnpaddedPieceSize, data []byte) { trpn.sectorStubs[sectorKey{sectorID, offset, length}] = data diff --git a/retrievalmarket/nodes.go b/retrievalmarket/nodes.go index 7ef2cb0d..8d20cdab 100644 --- a/retrievalmarket/nodes.go +++ b/retrievalmarket/nodes.go @@ -52,4 +52,8 @@ type RetrievalProviderNode interface { GetMinerWorkerAddress(ctx context.Context, miner address.Address, tok shared.TipSetToken) (address.Address, error) UnsealSector(ctx context.Context, sectorID abi.SectorNumber, offset abi.UnpaddedPieceSize, length abi.UnpaddedPieceSize) (io.ReadCloser, error) SavePaymentVoucher(ctx context.Context, paymentChannel address.Address, voucher *paych.SignedVoucher, proof []byte, expectedAmount abi.TokenAmount, tok shared.TipSetToken) (abi.TokenAmount, error) + + IsUnsealed(ctx context.Context, sectorID abi.SectorNumber, offset abi.UnpaddedPieceSize, length abi.UnpaddedPieceSize) (bool, error) + + GetDealPricingParams(ctx context.Context, dealID abi.DealID, tok shared.TipSetToken) (DealPricingParams, error) } diff --git a/retrievalmarket/provider.go b/retrievalmarket/provider.go index f2926aa0..e40a6d19 100644 --- a/retrievalmarket/provider.go +++ b/retrievalmarket/provider.go @@ -21,20 +21,8 @@ type RetrievalProvider interface { // Stop stops handling incoming requests Stop() error - // SetAsk sets the retrieval payment parameters that this miner will accept - SetAsk(ask *Ask) - - // GetAsk returns the retrieval providers pricing information - GetAsk() *Ask - // SubscribeToEvents listens for events that happen related to client retrievals SubscribeToEvents(subscriber ProviderSubscriber) Unsubscribe ListDeals() map[ProviderDealIdentifier]ProviderDealState } - -// AskStore is an interface which provides access to a persisted retrieval Ask -type AskStore interface { - GetAsk() *Ask - SetAsk(ask *Ask) error -} diff --git a/retrievalmarket/storage_retrieval_integration_test.go b/retrievalmarket/storage_retrieval_integration_test.go index c1e3e230..a1aecc09 100644 --- a/retrievalmarket/storage_retrieval_integration_test.go +++ b/retrievalmarket/storage_retrieval_integration_test.go @@ -487,9 +487,6 @@ func newRetrievalHarness(ctx context.Context, t *testing.T, sh *testharness.Stor pieceStore.ExpectCID(payloadCID, cidInfo) pieceStore.ExpectPiece(expectedPiece, pieceInfo) providerDs := namespace.Wrap(sh.TestData.Ds2, datastore.NewKey("/retrievals/provider")) - provider, err := retrievalimpl.NewProvider(providerPaymentAddr, providerNode, nw2, pieceStore, sh.TestData.MultiStore2, sh.DTProvider, providerDs) - require.NoError(t, err) - tut.StartAndWaitForReady(ctx, t, provider) var p retrievalmarket.Params if len(params) == 0 { @@ -503,12 +500,20 @@ func newRetrievalHarness(ctx context.Context, t *testing.T, sh *testharness.Stor p = params[0] } - ask := provider.GetAsk() - ask.PaymentInterval = p.PaymentInterval - ask.PaymentIntervalIncrease = p.PaymentIntervalIncrease - ask.PricePerByte = p.PricePerByte - ask.UnsealPrice = p.UnsealPrice - provider.SetAsk(ask) + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} + ask.PaymentInterval = p.PaymentInterval + ask.PaymentIntervalIncrease = p.PaymentIntervalIncrease + ask.PricePerByte = p.PricePerByte + ask.UnsealPrice = p.UnsealPrice + + return ask, nil + } + + provider, err := retrievalimpl.NewProvider(providerPaymentAddr, providerNode, nw2, pieceStore, sh.TestData.MultiStore2, sh.DTProvider, providerDs, + priceFunc) + require.NoError(t, err) + tut.StartAndWaitForReady(ctx, t, provider) return &retrievalHarness{ Ctx: ctx, diff --git a/retrievalmarket/types.go b/retrievalmarket/types.go index c89e0540..56faa0aa 100644 --- a/retrievalmarket/types.go +++ b/retrievalmarket/types.go @@ -382,3 +382,11 @@ type ChannelAvailableFunds struct { // and in the local datastore VoucherReedeemedAmt abi.TokenAmount } + +// DealPricingParams provides parameters required to price a retrieval deal +type DealPricingParams struct { + Client peer.ID + FastRetrieval bool + Unsealed bool + VerifiedDeal bool +} From 86145f7e3e804a75c325c110ad739b51870fb7b9 Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Fri, 23 Apr 2021 10:52:02 +0530 Subject: [PATCH 02/14] test differential pricing --- retrievalmarket/impl/provider_test.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/retrievalmarket/impl/provider_test.go b/retrievalmarket/impl/provider_test.go index ffa95b61..07177c16 100644 --- a/retrievalmarket/impl/provider_test.go +++ b/retrievalmarket/impl/provider_test.go @@ -80,6 +80,8 @@ func TestHandleQueryStream(t *testing.T) { expectedPaymentIntervalIncrease := uint64(100) expectedUnsealPrice := abi.NewTokenAmount(100) + expectedUnsealDiscount := abi.NewTokenAmount(1) + readWriteQueryStream := func() network.RetrievalQueryStream { qRead, qWrite := tut.QueryReadWriter() qrRead, qrWrite := tut.QueryResponseReadWriter() @@ -105,7 +107,12 @@ func TestHandleQueryStream(t *testing.T) { ask.PricePerByte = expectedPricePerByte ask.PaymentInterval = expectedPaymentInterval ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease - ask.UnsealPrice = expectedUnsealPrice + + if dealPricingParams.Unsealed { + ask.UnsealPrice = expectedUnsealDiscount + } else { + ask.UnsealPrice = expectedUnsealPrice + } return ask, nil } @@ -148,7 +155,7 @@ func TestHandleQueryStream(t *testing.T) { expectedUnsealPrice: expectedUnsealPrice, }, - {name: "When PieceCID is not provided, prefer a piece for which an unsealed sector already exists", + {name: "When PieceCID is not provided, prefer a piece for which an unsealed sector already exists and price it accordingly", nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { p := expectedPiece2.Deals[0] n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) @@ -167,7 +174,7 @@ func TestHandleQueryStream(t *testing.T) { expectedPricePerByte: expectedPricePerByte, expectedPaymentInterval: expectedPaymentInterval, expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, - expectedUnsealPrice: expectedUnsealPrice, + expectedUnsealPrice: expectedUnsealDiscount, }, {name: "When PieceCID is provided and both PieceCID and PayloadCID are found", From a93936a31d7d8c879d9efff87b1df3e10a2f8c9f Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Mon, 3 May 2021 19:05:33 +0530 Subject: [PATCH 03/14] dynamic pricing --- retrievalmarket/impl/provider.go | 35 +- retrievalmarket/impl/provider_environments.go | 80 +++- retrievalmarket/impl/provider_test.go | 412 ++++++++++++++++++ .../requestvalidation/requestvalidation.go | 4 +- .../requestvalidation_test.go | 3 +- .../testnodes/test_retrieval_provider_node.go | 22 +- retrievalmarket/network/network.go | 1 + retrievalmarket/network/old_query_stream.go | 4 + retrievalmarket/network/query_stream.go | 4 + retrievalmarket/nodes.go | 2 +- retrievalmarket/types.go | 7 +- shared_testutil/test_network_types.go | 10 +- 12 files changed, 545 insertions(+), 39 deletions(-) diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index 1d61423c..8468e36c 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -7,6 +7,7 @@ import ( "github.com/hannahhoward/go-pubsub" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" + "github.com/libp2p/go-libp2p-core/peer" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -14,6 +15,7 @@ import ( versioning "github.com/filecoin-project/go-ds-versioning/pkg" versionedfsm "github.com/filecoin-project/go-ds-versioning/pkg/fsm" "github.com/filecoin-project/go-multistore" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-statemachine/fsm" @@ -295,7 +297,24 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { answer.Size = uint64(pieceInfo.Deals[0].Length) // TODO: verify on intermediate answer.PieceCIDFound = retrievalmarket.QueryItemAvailable - ask, err := p.GetAsk(ctx, pieceInfo, isUnsealed) + var storageDeals []abi.DealID + if pieceCID != cid.Undef { + // If the user wants to retrieve the payload from a specific piece, we only need to inspect storage deals + // made for that piece to quote a price for retrieval. + for _, d := range pieceInfo.Deals { + storageDeals = append(storageDeals, d.DealID) + } + } else { + // If the user does NOT want to retrieve the payload from a specific piece, we'll have to inspect all deals + // made for that piece to quote a price for retrieval. + storageDeals, err = getAllDealsContainingPayload(p.pieceStore, query.PayloadCID) + if err != nil { + log.Errorf("Retrieval query: getAllDealsContainingPayload: %s", err) + return + } + } + + ask, err := p.GetAsk(ctx, storageDeals, isUnsealed, stream.RemotePeer()) if err != nil { log.Errorf("Retrieval query: GetAsk: %s", err) return @@ -320,19 +339,15 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { } } -func (p *Provider) GetAsk(ctx context.Context, piece piecestore.PieceInfo, isUnsealed bool) (retrievalmarket.Ask, error) { - // TODO What should we do here if we have multiple deals ? - // How to fetch Verified Deal & Fast Retrieval here ? - // What's a correct "DealPricingParam" - tok, _, err := p.node.GetChainHead(ctx) - if err != nil { - return retrievalmarket.Ask{}, xerrors.Errorf("failed to GetChainHead: %s", err) - } - dp, err := p.node.GetDealPricingParams(ctx, piece.Deals[0].DealID, tok) +func (p *Provider) GetAsk(ctx context.Context, storageDeals []abi.DealID, isUnsealed bool, client peer.ID) (retrievalmarket.Ask, error) { + // TODO How do we fetch the fast-retrieval flag here ? + dp, err := p.node.GetDealPricingParams(ctx, storageDeals) if err != nil { return retrievalmarket.Ask{}, xerrors.Errorf("GetDealPricingParams: %s", err) } dp.Unsealed = isUnsealed + dp.Client = client + ask, err := p.dealPricingFunc(ctx, dp) if err != nil { return retrievalmarket.Ask{}, xerrors.Errorf("dealPricingFunc: %s", err) diff --git a/retrievalmarket/impl/provider_environments.go b/retrievalmarket/impl/provider_environments.go index f5f94286..cf4bf6d5 100644 --- a/retrievalmarket/impl/provider_environments.go +++ b/retrievalmarket/impl/provider_environments.go @@ -30,8 +30,27 @@ type providerValidationEnvironment struct { p *Provider } -func (pve *providerValidationEnvironment) GetAsk(ctx context.Context, piece piecestore.PieceInfo, isUnsealed bool) (retrievalmarket.Ask, error) { - return pve.p.GetAsk(ctx, piece, isUnsealed) +func (pve *providerValidationEnvironment) GetAsk(ctx context.Context, payloadCid cid.Cid, pieceCid *cid.Cid, + piece piecestore.PieceInfo, isUnsealed bool, client peer.ID) (retrievalmarket.Ask, error) { + var storageDeals []abi.DealID + var err error + if pieceCid != nil { + // If the user wants to retrieve the payload from a specific piece, + // we only need to inspect storage deals made for that piece to quote a price. + for _, d := range piece.Deals { + storageDeals = append(storageDeals, d.DealID) + } + + } else { + // If the user does NOT want to retrieve from a specific piece, we'll have to inspect all storage deals + // made for that piece to quote a price. + storageDeals, err = getAllDealsContainingPayload(pve.p.pieceStore, payloadCid) + if err != nil { + return retrievalmarket.Ask{}, xerrors.Errorf("failed to fetch deals for payload, err=%s", err) + } + } + + return pve.p.GetAsk(ctx, storageDeals, isUnsealed, client) } func (pve *providerValidationEnvironment) GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, bool, error) { @@ -208,6 +227,30 @@ func pieceInUnsealedSector(ctx context.Context, n retrievalmarket.RetrievalProvi return false } +func getAllDealsContainingPayload(pieceStore piecestore.PieceStore, payloadCID cid.Cid) ([]abi.DealID, error) { + cidInfo, err := pieceStore.GetCIDInfo(payloadCID) + if err != nil { + return nil, xerrors.Errorf("get cid info: %w", err) + } + var dealsIds []abi.DealID + + for _, pieceBlockLocation := range cidInfo.PieceBlockLocations { + pieceInfo, err := pieceStore.GetPieceInfo(pieceBlockLocation.PieceCID) + if err != nil { + continue + } + for _, d := range pieceInfo.Deals { + dealsIds = append(dealsIds, d.DealID) + } + } + + if len(dealsIds) == 0 { + return nil, xerrors.New("no deals found") + } + + return dealsIds, nil +} + func getPieceInfoFromCid(ctx context.Context, n retrievalmarket.RetrievalProviderNode, pieceStore piecestore.PieceStore, payloadCID, pieceCID cid.Cid) (piecestore.PieceInfo, bool, error) { cidInfo, err := pieceStore.GetCIDInfo(payloadCID) if err != nil { @@ -218,25 +261,28 @@ func getPieceInfoFromCid(ctx context.Context, n retrievalmarket.RetrievalProvide for _, pieceBlockLocation := range cidInfo.PieceBlockLocations { pieceInfo, err := pieceStore.GetPieceInfo(pieceBlockLocation.PieceCID) - if err == nil { - // if client wants to retrieve the payload from a specific piece, just return that piece. - if !pieceCID.Equals(cid.Undef) && pieceInfo.PieceCID.Equals(pieceCID) { - return pieceInfo, pieceInUnsealedSector(ctx, n, pieceInfo), nil - } + if err != nil { + lastErr = err + continue + } - // if client dosen't have a preference for a particular piece, prefer a piece - // for which an unsealed sector exists. - if pieceCID.Equals(cid.Undef) { - if pieceInUnsealedSector(ctx, n, pieceInfo) { - return pieceInfo, true, nil - } + // if client wants to retrieve the payload from a specific piece, just return that piece. + if !pieceCID.Equals(cid.Undef) && pieceInfo.PieceCID.Equals(pieceCID) { + return pieceInfo, pieceInUnsealedSector(ctx, n, pieceInfo), nil + } - if sealedPieceInfo == nil { - sealedPieceInfo = &pieceInfo - } + // if client dosen't have a preference for a particular piece, prefer a piece + // for which an unsealed sector exists. + if pieceCID.Equals(cid.Undef) { + if pieceInUnsealedSector(ctx, n, pieceInfo) { + return pieceInfo, true, nil + } + + if sealedPieceInfo == nil { + sealedPieceInfo = &pieceInfo } } - lastErr = err + } if sealedPieceInfo != nil { diff --git a/retrievalmarket/impl/provider_test.go b/retrievalmarket/impl/provider_test.go index 07177c16..3db97267 100644 --- a/retrievalmarket/impl/provider_test.go +++ b/retrievalmarket/impl/provider_test.go @@ -37,6 +37,415 @@ import ( tut "github.com/filecoin-project/go-fil-markets/shared_testutil" ) +func TestDynamicPricing(t *testing.T) { + ctx := context.Background() + expectedAddress := address.TestAddress2 + + payloadCID := tut.GenerateCids(1)[0] + peer1 := peer.ID("peer1") + peer2 := peer.ID("peer2") + + // differential price per byte + expectedppbUnVerified := abi.NewTokenAmount(4321) + expectedppbVerified := abi.NewTokenAmount(2) + + // differential sealing/unsealing price + expectedUnsealPrice := abi.NewTokenAmount(100) + expectedUnsealDiscount := abi.NewTokenAmount(1) + + // differential payment interval + expectedpiPeer1 := uint64(4567) + expectedpiPeer2 := uint64(20) + + expectedPaymentIntervalIncrease := uint64(100) + + // multiple pieces have the same payload + expectedPieceCID1 := tut.GenerateCids(1)[0] + expectedPieceCID2 := tut.GenerateCids(1)[0] + + // sizes + piece1Size := uint64(1234) + piece2Size := uint64(2234) + + expectedCIDInfo := piecestore.CIDInfo{ + PieceBlockLocations: []piecestore.PieceBlockLocation{ + { + PieceCID: expectedPieceCID1, + }, + { + PieceCID: expectedPieceCID2, + }, + }, + } + + piece1 := piecestore.PieceInfo{ + PieceCID: expectedPieceCID1, + Deals: []piecestore.DealInfo{ + { + DealID: abi.DealID(1), + Length: abi.PaddedPieceSize(piece1Size), + }, + { + DealID: abi.DealID(11), + Length: abi.PaddedPieceSize(piece1Size), + }, + }, + } + + piece2 := piecestore.PieceInfo{ + PieceCID: expectedPieceCID2, + Deals: []piecestore.DealInfo{ + { + DealID: abi.DealID(2), + Length: abi.PaddedPieceSize(piece2Size), + }, + { + DealID: abi.DealID(22), + Length: abi.PaddedPieceSize(piece2Size), + }, + { + DealID: abi.DealID(222), + Length: abi.PaddedPieceSize(piece2Size), + }, + }, + } + + receiveStreamOnProvider := func(t *testing.T, node *testnodes.TestRetrievalProviderNode, qs network.RetrievalQueryStream, pieceStore piecestore.PieceStore) { + ds := dss.MutexWrap(datastore.NewMapDatastore()) + multiStore, err := multistore.NewMultiDstore(ds) + require.NoError(t, err) + dt := tut.NewTestDataTransfer() + net := tut.NewTestRetrievalMarketNetwork(tut.TestNetworkParams{}) + + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} + + if dealPricingParams.VerifiedDeal { + ask.PricePerByte = expectedppbVerified + } else { + ask.PricePerByte = expectedppbUnVerified + } + + if dealPricingParams.Unsealed { + ask.UnsealPrice = expectedUnsealDiscount + } else { + ask.UnsealPrice = expectedUnsealPrice + } + + fmt.Println("\n client is", dealPricingParams.Client.String()) + if dealPricingParams.Client == peer2 { + ask.PaymentInterval = expectedpiPeer2 + } else { + ask.PaymentInterval = expectedpiPeer1 + } + ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease + + return ask, nil + } + + c, err := retrievalimpl.NewProvider(expectedAddress, node, net, pieceStore, multiStore, dt, ds, priceFunc) + require.NoError(t, err) + + tut.StartAndWaitForReady(ctx, t, c) + + net.ReceiveQueryStream(qs) + } + + readWriteQueryStream := func() *tut.TestRetrievalQueryStream { + qRead, qWrite := tut.QueryReadWriter() + qrRead, qrWrite := tut.QueryResponseReadWriter() + qs := tut.NewTestRetrievalQueryStream(tut.TestQueryStreamParams{ + Reader: qRead, + Writer: qWrite, + RespReader: qrRead, + RespWriter: qrWrite, + }) + return qs + } + + tcs := map[string]struct { + query retrievalmarket.Query + expFunc func(t *testing.T, pieceStore *tut.TestPieceStore) + nodeFunc func(n *testnodes.TestRetrievalProviderNode) + peerIdFnc func(stream *tut.TestRetrievalQueryStream) + + expectedPricePerByte abi.TokenAmount + expectedPaymentInterval uint64 + expectedPaymentIntervalIncrease uint64 + expectedUnsealPrice abi.TokenAmount + expectedSize uint64 + }{ + // Retrieval request for a payloadCid without a pieceCid + "pieceCid no-op: quote correct price for sealed, unverified, peer1": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + + expectedPricePerByte: expectedppbUnVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealPrice, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece1Size, + }, + + "pieceCid no-op: quote correct price for sealed, unverified, peer2": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer2) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + + expectedPricePerByte: expectedppbUnVerified, + expectedPaymentInterval: expectedpiPeer2, + expectedUnsealPrice: expectedUnsealPrice, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece1Size, + }, + + "pieceCid no-op: quote correct price for sealed, verified, peer1": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + n.MarkVerified() + n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + + expectedPricePerByte: expectedppbVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealPrice, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece1Size, + }, + + "pieceCid no-op: quote correct price for unsealed, unverified, peer1": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece2.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + + expectedPricePerByte: expectedppbUnVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealDiscount, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece2Size, + }, + + "pieceCid no-op: quote correct price for unsealed, verified, peer1": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece2.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.MarkVerified() + n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + + expectedPricePerByte: expectedppbVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealDiscount, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece2Size, + }, + + // Retrieval request for a payloadCid inside a specific Cid + "specific sealed piece Cid, first piece Cid matches: quote correct price for sealed, unverified, peer1": { + query: retrievalmarket.Query{ + PayloadCID: payloadCID, + QueryParams: retrievalmarket.QueryParams{PieceCID: &expectedPieceCID1}, + }, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece2.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.ExpectPricingParamDeals([]abi.DealID{1, 11}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + }, + + expectedPricePerByte: expectedppbUnVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealPrice, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece1Size, + }, + + "specific sealed piece Cid, second piece Cid matches: quote correct price for sealed, unverified, peer1": { + query: retrievalmarket.Query{ + PayloadCID: payloadCID, + QueryParams: retrievalmarket.QueryParams{PieceCID: &expectedPieceCID2}, + }, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece1.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.ExpectPricingParamDeals([]abi.DealID{2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID1, piece2) + }, + + expectedPricePerByte: expectedppbUnVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealPrice, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece2Size, + }, + + "specific sealed piece Cid, first piece Cid matches: quote correct price for sealed, verified, peer1": { + query: retrievalmarket.Query{ + PayloadCID: payloadCID, + QueryParams: retrievalmarket.QueryParams{PieceCID: &expectedPieceCID1}, + }, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece2.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.ExpectPricingParamDeals([]abi.DealID{1, 11}) + n.MarkVerified() + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + }, + + expectedPricePerByte: expectedppbVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealPrice, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece1Size, + }, + + "specific sealed piece Cid, first piece Cid matches: quote correct price for unsealed, verified, peer1": { + query: retrievalmarket.Query{ + PayloadCID: payloadCID, + QueryParams: retrievalmarket.QueryParams{PieceCID: &expectedPieceCID1}, + }, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece1.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.MarkVerified() + n.ExpectPricingParamDeals([]abi.DealID{1, 11}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + }, + + expectedPricePerByte: expectedppbVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealDiscount, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece1Size, + }, + + "specific sealed piece Cid, first piece Cid matches: quote correct price for unsealed, verified, peer2": { + query: retrievalmarket.Query{ + PayloadCID: payloadCID, + QueryParams: retrievalmarket.QueryParams{PieceCID: &expectedPieceCID2}, + }, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer2) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece2.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.MarkVerified() + n.ExpectPricingParamDeals([]abi.DealID{2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + + expectedPricePerByte: expectedppbVerified, + expectedPaymentInterval: expectedpiPeer2, + expectedUnsealPrice: expectedUnsealDiscount, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece2Size, + }, + } + + for name, tc := range tcs { + t.Run(name, func(t *testing.T) { + node := testnodes.NewTestRetrievalProviderNode() + qs := readWriteQueryStream() + tc.peerIdFnc(qs) + + err := qs.WriteQuery(tc.query) + require.NoError(t, err) + pieceStore := tut.NewTestPieceStore() + tc.nodeFunc(node) + tc.expFunc(t, pieceStore) + receiveStreamOnProvider(t, node, qs, pieceStore) + + actualResp, err := qs.ReadQueryResponse() + pieceStore.VerifyExpectations(t) + node.VerifyExpectations(t) + + require.Equal(t, expectedAddress, actualResp.PaymentAddress) + require.Equal(t, tc.expectedPricePerByte, actualResp.MinPricePerByte) + require.Equal(t, tc.expectedPaymentInterval, actualResp.MaxPaymentInterval) + require.Equal(t, tc.expectedPaymentIntervalIncrease, actualResp.MaxPaymentIntervalIncrease) + require.Equal(t, tc.expectedUnsealPrice, actualResp.UnsealPrice) + require.Equal(t, tc.expectedSize, actualResp.Size) + }) + } +} + func TestHandleQueryStream(t *testing.T) { ctx := context.Background() @@ -59,6 +468,7 @@ func TestHandleQueryStream(t *testing.T) { }, } expectedPiece := piecestore.PieceInfo{ + PieceCID: expectedPieceCID, Deals: []piecestore.DealInfo{ { Length: abi.PaddedPieceSize(expectedSize), @@ -67,6 +477,7 @@ func TestHandleQueryStream(t *testing.T) { } expectedPiece2 := piecestore.PieceInfo{ + PieceCID: expectedPieceCID2, Deals: []piecestore.DealInfo{ { Length: abi.PaddedPieceSize(expectedSize2), @@ -80,6 +491,7 @@ func TestHandleQueryStream(t *testing.T) { expectedPaymentIntervalIncrease := uint64(100) expectedUnsealPrice := abi.NewTokenAmount(100) + // differential pricing expectedUnsealDiscount := abi.NewTokenAmount(1) readWriteQueryStream := func() network.RetrievalQueryStream { diff --git a/retrievalmarket/impl/requestvalidation/requestvalidation.go b/retrievalmarket/impl/requestvalidation/requestvalidation.go index 3c241813..1de805ad 100644 --- a/retrievalmarket/impl/requestvalidation/requestvalidation.go +++ b/retrievalmarket/impl/requestvalidation/requestvalidation.go @@ -31,7 +31,7 @@ func init() { // ValidationEnvironment contains the dependencies needed to validate deals type ValidationEnvironment interface { - GetAsk(ctx context.Context, piece piecestore.PieceInfo, isUnsealed bool) (retrievalmarket.Ask, error) + GetAsk(ctx context.Context, payloadCid cid.Cid, pieceCid *cid.Cid, piece piecestore.PieceInfo, isUnsealed bool, client peer.ID) (retrievalmarket.Ask, error) GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, bool, error) // CheckDealParams verifies the given deal params are acceptable @@ -147,7 +147,7 @@ func (rv *ProviderRequestValidator) acceptDeal(deal *retrievalmarket.ProviderDea return retrievalmarket.DealStatusErrored, err } - ask, err := rv.env.GetAsk(context.TODO(), pieceInfo, isUnsealed) + ask, err := rv.env.GetAsk(context.TODO(), deal.PayloadCID, deal.PieceCID, pieceInfo, isUnsealed, deal.Receiver) if err != nil { return retrievalmarket.DealStatusErrored, err } diff --git a/retrievalmarket/impl/requestvalidation/requestvalidation_test.go b/retrievalmarket/impl/requestvalidation/requestvalidation_test.go index 71a15604..955b55dc 100644 --- a/retrievalmarket/impl/requestvalidation/requestvalidation_test.go +++ b/retrievalmarket/impl/requestvalidation/requestvalidation_test.go @@ -225,7 +225,8 @@ type fakeValidationEnvironment struct { Ask retrievalmarket.Ask } -func (fve *fakeValidationEnvironment) GetAsk(ctx context.Context, piece piecestore.PieceInfo, isUnsealed bool) (retrievalmarket.Ask, error) { +func (fve *fakeValidationEnvironment) GetAsk(ctx context.Context, payloadCid cid.Cid, pieceCid *cid.Cid, + piece piecestore.PieceInfo, isUnsealed bool, client peer.ID) (retrievalmarket.Ask, error) { return fve.Ask, nil } diff --git a/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go b/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go index b6fcae67..ff21c00d 100644 --- a/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go +++ b/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go @@ -47,7 +47,10 @@ type TestRetrievalProviderNode struct { expectedVouchers map[expectedVoucherKey]voucherResult receivedVouchers map[expectedVoucherKey]struct{} - unsealed map[sectorKey]struct{} + expectedPricingParamDeals []abi.DealID + recievedPricingParamDeals []abi.DealID + unsealed map[sectorKey]struct{} + isVerified bool } var _ retrievalmarket.RetrievalProviderNode = &TestRetrievalProviderNode{} @@ -74,8 +77,19 @@ func (trpn *TestRetrievalProviderNode) MarkUnsealed(ctx context.Context, sectorI trpn.unsealed[sectorKey{sectorID, offset, length}] = struct{}{} } -func (trpn *TestRetrievalProviderNode) GetDealPricingParams(ctx context.Context, dealID abi.DealID, tok shared.TipSetToken) (retrievalmarket.DealPricingParams, error) { - return retrievalmarket.DealPricingParams{}, nil +func (trpn *TestRetrievalProviderNode) MarkVerified() { + trpn.isVerified = true +} + +func (trpn *TestRetrievalProviderNode) ExpectPricingParamDeals(deals []abi.DealID) { + trpn.expectedPricingParamDeals = deals +} + +func (trpn *TestRetrievalProviderNode) GetDealPricingParams(_ context.Context, deals []abi.DealID) (retrievalmarket.DealPricingParams, error) { + trpn.recievedPricingParamDeals = deals + return retrievalmarket.DealPricingParams{ + VerifiedDeal: trpn.isVerified, + }, nil } // StubUnseal stubs a response to attempting to unseal a sector with the given paramters @@ -112,6 +126,8 @@ func (trpn *TestRetrievalProviderNode) UnsealSector(ctx context.Context, sectorI func (trpn *TestRetrievalProviderNode) VerifyExpectations(t *testing.T) { require.Equal(t, len(trpn.expectedVouchers), len(trpn.receivedVouchers)) require.Equal(t, trpn.expectations, trpn.received) + + require.Equal(t, trpn.expectedPricingParamDeals, trpn.recievedPricingParamDeals) } // SavePaymentVoucher simulates saving a payment voucher with a stubbed result diff --git a/retrievalmarket/network/network.go b/retrievalmarket/network/network.go index c1c1bebe..404dea89 100644 --- a/retrievalmarket/network/network.go +++ b/retrievalmarket/network/network.go @@ -18,6 +18,7 @@ type RetrievalQueryStream interface { ReadQueryResponse() (retrievalmarket.QueryResponse, error) WriteQueryResponse(retrievalmarket.QueryResponse) error Close() error + RemotePeer() peer.ID } // RetrievalReceiver is the API for handling data coming in on diff --git a/retrievalmarket/network/old_query_stream.go b/retrievalmarket/network/old_query_stream.go index fd146ac6..940ce815 100644 --- a/retrievalmarket/network/old_query_stream.go +++ b/retrievalmarket/network/old_query_stream.go @@ -20,6 +20,10 @@ type oldQueryStream struct { var _ RetrievalQueryStream = (*oldQueryStream)(nil) +func (qs *oldQueryStream) RemotePeer() peer.ID { + return qs.p +} + func (qs *oldQueryStream) ReadQuery() (retrievalmarket.Query, error) { var q migrations.Query0 diff --git a/retrievalmarket/network/query_stream.go b/retrievalmarket/network/query_stream.go index 520ea99f..37c18a28 100644 --- a/retrievalmarket/network/query_stream.go +++ b/retrievalmarket/network/query_stream.go @@ -31,6 +31,10 @@ func (qs *queryStream) ReadQuery() (retrievalmarket.Query, error) { return q, nil } +func (qs *queryStream) RemotePeer() peer.ID { + return qs.p +} + func (qs *queryStream) WriteQuery(q retrievalmarket.Query) error { return cborutil.WriteCborRPC(qs.rw, &q) } diff --git a/retrievalmarket/nodes.go b/retrievalmarket/nodes.go index 8d20cdab..358b3c1b 100644 --- a/retrievalmarket/nodes.go +++ b/retrievalmarket/nodes.go @@ -55,5 +55,5 @@ type RetrievalProviderNode interface { IsUnsealed(ctx context.Context, sectorID abi.SectorNumber, offset abi.UnpaddedPieceSize, length abi.UnpaddedPieceSize) (bool, error) - GetDealPricingParams(ctx context.Context, dealID abi.DealID, tok shared.TipSetToken) (DealPricingParams, error) + GetDealPricingParams(ctx context.Context, storageDeals []abi.DealID) (DealPricingParams, error) } diff --git a/retrievalmarket/types.go b/retrievalmarket/types.go index 56faa0aa..e7b0d544 100644 --- a/retrievalmarket/types.go +++ b/retrievalmarket/types.go @@ -385,8 +385,7 @@ type ChannelAvailableFunds struct { // DealPricingParams provides parameters required to price a retrieval deal type DealPricingParams struct { - Client peer.ID - FastRetrieval bool - Unsealed bool - VerifiedDeal bool + Client peer.ID + VerifiedDeal bool + Unsealed bool } diff --git a/shared_testutil/test_network_types.go b/shared_testutil/test_network_types.go index b77687a4..89d0de03 100644 --- a/shared_testutil/test_network_types.go +++ b/shared_testutil/test_network_types.go @@ -51,7 +51,7 @@ type TestQueryStreamParams struct { // NewTestRetrievalQueryStream returns a new TestRetrievalQueryStream with the // behavior specified by the paramaters, or default behaviors if not specified. -func NewTestRetrievalQueryStream(params TestQueryStreamParams) rmnet.RetrievalQueryStream { +func NewTestRetrievalQueryStream(params TestQueryStreamParams) *TestRetrievalQueryStream { stream := TestRetrievalQueryStream{ p: params.PeerID, reader: TrivialQueryReader, @@ -74,6 +74,14 @@ func NewTestRetrievalQueryStream(params TestQueryStreamParams) rmnet.RetrievalQu return &stream } +func (trqs *TestRetrievalQueryStream) SetRemotePeer(rp peer.ID) { + trqs.p = rp +} + +func (trqs *TestRetrievalQueryStream) RemotePeer() peer.ID { + return trqs.p +} + // ReadDealStatusRequest calls the mocked query reader. func (trqs *TestRetrievalQueryStream) ReadQuery() (rm.Query, error) { return trqs.reader() From 8bd6d0019d143855afd6a0c7339dc75516bf2513 Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Thu, 6 May 2021 14:57:57 +0530 Subject: [PATCH 04/14] changes for dynamic pricing --- .../impl/askstore/askstore_impl.go | 129 ++++++++++++++ .../impl/askstore/askstore_impl_test.go | 75 ++++++++ retrievalmarket/impl/integration_test.go | 6 +- retrievalmarket/impl/provider.go | 66 +++++-- retrievalmarket/impl/provider_environments.go | 11 +- retrievalmarket/impl/provider_test.go | 162 ++++++++++++------ .../testnodes/test_retrieval_provider_node.go | 25 ++- retrievalmarket/nodes.go | 2 +- retrievalmarket/provider.go | 12 ++ .../storage_retrieval_integration_test.go | 2 +- retrievalmarket/types.go | 19 +- 11 files changed, 428 insertions(+), 81 deletions(-) create mode 100644 retrievalmarket/impl/askstore/askstore_impl.go create mode 100644 retrievalmarket/impl/askstore/askstore_impl_test.go diff --git a/retrievalmarket/impl/askstore/askstore_impl.go b/retrievalmarket/impl/askstore/askstore_impl.go new file mode 100644 index 00000000..857e06b5 --- /dev/null +++ b/retrievalmarket/impl/askstore/askstore_impl.go @@ -0,0 +1,129 @@ +package askstore + +import ( + "bytes" + "context" + "sync" + + "github.com/ipfs/go-datastore" + "golang.org/x/xerrors" + + cborutil "github.com/filecoin-project/go-cbor-util" + versioning "github.com/filecoin-project/go-ds-versioning/pkg" + versionedds "github.com/filecoin-project/go-ds-versioning/pkg/datastore" + + "github.com/filecoin-project/go-fil-markets/retrievalmarket" + "github.com/filecoin-project/go-fil-markets/retrievalmarket/migrations" +) + +// AskStoreImpl implements AskStore, persisting a retrieval Ask +// to disk. It also maintains a cache of the current Ask in memory +type AskStoreImpl struct { + lk sync.RWMutex + ask *retrievalmarket.Ask + ds datastore.Batching + key datastore.Key +} + +// NewAskStore returns a new instance of AskStoreImpl +// It will initialize a new default ask and store it if one is not set. +// Otherwise it loads the current Ask from disk +func NewAskStore(ds datastore.Batching, key datastore.Key) (*AskStoreImpl, error) { + askMigrations, err := migrations.AskMigrations.Build() + if err != nil { + return nil, err + } + versionedDs, migrateDs := versionedds.NewVersionedDatastore(ds, askMigrations, versioning.VersionKey("1")) + err = migrateDs(context.TODO()) + if err != nil { + return nil, err + } + s := &AskStoreImpl{ + ds: versionedDs, + key: key, + } + + if err := s.tryLoadAsk(); err != nil { + return nil, err + } + + if s.ask == nil { + // for now set a default retrieval ask + defaultAsk := &retrievalmarket.Ask{ + PricePerByte: retrievalmarket.DefaultPricePerByte, + UnsealPrice: retrievalmarket.DefaultUnsealPrice, + PaymentInterval: retrievalmarket.DefaultPaymentInterval, + PaymentIntervalIncrease: retrievalmarket.DefaultPaymentIntervalIncrease, + } + + if err := s.SetAsk(defaultAsk); err != nil { + return nil, xerrors.Errorf("failed setting a default retrieval ask: %w", err) + } + } + return s, nil +} + +// SetAsk stores retrieval provider's ask +func (s *AskStoreImpl) SetAsk(ask *retrievalmarket.Ask) error { + s.lk.Lock() + defer s.lk.Unlock() + + return s.saveAsk(ask) +} + +// GetAsk returns the current retrieval ask, or nil if one does not exist. +func (s *AskStoreImpl) GetAsk() *retrievalmarket.Ask { + s.lk.RLock() + defer s.lk.RUnlock() + if s.ask == nil { + return nil + } + ask := *s.ask + return &ask +} + +func (s *AskStoreImpl) tryLoadAsk() error { + s.lk.Lock() + defer s.lk.Unlock() + + err := s.loadAsk() + + if err != nil { + if xerrors.Is(err, datastore.ErrNotFound) { + // this is expected + return nil + } + return err + } + + return nil +} + +func (s *AskStoreImpl) loadAsk() error { + askb, err := s.ds.Get(s.key) + if err != nil { + return xerrors.Errorf("failed to load most recent retrieval ask from disk: %w", err) + } + + var ask retrievalmarket.Ask + if err := cborutil.ReadCborRPC(bytes.NewReader(askb), &ask); err != nil { + return err + } + + s.ask = &ask + return nil +} + +func (s *AskStoreImpl) saveAsk(a *retrievalmarket.Ask) error { + b, err := cborutil.Dump(a) + if err != nil { + return err + } + + if err := s.ds.Put(s.key, b); err != nil { + return err + } + + s.ask = a + return nil +} diff --git a/retrievalmarket/impl/askstore/askstore_impl_test.go b/retrievalmarket/impl/askstore/askstore_impl_test.go new file mode 100644 index 00000000..296ab2c8 --- /dev/null +++ b/retrievalmarket/impl/askstore/askstore_impl_test.go @@ -0,0 +1,75 @@ +package askstore_test + +import ( + "bytes" + "math/rand" + "testing" + + "github.com/ipfs/go-datastore" + dss "github.com/ipfs/go-datastore/sync" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/go-fil-markets/retrievalmarket" + "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/askstore" + "github.com/filecoin-project/go-fil-markets/retrievalmarket/migrations" +) + +func TestAskStoreImpl(t *testing.T) { + ds := dss.MutexWrap(datastore.NewMapDatastore()) + store, err := askstore.NewAskStore(ds, datastore.NewKey("retrieval-ask")) + require.NoError(t, err) + + // A new store returns the default ask + ask := store.GetAsk() + require.NotNil(t, ask) + + require.Equal(t, retrievalmarket.DefaultUnsealPrice, ask.UnsealPrice) + require.Equal(t, retrievalmarket.DefaultPricePerByte, ask.PricePerByte) + require.Equal(t, retrievalmarket.DefaultPaymentInterval, ask.PaymentInterval) + require.Equal(t, retrievalmarket.DefaultPaymentIntervalIncrease, ask.PaymentIntervalIncrease) + + // Store a new ask + newAsk := &retrievalmarket.Ask{ + PricePerByte: abi.NewTokenAmount(123), + UnsealPrice: abi.NewTokenAmount(456), + PaymentInterval: 789, + PaymentIntervalIncrease: 789, + } + err = store.SetAsk(newAsk) + require.NoError(t, err) + + // Fetch new ask + stored := store.GetAsk() + require.Equal(t, newAsk, stored) + + // Construct a new AskStore and make sure it returns the previously-stored ask + newStore, err := askstore.NewAskStore(ds, datastore.NewKey("retrieval-ask")) + require.NoError(t, err) + stored = newStore.GetAsk() + require.Equal(t, newAsk, stored) +} +func TestMigrations(t *testing.T) { + ds := dss.MutexWrap(datastore.NewMapDatastore()) + oldAsk := &migrations.Ask0{ + PricePerByte: abi.NewTokenAmount(rand.Int63()), + UnsealPrice: abi.NewTokenAmount(rand.Int63()), + PaymentInterval: rand.Uint64(), + PaymentIntervalIncrease: rand.Uint64(), + } + buf := new(bytes.Buffer) + err := oldAsk.MarshalCBOR(buf) + require.NoError(t, err) + ds.Put(datastore.NewKey("retrieval-ask"), buf.Bytes()) + newStore, err := askstore.NewAskStore(ds, datastore.NewKey("retrieval-ask")) + require.NoError(t, err) + ask := newStore.GetAsk() + expectedAsk := &retrievalmarket.Ask{ + PricePerByte: oldAsk.PricePerByte, + UnsealPrice: oldAsk.UnsealPrice, + PaymentInterval: oldAsk.PaymentInterval, + PaymentIntervalIncrease: oldAsk.PaymentIntervalIncrease, + } + require.Equal(t, expectedAsk, ask) +} diff --git a/retrievalmarket/impl/integration_test.go b/retrievalmarket/impl/integration_test.go index 972cba32..2aa2e770 100644 --- a/retrievalmarket/impl/integration_test.go +++ b/retrievalmarket/impl/integration_test.go @@ -157,7 +157,7 @@ func requireSetupTestClientAndProvider(ctx context.Context, t *testing.T, payChA require.NoError(t, err) providerDs := namespace.Wrap(testData.Ds2, datastore.NewKey("/retrievals/provider")) - priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { ask := retrievalmarket.Ask{} ask.PaymentInterval = expectedQR.MaxPaymentInterval ask.PaymentIntervalIncrease = expectedQR.MaxPaymentIntervalIncrease @@ -388,12 +388,14 @@ func TestClientCanMakeDealWithProvider(t *testing.T) { PieceCID: tut.GenerateCids(1)[0], Deals: []piecestore.DealInfo{ { + DealID: abi.DealID(100), SectorID: sectorID, Offset: offset, Length: abi.UnpaddedPieceSize(len(carData)).Padded(), }, }, } + providerNode.ExpectPricingParams(pieceInfo.PieceCID, []abi.DealID{100}) if testCase.failsUnseal { providerNode.ExpectFailedUnseal(sectorID, offset.Unpadded(), abi.UnpaddedPieceSize(len(carData))) } else { @@ -669,7 +671,7 @@ func setupProvider( opts = append(opts, retrievalimpl.DisableNewDeals()) } - priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { ask := retrievalmarket.Ask{} ask.PaymentInterval = expectedQR.MaxPaymentInterval ask.PaymentIntervalIncrease = expectedQR.MaxPaymentIntervalIncrease diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index 8468e36c..4ea7b9c1 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -7,26 +7,26 @@ import ( "github.com/hannahhoward/go-pubsub" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/ipfs/go-datastore/namespace" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" datatransfer "github.com/filecoin-project/go-data-transfer" versioning "github.com/filecoin-project/go-ds-versioning/pkg" versionedfsm "github.com/filecoin-project/go-ds-versioning/pkg/fsm" - "github.com/filecoin-project/go-multistore" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-statemachine/fsm" - "github.com/filecoin-project/go-fil-markets/piecestore" "github.com/filecoin-project/go-fil-markets/retrievalmarket" + "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/askstore" "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/dtutils" "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/providerstates" "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/requestvalidation" "github.com/filecoin-project/go-fil-markets/retrievalmarket/migrations" rmnet "github.com/filecoin-project/go-fil-markets/retrievalmarket/network" "github.com/filecoin-project/go-fil-markets/shared" + "github.com/filecoin-project/go-multistore" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-statemachine/fsm" ) // RetrievalProviderOption is a function that configures a retrieval provider @@ -35,7 +35,7 @@ type RetrievalProviderOption func(p *Provider) // DealDecider is a function that makes a decision about whether to accept a deal type DealDecider func(ctx context.Context, state retrievalmarket.ProviderDealState) (bool, string, error) -type DealPricingFunc func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) +type DealPricingFunc func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) // Provider is the production implementation of the RetrievalProvider interface type Provider struct { @@ -52,6 +52,7 @@ type Provider struct { stateMachines fsm.Group migrateStateMachines func(context.Context) error dealDecider DealDecider + askStore retrievalmarket.AskStore disableNewDeals bool dealPricingFunc DealPricingFunc } @@ -123,6 +124,12 @@ func NewProvider(minerAddress address.Address, return nil, err } + askStore, err := askstore.NewAskStore(namespace.Wrap(ds, datastore.NewKey("retrieval-ask")), datastore.NewKey("latest")) + if err != nil { + return nil, err + } + p.askStore = askStore + retrievalMigrations, err := migrations.ProviderMigrations.Build() if err != nil { return nil, err @@ -231,6 +238,21 @@ func (p *Provider) SubscribeToEvents(subscriber retrievalmarket.ProviderSubscrib return retrievalmarket.Unsubscribe(p.subscribers.Subscribe(subscriber)) } +// GetAsk returns the current deal parameters this provider accepts +func (p *Provider) GetAsk() *retrievalmarket.Ask { + return p.askStore.GetAsk() +} + +// SetAsk sets the deal parameters this provider accepts +func (p *Provider) SetAsk(ask *retrievalmarket.Ask) { + + err := p.askStore.SetAsk(ask) + + if err != nil { + log.Warnf("Error setting retrieval ask: %w", err) + } +} + // ListDeals lists all known retrieval deals func (p *Provider) ListDeals() map[retrievalmarket.ProviderDealIdentifier]retrievalmarket.ProviderDealState { var deals []retrievalmarket.ProviderDealState @@ -314,7 +336,16 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { } } - ask, err := p.GetAsk(ctx, storageDeals, isUnsealed, stream.RemotePeer()) + input := retrievalmarket.PricingInput{ + // piece from which the payload will be retrieved + // If user hasn't given a PieceCID, we try to choose an unsealed piece in the call to `getPieceInfoFromCid` above. + PieceCID: pieceInfo.PieceCID, + + PayloadCID: query.PayloadCID, + Unsealed: isUnsealed, + Client: stream.RemotePeer(), + } + ask, err := p.GetDynamicAsk(ctx, input, storageDeals) if err != nil { log.Errorf("Retrieval query: GetAsk: %s", err) return @@ -339,14 +370,23 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { } } -func (p *Provider) GetAsk(ctx context.Context, storageDeals []abi.DealID, isUnsealed bool, client peer.ID) (retrievalmarket.Ask, error) { - // TODO How do we fetch the fast-retrieval flag here ? - dp, err := p.node.GetDealPricingParams(ctx, storageDeals) +func (p *Provider) GetDynamicAsk(ctx context.Context, input retrievalmarket.PricingInput, storageDeals []abi.DealID) (retrievalmarket.Ask, error) { + dp, err := p.node.GetRetrievalPricingInput(ctx, input.PieceCID, storageDeals) if err != nil { return retrievalmarket.Ask{}, xerrors.Errorf("GetDealPricingParams: %s", err) } - dp.Unsealed = isUnsealed - dp.Client = client + // currAsk cannot be nil as we initialize the ask store with a default ask. + // Users can then change the values in the ask store using SetAsk but not remove it. + currAsk := p.GetAsk() + if currAsk == nil { + return retrievalmarket.Ask{}, xerrors.New("no ask configured in ask-store") + } + + dp.PayloadCID = input.PayloadCID + dp.PieceCID = input.PieceCID + dp.Unsealed = input.Unsealed + dp.Client = input.Client + dp.CurrentAsk = *currAsk ask, err := p.dealPricingFunc(ctx, dp) if err != nil { diff --git a/retrievalmarket/impl/provider_environments.go b/retrievalmarket/impl/provider_environments.go index cf4bf6d5..f714606d 100644 --- a/retrievalmarket/impl/provider_environments.go +++ b/retrievalmarket/impl/provider_environments.go @@ -50,7 +50,16 @@ func (pve *providerValidationEnvironment) GetAsk(ctx context.Context, payloadCid } } - return pve.p.GetAsk(ctx, storageDeals, isUnsealed, client) + input := retrievalmarket.PricingInput{ + // piece from which the payload will be retrieved + PieceCID: piece.PieceCID, + + PayloadCID: payloadCid, + Unsealed: isUnsealed, + Client: client, + } + + return pve.p.GetDynamicAsk(ctx, input, storageDeals) } func (pve *providerValidationEnvironment) GetPiece(c cid.Cid, pieceCID *cid.Cid) (piecestore.PieceInfo, bool, error) { diff --git a/retrievalmarket/impl/provider_test.go b/retrievalmarket/impl/provider_test.go index 3db97267..fdd568ac 100644 --- a/retrievalmarket/impl/provider_test.go +++ b/retrievalmarket/impl/provider_test.go @@ -110,45 +110,43 @@ func TestDynamicPricing(t *testing.T) { }, } - receiveStreamOnProvider := func(t *testing.T, node *testnodes.TestRetrievalProviderNode, qs network.RetrievalQueryStream, pieceStore piecestore.PieceStore) { - ds := dss.MutexWrap(datastore.NewMapDatastore()) - multiStore, err := multistore.NewMultiDstore(ds) - require.NoError(t, err) - dt := tut.NewTestDataTransfer() - net := tut.NewTestRetrievalMarketNetwork(tut.TestNetworkParams{}) - - priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { - ask := retrievalmarket.Ask{} - - if dealPricingParams.VerifiedDeal { - ask.PricePerByte = expectedppbVerified - } else { - ask.PricePerByte = expectedppbUnVerified - } + dPriceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { + ask := retrievalmarket.Ask{} - if dealPricingParams.Unsealed { - ask.UnsealPrice = expectedUnsealDiscount - } else { - ask.UnsealPrice = expectedUnsealPrice - } + if dealPricingParams.VerifiedDeal { + ask.PricePerByte = expectedppbVerified + } else { + ask.PricePerByte = expectedppbUnVerified + } - fmt.Println("\n client is", dealPricingParams.Client.String()) - if dealPricingParams.Client == peer2 { - ask.PaymentInterval = expectedpiPeer2 - } else { - ask.PaymentInterval = expectedpiPeer1 - } - ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease + if dealPricingParams.Unsealed { + ask.UnsealPrice = expectedUnsealDiscount + } else { + ask.UnsealPrice = expectedUnsealPrice + } - return ask, nil + fmt.Println("\n client is", dealPricingParams.Client.String()) + if dealPricingParams.Client == peer2 { + ask.PaymentInterval = expectedpiPeer2 + } else { + ask.PaymentInterval = expectedpiPeer1 } + ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease - c, err := retrievalimpl.NewProvider(expectedAddress, node, net, pieceStore, multiStore, dt, ds, priceFunc) + return ask, nil + } + + buildProvider := func(t *testing.T, node *testnodes.TestRetrievalProviderNode, qs network.RetrievalQueryStream, + pieceStore piecestore.PieceStore, net *tut.TestRetrievalMarketNetwork, pFnc retrievalimpl.DealPricingFunc) retrievalmarket.RetrievalProvider { + ds := dss.MutexWrap(datastore.NewMapDatastore()) + multiStore, err := multistore.NewMultiDstore(ds) require.NoError(t, err) + dt := tut.NewTestDataTransfer() + c, err := retrievalimpl.NewProvider(expectedAddress, node, net, pieceStore, multiStore, dt, ds, pFnc) + require.NoError(t, err) tut.StartAndWaitForReady(ctx, t, c) - - net.ReceiveQueryStream(qs) + return c } readWriteQueryStream := func() *tut.TestRetrievalQueryStream { @@ -164,10 +162,13 @@ func TestDynamicPricing(t *testing.T) { } tcs := map[string]struct { - query retrievalmarket.Query - expFunc func(t *testing.T, pieceStore *tut.TestPieceStore) - nodeFunc func(n *testnodes.TestRetrievalProviderNode) - peerIdFnc func(stream *tut.TestRetrievalQueryStream) + query retrievalmarket.Query + expFunc func(t *testing.T, pieceStore *tut.TestPieceStore) + nodeFunc func(n *testnodes.TestRetrievalProviderNode) + peerIdFnc func(stream *tut.TestRetrievalQueryStream) + providerFnc func(provider retrievalmarket.RetrievalProvider) + + pricingFnc retrievalimpl.DealPricingFunc expectedPricePerByte abi.TokenAmount expectedPaymentInterval uint64 @@ -182,13 +183,15 @@ func TestDynamicPricing(t *testing.T) { qs.SetRemotePeer(peer1) }, nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { - n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + n.ExpectPricingParams(expectedPieceCID1, []abi.DealID{1, 11, 2, 22, 222}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) pieceStore.ExpectPiece(expectedPieceCID2, piece2) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbUnVerified, expectedPaymentInterval: expectedpiPeer1, @@ -203,13 +206,15 @@ func TestDynamicPricing(t *testing.T) { qs.SetRemotePeer(peer2) }, nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { - n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + n.ExpectPricingParams(expectedPieceCID1, []abi.DealID{1, 11, 2, 22, 222}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) pieceStore.ExpectPiece(expectedPieceCID2, piece2) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbUnVerified, expectedPaymentInterval: expectedpiPeer2, @@ -225,13 +230,15 @@ func TestDynamicPricing(t *testing.T) { }, nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { n.MarkVerified() - n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + n.ExpectPricingParams(expectedPieceCID1, []abi.DealID{1, 11, 2, 22, 222}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) pieceStore.ExpectPiece(expectedPieceCID2, piece2) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbVerified, expectedPaymentInterval: expectedpiPeer1, @@ -248,13 +255,15 @@ func TestDynamicPricing(t *testing.T) { nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { p := piece2.Deals[0] n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) - n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + n.ExpectPricingParams(expectedPieceCID2, []abi.DealID{1, 11, 2, 22, 222}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) pieceStore.ExpectPiece(expectedPieceCID2, piece2) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbUnVerified, expectedPaymentInterval: expectedpiPeer1, @@ -272,13 +281,15 @@ func TestDynamicPricing(t *testing.T) { p := piece2.Deals[0] n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) n.MarkVerified() - n.ExpectPricingParamDeals([]abi.DealID{1, 11, 2, 22, 222}) + n.ExpectPricingParams(expectedPieceCID2, []abi.DealID{1, 11, 2, 22, 222}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) pieceStore.ExpectPiece(expectedPieceCID2, piece2) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbVerified, expectedPaymentInterval: expectedpiPeer1, @@ -287,7 +298,7 @@ func TestDynamicPricing(t *testing.T) { expectedSize: piece2Size, }, - // Retrieval request for a payloadCid inside a specific Cid + // Retrieval requests for a payloadCid inside a specific piece Cid "specific sealed piece Cid, first piece Cid matches: quote correct price for sealed, unverified, peer1": { query: retrievalmarket.Query{ PayloadCID: payloadCID, @@ -299,12 +310,14 @@ func TestDynamicPricing(t *testing.T) { nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { p := piece2.Deals[0] n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) - n.ExpectPricingParamDeals([]abi.DealID{1, 11}) + n.ExpectPricingParams(expectedPieceCID1, []abi.DealID{1, 11}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbUnVerified, expectedPaymentInterval: expectedpiPeer1, @@ -324,13 +337,15 @@ func TestDynamicPricing(t *testing.T) { nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { p := piece1.Deals[0] n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) - n.ExpectPricingParamDeals([]abi.DealID{2, 22, 222}) + n.ExpectPricingParams(expectedPieceCID2, []abi.DealID{2, 22, 222}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) pieceStore.ExpectPiece(expectedPieceCID1, piece2) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbUnVerified, expectedPaymentInterval: expectedpiPeer1, @@ -350,13 +365,15 @@ func TestDynamicPricing(t *testing.T) { nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { p := piece2.Deals[0] n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) - n.ExpectPricingParamDeals([]abi.DealID{1, 11}) + n.ExpectPricingParams(expectedPieceCID1, []abi.DealID{1, 11}) n.MarkVerified() }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbVerified, expectedPaymentInterval: expectedpiPeer1, @@ -377,12 +394,14 @@ func TestDynamicPricing(t *testing.T) { p := piece1.Deals[0] n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) n.MarkVerified() - n.ExpectPricingParamDeals([]abi.DealID{1, 11}) + n.ExpectPricingParams(expectedPieceCID1, []abi.DealID{1, 11}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbVerified, expectedPaymentInterval: expectedpiPeer1, @@ -403,13 +422,15 @@ func TestDynamicPricing(t *testing.T) { p := piece2.Deals[0] n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) n.MarkVerified() - n.ExpectPricingParamDeals([]abi.DealID{2, 22, 222}) + n.ExpectPricingParams(expectedPieceCID2, []abi.DealID{2, 22, 222}) }, expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { pieceStore.ExpectCID(payloadCID, expectedCIDInfo) pieceStore.ExpectPiece(expectedPieceCID1, piece1) pieceStore.ExpectPiece(expectedPieceCID2, piece2) }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) {}, + pricingFnc: dPriceFunc, expectedPricePerByte: expectedppbVerified, expectedPaymentInterval: expectedpiPeer2, @@ -417,6 +438,40 @@ func TestDynamicPricing(t *testing.T) { expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, expectedSize: piece2Size, }, + "pieceCid no-op: quote correct price for sealed, unverified, peer1 based on a pre-existing ask": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + n.ExpectPricingParams(expectedPieceCID1, []abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) { + ask := provider.GetAsk() + ask.PricePerByte = expectedppbUnVerified + ask.UnsealPrice = expectedUnsealPrice + provider.SetAsk(ask) + }, + pricingFnc: func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { + ask, _ := dPriceFunc(ctx, dealPricingParams) + ppb := big.Add(ask.PricePerByte, dealPricingParams.CurrentAsk.PricePerByte) + unseal := big.Add(ask.UnsealPrice, dealPricingParams.CurrentAsk.UnsealPrice) + ask.PricePerByte = ppb + ask.UnsealPrice = unseal + return ask, nil + }, + + expectedPricePerByte: big.Mul(expectedppbUnVerified, big.NewInt(2)), + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: big.Mul(expectedUnsealPrice, big.NewInt(2)), + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece1Size, + }, } for name, tc := range tcs { @@ -430,9 +485,14 @@ func TestDynamicPricing(t *testing.T) { pieceStore := tut.NewTestPieceStore() tc.nodeFunc(node) tc.expFunc(t, pieceStore) - receiveStreamOnProvider(t, node, qs, pieceStore) + + net := tut.NewTestRetrievalMarketNetwork(tut.TestNetworkParams{}) + p := buildProvider(t, node, qs, pieceStore, net, tc.pricingFnc) + tc.providerFnc(p) + net.ReceiveQueryStream(qs) actualResp, err := qs.ReadQueryResponse() + require.NoError(t, err) pieceStore.VerifyExpectations(t) node.VerifyExpectations(t) @@ -514,7 +574,7 @@ func TestHandleQueryStream(t *testing.T) { dt := tut.NewTestDataTransfer() net := tut.NewTestRetrievalMarketNetwork(tut.TestNetworkParams{}) - priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { ask := retrievalmarket.Ask{} ask.PricePerByte = expectedPricePerByte ask.PaymentInterval = expectedPaymentInterval @@ -739,7 +799,7 @@ func TestProvider_Construct(t *testing.T) { require.NoError(t, err) dt := tut.NewTestDataTransfer() - priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { ask := retrievalmarket.Ask{} return ask, nil } @@ -791,7 +851,7 @@ func TestProviderConfigOpts(t *testing.T) { multiStore, err := multistore.NewMultiDstore(ds) require.NoError(t, err) - priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { ask := retrievalmarket.Ask{} return ask, nil } @@ -971,7 +1031,7 @@ func TestProviderMigrations(t *testing.T) { err = providerDs.Put(datastore.NewKey("retrieval-ask"), askBuf.Bytes()) require.NoError(t, err) - priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { ask := retrievalmarket.Ask{} return ask, nil } diff --git a/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go b/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go index ff21c00d..2b41d0fe 100644 --- a/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go +++ b/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "testing" + "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" @@ -48,9 +49,13 @@ type TestRetrievalProviderNode struct { receivedVouchers map[expectedVoucherKey]struct{} expectedPricingParamDeals []abi.DealID - recievedPricingParamDeals []abi.DealID - unsealed map[sectorKey]struct{} - isVerified bool + receivedPricingParamDeals []abi.DealID + + expectedPricingPieceCID cid.Cid + receivedPricingPieceCID cid.Cid + + unsealed map[sectorKey]struct{} + isVerified bool } var _ retrievalmarket.RetrievalProviderNode = &TestRetrievalProviderNode{} @@ -81,13 +86,16 @@ func (trpn *TestRetrievalProviderNode) MarkVerified() { trpn.isVerified = true } -func (trpn *TestRetrievalProviderNode) ExpectPricingParamDeals(deals []abi.DealID) { +func (trpn *TestRetrievalProviderNode) ExpectPricingParams(pieceCID cid.Cid, deals []abi.DealID) { + trpn.expectedPricingPieceCID = pieceCID trpn.expectedPricingParamDeals = deals } -func (trpn *TestRetrievalProviderNode) GetDealPricingParams(_ context.Context, deals []abi.DealID) (retrievalmarket.DealPricingParams, error) { - trpn.recievedPricingParamDeals = deals - return retrievalmarket.DealPricingParams{ +func (trpn *TestRetrievalProviderNode) GetRetrievalPricingInput(_ context.Context, pieceCID cid.Cid, deals []abi.DealID) (retrievalmarket.PricingInput, error) { + trpn.receivedPricingParamDeals = deals + trpn.receivedPricingPieceCID = pieceCID + + return retrievalmarket.PricingInput{ VerifiedDeal: trpn.isVerified, }, nil } @@ -126,8 +134,9 @@ func (trpn *TestRetrievalProviderNode) UnsealSector(ctx context.Context, sectorI func (trpn *TestRetrievalProviderNode) VerifyExpectations(t *testing.T) { require.Equal(t, len(trpn.expectedVouchers), len(trpn.receivedVouchers)) require.Equal(t, trpn.expectations, trpn.received) + require.Equal(t, trpn.expectedPricingPieceCID, trpn.receivedPricingPieceCID) - require.Equal(t, trpn.expectedPricingParamDeals, trpn.recievedPricingParamDeals) + require.Equal(t, trpn.expectedPricingParamDeals, trpn.receivedPricingParamDeals) } // SavePaymentVoucher simulates saving a payment voucher with a stubbed result diff --git a/retrievalmarket/nodes.go b/retrievalmarket/nodes.go index 358b3c1b..8eb27e0d 100644 --- a/retrievalmarket/nodes.go +++ b/retrievalmarket/nodes.go @@ -55,5 +55,5 @@ type RetrievalProviderNode interface { IsUnsealed(ctx context.Context, sectorID abi.SectorNumber, offset abi.UnpaddedPieceSize, length abi.UnpaddedPieceSize) (bool, error) - GetDealPricingParams(ctx context.Context, storageDeals []abi.DealID) (DealPricingParams, error) + GetRetrievalPricingInput(ctx context.Context, pieceCID cid.Cid, storageDeals []abi.DealID) (PricingInput, error) } diff --git a/retrievalmarket/provider.go b/retrievalmarket/provider.go index e40a6d19..f2926aa0 100644 --- a/retrievalmarket/provider.go +++ b/retrievalmarket/provider.go @@ -21,8 +21,20 @@ type RetrievalProvider interface { // Stop stops handling incoming requests Stop() error + // SetAsk sets the retrieval payment parameters that this miner will accept + SetAsk(ask *Ask) + + // GetAsk returns the retrieval providers pricing information + GetAsk() *Ask + // SubscribeToEvents listens for events that happen related to client retrievals SubscribeToEvents(subscriber ProviderSubscriber) Unsubscribe ListDeals() map[ProviderDealIdentifier]ProviderDealState } + +// AskStore is an interface which provides access to a persisted retrieval Ask +type AskStore interface { + GetAsk() *Ask + SetAsk(ask *Ask) error +} diff --git a/retrievalmarket/storage_retrieval_integration_test.go b/retrievalmarket/storage_retrieval_integration_test.go index a1aecc09..b83602a1 100644 --- a/retrievalmarket/storage_retrieval_integration_test.go +++ b/retrievalmarket/storage_retrieval_integration_test.go @@ -500,7 +500,7 @@ func newRetrievalHarness(ctx context.Context, t *testing.T, sh *testharness.Stor p = params[0] } - priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.DealPricingParams) (retrievalmarket.Ask, error) { + priceFunc := func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { ask := retrievalmarket.Ask{} ask.PaymentInterval = p.PaymentInterval ask.PaymentIntervalIncrease = p.PaymentIntervalIncrease diff --git a/retrievalmarket/types.go b/retrievalmarket/types.go index e7b0d544..34680dd3 100644 --- a/retrievalmarket/types.go +++ b/retrievalmarket/types.go @@ -383,9 +383,20 @@ type ChannelAvailableFunds struct { VoucherReedeemedAmt abi.TokenAmount } -// DealPricingParams provides parameters required to price a retrieval deal -type DealPricingParams struct { - Client peer.ID +// PricingInput provides input parameters required to price a retrieval deal. +type PricingInput struct { + // PayloadCID is the cid of the payload to retrieve. + PayloadCID cid.Cid + // PieceCID is the cid of the Piece from which the Payload will be retrieved. + PieceCID cid.Cid + // PieceSize is the size of the Piece from which the payload will be retrieved. + PieceSize abi.UnpaddedPieceSize + // Client is the peerID of the retrieval client. + Client peer.ID + // VerifiedDeal is true if there exists a verified storage deal for the PayloadCID. VerifiedDeal bool - Unsealed bool + // Unsealed is true if there exists an unsealed sector from which we can retrieve the given payload. + Unsealed bool + // CurrentAsk is the current configured ask in the ask-store. + CurrentAsk Ask } From a18e80bc9e9d2163ac886e29c2088d31b5441177 Mon Sep 17 00:00:00 2001 From: Aarsh Shah Date: Fri, 7 May 2021 12:58:44 +0530 Subject: [PATCH 05/14] Apply suggestions from code review Co-authored-by: raulk Co-authored-by: dirkmc --- retrievalmarket/impl/provider.go | 4 ++-- retrievalmarket/impl/provider_environments.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index 4ea7b9c1..0e15c87a 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -54,7 +54,7 @@ type Provider struct { dealDecider DealDecider askStore retrievalmarket.AskStore disableNewDeals bool - dealPricingFunc DealPricingFunc + retrievalPricingFunc RetrievalPricingFunc } type internalProviderEvent struct { @@ -373,7 +373,7 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { func (p *Provider) GetDynamicAsk(ctx context.Context, input retrievalmarket.PricingInput, storageDeals []abi.DealID) (retrievalmarket.Ask, error) { dp, err := p.node.GetRetrievalPricingInput(ctx, input.PieceCID, storageDeals) if err != nil { - return retrievalmarket.Ask{}, xerrors.Errorf("GetDealPricingParams: %s", err) + return retrievalmarket.Ask{}, xerrors.Errorf("GetRetrievalPricingInput: %s", err) } // currAsk cannot be nil as we initialize the ask store with a default ask. // Users can then change the values in the ask store using SetAsk but not remove it. diff --git a/retrievalmarket/impl/provider_environments.go b/retrievalmarket/impl/provider_environments.go index f714606d..2f8a5803 100644 --- a/retrievalmarket/impl/provider_environments.go +++ b/retrievalmarket/impl/provider_environments.go @@ -276,7 +276,7 @@ func getPieceInfoFromCid(ctx context.Context, n retrievalmarket.RetrievalProvide } // if client wants to retrieve the payload from a specific piece, just return that piece. - if !pieceCID.Equals(cid.Undef) && pieceInfo.PieceCID.Equals(pieceCID) { + if pieceCID.Defined() && pieceInfo.PieceCID.Equals(pieceCID) { return pieceInfo, pieceInUnsealedSector(ctx, n, pieceInfo), nil } From f842a9dfe087847834fbcd05037c4f61975e7804 Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Fri, 7 May 2021 17:17:32 +0530 Subject: [PATCH 06/14] changes as per review and test json --- retrievalmarket/impl/integration_test.go | 2 +- retrievalmarket/impl/provider.go | 159 +++++++++--------- retrievalmarket/impl/provider_environments.go | 53 ++++-- retrievalmarket/impl/provider_test.go | 4 +- retrievalmarket/types_test.go | 30 ++++ 5 files changed, 154 insertions(+), 94 deletions(-) diff --git a/retrievalmarket/impl/integration_test.go b/retrievalmarket/impl/integration_test.go index 2aa2e770..9e935f08 100644 --- a/retrievalmarket/impl/integration_test.go +++ b/retrievalmarket/impl/integration_test.go @@ -69,7 +69,7 @@ func TestClientCanMakeQueryToProvider(t *testing.T) { t.Run("when there is some other error, returns error", func(t *testing.T) { unknownPiece := tut.GenerateCids(1)[0] expectedQR.Status = retrievalmarket.QueryResponseError - expectedQR.Message = "get cid info: GetCIDInfo failed" + expectedQR.Message = "failed to fetch piece to retrieve from: get cid info: GetCIDInfo failed" actualQR, err := client.Query(bgCtx, retrievalPeer, unknownPiece, retrievalmarket.QueryParams{}) assert.NoError(t, err) actualQR.MaxPaymentInterval = expectedQR.MaxPaymentInterval diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index 0e15c87a..f4997295 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -3,6 +3,7 @@ package retrievalimpl import ( "context" "errors" + "fmt" "github.com/hannahhoward/go-pubsub" "github.com/ipfs/go-cid" @@ -14,6 +15,11 @@ import ( datatransfer "github.com/filecoin-project/go-data-transfer" versioning "github.com/filecoin-project/go-ds-versioning/pkg" versionedfsm "github.com/filecoin-project/go-ds-versioning/pkg/fsm" + "github.com/filecoin-project/go-multistore" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-statemachine/fsm" + "github.com/filecoin-project/go-fil-markets/piecestore" "github.com/filecoin-project/go-fil-markets/retrievalmarket" "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl/askstore" @@ -23,10 +29,6 @@ import ( "github.com/filecoin-project/go-fil-markets/retrievalmarket/migrations" rmnet "github.com/filecoin-project/go-fil-markets/retrievalmarket/network" "github.com/filecoin-project/go-fil-markets/shared" - "github.com/filecoin-project/go-multistore" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-statemachine/fsm" ) // RetrievalProviderOption is a function that configures a retrieval provider @@ -35,7 +37,7 @@ type RetrievalProviderOption func(p *Provider) // DealDecider is a function that makes a decision about whether to accept a deal type DealDecider func(ctx context.Context, state retrievalmarket.ProviderDealState) (bool, string, error) -type DealPricingFunc func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) +type RetrievalPricingFunc func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) // Provider is the production implementation of the RetrievalProvider interface type Provider struct { @@ -54,7 +56,7 @@ type Provider struct { dealDecider DealDecider askStore retrievalmarket.AskStore disableNewDeals bool - retrievalPricingFunc RetrievalPricingFunc + retrievalPricingFunc RetrievalPricingFunc } type internalProviderEvent struct { @@ -99,7 +101,7 @@ func NewProvider(minerAddress address.Address, multiStore *multistore.MultiStore, dataTransfer datatransfer.Manager, ds datastore.Batching, - dealPricingFunc DealPricingFunc, + dealPricingFunc RetrievalPricingFunc, opts ...RetrievalProviderOption, ) (retrievalmarket.RetrievalProvider, error) { @@ -108,15 +110,15 @@ func NewProvider(minerAddress address.Address, } p := &Provider{ - multiStore: multiStore, - dataTransfer: dataTransfer, - node: node, - network: network, - minerAddress: minerAddress, - pieceStore: pieceStore, - subscribers: pubsub.New(providerDispatcher), - readySub: pubsub.New(shared.ReadyDispatcher), - dealPricingFunc: dealPricingFunc, + multiStore: multiStore, + dataTransfer: dataTransfer, + node: node, + network: network, + minerAddress: minerAddress, + pieceStore: pieceStore, + subscribers: pubsub.New(providerDispatcher), + readySub: pubsub.New(shared.ReadyDispatcher), + retrievalPricingFunc: dealPricingFunc, } err := shared.MoveKey(ds, "retrieval-ask", "retrieval-ask/latest") @@ -286,6 +288,12 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { return } + sendResp := func(resp retrievalmarket.QueryResponse) { + if err := stream.WriteQueryResponse(resp); err != nil { + log.Errorf("Retrieval query: WriteCborRPC: %s", err) + } + } + answer := retrievalmarket.QueryResponse{ Status: retrievalmarket.QueryResponseUnavailable, PieceCIDFound: retrievalmarket.QueryItemUnavailable, @@ -293,83 +301,84 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { UnsealPrice: big.Zero(), } + // get chain head to query actor states. ctx := context.TODO() - tok, _, err := p.node.GetChainHead(ctx) if err != nil { log.Errorf("Retrieval query: GetChainHead: %s", err) return } + // fetch the payment address the client should send the payment to. paymentAddress, err := p.node.GetMinerWorkerAddress(ctx, p.minerAddress, tok) if err != nil { log.Errorf("Retrieval query: Lookup Payment Address: %s", err) answer.Status = retrievalmarket.QueryResponseError - answer.Message = err.Error() - } else { - answer.PaymentAddress = paymentAddress - - pieceCID := cid.Undef - if query.PieceCID != nil { - pieceCID = *query.PieceCID - } - pieceInfo, isUnsealed, err := getPieceInfoFromCid(ctx, p.node, p.pieceStore, query.PayloadCID, pieceCID) - if err == nil && len(pieceInfo.Deals) > 0 { - answer.Status = retrievalmarket.QueryResponseAvailable - answer.Size = uint64(pieceInfo.Deals[0].Length) // TODO: verify on intermediate - answer.PieceCIDFound = retrievalmarket.QueryItemAvailable - - var storageDeals []abi.DealID - if pieceCID != cid.Undef { - // If the user wants to retrieve the payload from a specific piece, we only need to inspect storage deals - // made for that piece to quote a price for retrieval. - for _, d := range pieceInfo.Deals { - storageDeals = append(storageDeals, d.DealID) - } - } else { - // If the user does NOT want to retrieve the payload from a specific piece, we'll have to inspect all deals - // made for that piece to quote a price for retrieval. - storageDeals, err = getAllDealsContainingPayload(p.pieceStore, query.PayloadCID) - if err != nil { - log.Errorf("Retrieval query: getAllDealsContainingPayload: %s", err) - return - } - } - - input := retrievalmarket.PricingInput{ - // piece from which the payload will be retrieved - // If user hasn't given a PieceCID, we try to choose an unsealed piece in the call to `getPieceInfoFromCid` above. - PieceCID: pieceInfo.PieceCID, - - PayloadCID: query.PayloadCID, - Unsealed: isUnsealed, - Client: stream.RemotePeer(), - } - ask, err := p.GetDynamicAsk(ctx, input, storageDeals) - if err != nil { - log.Errorf("Retrieval query: GetAsk: %s", err) - return - } - - answer.MinPricePerByte = ask.PricePerByte - answer.MaxPaymentInterval = ask.PaymentInterval - answer.MaxPaymentIntervalIncrease = ask.PaymentIntervalIncrease - answer.UnsealPrice = ask.UnsealPrice - } - - if err != nil && !xerrors.Is(err, retrievalmarket.ErrNotFound) { - log.Errorf("Retrieval query: GetRefs: %s", err) + answer.Message = fmt.Sprintf("failed to look up payment address: %s", err) + sendResp(answer) + return + } + answer.PaymentAddress = paymentAddress + + // fetch the piece from which the payload will be retrieved. + // if user has specified the Piece in the request, we use that. + // Otherwise, we prefer a Piece which can retrieved from an unsealed sector. + pieceCID := cid.Undef + if query.PieceCID != nil { + pieceCID = *query.PieceCID + } + pieceInfo, isUnsealed, err := getPieceInfoFromCid(ctx, p.node, p.pieceStore, query.PayloadCID, pieceCID) + if err != nil { + log.Errorf("Retrieval query: getPieceInfoFromCid: %s", err) + if !xerrors.Is(err, retrievalmarket.ErrNotFound) { answer.Status = retrievalmarket.QueryResponseError - answer.Message = err.Error() + answer.Message = fmt.Sprintf("failed to fetch piece to retrieve from: %s", err) } + sendResp(answer) + return } - if err := stream.WriteQueryResponse(answer); err != nil { - log.Errorf("Retrieval query: WriteCborRPC: %s", err) + + answer.Status = retrievalmarket.QueryResponseAvailable + answer.Size = uint64(pieceInfo.Deals[0].Length) // TODO: verify on intermediate + answer.PieceCIDFound = retrievalmarket.QueryItemAvailable + + storageDeals, err := storageDealsForPiece(query.PieceCID != nil, query.PayloadCID, pieceInfo, p.pieceStore) + if err != nil { + log.Errorf("Retrieval query: storageDealsForPiece: %s", err) + answer.Status = retrievalmarket.QueryResponseError + answer.Message = fmt.Sprintf("failed to fetch storage deals containing payload: %s", err) + sendResp(answer) return } + + input := retrievalmarket.PricingInput{ + // piece from which the payload will be retrieved + // If user hasn't given a PieceCID, we try to choose an unsealed piece in the call to `getPieceInfoFromCid` above. + PieceCID: pieceInfo.PieceCID, + + PayloadCID: query.PayloadCID, + Unsealed: isUnsealed, + Client: stream.RemotePeer(), + } + ask, err := p.GetDynamicAsk(ctx, input, storageDeals) + if err != nil { + log.Errorf("Retrieval query: GetAsk: %s", err) + answer.Status = retrievalmarket.QueryResponseError + answer.Message = fmt.Sprintf("failed to price deal: %s", err) + sendResp(answer) + return + } + + answer.MinPricePerByte = ask.PricePerByte + answer.MaxPaymentInterval = ask.PaymentInterval + answer.MaxPaymentIntervalIncrease = ask.PaymentIntervalIncrease + answer.UnsealPrice = ask.UnsealPrice + sendResp(answer) } +// GetDynamicAsk quotes a dynamic price for the retrieval deal by calling the user configured +// dynamic pricing function. It passes the static price parameters set in the Ask Store to the pricing function. func (p *Provider) GetDynamicAsk(ctx context.Context, input retrievalmarket.PricingInput, storageDeals []abi.DealID) (retrievalmarket.Ask, error) { dp, err := p.node.GetRetrievalPricingInput(ctx, input.PieceCID, storageDeals) if err != nil { @@ -388,7 +397,7 @@ func (p *Provider) GetDynamicAsk(ctx context.Context, input retrievalmarket.Pric dp.Client = input.Client dp.CurrentAsk = *currAsk - ask, err := p.dealPricingFunc(ctx, dp) + ask, err := p.retrievalPricingFunc(ctx, dp) if err != nil { return retrievalmarket.Ask{}, xerrors.Errorf("dealPricingFunc: %s", err) } diff --git a/retrievalmarket/impl/provider_environments.go b/retrievalmarket/impl/provider_environments.go index 2f8a5803..f39424c1 100644 --- a/retrievalmarket/impl/provider_environments.go +++ b/retrievalmarket/impl/provider_environments.go @@ -32,22 +32,10 @@ type providerValidationEnvironment struct { func (pve *providerValidationEnvironment) GetAsk(ctx context.Context, payloadCid cid.Cid, pieceCid *cid.Cid, piece piecestore.PieceInfo, isUnsealed bool, client peer.ID) (retrievalmarket.Ask, error) { - var storageDeals []abi.DealID - var err error - if pieceCid != nil { - // If the user wants to retrieve the payload from a specific piece, - // we only need to inspect storage deals made for that piece to quote a price. - for _, d := range piece.Deals { - storageDeals = append(storageDeals, d.DealID) - } - } else { - // If the user does NOT want to retrieve from a specific piece, we'll have to inspect all storage deals - // made for that piece to quote a price. - storageDeals, err = getAllDealsContainingPayload(pve.p.pieceStore, payloadCid) - if err != nil { - return retrievalmarket.Ask{}, xerrors.Errorf("failed to fetch deals for payload, err=%s", err) - } + storageDeals, err := storageDealsForPiece(pieceCid != nil, payloadCid, piece, pve.p.pieceStore) + if err != nil { + return retrievalmarket.Ask{}, xerrors.Errorf("failed to fetch deals for payload, err=%s", err) } input := retrievalmarket.PricingInput{ @@ -226,6 +214,7 @@ func pieceInUnsealedSector(ctx context.Context, n retrievalmarket.RetrievalProvi for _, di := range pieceInfo.Deals { isUnsealed, err := n.IsUnsealed(ctx, di.SectorID, di.Offset.Unpadded(), di.Length.Unpadded()) if err != nil { + log.Errorf("failed to find out is sector %s is unsealed, err=%s", di.SectorID, err) continue } if isUnsealed { @@ -236,16 +225,43 @@ func pieceInUnsealedSector(ctx context.Context, n retrievalmarket.RetrievalProvi return false } +func storageDealsForPiece(clientSpecificPiece bool, payloadCID cid.Cid, pieceInfo piecestore.PieceInfo, pieceStore piecestore.PieceStore) ([]abi.DealID, error) { + var storageDeals []abi.DealID + var err error + if clientSpecificPiece { + // If the user wants to retrieve the payload from a specific piece, + // we only need to inspect storage deals made for that piece to quote a price. + for _, d := range pieceInfo.Deals { + storageDeals = append(storageDeals, d.DealID) + } + } else { + // If the user does NOT want to retrieve from a specific piece, we'll have to inspect all storage deals + // made for that piece to quote a price. + storageDeals, err = getAllDealsContainingPayload(pieceStore, payloadCID) + if err != nil { + return nil, xerrors.Errorf("failed to fetch deals for payload, err=%s", err) + } + } + + if len(storageDeals) == 0 { + return nil, xerrors.New("no storage deals found") + } + + return storageDeals, nil +} + func getAllDealsContainingPayload(pieceStore piecestore.PieceStore, payloadCID cid.Cid) ([]abi.DealID, error) { cidInfo, err := pieceStore.GetCIDInfo(payloadCID) if err != nil { return nil, xerrors.Errorf("get cid info: %w", err) } var dealsIds []abi.DealID + var lastErr error for _, pieceBlockLocation := range cidInfo.PieceBlockLocations { pieceInfo, err := pieceStore.GetPieceInfo(pieceBlockLocation.PieceCID) if err != nil { + lastErr = err continue } for _, d := range pieceInfo.Deals { @@ -253,10 +269,14 @@ func getAllDealsContainingPayload(pieceStore piecestore.PieceStore, payloadCID c } } - if len(dealsIds) == 0 { + if lastErr == nil && len(dealsIds) == 0 { return nil, xerrors.New("no deals found") } + if lastErr != nil && len(dealsIds) == 0 { + return nil, xerrors.Errorf("failed to fetch deals containing payload, err=%s", lastErr) + } + return dealsIds, nil } @@ -301,6 +321,7 @@ func getPieceInfoFromCid(ctx context.Context, n retrievalmarket.RetrievalProvide if lastErr == nil { lastErr = xerrors.Errorf("unknown pieceCID %s", pieceCID.String()) } + return piecestore.PieceInfoUndefined, false, xerrors.Errorf("could not locate piece: %w", lastErr) } diff --git a/retrievalmarket/impl/provider_test.go b/retrievalmarket/impl/provider_test.go index fdd568ac..bcae550c 100644 --- a/retrievalmarket/impl/provider_test.go +++ b/retrievalmarket/impl/provider_test.go @@ -137,7 +137,7 @@ func TestDynamicPricing(t *testing.T) { } buildProvider := func(t *testing.T, node *testnodes.TestRetrievalProviderNode, qs network.RetrievalQueryStream, - pieceStore piecestore.PieceStore, net *tut.TestRetrievalMarketNetwork, pFnc retrievalimpl.DealPricingFunc) retrievalmarket.RetrievalProvider { + pieceStore piecestore.PieceStore, net *tut.TestRetrievalMarketNetwork, pFnc retrievalimpl.RetrievalPricingFunc) retrievalmarket.RetrievalProvider { ds := dss.MutexWrap(datastore.NewMapDatastore()) multiStore, err := multistore.NewMultiDstore(ds) require.NoError(t, err) @@ -168,7 +168,7 @@ func TestDynamicPricing(t *testing.T) { peerIdFnc func(stream *tut.TestRetrievalQueryStream) providerFnc func(provider retrievalmarket.RetrievalProvider) - pricingFnc retrievalimpl.DealPricingFunc + pricingFnc retrievalimpl.RetrievalPricingFunc expectedPricePerByte abi.TokenAmount expectedPaymentInterval uint64 diff --git a/retrievalmarket/types_test.go b/retrievalmarket/types_test.go index 3bf186f2..bd9dc91e 100644 --- a/retrievalmarket/types_test.go +++ b/retrievalmarket/types_test.go @@ -2,11 +2,14 @@ package retrievalmarket_test import ( "bytes" + "encoding/json" "testing" "github.com/ipld/go-ipld-prime/codec/dagcbor" basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/libp2p/go-libp2p-core/test" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" @@ -39,3 +42,30 @@ func TestParamsMarshalUnmarshal(t *testing.T) { sel := nb.Build() assert.Equal(t, sel, allSelector) } + +func TestPricingInputMarshalUnmarshalJSON(t *testing.T) { + pid := test.RandPeerIDFatal(t) + + in := retrievalmarket.PricingInput{ + PayloadCID: tut.GenerateCids(1)[0], + PieceCID: tut.GenerateCids(1)[0], + PieceSize: abi.UnpaddedPieceSize(100), + Client: pid, + VerifiedDeal: true, + Unsealed: true, + CurrentAsk: retrievalmarket.Ask{ + PricePerByte: big.Zero(), + UnsealPrice: big.Zero(), + PaymentInterval: 0, + PaymentIntervalIncrease: 0, + }, + } + + bz, err := json.Marshal(in) + require.NoError(t, err) + + resp2 := retrievalmarket.PricingInput{} + require.NoError(t, json.Unmarshal(bz, &resp2)) + + require.Equal(t, in, resp2) +} From 169c536473c56b22e84a9cbf05dedfd30efcdf4f Mon Sep 17 00:00:00 2001 From: Aarsh Shah Date: Fri, 7 May 2021 17:46:42 +0530 Subject: [PATCH 07/14] Apply suggestions from code review Co-authored-by: dirkmc --- retrievalmarket/impl/provider.go | 6 +++--- retrievalmarket/impl/provider_environments.go | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index f4997295..b3466335 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -105,7 +105,7 @@ func NewProvider(minerAddress address.Address, opts ...RetrievalProviderOption, ) (retrievalmarket.RetrievalProvider, error) { - if dealPricingFunc == nil { + if retrievalPricingFunc == nil { return nil, xerrors.New("dealPricingFunc is nil") } @@ -290,7 +290,7 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { sendResp := func(resp retrievalmarket.QueryResponse) { if err := stream.WriteQueryResponse(resp); err != nil { - log.Errorf("Retrieval query: WriteCborRPC: %s", err) + log.Errorf("Retrieval query: writing query response: %s", err) } } @@ -399,7 +399,7 @@ func (p *Provider) GetDynamicAsk(ctx context.Context, input retrievalmarket.Pric ask, err := p.retrievalPricingFunc(ctx, dp) if err != nil { - return retrievalmarket.Ask{}, xerrors.Errorf("dealPricingFunc: %s", err) + return retrievalmarket.Ask{}, xerrors.Errorf("retrievalPricingFunc: %w", err) } return ask, nil } diff --git a/retrievalmarket/impl/provider_environments.go b/retrievalmarket/impl/provider_environments.go index f39424c1..a91b24b4 100644 --- a/retrievalmarket/impl/provider_environments.go +++ b/retrievalmarket/impl/provider_environments.go @@ -56,8 +56,7 @@ func (pve *providerValidationEnvironment) GetPiece(c cid.Cid, pieceCID *cid.Cid) inPieceCid = *pieceCID } - pi, isUnsealed, err := getPieceInfoFromCid(context.TODO(), pve.p.node, pve.p.pieceStore, c, inPieceCid) - return pi, isUnsealed, err + return getPieceInfoFromCid(context.TODO(), pve.p.node, pve.p.pieceStore, c, inPieceCid) } // CheckDealParams verifies the given deal params are acceptable @@ -214,7 +213,7 @@ func pieceInUnsealedSector(ctx context.Context, n retrievalmarket.RetrievalProvi for _, di := range pieceInfo.Deals { isUnsealed, err := n.IsUnsealed(ctx, di.SectorID, di.Offset.Unpadded(), di.Length.Unpadded()) if err != nil { - log.Errorf("failed to find out is sector %s is unsealed, err=%s", di.SectorID, err) + log.Errorf("failed to find out if sector %d is unsealed, err=%s", di.SectorID, err) continue } if isUnsealed { @@ -239,7 +238,7 @@ func storageDealsForPiece(clientSpecificPiece bool, payloadCID cid.Cid, pieceInf // made for that piece to quote a price. storageDeals, err = getAllDealsContainingPayload(pieceStore, payloadCID) if err != nil { - return nil, xerrors.Errorf("failed to fetch deals for payload, err=%s", err) + return nil, xerrors.Errorf("failed to fetch deals for payload: %w", err) } } @@ -274,7 +273,7 @@ func getAllDealsContainingPayload(pieceStore piecestore.PieceStore, payloadCID c } if lastErr != nil && len(dealsIds) == 0 { - return nil, xerrors.Errorf("failed to fetch deals containing payload, err=%s", lastErr) + return nil, xerrors.Errorf("failed to fetch deals containing payload %s: %w", payloadCID, lastErr) } return dealsIds, nil From 382aea4d2d06c19433d205c7c6b3df321c26822b Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Fri, 7 May 2021 17:48:55 +0530 Subject: [PATCH 08/14] fix compilation --- retrievalmarket/impl/provider.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index b3466335..cf3dd293 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -101,12 +101,12 @@ func NewProvider(minerAddress address.Address, multiStore *multistore.MultiStore, dataTransfer datatransfer.Manager, ds datastore.Batching, - dealPricingFunc RetrievalPricingFunc, + retrievalPricingFunc RetrievalPricingFunc, opts ...RetrievalProviderOption, ) (retrievalmarket.RetrievalProvider, error) { if retrievalPricingFunc == nil { - return nil, xerrors.New("dealPricingFunc is nil") + return nil, xerrors.New("retrievalPricingFunc is nil") } p := &Provider{ @@ -118,7 +118,7 @@ func NewProvider(minerAddress address.Address, pieceStore: pieceStore, subscribers: pubsub.New(providerDispatcher), readySub: pubsub.New(shared.ReadyDispatcher), - retrievalPricingFunc: dealPricingFunc, + retrievalPricingFunc: retrievalPricingFunc, } err := shared.MoveKey(ds, "retrieval-ask", "retrieval-ask/latest") From d32334ae625914d4c05bb602a80a53a29c2967bc Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Fri, 7 May 2021 20:27:25 +0530 Subject: [PATCH 09/14] changes as per review --- retrievalmarket/impl/provider.go | 7 ++++++- retrievalmarket/impl/providerstates/provider_states.go | 2 +- .../impl/requestvalidation/requestvalidation.go | 8 +++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index cf3dd293..b1dfa0c0 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "time" "github.com/hannahhoward/go-pubsub" "github.com/ipfs/go-cid" @@ -39,6 +40,8 @@ type DealDecider func(ctx context.Context, state retrievalmarket.ProviderDealSta type RetrievalPricingFunc func(ctx context.Context, dealPricingParams retrievalmarket.PricingInput) (retrievalmarket.Ask, error) +var queryTimeout = 5 * time.Second + // Provider is the production implementation of the RetrievalProvider interface type Provider struct { multiStore *multistore.MultiStore @@ -282,6 +285,9 @@ A Provider handling a retrieval `Query` does the following: The connection is kept open only as long as the query-response exchange. */ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { + ctx, cancel := context.WithTimeout(context.TODO(), queryTimeout) + defer cancel() + defer stream.Close() query, err := stream.ReadQuery() if err != nil { @@ -302,7 +308,6 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { } // get chain head to query actor states. - ctx := context.TODO() tok, _, err := p.node.GetChainHead(ctx) if err != nil { log.Errorf("Retrieval query: GetChainHead: %s", err) diff --git a/retrievalmarket/impl/providerstates/provider_states.go b/retrievalmarket/impl/providerstates/provider_states.go index 2109b9f7..ba0c93f6 100644 --- a/retrievalmarket/impl/providerstates/provider_states.go +++ b/retrievalmarket/impl/providerstates/provider_states.go @@ -30,7 +30,6 @@ type ProviderDealEnvironment interface { } func firstSuccessfulUnseal(ctx context.Context, node rm.RetrievalProviderNode, pieceInfo piecestore.PieceInfo) (io.ReadCloser, error) { - // prefer an unsealed sector containing the piece if one exists for _, deal := range pieceInfo.Deals { isUnsealed, err := node.IsUnsealed(ctx, deal.SectorID, deal.Offset.Unpadded(), deal.Length.Unpadded()) @@ -38,6 +37,7 @@ func firstSuccessfulUnseal(ctx context.Context, node rm.RetrievalProviderNode, p continue } if isUnsealed { + // UnsealSector will NOT unseal a sector if we already have an unsealed copy lying around. reader, err := node.UnsealSector(ctx, deal.SectorID, deal.Offset.Unpadded(), deal.Length.Unpadded()) if err == nil { return reader, nil diff --git a/retrievalmarket/impl/requestvalidation/requestvalidation.go b/retrievalmarket/impl/requestvalidation/requestvalidation.go index 1de805ad..2af6928a 100644 --- a/retrievalmarket/impl/requestvalidation/requestvalidation.go +++ b/retrievalmarket/impl/requestvalidation/requestvalidation.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "time" "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" @@ -23,6 +24,8 @@ import ( var allSelectorBytes []byte +var askTimeout = 5 * time.Second + func init() { buf := new(bytes.Buffer) _ = dagcbor.Encoder(shared.AllSelector(), buf) @@ -147,7 +150,10 @@ func (rv *ProviderRequestValidator) acceptDeal(deal *retrievalmarket.ProviderDea return retrievalmarket.DealStatusErrored, err } - ask, err := rv.env.GetAsk(context.TODO(), deal.PayloadCID, deal.PieceCID, pieceInfo, isUnsealed, deal.Receiver) + ctx, cancel := context.WithTimeout(context.TODO(), askTimeout) + defer cancel() + + ask, err := rv.env.GetAsk(ctx, deal.PayloadCID, deal.PieceCID, pieceInfo, isUnsealed, deal.Receiver) if err != nil { return retrievalmarket.DealStatusErrored, err } From 3f47318a3cf008fa5a5faa764902a8fa28172479 Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Tue, 11 May 2021 18:20:24 +0530 Subject: [PATCH 10/14] default retrieval pricing function --- retrievalmarket/impl/provider.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index b1dfa0c0..32be6a99 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -424,3 +424,22 @@ var ProviderFSMParameterSpec = fsm.Parameters{ Events: providerstates.ProviderEvents, StateEntryFuncs: providerstates.ProviderStateEntryFuncs, } + +// DefaultPricingFunc is the default pricing policy that will be used to price retrieval deals. +var DefaultPricingFunc = func(VerifiedDealsFreeTransfer bool) func(ctx context.Context, pricingInput retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { + return func(ctx context.Context, pricingInput retrievalmarket.PricingInput) (retrievalmarket.Ask, error) { + ask := pricingInput.CurrentAsk + + // don't charge for Unsealing if we have an Unsealed copy. + if pricingInput.Unsealed { + ask.UnsealPrice = big.Zero() + } + + // don't charge for data transfer for verified deals if it's been configured to do so. + if pricingInput.VerifiedDeal && VerifiedDealsFreeTransfer { + ask.PricePerByte = big.Zero() + } + + return ask, nil + } +} From 7d33a6e5f7939afaf84d43f6f0d4c08a14b07e02 Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Sat, 22 May 2021 10:21:13 +0530 Subject: [PATCH 11/14] fix compilation --- .../impl/testnodes/test_retrieval_provider_node.go | 6 +++--- retrievalmarket/types_test.go | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go b/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go index 50e51704..2e343dff 100644 --- a/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go +++ b/retrievalmarket/impl/testnodes/test_retrieval_provider_node.go @@ -61,8 +61,8 @@ type TestRetrievalProviderNode struct { expectedPricingPieceCID cid.Cid receivedPricingPieceCID cid.Cid - unsealed map[sectorKey]struct{} - isVerified bool + unsealed map[sectorKey]struct{} + isVerified bool receivedVouchers []abi.TokenAmount unsealPaused chan struct{} } @@ -76,7 +76,7 @@ func NewTestRetrievalProviderNode() *TestRetrievalProviderNode { expectations: make(map[sectorKey]struct{}), received: make(map[sectorKey]struct{}), expectedVouchers: make(map[expectedVoucherKey]voucherResult), - unsealed: make(map[sectorKey]struct{}), + unsealed: make(map[sectorKey]struct{}), } } diff --git a/retrievalmarket/types_test.go b/retrievalmarket/types_test.go index a45b920a..1af72815 100644 --- a/retrievalmarket/types_test.go +++ b/retrievalmarket/types_test.go @@ -43,7 +43,6 @@ func TestParamsMarshalUnmarshal(t *testing.T) { assert.Equal(t, sel, allSelector) } - func TestPricingInputMarshalUnmarshalJSON(t *testing.T) { pid := test.RandPeerIDFatal(t) @@ -69,9 +68,9 @@ func TestPricingInputMarshalUnmarshalJSON(t *testing.T) { require.NoError(t, json.Unmarshal(bz, &resp2)) require.Equal(t, in, resp2) +} - - func TestParamsIntervalBounds(t *testing.T) { +func TestParamsIntervalBounds(t *testing.T) { testCases := []struct { name string currentInterval uint64 From 6f4ff9385eca14e51c89df56462f3ef0348faf8b Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Sat, 22 May 2021 20:20:27 +0530 Subject: [PATCH 12/14] test the default pricing function --- retrievalmarket/impl/provider_test.go | 100 +++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/retrievalmarket/impl/provider_test.go b/retrievalmarket/impl/provider_test.go index bcae550c..94065ce0 100644 --- a/retrievalmarket/impl/provider_test.go +++ b/retrievalmarket/impl/provider_test.go @@ -298,6 +298,104 @@ func TestDynamicPricing(t *testing.T) { expectedSize: piece2Size, }, + "pieceCid no-op: quote correct price for unsealed, verified, peer1 using default pricing policy if data transfer fee set to zero for verified deals": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece2.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.MarkVerified() + n.ExpectPricingParams(expectedPieceCID2, []abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + + providerFnc: func(provider retrievalmarket.RetrievalProvider) { + ask := provider.GetAsk() + ask.PaymentInterval = expectedpiPeer1 + ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease + provider.SetAsk(ask) + }, + + pricingFnc: retrievalimpl.DefaultPricingFunc(true), + + expectedPricePerByte: big.Zero(), + expectedUnsealPrice: big.Zero(), + expectedPaymentInterval: expectedpiPeer1, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece2Size, + }, + + "pieceCid no-op: quote correct price for unsealed, verified, peer1 using default pricing policy if data transfer fee not set to zero for verified deals": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + p := piece2.Deals[0] + n.MarkUnsealed(context.TODO(), p.SectorID, p.Offset.Unpadded(), p.Length.Unpadded()) + n.MarkVerified() + n.ExpectPricingParams(expectedPieceCID2, []abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + + providerFnc: func(provider retrievalmarket.RetrievalProvider) { + ask := provider.GetAsk() + ask.PricePerByte = expectedppbVerified + ask.PaymentInterval = expectedpiPeer1 + ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease + provider.SetAsk(ask) + }, + + pricingFnc: retrievalimpl.DefaultPricingFunc(false), + + expectedPricePerByte: expectedppbVerified, + expectedUnsealPrice: big.Zero(), + expectedPaymentInterval: expectedpiPeer1, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece2Size, + }, + + "pieceCid no-op: quote correct price for sealed, verified, peer1 using default pricing policy": { + query: retrievalmarket.Query{PayloadCID: payloadCID}, + peerIdFnc: func(qs *tut.TestRetrievalQueryStream) { + qs.SetRemotePeer(peer1) + }, + nodeFunc: func(n *testnodes.TestRetrievalProviderNode) { + n.MarkVerified() + n.ExpectPricingParams(expectedPieceCID1, []abi.DealID{1, 11, 2, 22, 222}) + }, + expFunc: func(t *testing.T, pieceStore *tut.TestPieceStore) { + pieceStore.ExpectCID(payloadCID, expectedCIDInfo) + pieceStore.ExpectPiece(expectedPieceCID1, piece1) + pieceStore.ExpectPiece(expectedPieceCID2, piece2) + }, + providerFnc: func(provider retrievalmarket.RetrievalProvider) { + ask := provider.GetAsk() + ask.PricePerByte = expectedppbVerified + ask.PaymentInterval = expectedpiPeer1 + ask.PaymentIntervalIncrease = expectedPaymentIntervalIncrease + ask.UnsealPrice = expectedUnsealPrice + provider.SetAsk(ask) + }, + pricingFnc: retrievalimpl.DefaultPricingFunc(false), + + expectedPricePerByte: expectedppbVerified, + expectedPaymentInterval: expectedpiPeer1, + expectedUnsealPrice: expectedUnsealPrice, + expectedPaymentIntervalIncrease: expectedPaymentIntervalIncrease, + expectedSize: piece1Size, + }, + // Retrieval requests for a payloadCid inside a specific piece Cid "specific sealed piece Cid, first piece Cid matches: quote correct price for sealed, unverified, peer1": { query: retrievalmarket.Query{ @@ -498,9 +596,9 @@ func TestDynamicPricing(t *testing.T) { require.Equal(t, expectedAddress, actualResp.PaymentAddress) require.Equal(t, tc.expectedPricePerByte, actualResp.MinPricePerByte) + require.Equal(t, tc.expectedUnsealPrice, actualResp.UnsealPrice) require.Equal(t, tc.expectedPaymentInterval, actualResp.MaxPaymentInterval) require.Equal(t, tc.expectedPaymentIntervalIncrease, actualResp.MaxPaymentIntervalIncrease) - require.Equal(t, tc.expectedUnsealPrice, actualResp.UnsealPrice) require.Equal(t, tc.expectedSize, actualResp.Size) }) } From 6c1159720a9b7bcedb0ee07b437adde022c607d6 Mon Sep 17 00:00:00 2001 From: aarshkshah1992 Date: Sun, 23 May 2021 10:49:04 +0530 Subject: [PATCH 13/14] fix bug in quoting price --- retrievalmarket/impl/integration_test.go | 3 +++ retrievalmarket/impl/provider.go | 2 +- retrievalmarket/impl/provider_test.go | 28 ++++++++++++++---------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/retrievalmarket/impl/integration_test.go b/retrievalmarket/impl/integration_test.go index 9027e0a0..cfc69074 100644 --- a/retrievalmarket/impl/integration_test.go +++ b/retrievalmarket/impl/integration_test.go @@ -176,6 +176,9 @@ func requireSetupTestClientAndProvider(ctx context.Context, t *testing.T, payChA ID: testData.Host2.ID(), } rcNode1.ExpectKnownAddresses(retrievalPeer, nil) + + expectedQR.Size = uint64(abi.PaddedPieceSize(expectedQR.Size).Unpadded()) + return client, expectedCIDs, missingCID, expectedQR, retrievalPeer, provider } diff --git a/retrievalmarket/impl/provider.go b/retrievalmarket/impl/provider.go index 32be6a99..263b1930 100644 --- a/retrievalmarket/impl/provider.go +++ b/retrievalmarket/impl/provider.go @@ -345,7 +345,7 @@ func (p *Provider) HandleQueryStream(stream rmnet.RetrievalQueryStream) { } answer.Status = retrievalmarket.QueryResponseAvailable - answer.Size = uint64(pieceInfo.Deals[0].Length) // TODO: verify on intermediate + answer.Size = uint64(pieceInfo.Deals[0].Length.Unpadded()) // TODO: verify on intermediate answer.PieceCIDFound = retrievalmarket.QueryItemAvailable storageDeals, err := storageDealsForPiece(query.PieceCID != nil, query.PayloadCID, pieceInfo, p.pieceStore) diff --git a/retrievalmarket/impl/provider_test.go b/retrievalmarket/impl/provider_test.go index 94065ce0..91883ca7 100644 --- a/retrievalmarket/impl/provider_test.go +++ b/retrievalmarket/impl/provider_test.go @@ -64,8 +64,11 @@ func TestDynamicPricing(t *testing.T) { expectedPieceCID2 := tut.GenerateCids(1)[0] // sizes - piece1Size := uint64(1234) - piece2Size := uint64(2234) + piece1SizePadded := uint64(1234) + piece1Size := uint64(abi.PaddedPieceSize(piece1SizePadded).Unpadded()) + + piece2SizePadded := uint64(2234) + piece2Size := uint64(abi.PaddedPieceSize(piece2SizePadded).Unpadded()) expectedCIDInfo := piecestore.CIDInfo{ PieceBlockLocations: []piecestore.PieceBlockLocation{ @@ -83,11 +86,11 @@ func TestDynamicPricing(t *testing.T) { Deals: []piecestore.DealInfo{ { DealID: abi.DealID(1), - Length: abi.PaddedPieceSize(piece1Size), + Length: abi.PaddedPieceSize(piece1SizePadded), }, { DealID: abi.DealID(11), - Length: abi.PaddedPieceSize(piece1Size), + Length: abi.PaddedPieceSize(piece1SizePadded), }, }, } @@ -97,15 +100,15 @@ func TestDynamicPricing(t *testing.T) { Deals: []piecestore.DealInfo{ { DealID: abi.DealID(2), - Length: abi.PaddedPieceSize(piece2Size), + Length: abi.PaddedPieceSize(piece2SizePadded), }, { DealID: abi.DealID(22), - Length: abi.PaddedPieceSize(piece2Size), + Length: abi.PaddedPieceSize(piece2SizePadded), }, { DealID: abi.DealID(222), - Length: abi.PaddedPieceSize(piece2Size), + Length: abi.PaddedPieceSize(piece2SizePadded), }, }, } @@ -609,8 +612,11 @@ func TestHandleQueryStream(t *testing.T) { payloadCID := tut.GenerateCids(1)[0] expectedPeer := peer.ID("somepeer") - expectedSize := uint64(1234) - expectedSize2 := uint64(2234) + paddedSize := uint64(1234) + expectedSize := uint64(abi.PaddedPieceSize(paddedSize).Unpadded()) + + paddedSize2 := uint64(2234) + expectedSize2 := uint64(abi.PaddedPieceSize(paddedSize2).Unpadded()) expectedPieceCID := tut.GenerateCids(1)[0] expectedPieceCID2 := tut.GenerateCids(1)[0] @@ -629,7 +635,7 @@ func TestHandleQueryStream(t *testing.T) { PieceCID: expectedPieceCID, Deals: []piecestore.DealInfo{ { - Length: abi.PaddedPieceSize(expectedSize), + Length: abi.PaddedPieceSize(paddedSize), }, }, } @@ -638,7 +644,7 @@ func TestHandleQueryStream(t *testing.T) { PieceCID: expectedPieceCID2, Deals: []piecestore.DealInfo{ { - Length: abi.PaddedPieceSize(expectedSize2), + Length: abi.PaddedPieceSize(paddedSize2), }, }, } From bae766b44c37cadf8caf2b72254ce0d37fa6e86e Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Fri, 4 Jun 2021 07:19:34 -0600 Subject: [PATCH 14/14] fix: go mod tidy --- go.sum | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/go.sum b/go.sum index 993f9b4b..8d589fcb 100644 --- a/go.sum +++ b/go.sum @@ -853,15 +853,14 @@ github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -github.com/xlab/c-for-go v0.0.0-20201112171043-ea6dce5809cb h1:/7/dQyiKnxAOj9L69FhST7uMe17U015XPzX7cy+5ykM= -github.com/xlab/c-for-go v0.0.0-20201112171043-ea6dce5809cb/go.mod h1:pbNsDSxn1ICiNn9Ct4ZGNrwzfkkwYbx/lw8VuyutFIg= +github.com/xlab/c-for-go v0.0.0-20200718154222-87b0065af829 h1:wb7xrDzfkLgPHsSEBm+VSx6aDdi64VtV0xvP0E6j8bk= +github.com/xlab/c-for-go v0.0.0-20200718154222-87b0065af829/go.mod h1:h/1PEBwj7Ym/8kOuMWvO2ujZ6Lt+TMbySEXNhjjR87I= github.com/xlab/pkgconfig v0.0.0-20170226114623-cea12a0fd245 h1:Sw125DKxZhPUI4JLlWugkzsrlB50jR9v2khiD9FxuSo= github.com/xlab/pkgconfig v0.0.0-20170226114623-cea12a0fd245/go.mod h1:C+diUUz7pxhNY6KAoLgrTYARGWnt82zWTylZlxT92vk= github.com/xorcare/golden v0.6.0 h1:E8emU8bhyMIEpYmgekkTUaw4vtcrRE+Wa0c5wYIcgXc= github.com/xorcare/golden v0.6.0/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -911,7 +910,6 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -984,9 +982,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1067,9 +1064,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3 h1:r3P/5xOq/dK1991B65Oy6E1fRF/2d/fSYZJ/fXGVfJc= golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696 h1:Bfazo+enXJET5SbHeh95NtxabJF6fJ9r/jpfRJgd3j4= -golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1166,12 +1162,8 @@ honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXe honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= modernc.org/cc v1.0.0 h1:nPibNuDEx6tvYrUAtvDTTw98rx5juGsa5zuDnKwEEQQ= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= +modernc.org/golex v1.0.0 h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/golex v1.0.1 h1:EYKY1a3wStt0RzHaH8mdSRNg78Ub0OHxYfCRWw35YtM= -modernc.org/golex v1.0.1/go.mod h1:QCA53QtsT1NdGkaZZkF5ezFwk4IXh4BGNafAARTC254= -modernc.org/lex v1.0.0/go.mod h1:G6rxMTy3cH2iA0iXL/HRRv4Znu8MK4higxph/lE7ypk= -modernc.org/lexer v1.0.0/go.mod h1:F/Dld0YKYdZCLQ7bD0USbWL4YKCyTDRDHiDTOs0q0vk= modernc.org/mathutil v1.1.1 h1:FeylZSVX8S+58VsyJlkEj2bcpdytmp9MmDKZkKx8OIE= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc=