Documentation
¶
Index ¶
- Constants
- Variables
- func CheckIfIndexingNeeded(ethClient *ethclient.Client, dataSetId uint64) (bool, error)
- func CheckIfIndexingNeededFromExtraData(extraData []byte) (bool, error)
- func EnableIndexingForPiecesInTx(tx *harmonydb.Tx, serviceLabel string, subPieceCids []string) error
- func PadPieceSize(rawSize int64) int64
- func Routes(r *chi.Mux, p *PDPService)
- func ValidatePullSourceURL(sourceURL string, expectedPieceCid string) error
- type AddPieceRequest
- type AddPiecesValidator
- type AddPiecesValidatorParams
- type Auth
- type EthCallValidator
- type JWTAuth
- type NullAuth
- type PDPService
- type PDPServiceNodeApi
- type ParkedPieceEntry
- type PieceCidInfo
- type PieceData
- type PieceEntry
- type PieceStatus
- type PullHandler
- type PullItemStatus
- type PullPiece
- type PullPieceRequest
- type PullPieceStatus
- type PullRecord
- type PullRequest
- type PullResponse
- type PullStatus
- type PullStore
- type SubPieceEntry
- type SubPieceInfo
- type TimeoutLimitReader
Constants ¶
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 )
const PDPRoutePath = "/pdp"
PDPRoutePath is the base path for PDP routes
const UploadSizeLimit = int64(1065353216) // 1 GiB.Unpadded()
Variables ¶
var PieceSizeLimit = abi.PaddedPieceSize(proof.MaxMemtreeSize).Unpadded()
PieceSizeLimit in bytes
Functions ¶
func CheckIfIndexingNeeded ¶ added in v1.27.3
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
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
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
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 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 PDPService ¶
type PDPService struct {
Auth
// contains filtered or unexported fields
}
PDPService represents the service for managing data sets and pieces
func NewPDPService ¶
func NewPDPService(ctx context.Context, db *harmonydb.DB, stor paths.StashStore, ec *ethclient.Client, fc PDPServiceNodeApi, sn *message.SenderETH) *PDPService
NewPDPService creates a new instance of PDPService with the provided stores
type PDPServiceNodeApi ¶
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 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:
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.
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 ¶
- Client calls POST /pdp/piece/pull with piece CIDs and source URLs
- Server validates extraData via eth_call (ensures authorization)
- Server creates pull tracking record and queues pieces for download
- Background task (StorePiece) downloads pieces from source URLs
- Client polls the same endpoint to check status (idempotent)
- 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
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