Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: PDP #227

Draft
wants to merge 72 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
fc7399d
pdp: Inital scaffold
magik6k Sep 30, 2024
40aad8c
pdp: Drop unneded handlers
magik6k Oct 1, 2024
e3ad509
pdp: wip uploads
magik6k Oct 1, 2024
ececf7c
pdp: Proper pieceref tracking
magik6k Oct 2, 2024
ed8c966
storage: Implement local stash storage
magik6k Oct 2, 2024
afa54ec
storage: Make Local use a single URL
magik6k Oct 2, 2024
794d167
pdp: Complete upload
magik6k Oct 2, 2024
6f36c6b
piecepark: Special long-term storage mode
magik6k Oct 2, 2024
041675a
dealdata: Handle custore://
magik6k Oct 2, 2024
4daac0c
pdp: Notify task
magik6k Oct 3, 2024
cf7ae93
retrieval: Foundations for PiecePark readers
magik6k Oct 3, 2024
2082fe9
retrieval: Parked piece /piece retrievals
magik6k Oct 3, 2024
cea21f7
pdptool for interacting with curio pdp endpoints
magik6k Oct 3, 2024
76634fd
pdp webui: Basic page, adding services through UI
magik6k Oct 3, 2024
14487f2
pdp webui: Remove service button
magik6k Oct 3, 2024
7ef7681
get http server cert cache working
magik6k Oct 3, 2024
a773193
pdp: server and tool ping functionality
magik6k Oct 3, 2024
5cdf4a2
slowly getting data import to actually work
magik6k Oct 4, 2024
51d0d28
separate pdp task config
magik6k Oct 4, 2024
79e6f1b
make gen
magik6k Oct 4, 2024
66a2c41
basic pdp key management
magik6k Oct 4, 2024
c7f676f
eth sender
magik6k Oct 7, 2024
37c327d
more working pdp message sender
magik6k Oct 7, 2024
bf1c121
initial eth waiter
magik6k Oct 7, 2024
d828a8d
eth waiter fixes
magik6k Oct 7, 2024
eb5dd69
wip impl /create-proofset
magik6k Oct 7, 2024
cc72018
wire up proofset create
magik6k Oct 7, 2024
8336ce8
pdptool: Add a tool for proofset-create
magik6k Oct 7, 2024
551c65a
gas estimation in eth sender
magik6k Oct 7, 2024
caf57c0
proofset receipt parsing
magik6k Oct 8, 2024
986151b
pdp: Endpoint to get proofset creation status
magik6k Oct 8, 2024
511125e
pdp: get-proof-set endpoint
magik6k Oct 8, 2024
9fbf574
pdp: Add-root endpoint
magik6k Oct 9, 2024
752d9b4
pdp: Separate table for root adds
magik6k Oct 9, 2024
ee7e30f
pdp: Working root adds
magik6k Oct 9, 2024
dd6a47c
pdp: Add-root watcher
magik6k Oct 9, 2024
b73adc2
pdp: working root adding
magik6k Oct 9, 2024
8c4e566
pdp: Watch challenge epochs
magik6k Oct 10, 2024
e06f759
pdp: Scaffold prove task
magik6k Oct 10, 2024
4f50a46
pdp: Prove scheduling
magik6k Oct 10, 2024
07ad29f
pdp: Path from challenge to prove
magik6k Oct 10, 2024
7e1798a
proof: Sha memtree
magik6k Oct 10, 2024
04b4bc5
proof: Binary memtree proover
magik6k Oct 10, 2024
67ba074
pdp: Require sorted subroot pieces
magik6k Oct 11, 2024
5538028
pdp: Getting there with proof verification
magik6k Oct 11, 2024
b26d885
pdp: Proof bugfixing progress
magik6k Oct 12, 2024
eeca445
pdp: PROOFS WORK!
magik6k Oct 12, 2024
a03ba8d
pdp: An actually working poller this time
magik6k Oct 12, 2024
c555027
pdp: better praams
magik6k Oct 13, 2024
b910e95
pdp: Update contarcts
magik6k Oct 22, 2024
ecc52c9
make gen, consistent max piece size
magik6k Oct 22, 2024
2e4f9b9
fix local path test
magik6k Oct 22, 2024
2adff15
pdp: Implement fees
magik6k Oct 22, 2024
16ccaad
pdp chall lookback in prove
magik6k Oct 22, 2024
4ac1e85
update to new proving scheme
magik6k Oct 23, 2024
2cea05f
small fixes
magik6k Oct 23, 2024
7513819
pdp: Support arbitrary hash funcs in uploads
magik6k Oct 24, 2024
ff3d92f
pdp: api readme
magik6k Oct 24, 2024
3190e7c
fix lint
magik6k Oct 24, 2024
4cb6643
feat(pdp): add endpoint to poll for completed upload (#304)
hannahhoward Oct 30, 2024
143411a
enable chain sched with just eth sender
magik6k Nov 1, 2024
4b70e1d
feat(pdp): add ed25519 support for JWT (#323)
hannahhoward Nov 9, 2024
921be6f
fix(pdp): fix ed25519 auth (#324)
hannahhoward Nov 10, 2024
7375a08
Zen/update interface (#334)
ZenGround0 Dec 11, 2024
88659ba
Integrate pdp updates -- nextProvingPeriod called after first add (#347)
ZenGround0 Jan 28, 2025
8a788b6
Delete pdp roots from proofset (#384)
ZenGround0 Jan 28, 2025
db1cf8e
Merge remote-tracking branch 'origin/main' into feat/pdp2
magik6k Jan 28, 2025
092e15f
make gen
magik6k Jan 28, 2025
dce9f17
drop some unused things
magik6k Jan 29, 2025
a29c049
crypto rand in pdptool
magik6k Jan 29, 2025
c2316aa
reduce log verbosity, ethwaits index
magik6k Jan 29, 2025
15d5b1b
Nice tool for pdp upload of large files (#397)
ZenGround0 Feb 21, 2025
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
Prev Previous commit
Next Next commit
Delete pdp roots from proofset (#384)
* Core api and prove task logic changes

* Update pdptool to process deletes

* SQL ordering fix

* NoSQL just track removals on chain

* Log removals

* correct pdptool output

* correct delete api interface

* remove

* remove

* Update tasks/pdp/task_prove.go

Co-authored-by: Łukasz Magiera <magik6k@users.noreply.github.com>

* review response

* Fix up

---------

Co-authored-by: zenground0 <ZenGround0@users.noreply.github.com>
Co-authored-by: Łukasz Magiera <magik6k@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 28, 2025
commit 8a788b6edc9eda0460e0350caf6ce779b703abd5
74 changes: 74 additions & 0 deletions cmd/pdptool/main.go
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ func main() {
getProofSetCmd, // retrieve the details of a proof set from the PDP service

addRootsCmd,
removeRootsCmd, // schedule roots for removal after next proof submission
},
}
app.Setup()
@@ -992,3 +993,76 @@ var addRootsCmd = &cli.Command{
return nil
},
}

var removeRootsCmd = &cli.Command{
Name: "remove-roots",
Usage: "Schedule roots for removal after next proof submission",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "service-url",
Usage: "URL of the PDP service",
Required: true,
},
&cli.Uint64Flag{
Name: "proof-set-id",
Usage: "ID of the proof set to which roots will be added",
Required: true,
},
&cli.StringFlag{
Name: "service-name",
Usage: "Service Name to include in the JWT token",
Required: true,
},
&cli.Uint64Flag{
Name: "root-id",
Usage: "Root ID for removal",
Required: true,
},
},
Action: func(cctx *cli.Context) error {
serviceURL := cctx.String("service-url")
serviceName := cctx.String("service-name")
proofSetID := cctx.Uint64("proof-set-id")
rootID := cctx.Uint64("root-id")

// Load the private key (implement `loadPrivateKey` according to your setup)
privKey, err := loadPrivateKey()
if err != nil {
return fmt.Errorf("failed to load private key: %v", err)
}

// Create the JWT token (implement `createJWTToken` according to your setup)
jwtToken, err := createJWTToken(serviceName, privKey)
if err != nil {
return fmt.Errorf("failed to create JWT token: %v", err)
}

// Construct the POST URL
deleteURL := fmt.Sprintf("%s/pdp/proof-sets/%d/roots/%d", serviceURL, proofSetID, rootID)
fmt.Printf("Delete URL: %s\n", deleteURL)

// Create the POST request
req, err := http.NewRequest("DELETE", deleteURL, nil)
if err != nil {
return fmt.Errorf("failed to create request: %v", err)
}
req.Header.Set("Authorization", "Bearer "+jwtToken)
req.Header.Set("Content-Type", "application/json")

// Send the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send request: %v", err)
}

// Read and display the response
if resp.StatusCode == http.StatusNoContent {
fmt.Printf("Root %d scheduled for removal from proof set ID %d.\n", rootID, proofSetID)
} else {
return fmt.Errorf("failed to add roots, status code %d", resp.StatusCode)
}

return nil
},
}
9 changes: 9 additions & 0 deletions cmd/pdptool/pdp_cleanup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Clear tables in correct order (respecting foreign key constraints)
TRUNCATE TABLE curio.pdp_proof_sets CASCADE;
TRUNCATE TABLE curio.pdp_piecerefs CASCADE;
TRUNCATE TABLE curio.pdp_proofset_root_adds CASCADE;
TRUNCATE TABLE curio.pdp_proofset_roots CASCADE;
TRUNCATE TABLE curio.pdp_prove_tasks CASCADE;
TRUNCATE TABLE curio.pdp_proofset_creates CASCADE;
TRUNCATE TABLE curio.pdp_piece_uploads CASCADE;

148 changes: 123 additions & 25 deletions pdp/handlers.go
Original file line number Diff line number Diff line change
@@ -883,6 +883,129 @@ func (p *PDPService) handleAddRootToProofSet(w http.ResponseWriter, r *http.Requ
w.WriteHeader(http.StatusCreated)
}

func (p *PDPService) handleDeleteProofSetRoot(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Step 1: Verify that the request is authorized using ECDSA JWT
serviceLabel, err := p.verifyJWTToken(r)
if err != nil {
http.Error(w, "Unauthorized: "+err.Error(), http.StatusUnauthorized)
return
}

// Step 2: Extract parameters from the URL
proofSetIdStr := chi.URLParam(r, "proofSetID")
if proofSetIdStr == "" {
http.Error(w, "Missing proof set ID in URL", http.StatusBadRequest)
return
}
rootIdStr := chi.URLParam(r, "rootID")
if rootIdStr == "" {
http.Error(w, "Missing root ID in URL", http.StatusBadRequest)
return
}

// Convert proofSetId to uint64
proofSetID, err := strconv.ParseUint(proofSetIdStr, 10, 64)
if err != nil {
http.Error(w, "Invalid proof set ID format", http.StatusBadRequest)
return
}
rootID, err := strconv.ParseUint(rootIdStr, 10, 64)
if err != nil {
http.Error(w, "Invalid root ID format", http.StatusBadRequest)
return
}

// check if the proofset belongs to the service in pdp_proof_sets
var proofSetService string
err = p.db.QueryRow(ctx, `
SELECT service
FROM pdp_proof_sets
WHERE id = $1
`, proofSetID).Scan(&proofSetService)
if err != nil {
if err == pgx.ErrNoRows {
http.Error(w, "Proof set not found", http.StatusNotFound)
return
}
http.Error(w, "Failed to retrieve proof set: "+err.Error(), http.StatusInternalServerError)
return
}

if proofSetService != serviceLabel {
// same as when actually not found to avoid leaking information in obvious ways
http.Error(w, "Proof set not found", http.StatusNotFound)
return
}

// Get the ABI and pack the transaction data
abiData, err := contract.PDPVerifierMetaData.GetAbi()
if err != nil {
http.Error(w, "Failed to get contract ABI: "+err.Error(), http.StatusInternalServerError)
return
}

// Pack the method call data
data, err := abiData.Pack("scheduleRemovals",
big.NewInt(int64(proofSetID)),
[]*big.Int{big.NewInt(int64(rootID))},
[]byte{},
)
if err != nil {
http.Error(w, "Failed to pack method call: "+err.Error(), http.StatusInternalServerError)
return
}

// Get the sender address
fromAddress, err := p.getSenderAddress(ctx)
if err != nil {
http.Error(w, "Failed to get sender address: "+err.Error(), http.StatusInternalServerError)
return
}

// Prepare the transaction
ethTx := types.NewTransaction(
0, // nonce will be set by SenderETH
contract.ContractAddresses().PDPVerifier,
big.NewInt(0), // value
0, // gas limit (will be estimated)
nil, // gas price (will be set by SenderETH)
data,
)

// Send the transaction
reason := "pdp-delete-root"
txHash, err := p.sender.Send(ctx, fromAddress, ethTx, reason)
if err != nil {
http.Error(w, "Failed to send transaction: "+err.Error(), http.StatusInternalServerError)
log.Errorf("Failed to send transaction: %+v", err)
return
}

// Schedule deletion of the root from the proof set using a transaction
_, err = p.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) {
// Insert into message_waits_eth
_, err := tx.Exec(`
INSERT INTO message_waits_eth (signed_tx_hash, tx_status)
VALUES ($1, $2)
`, txHash.Hex(), "pending")
if err != nil {
return false, err
}

return true, nil
}, harmonydb.OptionRetry())

if err != nil {
http.Error(w, "Failed to schedule delete root: "+err.Error(), http.StatusInternalServerError)
return
}

// Return 204 No Content on successful deletion
w.WriteHeader(http.StatusNoContent)

}

func (p *PDPService) handleGetProofSetRoot(w http.ResponseWriter, r *http.Request) {
// Spec snippet:
// ### GET /proof-sets/{set id}/roots/{root id}
@@ -930,31 +1053,6 @@ func (p *PDPService) handleGetProofSetRoot(w http.ResponseWriter, r *http.Reques
}*/
}

func (p *PDPService) handleDeleteProofSetRoot(w http.ResponseWriter, r *http.Request) {
// Spec snippet:
// ### DEL /proof-sets/{set id}/roots/{root id}

proofSetIDStr := chi.URLParam(r, "proofSetID")
proofSetID, err := strconv.ParseInt(proofSetIDStr, 10, 64)
if err != nil {
http.Error(w, "Invalid proof set ID", http.StatusBadRequest)
return
}

rootIDStr := chi.URLParam(r, "rootID")
rootID, err := strconv.ParseInt(rootIDStr, 10, 64)
if err != nil {
http.Error(w, "Invalid root ID", http.StatusBadRequest)
return
}

_ = proofSetID
_ = rootID

// Respond with 204 No Content
w.WriteHeader(http.StatusNoContent)
}

// Data models corresponding to the updated schema

// PDPOwnerAddress represents the owner address with its private key
78 changes: 71 additions & 7 deletions tasks/pdp/task_prove.go
Original file line number Diff line number Diff line change
@@ -170,19 +170,19 @@ func (p *ProveTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done
}

pdpContracts := contract.ContractAddresses()
pdpServiceAddress := pdpContracts.PDPVerifier
pdpVerifierAddress := pdpContracts.PDPVerifier

pdpService, err := contract.NewPDPVerifier(pdpServiceAddress, p.ethClient)
pdpVerifier, err := contract.NewPDPVerifier(pdpVerifierAddress, p.ethClient)
if err != nil {
return false, xerrors.Errorf("failed to instantiate PDPVerifier contract at %s: %w", pdpServiceAddress.Hex(), err)
return false, xerrors.Errorf("failed to instantiate PDPVerifier contract at %s: %w", pdpVerifierAddress.Hex(), err)
}

callOpts := &bind.CallOpts{
Context: ctx,
}

// Proof parameters
challengeEpoch, err := pdpService.GetNextChallengeEpoch(callOpts, big.NewInt(proofSetID))
challengeEpoch, err := pdpVerifier.GetNextChallengeEpoch(callOpts, big.NewInt(proofSetID))
if err != nil {
return false, xerrors.Errorf("failed to get next challenge epoch: %w", err)
}
@@ -192,7 +192,7 @@ func (p *ProveTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done
return false, xerrors.Errorf("failed to get chain randomness from beacon for pdp prove: %w", err)
}

proofs, err := p.GenerateProofs(ctx, pdpService, proofSetID, seed, contract.NumChallenges)
proofs, err := p.GenerateProofs(ctx, pdpVerifier, proofSetID, seed, contract.NumChallenges)
if err != nil {
return false, xerrors.Errorf("failed to generate proofs: %w", err)
}
@@ -233,7 +233,7 @@ func (p *ProveTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done
// If gas used is 0 fee is maximized
gasFee := big.NewInt(0)
log.Infow("PDP Prove Task", "gasFeeEstimate", gasFee)
proofFee, err := pdpService.CalculateProofFee(callOpts, big.NewInt(proofSetID), gasFee)
proofFee, err := pdpVerifier.CalculateProofFee(callOpts, big.NewInt(proofSetID), gasFee)
if err != nil {
return false, xerrors.Errorf("failed to calculate proof fee: %w", err)
}
@@ -247,7 +247,6 @@ func (p *ProveTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done
if err != nil {
return false, xerrors.Errorf("failed to get sender address: %w", err)
}
pdpVerifierAddress := contract.ContractAddresses().PDPVerifier

// Prepare the transaction (nonce will be set to 0, SenderETH will assign it)
txEth := types.NewTransaction(
@@ -271,6 +270,12 @@ func (p *ProveTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done
return false, xerrors.Errorf("failed to send transaction: %w", err)
}

// Remove the roots previously scheduled for deletion
err = p.cleanupDeletedRoots(ctx, proofSetID, pdpVerifier)
if err != nil {
return false, xerrors.Errorf("failed to cleanup deleted roots: %w", err)
}

log.Infow("PDP Prove Task: transaction sent", "txHash", txHash, "proofSetID", proofSetID, "taskID", taskID)

// Task completed successfully
@@ -631,6 +636,65 @@ func (p *ProveTask) getSenderAddress(ctx context.Context) (common.Address, error
return address, nil
}

func (p *ProveTask) cleanupDeletedRoots(ctx context.Context, proofSetID int64, pdpVerifier *contract.PDPVerifier) error {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also the pdp_piecerefs table, and I'm not entirely sure what's the best way to approach this because:

  • On upload the piece is only referred in pdp_piecerefs (+the piecepark tables)
  • Only after upload is complete and data is verified the PDP service can add the piece to a proofset
  • Technically one piece can be in multiple proofsets / roots (as a subroot)
  • So probably:
    • Add a flag to pdp_piecerefs which is optionally settable through the delete api which tells curio to drop the piece reference when all pdp_proofset_roots are gone
    • Possibly add an endpoint which lists all pdp_proofset_roots for a given piece (so the service can know why a given piece may be "locked")


removals, err := pdpVerifier.GetScheduledRemovals(nil, big.NewInt(proofSetID))
if err != nil {
return xerrors.Errorf("failed to get scheduled removals: %w", err)
}

// Execute cleanup in a transaction
ok, err := p.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) {

for _, removeID := range removals {
log.Infow("cleanupDeletedRoots", "removeID", removeID)
// Get the pdp_pieceref ID for the root before deleting
var pdpPieceRefID int64
err := tx.QueryRow(`
SELECT pdp_pieceref
FROM pdp_proofset_roots
WHERE proofset = $1 AND root_id = $2
`, proofSetID, removeID.Int64()).Scan(&pdpPieceRefID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
// Root already deleted, skip
continue
}
return false, xerrors.Errorf("failed to get piece ref for root %d: %w", removeID, err)
}

// Delete the parked piece ref, this will cascade to the pdp piece ref too
_, err = tx.Exec(`
DELETE FROM parked_piece_refs
WHERE ref_id = $1
`, pdpPieceRefID)
if err != nil {
return false, xerrors.Errorf("failed to delete parked piece ref %d: %w", pdpPieceRefID, err)
}

// Delete the root entry
_, err = tx.Exec(`
DELETE FROM pdp_proofset_roots
WHERE proofset = $1 AND root_id = $2
`, proofSetID, removeID)
if err != nil {
return false, xerrors.Errorf("failed to delete root %d: %w", removeID, err)
}
}

return true, nil
}, harmonydb.OptionRetry())

if err != nil {
return xerrors.Errorf("failed to cleanup deleted roots: %w", err)
}
if !ok {
return xerrors.Errorf("database delete not committed")
}

return nil
}

func (p *ProveTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) {
if len(ids) == 0 {
return nil, nil