@@ -18,12 +18,8 @@ import (
1818 "github.com/ethereum/go-ethereum/core/types"
1919 "github.com/ethereum/go-ethereum/ethclient"
2020 "github.com/go-chi/chi/v5"
21- "github.com/ipfs/go-cid"
22- "github.com/multiformats/go-multicodec"
2321 "github.com/yugabyte/pgx/v5"
2422
25- commcid "github.com/filecoin-project/go-fil-commcid"
26-
2723 "github.com/filecoin-project/curio/harmony/harmonydb"
2824 "github.com/filecoin-project/curio/lib/paths"
2925 "github.com/filecoin-project/curio/pdp/contract"
@@ -56,6 +52,8 @@ type PDPService struct {
5652 sender * message.SenderETH
5753 ethClient * ethclient.Client
5854 filClient PDPServiceNodeApi
55+
56+ pullHandler * PullHandler
5957}
6058
6159type PDPServiceNodeApi interface {
@@ -64,14 +62,20 @@ type PDPServiceNodeApi interface {
6462
6563// NewPDPService creates a new instance of PDPService with the provided stores
6664func NewPDPService (ctx context.Context , db * harmonydb.DB , stor paths.StashStore , ec * ethclient.Client , fc PDPServiceNodeApi , sn * message.SenderETH ) * PDPService {
65+ auth := & NullAuth {}
66+ pullStore := NewDBPullStore (db )
67+ pullValidator := NewEthCallValidator (ec , db )
68+
6769 p := & PDPService {
68- Auth : & NullAuth {} ,
70+ Auth : auth ,
6971 db : db ,
7072 storage : stor ,
7173
7274 sender : sn ,
7375 ethClient : ec ,
7476 filClient : fc ,
77+
78+ pullHandler : NewPullHandler (auth , pullStore , pullValidator ),
7579 }
7680
7781 go p .cleanup (ctx )
@@ -146,6 +150,9 @@ func Routes(r *chi.Mux, p *PDPService) {
146150
147151 // POST /pdp/piece/uploads/{uploadUUID}
148152 r .Post (path .Join (PDPRoutePath , "/piece/uploads/{uploadUUID}" ), p .handleFinalizeStreamingUpload )
153+
154+ // POST /pdp/piece/pull - Pull pieces from other SPs
155+ r .Post (path .Join (PDPRoutePath , "/piece/pull" ), p .pullHandler .HandlePull )
149156}
150157
151158// Handler functions
@@ -181,12 +188,12 @@ func (p *PDPService) handleGetPieceStatus(w http.ResponseWriter, r *http.Request
181188 }
182189
183190 // Convert to v1 format (database stores v1)
184- pieceCidV1 , err := asPieceCIDv1 (pieceCidStr )
191+ info , err := ParsePieceCid (pieceCidStr )
185192 if err != nil {
186193 http .Error (w , "Invalid pieceCid format: " + err .Error (), http .StatusBadRequest )
187194 return
188195 }
189- pieceCidV1Str := pieceCidV1 .String ()
196+ pieceCidV1Str := info . CidV1 .String ()
190197
191198 // Query status from database
192199 var result struct {
@@ -256,7 +263,7 @@ func (p *PDPService) handleGetPieceStatus(w http.ResponseWriter, r *http.Request
256263 }
257264
258265 // Convert authoritative PieceCID back from v1 to v2 for external API
259- pieceCidV2 , _ , err := asPieceCIDv2 (result .PieceCID , result .PieceRawSize )
266+ pieceInfo , err := PieceCidV2FromV1Str (result .PieceCID , result .PieceRawSize )
260267 if err != nil {
261268 http .Error (w , "Failed to convert PieceCID to v2: " + err .Error (), http .StatusInternalServerError )
262269 return
@@ -271,7 +278,7 @@ func (p *PDPService) handleGetPieceStatus(w http.ResponseWriter, r *http.Request
271278 Retrieved bool `json:"retrieved"`
272279 RetrievedAt * time.Time `json:"retrievedAt,omitempty"`
273280 }{
274- PieceCID : pieceCidV2 .String (),
281+ PieceCID : pieceInfo . CidV2 .String (),
275282 Status : result .Status ,
276283 Indexed : result .Indexed ,
277284 Advertised : result .Advertised ,
@@ -573,25 +580,25 @@ func (p *PDPService) handleGetDataSet(w http.ResponseWriter, r *http.Request) {
573580 pcv2Str , exists := aggregatePieceCIDs [piece .PieceID ]
574581 if ! exists {
575582 aggregateRawSize := pieceRawSizes [piece .PieceID ]
576- pcv2 , _ , err := asPieceCIDv2 (piece .PieceCid , aggregateRawSize )
583+ pcInfo , err := PieceCidV2FromV1Str (piece .PieceCid , aggregateRawSize )
577584 if err != nil {
578585 http .Error (w , "Invalid PieceCID: " + err .Error (), http .StatusBadRequest )
579586 return
580587 }
581- pcv2Str = pcv2 .String ()
588+ pcv2Str = pcInfo . CidV2 .String ()
582589 aggregatePieceCIDs [piece .PieceID ] = pcv2Str
583590 }
584591
585592 // Use the raw size for the sub piece
586- spcv2 , _ , err := asPieceCIDv2 (piece .SubPieceCID , piece .SubPieceRawSize )
593+ spcInfo , err := PieceCidV2FromV1Str (piece .SubPieceCID , piece .SubPieceRawSize )
587594 if err != nil {
588595 http .Error (w , "Invalid SubPieceCID: " + err .Error (), http .StatusBadRequest )
589596 return
590597 }
591598 response .Pieces = append (response .Pieces , PieceEntry {
592599 PieceID : piece .PieceID ,
593600 PieceCID : pcv2Str ,
594- SubPieceCID : spcv2 .String (),
601+ SubPieceCID : spcInfo . CidV2 .String (),
595602 SubPieceOffset : piece .SubPieceOffset ,
596603 })
597604 }
@@ -1095,48 +1102,6 @@ func (p *PDPService) handleGetDataSetPiece(w http.ResponseWriter, r *http.Reques
10951102 }
10961103}
10971104
1098- func asPieceCIDv1 (cidStr string ) (cid.Cid , error ) {
1099- pieceCid , err := cid .Decode (cidStr )
1100- if err != nil {
1101- return cid .Undef , fmt .Errorf ("failed to decode PieceCID: %w" , err )
1102- }
1103- if pieceCid .Prefix ().MhType == uint64 (multicodec .Fr32Sha256Trunc254Padbintree ) {
1104- c1 , _ , err := commcid .PieceCidV1FromV2 (pieceCid )
1105- return c1 , err
1106- }
1107- return pieceCid , nil
1108- }
1109-
1110- // asPieceCIDv2 converts a string to a PieceCIDv2. Where the input is expected to be a PieceCIDv1,
1111- // a size argument is required. Where it's expected to be a v2, the size argument is ignored. The
1112- // size either derived from the v2 or from the size argument in the case of a v1 is returned.
1113- func asPieceCIDv2 (cidStr string , size uint64 ) (cid.Cid , uint64 , error ) {
1114- pieceCid , err := cid .Decode (cidStr )
1115- if err != nil {
1116- return cid .Undef , 0 , fmt .Errorf ("failed to decode PieceCid: %w" , err )
1117- }
1118- switch pieceCid .Prefix ().MhType {
1119- case uint64 (multicodec .Sha2_256Trunc254Padded ):
1120- if size == 0 {
1121- return cid .Undef , 0 , fmt .Errorf ("size must be provided for PieceCIDv1" )
1122- }
1123- c , err := commcid .PieceCidV2FromV1 (pieceCid , size )
1124- if err != nil {
1125- return cid .Undef , 0 , err
1126- }
1127- return c , size , nil
1128- case uint64 (multicodec .Fr32Sha256Trunc254Padbintree ):
1129- // get the size from the CID, not the argument
1130- _ , size , err := commcid .PieceCidV2ToDataCommitment (pieceCid )
1131- if err != nil {
1132- return cid .Undef , 0 , fmt .Errorf ("failed to get size from Sha2_256Trunc254Padded PieceCid: %w" , err )
1133- }
1134- return pieceCid , size , nil
1135- default :
1136- return cid .Undef , 0 , fmt .Errorf ("unsupported piece CID type: %d" , pieceCid .Prefix ().MhType )
1137- }
1138- }
1139-
11401105func (p * PDPService ) cleanup (ctx context.Context ) {
11411106 rm := func (ctx context.Context , db * harmonydb.DB ) {
11421107 var RefIDs []int64
@@ -1155,6 +1120,13 @@ func (p *PDPService) cleanup(ctx context.Context) {
11551120 log .Errorw ("failed to delete non-finalized uploads" , "error" , err )
11561121 }
11571122 }
1123+
1124+ // Clean up old piece fetch records (older than 5 days)
1125+ // CASCADE deletes pdp_piece_fetch_items automatically
1126+ _ , err = db .Exec (ctx , `DELETE FROM pdp_piece_fetches WHERE created_at < NOW() - INTERVAL '5 days'` )
1127+ if err != nil {
1128+ log .Errorw ("failed to delete old piece fetch records" , "error" , err )
1129+ }
11581130 }
11591131
11601132 ticker := time .NewTicker (time .Minute * 5 )
0 commit comments