pdp

package
v1.27.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 17, 2026 License: Apache-2.0, MIT Imports: 49 Imported by: 0

README

PDP Service API Documentation

Base URL

All endpoints are rooted at /pdp.


Endpoints

1. Ping
  • Endpoint: GET /pdp/ping
  • Description: A simple endpoint to verify that the service is reachable and the JWT token is valid.
  • Authentication: Requires a valid JWT token in the Authorization header.
Response
  • Status Code: 200 OK

2. Upload a Piece
2.1. Initiate Upload
  • Endpoint: POST /pdp/piece
  • Description: Initiate the process of uploading a piece. If the piece already exists on the server, the server will respond accordingly.
  • Authentication: Requires a valid JWT token in the Authorization header.
  • Request Body:
{
  "check": {
    "name": "<hash-function-name>",
    "hash": "<hex-encoded-hash>",
    "size": <size-in-bytes>
  },
  "notify": "<optional-notification-URL>"
}
  • Fields:
    • check: An object containing the hash details of the piece.
      • name: The name of the hash function used:
        • "sha2-256" for SHA-256 of the raw piece data.
        • "sha2-256-trunc254-padded" for the CommP (Piece Commitment).
      • hash: The hex-encoded hash value (multihash payload, not the full multihash)
      • size: The size of the piece in bytes (unpadded size).
    • notify: (Optional) A URL to be notified when the piece has been processed successfully.
Responses
  1. Piece Already Exists

    • Status Code: 200 OK

    • Response Body:

      {
        "pieceCID": "<piece-CID>"
      }
      
  2. Piece Does Not Exist (Upload Required)

    • Status Code: 201 Created
    • Headers:
      • Location: The URL where the piece data can be uploaded via PUT.
Errors
  • 400 Bad Request: Invalid request body or piece size exceeds the maximum allowed size.
  • 401 Unauthorized: Missing or invalid JWT token.

2.2. Upload Piece Data
  • Endpoint: PUT /pdp/piece/upload/{uploadUUID}
  • Description: Upload the actual bytes of the piece to the server using the provided uploadUUID.
  • URL Parameters:
    • uploadUUID: The UUID provided in the Location header from the previous POST /pdp/piece request.
  • Request Body: The raw bytes of the piece data.
  • Headers:
    • Content-Length: The size of the piece.
    • Content-Type: application/octet-stream.
Response
  • Status Code: 204 No Content
Errors
  • 400 Bad Request: Piece size does not match the expected size or computed hash does not match the expected hash.
  • 401 Unauthorized: Missing or invalid JWT token.
  • 404 Not Found: The provided uploadUUID is not found.
  • 409 Conflict: Data has already been uploaded for this uploadUUID.
  • 413 Payload Too Large: Piece data exceeds the maximum allowed size.

3. Notifications

When you initiate an upload with the notify field specified, the PDP Service will send a notification to the provided URL once the piece has been successfully processed and stored.

3.1. Notification Request
  • Method: POST
  • URL: The notify URL provided during the upload initiation (POST /pdp/piece).
  • Headers:
    • Content-Type: application/json
  • Request Body:
{
  "id": "<upload-ID>",
  "service": "<service-name>",
  "pieceCID": "<piece-CID or null>",
  "notify_url": "<original-notify-URL>",
  "check_hash_codec": "<hash-function-name>",
  "check_hash": "<byte-array-of-hash>"
}
  • Fields:
    • id: The upload ID.
    • service: The service name.
    • pieceCID: The Piece CID of the stored piece (may be null if not applicable).
    • notify_url: The original notification URL provided.
    • check_hash_codec: The hash function used (e.g., "sha2-256" or "sha2-256-trunc254-padded").
    • check_hash: The byte array of the original hash provided in the upload initiation.
3.2. Expected Response from Your Server
  • Status Code: 200 OK to acknowledge receipt.
  • Response Body: (Optional) Can be empty or contain a message.
3.3. Notes
  • The PDP Service may retry the notification if it fails.
  • Ensure that your server is accessible from the PDP Service and can handle incoming POST requests.
  • The notification does not include the piece data; it confirms that the piece has been successfully stored.

4. Create a Data Set
  • Endpoint: POST /pdp/data-sets
  • Description: Create a new data set.
  • Authentication: Requires a valid JWT token in the Authorization header.
  • Request Body:
{
  "recordKeeper": "<Ethereum-address-of-record-keeper>"
}
  • Fields:
    • recordKeeper: The Ethereum address of the record keeper.
Response
  • Status Code: 201 Created
  • Headers:
    • Location: The URL to check the status of the data set creation.
Errors
  • 400 Bad Request: Missing or invalid recordKeeper address.
  • 401 Unauthorized: Missing or invalid JWT token.
  • 500 Internal Server Error: Failed to process the request.

5. Check Data Set Creation Status
  • Endpoint: GET /pdp/data-sets/created/{txHash}
  • Description: Retrieve the status of a data set creation.
  • Authentication: Requires a valid JWT token in the Authorization header.
  • URL Parameters:
    • txHash: The transaction hash returned when creating the data set.
Response
  • Status Code: 200 OK
  • Response Body:
{
  "createMessageHash": "<transaction-hash>",
  "dataSetCreated": <boolean>,
  "service": "<service-name>",
  "txStatus": "<transaction-status>",
  "ok": <null-or-boolean>,
  "dataSetId": <data-set-id-or-null>
}
  • Fields:
    • createMessageHash: The transaction hash used to create the data set.
    • dataSetCreated: Whether the data set has been created (true or false).
    • service: The service name.
    • txStatus: The transaction status ("pending", "confirmed", etc.).
    • ok: true if the transaction was successful, false if it failed, or null if pending.
    • dataSetId: The ID of the created data set, if available.
Errors
  • 400 Bad Request: Missing or invalid txHash.
  • 401 Unauthorized: Missing or invalid JWT token, or service label mismatch.
  • 404 Not Found: Data set creation not found for the given txHash.

6. Get Data Set Details
  • Endpoint: GET /pdp/data-sets/{dataSetId}
  • Description: Retrieve the details of a data set, including its pieces.
  • Authentication: Requires a valid JWT token in the Authorization header.
  • URL Parameters:
    • dataSetId: The ID of the data set.
Response
  • Status Code: 200 OK
  • Response Body:
{
  "id": <dataSetId>,
  "pieces": [
    {
      "pieceId": <pieceId>,
      "pieceCid": "<pieceCID>",
      "subPieceCid": "<subpieceCID>",
      "subPieceOffset": <subpieceOffset>
    },
    // ...
  ]
}
  • Fields:
    • id: The ID of the data set.
    • pieces: An array of piece entries.
      • pieceId: The ID of the piece.
      • pieceCid: The CID of the piece.
      • subPieceCid: The CID of the subPiece.
      • subPieceOffset: The offset of the subPiece.
Errors
  • 400 Bad Request: Missing or invalid dataSetId.
  • 401 Unauthorized: Missing or invalid JWT token, or data set does not belong to the service.
  • 404 Not Found: Data set not found.

7. Delete a Data Set (To be implemented)
  • Endpoint: DELETE /pdp/data-sets/{dataSetId}
  • Description: Remove the specified data set entirely.
  • Authentication: Requires a valid JWT token in the Authorization header.
  • URL Parameters:
    • dataSetId: The ID of the data set.
Response
  • Status Code: 204 No Content
Errors
  • 400 Bad Request: Invalid request.
  • 401 Unauthorized: Missing or invalid JWT token.
  • 404 Not Found: Data set not found.

8. Add Pieces to a Data Set
  • Endpoint: POST /pdp/data-sets/{dataSetId}/pieces
  • Description: Add pieces to a data set.
  • Authentication: Requires a valid JWT token in the Authorization header.
  • URL Parameters:
    • dataSetId: The ID of the data set.
  • Request Body: An object containing an array of piece entries.
{
  "pieces": [
    {
      "pieceCid": "<pieceCID>",
      "subPieces": [
        {
          "subPieceCid": "<subpieceCID1>"
        },
        {
          "subPieceCid": "<subpieceCID2>"
        },
        // ...
      ]
    },
    // ...
  ],
  "extraData": "<optional-hex-data>"
}
  • Fields:
    • pieces: An array of piece entries.
      • Each piece entry contains:
        • pieceCid: The piece CID.
        • subPieces: An array of subPiece entries.
          • Each subPiece entry contains:
            • subPieceCid: The CID of the subPiece.
    • extraData: (Optional) Additional hex-encoded data for the transaction.
Constraints and Requirements
  • SubPieces Ordering: The subPieces must be provided in order from largest to smallest size. This ensures that no padding is required between subPieces during the computation of the piece CID.
  • SubPieces Ownership: All subPieces must belong to the service making the request, and they must be previously uploaded and stored on the PDP service.
  • SubPiece Sizes:
    • Each subPiece size must be at least 128 bytes.
  • SubPiece Alignment: The subPieces are concatenated without padding. Proper ordering ensures that the concatenated data aligns correctly for piece computation.
  • Piece CID Verification:
    • The provider computes the piece CID from the provided subPieces and verifies that it matches the pieceCid specified in the request.
    • If the computed piece CID does not match, the request is rejected.
Response
  • Status Code: 201 Created
Errors
  • 400 Bad Request: Invalid request body, missing fields, validation errors, or subPieces not ordered correctly.
  • 401 Unauthorized: Missing or invalid JWT token.
  • 404 Not Found: Data set not found or subPieces not found.
  • 500 Internal Server Error: Failed to process the request.

9. Get Piece Details (To be implemented)
  • Endpoint: GET /pdp/data-sets/{dataSetId}/pieces/{pieceId}
  • Description: Retrieve the details of a piece in a data set.
  • Authentication: Requires a valid JWT token in the Authorization header.
  • URL Parameters:
    • dataSetId: The ID of the data set.
    • pieceId: The ID of the piece.
Response
  • Status Code: 200 OK
  • Response Body: Piece details (to be defined).
Errors
  • 400 Bad Request: Invalid request.
  • 401 Unauthorized: Missing or invalid JWT token.
  • 404 Not Found: Data set or piece not found.

10. Delete a Piece from a Data Set (To be implemented)
  • Endpoint: DELETE /pdp/data-sets/{dataSetId}/pieces/{pieceId}
  • Description: Remove a piece from a data set.
  • Authentication: Requires a valid JWT token in the Authorization header.
  • URL Parameters:
    • dataSetId: The ID of the data set.
    • pieceId: The ID of the piece.
Response
  • Status Code: 204 No Content
Errors
  • 400 Bad Request: Invalid request.
  • 401 Unauthorized: Missing or invalid JWT token.
  • 404 Not Found: Data set or piece not found.

Authentication

All endpoints require authentication using a JWT (JSON Web Token). The token should be provided in the Authorization header as follows:

Authorization: Bearer <JWT-token>
Token Contents

The JWT token should be signed using ECDSA with the ES256 algorithm and contain the following claim:

  • service_name: The name of the service (string). This acts as the service identifier.
Token Verification Process

The server verifies the JWT token as follows:

  1. Extracting the Token:

    • The server reads the Authorization header and extracts the token following the Bearer prefix.
  2. Parsing and Validating the Token:

    • The token is parsed and validated using the ES256 signing method.
    • The service_name claim is extracted from the token.
  3. Retrieving the Public Key:

    • The server retrieves the public key associated with the service_name from its database or configuration.
    • Note: The PDP Service should provide the PDP provider (SP) with the service_name they should use and the corresponding public key.
  4. Verifying the Signature:

    • The public key is used to verify the token's signature.
    • If verification succeeds and the token is valid (not expired), the request is authorized.
Error Responses
  • Missing Authorization Header:

    • Status Code: 401 Unauthorized
    • Message: missing Authorization header
  • Invalid Token Format:

    • Status Code: 401 Unauthorized
    • Message: invalid Authorization header format or empty token
  • Invalid Signing Method:

    • Status Code: 401 Unauthorized
    • Message: unexpected signing method
  • Missing or Invalid Claims:

    • Status Code: 401 Unauthorized
    • Message: invalid token claims or missing service_name claim
  • Public Key Retrieval Failure:

    • Status Code: 401 Unauthorized
    • Message: failed to retrieve public key for service_name
  • Signature Verification Failure:

    • Status Code: 401 Unauthorized
    • Message: invalid token

Piece CID Computation from SubPieces

When adding pieces to a data set using the POST /pdp/data-sets/{dataSetId}/pieces endpoint, the server performs validation and computation of the piece CID from the provided subPieces.

Piece Computation Process
  1. Validating SubPieces:

    • Ensure that all subPieces are owned by the requesting service.
    • The subPieces must have been previously uploaded and stored on the server.
  2. Ordering SubPieces:

    • Important: SubPieces must be ordered from largest to smallest size.
    • This ordering ensures that no padding is required between the subPieces, aligning them correctly for piece computation.
  3. Piece Sizes and Alignment:

    • Each subPiece corresponds to a data segment with a size that is a power of two (e.g., 128 bytes, 256 bytes, 512 bytes).
    • The concatenation of the subPieces must not require padding to align to the sector size used in the computation.
  4. Computing the Piece CID:

    • The server uses the GenerateUnsealedCID function to compute the piece CID from the subPieces.
    • This function emulates the computation performed in the Filecoin proofs implementation.
    • The process involves stacking the subPieces and combining them using a Merkle tree hash function.
  5. Validation of Computed Piece CID:

    • The computed piece CID is compared with the pieceCid provided in the request.
    • If the computed piece CID does not match the provided pieceCid, the request is rejected with an error.
Constraints and Requirements
  • SubPieces Ownership: All subPiece CIDs must belong to the requesting service.
  • SubPieces Existence: All subPiece CIDs must be valid and previously stored on the server.
  • Ordering of SubPieces: Must be ordered from largest to smallest. The sizes must be decreasing or equal; no subPiece can be larger than the preceding one.
  • SubPiece Sizes: Each subPiece size must be a power of two and at least 128 bytes.
  • Total Size Limit: The total size of the concatenated subPieces must not exceed the maximum allowed sector size.
Error Responses
  • Invalid SubPiece Order:

    • Status Code: 400 Bad Request
    • Message: SubPieces must be in descending order of size
  • SubPiece Not Found or Unauthorized:

    • Status Code: 400 Bad Request
    • Message: subPiece CID <CID> not found or does not belong to service
  • Piece CID Mismatch:

    • Status Code: 400 Bad Request
    • Message: provided PieceCID does not match generated PieceCID
Piece Computation Function
func GenerateUnsealedCID(proofType abi.RegisteredSealProof, pieceInfos []abi.PieceInfo) (cid.Cid, error)

Where:

  • pieceInfos is a list of pieces (subPieces) with their sizes and CIDs.
  • The function builds a CommP tree from the subPieces, combining them correctly according to their sizes and alignment.

Data Models

PieceHash

Represents hash information about a piece.

{
  "name": "<hash-function-name>",
  "hash": "<hex-encoded-hash>",
  "size": <size-in-bytes>
}
  • Fields:
    • name: Name of the hash function used (e.g., "sha2-256", "sha2-256-trunc254-padded").
    • hash: Hex-encoded hash value.
    • size: Size of the piece in bytes.
PieceEntry

Represents a piece entry in a data set.

{
  "pieceId": <pieceID>,
  "pieceCid": "<pieceCID>",
  "subPieceCid": "<subPieceCID>",
  "subPieceOffset": <subPieceOffset>
}
  • Fields:
    • pieceId: The ID of the piece.
    • pieceCid: The CID of the piece.
    • subPieceCid: The CID of the subPiece.
    • subPieceOffset: The offset of the subPiece.

Common Errors

  • 400 Bad Request: The request is invalid or missing required parameters.
  • 401 Unauthorized: Missing or invalid JWT token.
  • 404 Not Found: The requested resource was not found.
  • 409 Conflict: The request could not be completed due to a conflict with the current state of the resource.
  • 413 Payload Too Large: The uploaded data exceeds the maximum allowed size.
  • 500 Internal Server Error: An unexpected error occurred on the server.

Error responses typically include an error message in the response body.


Example Usage

Uploading a Piece
  1. Initiate Upload:

    Request:

    POST /pdp/piece HTTP/1.1
    Host: example.com
    Authorization: Bearer <JWT-token>
    Content-Type: application/json
    
    {
      "check": {
        "name": "sha2-256",
        "hash": "<hex-encoded-sha256-hash>",
        "size": 12345
      },
      "notify": "https://example.com/notify"
    }
    

    Possible Responses:

    • Piece Already Exists:

      HTTP/1.1 200 OK
      Content-Type: application/json
      
      {
        "pieceCID": "<piece-CID>"
      }
      
    • Upload Required:

      HTTP/1.1 201 Created
      Location: /pdp/piece/upload/{uploadUUID}
      
  2. Upload Piece Data:

    Request:

    PUT /pdp/piece/upload/{uploadUUID} HTTP/1.1
    Host: example.com
    Content-Type: application/octet-stream
    Content-Length: 12345
    
    <binary piece data>
    

    Response:

    HTTP/1.1 204 No Content
    
  3. Receive Notification (if notify was provided):

    Server's Notification Request:

    POST /notify HTTP/1.1
    Host: example.com
    Content-Type: application/json
    
    {
      "id": "<upload-ID>",
      "service": "<service-name>",
      "pieceCID": "<piece-CID>",
      "notify_url": "https://example.com/notify",
      "check_hash_codec": "sha2-256",
      "check_hash": "<b64-byte-array-of-hash>"
    }
    

    Your Response:

    HTTP/1.1 200 OK
    
Creating a Data Set

Request:

POST /pdp/data-sets HTTP/1.1
Host: example.com
Authorization: Bearer <JWT-token>
Content-Type: application/json

{
  "recordKeeper": "0x1234567890abcdef..."
}

Response:

HTTP/1.1 201 Created
Location: /pdp/data-sets/created/0xabc123...
Adding Pieces to a Data Set

Request:

POST /pdp/data-sets/{dataSetId}/pieces HTTP/1.1
Host: example.com
Authorization: Bearer <JWT-token>
Content-Type: application/json

{
  "pieces": [
    {
      "pieceCid": "<pieceCID>",
      "subPieces": [
        { "subPieceCid": "<subPieceCID1>" },
        { "subPieceCid": "<subPieceCID2>" },
        { "subPieceCid": "<subPieceCID3>" }
      ]
    },
    // ... Additional pieces if needed
  ]
}

Response:

HTTP/1.1 201 Created

Documentation

Index

Constants

View Source
const (
	// MaxCreateDataSetExtraDataSize defines the limit for extraData size in CreateDataSet calls (4KB).
	MaxCreateDataSetExtraDataSize = 4096

	// MaxAddPiecesExtraDataSize defines the limit for extraData size in AddPieces calls (8KB).
	MaxAddPiecesExtraDataSize = 8192

	// MaxDeletePieceExtraDataSize defines the limit for extraData size in DeletePiece calls (256B).
	MaxDeletePieceExtraDataSize = 256
)
View Source
const PDPRoutePath = "/pdp"

PDPRoutePath is the base path for PDP routes

View Source
const UploadSizeLimit = int64(1065353216) // 1 GiB.Unpadded()

Variables

View Source
var PieceSizeLimit = abi.PaddedPieceSize(proof.MaxMemtreeSize).Unpadded()

PieceSizeLimit in bytes

Functions

func CheckIfIndexingNeeded added in v1.27.3

func CheckIfIndexingNeeded(
	ethClient *ethclient.Client,
	dataSetId uint64,
) (bool, error)

CheckIfIndexingNeeded checks if a data set has the withIPFSIndexing metadata flag. Returns true if indexing is needed, false otherwise. This is a read-only check that can be done outside a transaction for existing datasets.

func CheckIfIndexingNeededFromExtraData added in v1.27.3

func CheckIfIndexingNeededFromExtraData(extraData []byte) (bool, error)

CheckIfIndexingNeededFromExtraData checks if extraData contains withIPFSIndexing metadata. This is used for the CreateDataSet+AddPieces combined operation where the dataset doesn't exist yet. The extraData format for combined operations is: (bytes createPayload, bytes addPayload) We attempt to decode the createPayload format is is decoded according to the FilecoinWarmStorageService format:

(address payer, uint256 clientDataSetId, string[] keys, string[] values, bytes signature)

func EnableIndexingForPiecesInTx added in v1.27.3

func EnableIndexingForPiecesInTx(
	tx *harmonydb.Tx,
	serviceLabel string,
	subPieceCids []string,
) error

EnableIndexingForPiecesInTx marks the specified pieces as needing indexing within a transaction.

func PadPieceSize added in v1.27.3

func PadPieceSize(rawSize int64) int64

PadPieceSize calculates the padded piece size from raw size using FR32 padding. FR32 encoding: 127 bytes of data become 128 bytes, then rounded up to next power of 2.

func Routes

func Routes(r *chi.Mux, p *PDPService)

Routes registers the HTTP routes with the provided router

func ValidatePullSourceURL added in v1.27.3

func ValidatePullSourceURL(sourceURL string, expectedPieceCid string) error

ValidatePullSourceURL validates that a source URL is safe and properly formatted for pulling a piece from another SP.

Validation rules:

  • Must be HTTPS
  • Path must end with /piece/{pieceCid}
  • The pieceCid in the URL must match the expected pieceCid
  • Host must not be localhost, private IP, or link-local address

Types

type AddPieceRequest added in v1.27.3

type AddPieceRequest struct {
	PieceCID string `json:"pieceCid"`

	SubPieces []SubPieceEntry `json:"subPieces"`
	// contains filtered or unexported fields
}

type AddPiecesValidator added in v1.27.3

type AddPiecesValidator interface {
	// ValidateAddPieces performs an eth_call to validate the extraData
	// Returns nil if validation passes, error otherwise
	ValidateAddPieces(ctx context.Context, params *AddPiecesValidatorParams) error
}

AddPiecesValidator validates extraData against the contract via eth_call

type AddPiecesValidatorParams added in v1.27.3

type AddPiecesValidatorParams struct {
	DataSetId    *big.Int // 0 for create-new
	RecordKeeper common.Address
	PieceData    []contract.CidsCid
	ExtraData    []byte
}

AddPiecesValidatorParams contains parameters for eth_call validation

type Auth added in v1.26.0

type Auth interface {
	AuthService(r *http.Request) (string, error)
}

type EthCallValidator added in v1.27.3

type EthCallValidator struct {
	// contains filtered or unexported fields
}

EthCallValidator validates via eth_call to PDPVerifier contract

func NewEthCallValidator added in v1.27.3

func NewEthCallValidator(ethClient *ethclient.Client, db *harmonydb.DB) *EthCallValidator

NewEthCallValidator creates a validator that uses eth_call

func (*EthCallValidator) ValidateAddPieces added in v1.27.3

func (v *EthCallValidator) ValidateAddPieces(ctx context.Context, params *AddPiecesValidatorParams) error

type JWTAuth added in v1.26.0

type JWTAuth struct {
	// contains filtered or unexported fields
}

func (*JWTAuth) AuthService added in v1.26.0

func (a *JWTAuth) AuthService(r *http.Request) (string, error)

JWTAuth extracts and verifies the JWT token from the request and returns the serviceID.

type NullAuth added in v1.26.0

type NullAuth struct{}

func (*NullAuth) AuthService added in v1.26.0

func (a *NullAuth) AuthService(r *http.Request) (string, error)

type PDPService

type PDPService struct {
	Auth
	// contains filtered or unexported fields
}

PDPService represents the service for managing data sets and pieces

func NewPDPService

NewPDPService creates a new instance of PDPService with the provided stores

type PDPServiceNodeApi

type PDPServiceNodeApi interface {
	ChainHead(ctx context.Context) (*types2.TipSet, error)
}

type ParkedPieceEntry added in v1.27.3

type ParkedPieceEntry struct {
	PieceCid        string
	PiecePaddedSize int64
	PieceRawSize    int64
	DataURL         string
}

ParkedPieceEntry represents the data needed to create a parked piece entry

type PieceCidInfo added in v1.27.3

type PieceCidInfo struct {
	CidV1   cid.Cid // Always populated
	CidV2   cid.Cid // Undef if parsed from v1 without size
	RawSize uint64  // 0 if parsed from v1 without size
}

PieceCidInfo holds all derived information from a piece CID. Depending on how it was constructed, some fields may be zero values.

func ParsePieceCid added in v1.27.3

func ParsePieceCid(cidStr string) (*PieceCidInfo, error)

ParsePieceCid parses any valid piece CID string (v1 or v2). If the input is v1, CidV2 will be cid.Undef and RawSize will be 0. If the input is v2, all fields will be populated.

func ParsePieceCidV2 added in v1.27.3

func ParsePieceCidV2(cidStr string) (*PieceCidInfo, error)

ParsePieceCidV2 parses a piece CID string that must be in v2 format. Returns an error if the input is a v1 CID. All fields in the returned PieceCidInfo will be populated.

func PieceCidV2FromV1 added in v1.27.3

func PieceCidV2FromV1(v1 cid.Cid, rawSize uint64) (*PieceCidInfo, error)

PieceCidV2FromV1 constructs a complete PieceCidInfo from a v1 CID and raw size. This is typically used when reading from the database (which stores v1 + size) and needing to return v2 in API responses.

func PieceCidV2FromV1Str added in v1.27.3

func PieceCidV2FromV1Str(v1Str string, rawSize uint64) (*PieceCidInfo, error)

PieceCidV2FromV1Str is a convenience function that parses a v1 CID string and constructs a complete PieceCidInfo. This handles the common case of reading a v1 CID string from the database along with its size.

func (*PieceCidInfo) HasV2 added in v1.27.3

func (p *PieceCidInfo) HasV2() bool

HasV2 returns true if the CidV2 is available (not Undef).

type PieceData added in v1.27.3

type PieceData struct {
	Data []byte // CID
}

type PieceEntry added in v1.27.3

type PieceEntry struct {
	PieceID        uint64 `json:"pieceId"`
	PieceCID       string `json:"pieceCid"`
	SubPieceCID    string `json:"subPieceCid"`
	SubPieceOffset int64  `json:"subPieceOffset"`
}

PieceEntry represents a piece in the data set for JSON serialization

type PieceStatus added in v1.27.3

type PieceStatus struct {
	PieceCid   string
	Complete   bool
	TaskID     *int64 // task_id from parked_pieces (may point to deleted task)
	TaskExists bool   // true if task_id exists in harmony_task
	Retries    int    // retry count from harmony_task (0 if task doesn't exist)
}

PieceStatus represents the status of a piece in storage

type PullHandler added in v1.27.3

type PullHandler struct {
	// contains filtered or unexported fields
}

PullHandler handles piece pull requests

func NewPullHandler added in v1.27.3

func NewPullHandler(auth Auth, store PullStore, validator AddPiecesValidator) *PullHandler

NewPullHandler creates a new PullHandler

func (*PullHandler) HandlePull added in v1.27.3

func (h *PullHandler) HandlePull(w http.ResponseWriter, r *http.Request)

HandlePull handles POST /pdp/piece/pull requests for SP-to-SP piece pull.

Overview

This endpoint allows a client to request that pieces be pulled from other storage providers and stored locally. It is designed for scenarios where data already exists on one SP and needs to be replicated to another, without requiring the client to re-upload the data.

Request Format

The request body is a JSON object with the following fields:

  • extraData (required): Hex-encoded bytes that will be validated against the PDPVerifier contract via eth_call. This ensures the caller has authorization to add these pieces. See "ExtraData and Authorization" below.

  • dataSetId (optional): The target dataset ID for the eth_call validation. If omitted or zero, validation simulates creating a new dataset.

  • recordKeeper (required when creating new dataset): The contract address that will receive callbacks from PDPVerifier (typically FilecoinWarmStorageService). Must be in the allowed list for public services.

  • pieces (required): Array of pieces to pull, each containing:

  • pieceCid: PieceCIDv2 format (encodes both CommP and raw size)

  • sourceUrl: HTTPS URL ending in /piece/{pieceCid} on a public host

ExtraData and Authorization

The extraData field serves two purposes:

  1. Authorization: It is validated via eth_call to PDPVerifier.addPieces(), which forwards to the recordKeeper contract for validation. PDPVerifier itself only checks for valid input format; the recordKeeper (e.g., FilecoinWarmStorageService) performs the actual authorization checks such as signature verification and ensuring sufficient funds are available.

  2. Idempotency key: Combined with service, dataSetId, and recordKeeper, a hash of extraData forms the idempotency key. Repeated requests with the same key return the status of the existing pull rather than creating duplicates.

The extraData used here does NOT need to match the extraData used in the subsequent addPieces call to the contract. This allows for a two-phase flow where:

  • Phase 1 (this endpoint): Authorize and initiate piece pulling
  • Phase 2 (contract call): Add pieces to the dataset with potentially different extraData

Workflow

  1. Client calls POST /pdp/piece/pull with piece CIDs and source URLs
  2. Server validates extraData via eth_call (ensures authorization)
  3. Server creates pull tracking record and queues pieces for download
  4. Background task (StorePiece) downloads pieces from source URLs
  5. Client polls the same endpoint to check status (idempotent)
  6. Once all pieces are "complete", client calls the contract to add pieces to dataset

Status Progression

Each piece progresses through these statuses:

  • pending: Piece is queued but download hasn't started
  • inProgress: Download task is actively running (first attempt)
  • retrying: Download task is running after one or more failures
  • complete: Piece successfully downloaded and verified
  • failed: Piece permanently failed after exhausting retries (currently 5 attempts)

The overall response status reflects the worst-case across all pieces: failed > retrying > inProgress > pending > complete

Safety and Verification

Several safety measures protect against malicious sources:

  • Source URL validation: Must be HTTPS, path must match /piece/{pieceCid}, host must not be localhost/private IP/link-local

  • Size limits: Piece size (encoded in PieceCIDv2) must not exceed PieceSizeLimit. Downloads are capped at the declared size to prevent abuse.

  • CommP verification: After download, the CommP (piece commitment) is computed and verified against the expected value from PieceCIDv2. Mismatches are rejected.

  • Size verification: Actual downloaded size must match the declared size. Both truncation and oversized data are rejected.

Response Format

Returns JSON with overall status and per-piece status:

{
  "status": "inProgress",
  "pieces": [
    {"pieceCid": "bafk...", "status": "complete"},
    {"pieceCid": "bafk...", "status": "inProgress"}
  ]
}

Idempotency

Requests are idempotent based on (service, sha256(extraData), dataSetId, recordKeeper). Calling with the same parameters returns the current status without creating new work. This allows safe retries and status polling using the same request.

type PullItemStatus added in v1.27.3

type PullItemStatus struct {
	TaskID     *int64 // task_id from pdp_piece_pull_items
	TaskExists bool   // true if task_id exists in harmony_task
	Retries    int    // retry count from harmony_task (0 if task doesn't exist)
	Failed     bool   // true if piece permanently failed
}

PullItemStatus represents the status of a pull item

type PullPiece added in v1.27.3

type PullPiece struct {
	CidV1      cid.Cid
	RawSize    uint64
	SourceURL  string // external SP URL to pull from
	Failed     bool   // true if piece permanently failed
	FailReason string // error message when failed
}

PullPiece represents a piece stored in a pull request (v1 CID + raw size for v2 reconstruction)

type PullPieceRequest added in v1.27.3

type PullPieceRequest struct {
	PieceCid  string `json:"pieceCid"`
	SourceURL string `json:"sourceUrl"`
}

PullPieceRequest represents a single piece in a pull request

type PullPieceStatus added in v1.27.3

type PullPieceStatus struct {
	PieceCid string     `json:"pieceCid"`
	Status   PullStatus `json:"status"`
}

PullPieceStatus represents the status of a single piece

type PullRecord added in v1.27.3

type PullRecord struct {
	ID            int64
	Service       string
	ExtraDataHash []byte
	DataSetId     uint64 // 0 = create new
	RecordKeeper  string // address, required when DataSetId is 0
}

PullRecord represents a pull request record from the database

type PullRequest added in v1.27.3

type PullRequest struct {
	ExtraData    string             `json:"extraData"`
	DataSetId    *uint64            `json:"dataSetId,omitempty"`    // nil or 0 = create new dataset
	RecordKeeper *string            `json:"recordKeeper,omitempty"` // required when dataSetId is nil/0
	Pieces       []PullPieceRequest `json:"pieces"`
}

PullRequest represents the incoming pull request body

func (*PullRequest) IsCreateNew added in v1.27.3

func (r *PullRequest) IsCreateNew() bool

IsCreateNew returns true if this pull will create a new dataset (dataSetId is nil or 0)

func (*PullRequest) Validate added in v1.27.3

func (r *PullRequest) Validate() error

Validate performs validation on the entire pull request

type PullResponse added in v1.27.3

type PullResponse struct {
	Status PullStatus        `json:"status"`
	Pieces []PullPieceStatus `json:"pieces"`
}

PullResponse represents the response from a pull request

func (*PullResponse) ComputeOverallStatus added in v1.27.3

func (r *PullResponse) ComputeOverallStatus()

ComputeOverallStatus derives the overall status from individual piece statuses. Priority: failed > retrying > inProgress > pending > complete

type PullStatus added in v1.27.3

type PullStatus string

PullStatus represents the status of a pull operation or piece

const (
	PullStatusPending    PullStatus = "pending"
	PullStatusInProgress PullStatus = "inProgress"
	PullStatusRetrying   PullStatus = "retrying"
	PullStatusComplete   PullStatus = "complete"
	PullStatusFailed     PullStatus = "failed"
)

type PullStore added in v1.27.3

type PullStore interface {
	// GetPullByKey retrieves a pull record by its idempotency key
	GetPullByKey(ctx context.Context, service string, hash []byte, dataSetId uint64, recordKeeper string) (*PullRecord, error)

	// CreatePullWithPieces creates a pull record and its associated piece items in a transaction
	// Returns the created pull ID
	CreatePullWithPieces(ctx context.Context, pull *PullRecord, pieces []PullPiece) (int64, error)

	// GetPieceStatuses retrieves the status of multiple pieces from parked_pieces (keyed by v1 CID string)
	// This checks if pieces have been successfully stored (complete=true)
	GetPieceStatuses(ctx context.Context, pieceCids []cid.Cid) (map[string]*PieceStatus, error)

	// GetPullItemStatuses retrieves the status of pull items (keyed by v1 CID string)
	// This checks the pull task state (task_id, failed)
	GetPullItemStatuses(ctx context.Context, pullID int64, pieceCids []cid.Cid) (map[string]*PullItemStatus, error)

	// GetPullPieces retrieves all pieces associated with a pull record (includes failure info)
	GetPullPieces(ctx context.Context, pullID int64) ([]PullPiece, error)

	// MarkPieceFailed marks a piece as permanently failed in the pull items table
	MarkPieceFailed(ctx context.Context, pullID int64, pieceCid string, reason string) error

	// CheckTaskExhaustedRetries checks if a task exhausted its retries by looking at harmony_task_history
	// Returns true if the task failed permanently, along with the error message
	CheckTaskExhaustedRetries(ctx context.Context, taskID int64) (bool, string, error)
}

PullStore abstracts database operations for the pull handler

func NewDBPullStore added in v1.27.3

func NewDBPullStore(db *harmonydb.DB) PullStore

NewDBPullStore creates a PullStore backed by harmonydb

type SubPieceEntry added in v1.27.3

type SubPieceEntry struct {
	SubPieceCID string `json:"subPieceCid"`
	// contains filtered or unexported fields
}

type SubPieceInfo added in v1.27.3

type SubPieceInfo struct {
	PieceCIDv1     cid.Cid
	PaddedSize     abi.PaddedPieceSize
	RawSize        uint64 // RawSize is the size of the piece with no padding applied
	PDPPieceRefID  int64
	SubPieceOffset uint64
}

Map to store subPieceCID -> [pieceInfo, pdp_pieceref.id, subPieceOffset]

type TimeoutLimitReader added in v1.27.3

type TimeoutLimitReader struct {
	// contains filtered or unexported fields
}

func NewTimeoutLimitReader added in v1.27.3

func NewTimeoutLimitReader(r io.Reader, timeout time.Duration) *TimeoutLimitReader

func (*TimeoutLimitReader) Read added in v1.27.3

func (t *TimeoutLimitReader) Read(p []byte) (int, error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL