Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions service/contentprovider/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/data-preservation-programs/singularity/storagesystem"
"github.com/data-preservation-programs/singularity/store"
"github.com/data-preservation-programs/singularity/util"
commcid "github.com/filecoin-project/go-fil-commcid"
"github.com/fxamacker/cbor/v2"
"github.com/ipfs/boxo/blockservice"
"github.com/ipfs/boxo/exchange/offline"
Expand Down Expand Up @@ -207,9 +208,9 @@ func getPieceMetadata(ctx context.Context, db *gorm.DB, car model.Car) (*PieceMe
// - An error if there was a problem handling the request.
func GetMetadataHandler(c echo.Context, db *gorm.DB) error {
id := c.Param("id")
pieceCid, err := cid.Parse(id)
pieceCid, err := parsePieceCID(id)
if err != nil {
return c.String(http.StatusBadRequest, "failed to parse piece CID: "+err.Error())
return c.String(http.StatusBadRequest, err.Error())
}

// filter to rows with an attachment -- orphaned cars (attachment_id NULL
Expand Down Expand Up @@ -250,6 +251,26 @@ func (s *HTTPServer) getMetadataHandler(c echo.Context) error {
return GetMetadataHandler(c, s.dbNoContext.WithContext(c.Request().Context()))
}

// parsePieceCID accepts either a v1 (FilCommitmentUnsealed multicodec) or
// v2 (raw multicodec with PieceMh hash carrying CommP + payload size) CID
// and returns the v1 form. Curio and other FWSS-mediated callers use v2
// CIDs in their /piece/<cid> URLs; the singularity store still indexes by
// v1, so we normalize on input.
func parsePieceCID(id string) (cid.Cid, error) {
c, err := cid.Parse(id)
if err != nil {
return cid.Undef, fmt.Errorf("failed to parse piece CID: %w", err)
}
if c.Type() == cid.FilCommitmentUnsealed {
return c, nil
}
v1, _, err := commcid.PieceCidV1FromV2(c)
if err != nil {
return cid.Undef, fmt.Errorf("CID %s is neither v1 commp nor a v2 piece CID: %w", id, err)
}
return v1, nil
}

type PieceMetadata struct {
Car model.Car `cbor:"1,keyasint,omitempty" json:"car"`
Storage model.Storage `cbor:"2,keyasint,omitempty" json:"storage"`
Expand Down Expand Up @@ -383,12 +404,9 @@ func SetCommonHeaders(c echo.Context, pieceCid string) {
// - An error if there was a problem handling the request.
func (s *HTTPServer) handleGetPiece(c echo.Context) error {
id := c.Param("id")
pieceCid, err := cid.Parse(id)
pieceCid, err := parsePieceCID(id)
if err != nil {
return c.String(http.StatusBadRequest, "failed to parse piece CID: "+err.Error())
}
if pieceCid.Type() != cid.FilCommitmentUnsealed {
return c.String(http.StatusBadRequest, "CID is not a commp")
return c.String(http.StatusBadRequest, err.Error())
}

reader, lastModified, err := s.findPiece(c.Request().Context(), pieceCid)
Expand Down
36 changes: 36 additions & 0 deletions service/contentprovider/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/data-preservation-programs/singularity/model"
"github.com/data-preservation-programs/singularity/store"
"github.com/data-preservation-programs/singularity/util/testutil"
commcid "github.com/filecoin-project/go-fil-commcid"
"github.com/gotidy/ptr"
"github.com/ipfs/boxo/blockservice"
"github.com/ipfs/boxo/exchange/offline"
Expand Down Expand Up @@ -450,3 +451,38 @@ func TestIPFSGateway_FileChanged(t *testing.T) {
require.Equal(t, http.StatusConflict, rec.Code)
})
}

func TestParsePieceCID(t *testing.T) {
commD := make([]byte, 32)
for i := range commD {
commD[i] = byte(i)
}
v1, err := commcid.DataCommitmentV1ToCID(commD)
require.NoError(t, err)
require.Equal(t, uint64(cid.FilCommitmentUnsealed), v1.Type())

// v2 piece CID carrying the same commitment; Curio/FWSS callers use this form
v2, err := commcid.PieceCidV2FromV1(v1, 1016)
require.NoError(t, err)
require.NotEqual(t, v1, v2)

t.Run("v1 passes through unchanged", func(t *testing.T) {
got, err := parsePieceCID(v1.String())
require.NoError(t, err)
require.Equal(t, v1, got)
})
t.Run("v2 normalizes to v1", func(t *testing.T) {
got, err := parsePieceCID(v2.String())
require.NoError(t, err)
require.Equal(t, v1, got)
})
t.Run("non-piece CID is rejected", func(t *testing.T) {
raw := cid.NewCidV1(cid.Raw, util.Hash([]byte("not a piece")))
_, err := parsePieceCID(raw.String())
require.Error(t, err)
})
t.Run("garbage is rejected", func(t *testing.T) {
_, err := parsePieceCID("not-a-cid")
require.Error(t, err)
})
}
Loading