diff --git a/routing/http/client/client.go b/routing/http/client/client.go index c504a03150..d95ccc1b0b 100644 --- a/routing/http/client/client.go +++ b/routing/http/client/client.go @@ -10,13 +10,10 @@ import ( "mime" "net/http" "strings" - "time" "github.com/benbjohnson/clock" ipns "github.com/ipfs/boxo/ipns" "github.com/ipfs/boxo/routing/http/contentrouter" - "github.com/ipfs/boxo/routing/http/internal/drjson" - "github.com/ipfs/boxo/routing/http/server" "github.com/ipfs/boxo/routing/http/types" "github.com/ipfs/boxo/routing/http/types/iter" jsontypes "github.com/ipfs/boxo/routing/http/types/json" @@ -58,10 +55,6 @@ type client struct { peerID peer.ID addrs []types.Multiaddr identity crypto.PrivKey - - // called immeidately after signing a provide req - // used for testing, e.g. testing the server with a mangled signature - afterSignCallback func(req *types.WriteBitswapProviderRecord) } // defaultUserAgent is used as a fallback to inform HTTP server which library @@ -168,7 +161,7 @@ func (c *client) FindProviders(ctx context.Context, key cid.Cid) (provs iter.Res // TODO test measurements m := newMeasurement("FindProviders") - url := c.baseURL + server.ProvidePath + key.String() + url := c.baseURL + "/routing/v1/providers/" + key.String() req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err @@ -238,95 +231,6 @@ func (c *client) FindProviders(ctx context.Context, key cid.Cid) (provs iter.Res return &measuringIter[iter.Result[types.ProviderResponse]]{Iter: it, ctx: ctx, m: m}, nil } -func (c *client) ProvideBitswap(ctx context.Context, keys []cid.Cid, ttl time.Duration) (time.Duration, error) { - if c.identity == nil { - return 0, errors.New("cannot provide Bitswap records without an identity") - } - if c.peerID.Size() == 0 { - return 0, errors.New("cannot provide Bitswap records without a peer ID") - } - - ks := make([]types.CID, len(keys)) - for i, c := range keys { - ks[i] = types.CID{Cid: c} - } - - now := c.clock.Now() - - req := types.WriteBitswapProviderRecord{ - Protocol: "transport-bitswap", - Schema: types.SchemaBitswap, - Payload: types.BitswapPayload{ - Keys: ks, - AdvisoryTTL: &types.Duration{Duration: ttl}, - Timestamp: &types.Time{Time: now}, - ID: &c.peerID, - Addrs: c.addrs, - }, - } - err := req.Sign(c.peerID, c.identity) - if err != nil { - return 0, err - } - - if c.afterSignCallback != nil { - c.afterSignCallback(&req) - } - - advisoryTTL, err := c.provideSignedBitswapRecord(ctx, &req) - if err != nil { - return 0, err - } - - return advisoryTTL, err -} - -// ProvideAsync makes a provide request to a delegated router -func (c *client) provideSignedBitswapRecord(ctx context.Context, bswp *types.WriteBitswapProviderRecord) (time.Duration, error) { - req := jsontypes.WriteProvidersRequest{Providers: []types.WriteProviderRecord{bswp}} - - url := c.baseURL + server.ProvidePath - - b, err := drjson.MarshalJSONBytes(req) - if err != nil { - return 0, err - } - - httpReq, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewBuffer(b)) - if err != nil { - return 0, err - } - - resp, err := c.httpClient.Do(httpReq) - if err != nil { - return 0, fmt.Errorf("making HTTP req to provide a signed record: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return 0, httpError(resp.StatusCode, resp.Body) - } - var provideResult jsontypes.WriteProvidersResponse - err = json.NewDecoder(resp.Body).Decode(&provideResult) - if err != nil { - return 0, err - } - if len(provideResult.ProvideResults) != 1 { - return 0, fmt.Errorf("expected 1 result but got %d", len(provideResult.ProvideResults)) - } - - v, ok := provideResult.ProvideResults[0].(*types.WriteBitswapProviderRecordResponse) - if !ok { - return 0, fmt.Errorf("expected AdvisoryTTL field") - } - - if v.AdvisoryTTL != nil { - return v.AdvisoryTTL.Duration, nil - } - - return 0, nil -} - func (c *client) FindIPNSRecord(ctx context.Context, name ipns.Name) (*ipns.Record, error) { url := c.baseURL + "/routing/v1/ipns/" + name.String() diff --git a/routing/http/client/client_test.go b/routing/http/client/client_test.go index c1690b3f23..b386ce13d3 100644 --- a/routing/http/client/client_test.go +++ b/routing/http/client/client_test.go @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/benbjohnson/clock" "github.com/ipfs/boxo/coreiface/path" ipns "github.com/ipfs/boxo/ipns" ipfspath "github.com/ipfs/boxo/path" @@ -22,7 +21,6 @@ import ( "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" - "github.com/multiformats/go-multibase" "github.com/multiformats/go-multihash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -36,16 +34,6 @@ func (m *mockContentRouter) FindProviders(ctx context.Context, key cid.Cid, limi return args.Get(0).(iter.ResultIter[types.ProviderResponse]), args.Error(1) } -func (m *mockContentRouter) ProvideBitswap(ctx context.Context, req *server.BitswapWriteProvideRequest) (time.Duration, error) { - args := m.Called(ctx, req) - return args.Get(0).(time.Duration), args.Error(1) -} - -func (m *mockContentRouter) Provide(ctx context.Context, req *server.WriteProvideRequest) (types.ProviderResponse, error) { - args := m.Called(ctx, req) - return args.Get(0).(types.ProviderResponse), args.Error(1) -} - func (m *mockContentRouter) FindIPNSRecord(ctx context.Context, name ipns.Name) (*ipns.Record, error) { args := m.Called(ctx, name) return args.Get(0).(*ipns.Record), args.Error(1) @@ -151,13 +139,6 @@ func addrsToDRAddrs(addrs []multiaddr.Multiaddr) (drmas []types.Multiaddr) { return } -func drAddrsToAddrs(drmas []types.Multiaddr) (addrs []multiaddr.Multiaddr) { - for _, a := range drmas { - addrs = append(addrs, a.Multiaddr) - } - return -} - func makeBSReadProviderResp() types.ReadBitswapProviderRecord { peerID, addrs, _ := makeProviderAndIdentity() return types.ReadBitswapProviderRecord{ @@ -333,130 +314,6 @@ func TestClient_FindProviders(t *testing.T) { } } -func TestClient_Provide(t *testing.T) { - cases := []struct { - name string - manglePath bool - mangleSignature bool - stopServer bool - noProviderInfo bool - noIdentity bool - - cids []cid.Cid - ttl time.Duration - - routerAdvisoryTTL time.Duration - routerErr error - - expErrContains string - expWinErrContains string - - expAdvisoryTTL time.Duration - }{ - { - name: "happy case", - cids: []cid.Cid{makeCID()}, - ttl: 1 * time.Hour, - routerAdvisoryTTL: 1 * time.Minute, - - expAdvisoryTTL: 1 * time.Minute, - }, - { - name: "should return a 403 if the payload signature verification fails", - cids: []cid.Cid{}, - mangleSignature: true, - expErrContains: "HTTP error with StatusCode=403", - }, - { - name: "should return error if identity is not provided", - noIdentity: true, - expErrContains: "cannot provide Bitswap records without an identity", - }, - { - name: "should return error if provider is not provided", - noProviderInfo: true, - expErrContains: "cannot provide Bitswap records without a peer ID", - }, - { - name: "returns an error if there's a non-200 response", - manglePath: true, - expErrContains: "HTTP error with StatusCode=404: 404 page not found", - }, - { - name: "returns an error if the HTTP client returns a non-HTTP error", - stopServer: true, - expErrContains: "connect: connection refused", - expWinErrContains: "connectex: No connection could be made because the target machine actively refused it.", - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - deps := makeTestDeps(t, nil, nil) - client := deps.client - router := deps.router - - if c.noIdentity { - client.identity = nil - } - if c.noProviderInfo { - client.peerID = "" - client.addrs = nil - } - - clock := clock.NewMock() - clock.Set(time.Now()) - client.clock = clock - - ctx := context.Background() - - if c.manglePath { - client.baseURL += "/foo" - } - if c.stopServer { - deps.server.Close() - } - if c.mangleSignature { - client.afterSignCallback = func(req *types.WriteBitswapProviderRecord) { - mh, err := multihash.Encode([]byte("boom"), multihash.SHA2_256) - require.NoError(t, err) - mb, err := multibase.Encode(multibase.Base64, mh) - require.NoError(t, err) - - req.Signature = mb - } - } - - expectedProvReq := &server.BitswapWriteProvideRequest{ - Keys: c.cids, - Timestamp: clock.Now().Truncate(time.Millisecond), - AdvisoryTTL: c.ttl, - Addrs: drAddrsToAddrs(client.addrs), - ID: client.peerID, - } - - router.On("ProvideBitswap", mock.Anything, expectedProvReq). - Return(c.routerAdvisoryTTL, c.routerErr) - - advisoryTTL, err := client.ProvideBitswap(ctx, c.cids, c.ttl) - - var errorString string - if runtime.GOOS == "windows" && c.expWinErrContains != "" { - errorString = c.expWinErrContains - } else { - errorString = c.expErrContains - } - - if errorString != "" { - require.ErrorContains(t, err, errorString) - } else { - require.NoError(t, err) - } - - assert.Equal(t, c.expAdvisoryTTL, advisoryTTL) - }) - } -} - func makeName(t *testing.T) (crypto.PrivKey, ipns.Name) { sk, _, err := crypto.GenerateEd25519Key(rand.Reader) require.NoError(t, err) diff --git a/routing/http/contentrouter/contentrouter.go b/routing/http/contentrouter/contentrouter.go index 8318a31632..7382f5a7a9 100644 --- a/routing/http/contentrouter/contentrouter.go +++ b/routing/http/contentrouter/contentrouter.go @@ -5,7 +5,6 @@ import ( "reflect" "time" - "github.com/ipfs/boxo/routing/http/internal" "github.com/ipfs/boxo/routing/http/types" "github.com/ipfs/boxo/routing/http/types/iter" "github.com/ipfs/go-cid" @@ -13,7 +12,6 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" "github.com/multiformats/go-multiaddr" - "github.com/multiformats/go-multihash" ) var logger = logging.Logger("service/contentrouting") @@ -21,7 +19,6 @@ var logger = logging.Logger("service/contentrouting") const ttl = 24 * time.Hour type Client interface { - ProvideBitswap(ctx context.Context, keys []cid.Cid, ttl time.Duration) (time.Duration, error) FindProviders(ctx context.Context, key cid.Cid) (iter.ResultIter[types.ProviderResponse], error) } @@ -60,41 +57,7 @@ func NewContentRoutingClient(c Client, opts ...option) *contentRouter { } func (c *contentRouter) Provide(ctx context.Context, key cid.Cid, announce bool) error { - // If 'true' is - // passed, it also announces it, otherwise it is just kept in the local - // accounting of which objects are being provided. - if !announce { - return nil - } - - _, err := c.client.ProvideBitswap(ctx, []cid.Cid{key}, ttl) - return err -} - -// ProvideMany provides a set of keys to the remote delegate. -// Large sets of keys are chunked into multiple requests and sent concurrently, according to the concurrency configuration. -// TODO: implement retries through transient errors -func (c *contentRouter) ProvideMany(ctx context.Context, mhKeys []multihash.Multihash) error { - keys := make([]cid.Cid, 0, len(mhKeys)) - for _, m := range mhKeys { - keys = append(keys, cid.NewCidV1(cid.Raw, m)) - } - - if len(keys) <= c.maxProvideBatchSize { - _, err := c.client.ProvideBitswap(ctx, keys, ttl) - return err - } - - return internal.DoBatch( - ctx, - c.maxProvideBatchSize, - c.maxProvideConcurrency, - keys, - func(ctx context.Context, batch []cid.Cid) error { - _, err := c.client.ProvideBitswap(ctx, batch, ttl) - return err - }, - ) + return routing.ErrNotSupported } // Ready is part of the existing `ProvideMany` interface. diff --git a/routing/http/contentrouter/contentrouter_test.go b/routing/http/contentrouter/contentrouter_test.go index 4ca620c5dc..00ccc4d54f 100644 --- a/routing/http/contentrouter/contentrouter_test.go +++ b/routing/http/contentrouter/contentrouter_test.go @@ -4,25 +4,18 @@ import ( "context" "crypto/rand" "testing" - "time" "github.com/ipfs/boxo/routing/http/types" "github.com/ipfs/boxo/routing/http/types/iter" "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multihash" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) type mockClient struct{ mock.Mock } -func (m *mockClient) ProvideBitswap(ctx context.Context, keys []cid.Cid, ttl time.Duration) (time.Duration, error) { - args := m.Called(ctx, keys, ttl) - return args.Get(0).(time.Duration), args.Error(1) -} - func (m *mockClient) FindProviders(ctx context.Context, key cid.Cid) (iter.ResultIter[types.ProviderResponse], error) { args := m.Called(ctx, key) return args.Get(0).(iter.ResultIter[types.ProviderResponse]), args.Error(1) @@ -45,60 +38,6 @@ func makeCID() cid.Cid { return c } -func TestProvide(t *testing.T) { - for _, c := range []struct { - name string - announce bool - - expNotProvided bool - }{ - { - name: "announce=false results in no client request", - announce: false, - expNotProvided: true, - }, - { - name: "announce=true results in a client req", - announce: true, - }, - } { - t.Run(c.name, func(t *testing.T) { - ctx := context.Background() - key := makeCID() - client := &mockClient{} - crc := NewContentRoutingClient(client) - - if !c.expNotProvided { - client.On("ProvideBitswap", ctx, []cid.Cid{key}, ttl).Return(time.Minute, nil) - } - - err := crc.Provide(ctx, key, c.announce) - assert.NoError(t, err) - - if c.expNotProvided { - client.AssertNumberOfCalls(t, "ProvideBitswap", 0) - } - - }) - } -} - -func TestProvideMany(t *testing.T) { - cids := []cid.Cid{makeCID(), makeCID()} - var mhs []multihash.Multihash - for _, c := range cids { - mhs = append(mhs, c.Hash()) - } - ctx := context.Background() - client := &mockClient{} - crc := NewContentRoutingClient(client) - - client.On("ProvideBitswap", ctx, cids, ttl).Return(time.Minute, nil) - - err := crc.ProvideMany(ctx, mhs) - require.NoError(t, err) -} - func TestFindProvidersAsync(t *testing.T) { key := makeCID() ctx := context.Background() diff --git a/routing/http/server/server.go b/routing/http/server/server.go index 8526fed844..1d5fbb9072 100644 --- a/routing/http/server/server.go +++ b/routing/http/server/server.go @@ -3,7 +3,6 @@ package server import ( "bytes" "context" - "encoding/json" "errors" "fmt" "io" @@ -11,7 +10,6 @@ import ( "net/http" "strconv" "strings" - "time" "github.com/cespare/xxhash/v2" "github.com/gorilla/mux" @@ -21,8 +19,6 @@ import ( "github.com/ipfs/boxo/routing/http/types/iter" jsontypes "github.com/ipfs/boxo/routing/http/types/json" "github.com/ipfs/go-cid" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multiaddr" logging "github.com/ipfs/go-log/v2" ) @@ -40,7 +36,6 @@ const ( var logger = logging.Logger("service/server/delegatedrouting") const ( - ProvidePath = "/routing/v1/providers/" FindProvidersPath = "/routing/v1/providers/{cid}" IPNSPath = "/routing/v1/ipns/{cid}" ) @@ -54,8 +49,6 @@ type ContentRouter interface { // FindProviders searches for peers who are able to provide a given key. Limit // indicates the maximum amount of results to return. 0 means unbounded. FindProviders(ctx context.Context, key cid.Cid, limit int) (iter.ResultIter[types.ProviderResponse], error) - ProvideBitswap(ctx context.Context, req *BitswapWriteProvideRequest) (time.Duration, error) - Provide(ctx context.Context, req *WriteProvideRequest) (types.ProviderResponse, error) // FindIPNSRecord searches for an [ipns.Record] for the given [ipns.Name]. FindIPNSRecord(ctx context.Context, name ipns.Name) (*ipns.Record, error) @@ -65,20 +58,6 @@ type ContentRouter interface { ProvideIPNSRecord(ctx context.Context, name ipns.Name, record *ipns.Record) error } -type BitswapWriteProvideRequest struct { - Keys []cid.Cid - Timestamp time.Time - AdvisoryTTL time.Duration - ID peer.ID - Addrs []multiaddr.Multiaddr -} - -type WriteProvideRequest struct { - Protocol string - Schema string - Bytes []byte -} - type Option func(s *server) // WithStreamingResultsDisabled disables ndjson responses, so that the server only supports JSON responses. @@ -116,7 +95,6 @@ func Handler(svc ContentRouter, opts ...Option) http.Handler { } r := mux.NewRouter() - r.HandleFunc(ProvidePath, server.provide).Methods(http.MethodPut) r.HandleFunc(FindProvidersPath, server.findProviders).Methods(http.MethodGet) r.HandleFunc(IPNSPath, server.getIPNSRecord).Methods(http.MethodGet) @@ -132,73 +110,6 @@ type server struct { streamingRecordsLimit int } -func (s *server) provide(w http.ResponseWriter, httpReq *http.Request) { - req := jsontypes.WriteProvidersRequest{} - err := json.NewDecoder(httpReq.Body).Decode(&req) - _ = httpReq.Body.Close() - if err != nil { - writeErr(w, "Provide", http.StatusBadRequest, fmt.Errorf("invalid request: %w", err)) - return - } - - resp := jsontypes.WriteProvidersResponse{} - - for i, prov := range req.Providers { - switch v := prov.(type) { - case *types.WriteBitswapProviderRecord: - err := v.Verify() - if err != nil { - logErr("Provide", "signature verification failed", err) - writeErr(w, "Provide", http.StatusForbidden, errors.New("signature verification failed")) - return - } - - keys := make([]cid.Cid, len(v.Payload.Keys)) - for i, k := range v.Payload.Keys { - keys[i] = k.Cid - - } - addrs := make([]multiaddr.Multiaddr, len(v.Payload.Addrs)) - for i, a := range v.Payload.Addrs { - addrs[i] = a.Multiaddr - } - advisoryTTL, err := s.svc.ProvideBitswap(httpReq.Context(), &BitswapWriteProvideRequest{ - Keys: keys, - Timestamp: v.Payload.Timestamp.Time, - AdvisoryTTL: v.Payload.AdvisoryTTL.Duration, - ID: *v.Payload.ID, - Addrs: addrs, - }) - if err != nil { - writeErr(w, "Provide", http.StatusInternalServerError, fmt.Errorf("delegate error: %w", err)) - return - } - resp.ProvideResults = append(resp.ProvideResults, - &types.WriteBitswapProviderRecordResponse{ - Protocol: v.Protocol, - Schema: v.Schema, - AdvisoryTTL: &types.Duration{Duration: advisoryTTL}, - }, - ) - case *types.UnknownProviderRecord: - provResp, err := s.svc.Provide(httpReq.Context(), &WriteProvideRequest{ - Protocol: v.Protocol, - Schema: v.Schema, - Bytes: v.Bytes, - }) - if err != nil { - writeErr(w, "Provide", http.StatusInternalServerError, fmt.Errorf("delegate error: %w", err)) - return - } - resp.ProvideResults = append(resp.ProvideResults, provResp) - default: - writeErr(w, "Provide", http.StatusBadRequest, fmt.Errorf("provider record %d does not contain a protocol", i)) - return - } - } - writeJSONResult(w, "Provide", resp) -} - func (s *server) findProviders(w http.ResponseWriter, httpReq *http.Request) { vars := mux.Vars(httpReq) cidStr := vars["cid"] diff --git a/routing/http/server/server_test.go b/routing/http/server/server_test.go index bec959cf8d..bb40491982 100644 --- a/routing/http/server/server_test.go +++ b/routing/http/server/server_test.go @@ -42,13 +42,13 @@ func TestHeaders(t *testing.T) { router.On("FindProviders", mock.Anything, cb, DefaultRecordsLimit). Return(results, nil) - resp, err := http.Get(serverAddr + ProvidePath + c) + resp, err := http.Get(serverAddr + "/routing/v1/providers/" + c) require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) header := resp.Header.Get("Content-Type") require.Equal(t, mediaTypeJSON, header) - resp, err = http.Get(serverAddr + ProvidePath + "BAD_CID") + resp, err = http.Get(serverAddr + "/routing/v1/providers/" + "BAD_CID") require.NoError(t, err) defer resp.Body.Close() require.Equal(t, 400, resp.StatusCode) @@ -96,7 +96,7 @@ func TestResponse(t *testing.T) { limit = DefaultStreamingRecordsLimit } router.On("FindProviders", mock.Anything, cid, limit).Return(results, nil) - urlStr := serverAddr + ProvidePath + cidStr + urlStr := serverAddr + "/routing/v1/providers/" + cidStr req, err := http.NewRequest(http.MethodGet, urlStr, nil) require.NoError(t, err) @@ -261,15 +261,6 @@ func (m *mockContentRouter) FindProviders(ctx context.Context, key cid.Cid, limi args := m.Called(ctx, key, limit) return args.Get(0).(iter.ResultIter[types.ProviderResponse]), args.Error(1) } -func (m *mockContentRouter) ProvideBitswap(ctx context.Context, req *BitswapWriteProvideRequest) (time.Duration, error) { - args := m.Called(ctx, req) - return args.Get(0).(time.Duration), args.Error(1) -} - -func (m *mockContentRouter) Provide(ctx context.Context, req *WriteProvideRequest) (types.ProviderResponse, error) { - args := m.Called(ctx, req) - return args.Get(0).(types.ProviderResponse), args.Error(1) -} func (m *mockContentRouter) FindIPNSRecord(ctx context.Context, name ipns.Name) (*ipns.Record, error) { args := m.Called(ctx, name) diff --git a/routing/http/types/json/provider.go b/routing/http/types/json/provider.go index 351197338b..6922da296c 100644 --- a/routing/http/types/json/provider.go +++ b/routing/http/types/json/provider.go @@ -40,77 +40,3 @@ func (r *ReadProvidersResponse) UnmarshalJSON(b []byte) error { } return nil } - -type WriteProvidersRequest struct { - Providers []types.WriteProviderRecord -} - -func (r *WriteProvidersRequest) UnmarshalJSON(b []byte) error { - type wpr struct{ Providers []json.RawMessage } - var tempWPR wpr - err := json.Unmarshal(b, &tempWPR) - if err != nil { - return err - } - - for _, provBytes := range tempWPR.Providers { - var rawProv types.UnknownProviderRecord - err := json.Unmarshal(provBytes, &rawProv) - if err != nil { - return err - } - - switch rawProv.Schema { - case types.SchemaBitswap: - var prov types.WriteBitswapProviderRecord - err := json.Unmarshal(rawProv.Bytes, &prov) - if err != nil { - return err - } - r.Providers = append(r.Providers, &prov) - default: - var prov types.UnknownProviderRecord - err := json.Unmarshal(b, &prov) - if err != nil { - return err - } - r.Providers = append(r.Providers, &prov) - } - } - return nil -} - -// WriteProvidersResponse is the result of a Provide operation -type WriteProvidersResponse struct { - ProvideResults []types.ProviderResponse -} - -func (r *WriteProvidersResponse) UnmarshalJSON(b []byte) error { - var tempWPR struct{ ProvideResults []json.RawMessage } - err := json.Unmarshal(b, &tempWPR) - if err != nil { - return err - } - - for _, provBytes := range tempWPR.ProvideResults { - var rawProv types.UnknownProviderRecord - err := json.Unmarshal(provBytes, &rawProv) - if err != nil { - return err - } - - switch rawProv.Schema { - case types.SchemaBitswap: - var prov types.WriteBitswapProviderRecordResponse - err := json.Unmarshal(rawProv.Bytes, &prov) - if err != nil { - return err - } - r.ProvideResults = append(r.ProvideResults, &prov) - default: - r.ProvideResults = append(r.ProvideResults, &rawProv) - } - } - - return nil -} diff --git a/routing/http/types/provider.go b/routing/http/types/provider.go index 6e8e303f7c..1a649748ca 100644 --- a/routing/http/types/provider.go +++ b/routing/http/types/provider.go @@ -1,10 +1,5 @@ package types -// WriteProviderRecord is a type that enforces structs to imlement it to avoid confusion -type WriteProviderRecord interface { - IsWriteProviderRecord() -} - // ReadProviderRecord is a type that enforces structs to imlement it to avoid confusion type ReadProviderRecord interface { IsReadProviderRecord() diff --git a/routing/http/types/provider_bitswap.go b/routing/http/types/provider_bitswap.go index 66243dd5dc..0ca451277d 100644 --- a/routing/http/types/provider_bitswap.go +++ b/routing/http/types/provider_bitswap.go @@ -1,32 +1,11 @@ package types import ( - "crypto/sha256" - "encoding/json" - "errors" - "fmt" - - "github.com/ipfs/boxo/routing/http/internal/drjson" - "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multibase" ) const SchemaBitswap = "bitswap" -var _ WriteProviderRecord = &WriteBitswapProviderRecord{} - -// WriteBitswapProviderRecord is used when we want to add a new provider record that is using bitswap. -type WriteBitswapProviderRecord struct { - Protocol string - Schema string - Signature string - - // this content must be untouched because it is signed and we need to verify it - RawPayload json.RawMessage `json:"Payload"` - Payload BitswapPayload `json:"-"` -} - type BitswapPayload struct { Keys []CID Timestamp *Time @@ -35,133 +14,6 @@ type BitswapPayload struct { Addrs []Multiaddr } -func (*WriteBitswapProviderRecord) IsWriteProviderRecord() {} - -type tmpBWPR WriteBitswapProviderRecord - -func (p *WriteBitswapProviderRecord) UnmarshalJSON(b []byte) error { - var bwp tmpBWPR - err := json.Unmarshal(b, &bwp) - if err != nil { - return err - } - - p.Protocol = bwp.Protocol - p.Schema = bwp.Schema - p.Signature = bwp.Signature - p.RawPayload = bwp.RawPayload - - return json.Unmarshal(bwp.RawPayload, &p.Payload) -} - -func (p *WriteBitswapProviderRecord) IsSigned() bool { - return p.Signature != "" -} - -func (p *WriteBitswapProviderRecord) setRawPayload() error { - payloadBytes, err := drjson.MarshalJSONBytes(p.Payload) - if err != nil { - return fmt.Errorf("marshaling bitswap write provider payload: %w", err) - } - - p.RawPayload = payloadBytes - - return nil -} - -func (p *WriteBitswapProviderRecord) Sign(peerID peer.ID, key crypto.PrivKey) error { - if p.IsSigned() { - return errors.New("already signed") - } - - if key == nil { - return errors.New("no key provided") - } - - sid, err := peer.IDFromPrivateKey(key) - if err != nil { - return err - } - if sid != peerID { - return errors.New("not the correct signing key") - } - - err = p.setRawPayload() - if err != nil { - return err - } - hash := sha256.Sum256([]byte(p.RawPayload)) - sig, err := key.Sign(hash[:]) - if err != nil { - return err - } - - sigStr, err := multibase.Encode(multibase.Base64, sig) - if err != nil { - return fmt.Errorf("multibase-encoding signature: %w", err) - } - - p.Signature = sigStr - return nil -} - -func (p *WriteBitswapProviderRecord) Verify() error { - if !p.IsSigned() { - return errors.New("not signed") - } - - if p.Payload.ID == nil { - return errors.New("peer ID must be specified") - } - - // note that we only generate and set the payload if it hasn't already been set - // to allow for passing through the payload untouched if it is already provided - if p.RawPayload == nil { - err := p.setRawPayload() - if err != nil { - return err - } - } - - pk, err := p.Payload.ID.ExtractPublicKey() - if err != nil { - return fmt.Errorf("extracing public key from peer ID: %w", err) - } - - _, sigBytes, err := multibase.Decode(p.Signature) - if err != nil { - return fmt.Errorf("multibase-decoding signature to verify: %w", err) - } - - hash := sha256.Sum256([]byte(p.RawPayload)) - ok, err := pk.Verify(hash[:], sigBytes) - if err != nil { - return fmt.Errorf("verifying hash with signature: %w", err) - } - if !ok { - return errors.New("signature failed to verify") - } - - return nil -} - -var _ ProviderResponse = &WriteBitswapProviderRecordResponse{} - -// WriteBitswapProviderRecordResponse will be returned as a result of WriteBitswapProviderRecord -type WriteBitswapProviderRecordResponse struct { - Protocol string - Schema string - AdvisoryTTL *Duration -} - -func (wbprr *WriteBitswapProviderRecordResponse) GetProtocol() string { - return wbprr.Protocol -} - -func (wbprr *WriteBitswapProviderRecordResponse) GetSchema() string { - return wbprr.Schema -} - var _ ReadProviderRecord = &ReadBitswapProviderRecord{} var _ ProviderResponse = &ReadBitswapProviderRecord{} diff --git a/routing/http/types/provider_unknown.go b/routing/http/types/provider_unknown.go index 3dadc0e9b9..0dedff445b 100644 --- a/routing/http/types/provider_unknown.go +++ b/routing/http/types/provider_unknown.go @@ -7,7 +7,6 @@ import ( ) var _ ReadProviderRecord = &UnknownProviderRecord{} -var _ WriteProviderRecord = &UnknownProviderRecord{} var _ ProviderResponse = &UnknownProviderRecord{} // UnknownProviderRecord is used when we cannot parse the provider record using `GetProtocol` @@ -26,7 +25,6 @@ func (u *UnknownProviderRecord) GetSchema() string { } func (u *UnknownProviderRecord) IsReadProviderRecord() {} -func (u UnknownProviderRecord) IsWriteProviderRecord() {} func (u *UnknownProviderRecord) UnmarshalJSON(b []byte) error { m := map[string]interface{}{}