diff --git a/cmd/curio/tasks/tasks.go b/cmd/curio/tasks/tasks.go index 4b0d9855d..881e9b122 100644 --- a/cmd/curio/tasks/tasks.go +++ b/cmd/curio/tasks/tasks.go @@ -40,6 +40,7 @@ import ( "github.com/filecoin-project/curio/tasks/indexing" "github.com/filecoin-project/curio/tasks/message" "github.com/filecoin-project/curio/tasks/metadata" + "github.com/filecoin-project/curio/tasks/pdp" piece2 "github.com/filecoin-project/curio/tasks/piece" "github.com/filecoin-project/curio/tasks/scrub" "github.com/filecoin-project/curio/tasks/seal" @@ -98,7 +99,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps, shutdownChan chan machine := dependencies.ListenAddr prover := dependencies.Prover iStore := dependencies.IndexStore - pp := dependencies.PieceProvider + pp := dependencies.SectorReader var activeTasks []harmonytask.TaskInterface @@ -139,6 +140,24 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps, shutdownChan chan } } + // eth message sender as needed + var senderEth *message.SenderETH + var senderEthOnce sync.Once + var getSenderEth = func() *message.SenderETH { + senderEthOnce.Do(func() { + ec, err := dependencies.EthClient.Val() + if err != nil { + log.Errorw("failed to get eth client", "error", err) + return + } + + var ethSenderTask *message.SendTaskETH + senderEth, ethSenderTask = message.NewSenderETH(ec, db) + activeTasks = append(activeTasks, ethSenderTask) + }) + return senderEth + } + /////////////////////////////////////////////////////////////////////// ///// Task Selection /////////////////////////////////////////////////////////////////////// @@ -249,6 +268,22 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps, shutdownChan chan go libp2p.NewDealProvider(ctx, db, cfg, dm.MK12Handler, full, sender, miners, machine, shutdownChan) } + var sdeps cuhttp.ServiceDeps + + if cfg.Subsystems.EnablePDP { + es := getSenderEth() + sdeps.EthSender = es + + pdp.NewWatcherCreate(db, must.One(dependencies.EthClient.Val()), chainSched) + pdp.NewWatcherRootAdd(db, must.One(dependencies.EthClient.Val()), chainSched) + + pdpProveTask := pdp.NewProveTask(chainSched, db, must.One(dependencies.EthClient.Val()), dependencies.Chain, es, dependencies.CachedPieceReader) + pdpNextProvingPeriodTask := pdp.NewNextProvingPeriodTask(db, must.One(dependencies.EthClient.Val()), dependencies.Chain, chainSched, es) + pdpInitProvingPeriodTask := pdp.NewInitProvingPeriodTask(db, must.One(dependencies.EthClient.Val()), dependencies.Chain, chainSched, es) + pdpNotifTask := pdp.NewPDPNotifyTask(db) + activeTasks = append(activeTasks, pdpNotifTask, pdpProveTask, pdpNextProvingPeriodTask, pdpInitProvingPeriodTask) + } + idxMax := taskhelp.Max(cfg.Subsystems.IndexingMaxTasks) indexingTask := indexing.NewIndexingTask(db, sc, iStore, pp, cfg, idxMax) @@ -256,7 +291,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps, shutdownChan chan activeTasks = append(activeTasks, ipniTask, indexingTask) if cfg.HTTP.Enable { - err = cuhttp.StartHTTPServer(ctx, dependencies) + err = cuhttp.StartHTTPServer(ctx, dependencies, &sdeps) if err != nil { return nil, xerrors.Errorf("failed to start the HTTP server: %w", err) } @@ -290,7 +325,16 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps, shutdownChan chan _ = watcher } - if cfg.Subsystems.EnableWindowPost || hasAnySealingTask { + if senderEth != nil { + watcherEth, err := message.NewMessageWatcherEth(db, ht, chainSched, must.One(dependencies.EthClient.Val())) + if err != nil { + return nil, err + } + _ = watcherEth + + } + + if cfg.Subsystems.EnableWindowPost || hasAnySealingTask || senderEth != nil { go chainSched.Run(ctx) } @@ -374,7 +418,18 @@ func addSealingTasks( if cfg.Subsystems.EnableMoveStorage { moveStorageTask := seal.NewMoveStorageTask(sp, slr, db, cfg.Subsystems.MoveStorageMaxTasks) moveStorageSnapTask := snap.NewMoveStorageTask(slr, db, cfg.Subsystems.MoveStorageMaxTasks) - activeTasks = append(activeTasks, moveStorageTask, moveStorageSnapTask) + + storePieceTask, err := piece2.NewStorePieceTask(db, must.One(slrLazy.Val()), stor, cfg.Subsystems.MoveStorageMaxTasks) + if err != nil { + return nil, err + } + + activeTasks = append(activeTasks, moveStorageTask, moveStorageSnapTask, storePieceTask) + if !cfg.Subsystems.EnableParkPiece { + // add cleanup if it's not added above with park piece + cleanupPieceTask := piece2.NewCleanupPieceTask(db, must.One(slrLazy.Val()), 0) + activeTasks = append(activeTasks, cleanupPieceTask) + } if !cfg.Subsystems.NoUnsealedDecode { unsealTask := unseal.NewTaskUnsealDecode(slr, db, cfg.Subsystems.MoveStorageMaxTasks, full) diff --git a/cmd/pdptool/main.go b/cmd/pdptool/main.go new file mode 100644 index 000000000..629857952 --- /dev/null +++ b/cmd/pdptool/main.go @@ -0,0 +1,1068 @@ +package main + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/hex" + "encoding/json" + "encoding/pem" + "fmt" + "io" + "net" + "net/http" + "os" + "strconv" + "strings" + "time" + + "github.com/fatih/color" + "github.com/golang-jwt/jwt/v4" + "github.com/minio/sha256-simd" + "github.com/urfave/cli/v2" + + commcid "github.com/filecoin-project/go-fil-commcid" + commp "github.com/filecoin-project/go-fil-commp-hashhash" + + curiobuild "github.com/filecoin-project/curio/build" +) + +func main() { + app := &cli.App{ + Name: "pdptool", + Usage: "tool for testing PDP capabilities", + Version: curiobuild.UserVersion(), + Commands: []*cli.Command{ + authCreateServiceSecretCmd, // generates pdpservice.json, outputs pubkey + authCreateJWTTokenCmd, // generates jwt token from a secret + + pingCmd, + + piecePrepareCmd, // hash a piece to get a piece cid + pieceUploadCmd, // upload a piece to a pdp service + + createProofSetCmd, // create a new proof set on the PDP service + getProofSetStatusCmd, // get the status of a proof set creation on the PDP service + getProofSetCmd, // retrieve the details of a proof set from the PDP service + + addRootsCmd, + removeRootsCmd, // schedule roots for removal after next proof submission + }, + } + app.Setup() + + if err := app.Run(os.Args); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "error: %s\n", err) + os.Exit(1) + } +} + +var authCreateServiceSecretCmd = &cli.Command{ + Name: "create-service-secret", + Usage: "Generate a new service secret and public key", + Action: func(cctx *cli.Context) error { + // Generate an ECDSA private key + privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return fmt.Errorf("failed to generate private key: %v", err) + } + + // Serialize the private key to PEM + privBytes, err := x509.MarshalPKCS8PrivateKey(privKey) + if err != nil { + return fmt.Errorf("failed to marshal private key: %v", err) + } + privPEM := pem.EncodeToMemory(&pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: privBytes, + }) + + // Serialize the public key to PEM + pubBytes, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey) + if err != nil { + return fmt.Errorf("failed to marshal public key: %v", err) + } + pubPEM := pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Bytes: pubBytes, + }) + + // Save the private key to pdpservice.json + serviceSecret := map[string]string{ + "private_key": string(privPEM), + } + + file, err := os.OpenFile("pdpservice.json", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return fmt.Errorf("failed to open pdpservice.json for writing: %v", err) + } + defer file.Close() + encoder := json.NewEncoder(file) + if err := encoder.Encode(&serviceSecret); err != nil { + return fmt.Errorf("failed to write to pdpservice.json: %v", err) + } + + // Output the public key + fmt.Printf("Public Key:\n%s\n", pubPEM) + + return nil + }, +} + +var authCreateJWTTokenCmd = &cli.Command{ + Name: "create-jwt-token", + Usage: "Generate a JWT token using the service secret", + ArgsUsage: "[service_name]", + Action: func(cctx *cli.Context) error { + // Read the private key from pdpservice.json + privKey, err := loadPrivateKey() + if err != nil { + return err + } + + // Get the service name + serviceName := cctx.Args().First() + if serviceName == "" { + return fmt.Errorf("service_name argument is required") + } + + // Create JWT token using the common function + tokenString, err := createJWTToken(serviceName, privKey) + if err != nil { + return err + } + + // Output the token + fmt.Printf("JWT Token:\n%s\n", tokenString) + + return nil + }, +} + +var pingCmd = &cli.Command{ + Name: "ping", + Usage: "Ping the /pdp/ping endpoint of a PDP service", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "service-url", + Usage: "URL of the PDP service", + Required: true, + }, + &cli.StringFlag{ + Name: "service-name", + Usage: "Service Name to include in the JWT token", + }, + }, + Action: func(cctx *cli.Context) error { + serviceURL := cctx.String("service-url") + serviceName := cctx.String("service-name") + + if serviceName == "" { + return fmt.Errorf("either --jwt-token or --service-name must be provided") + } + privKey, err := loadPrivateKey() + if err != nil { + return err + } + var errCreateToken error + jwtToken, errCreateToken := createJWTToken(serviceName, privKey) + if errCreateToken != nil { + return errCreateToken + } + + // Append /pdp/ping to the service URL + pingURL := serviceURL + "/pdp/ping" + + // Create the GET request + req, err := http.NewRequest("GET", pingURL, nil) + if err != nil { + return fmt.Errorf("failed to create request: %v", err) + } + req.Header.Set("Authorization", "Bearer "+jwtToken) + + // Send the request + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + // Check the response + if resp.StatusCode == http.StatusOK { + color.Green("Ping successful: Service is reachable and JWT token is valid.") + } else { + body, _ := io.ReadAll(resp.Body) + return fmt.Errorf("ping failed with status code %d: %s", resp.StatusCode, string(body)) + } + + return nil + }, +} + +func createJWTToken(serviceName string, privateKey *ecdsa.PrivateKey) (string, error) { + // Create JWT claims + claims := jwt.MapClaims{ + "service_name": serviceName, + "exp": time.Now().Add(time.Hour * 24).Unix(), + } + + // Create the token + token := jwt.NewWithClaims(jwt.SigningMethodES256, claims) + + // Sign the token + tokenString, err := token.SignedString(privateKey) + if err != nil { + return "", fmt.Errorf("failed to sign token: %v", err) + } + + return tokenString, nil +} + +func loadPrivateKey() (*ecdsa.PrivateKey, error) { + file, err := os.Open("pdpservice.json") + if err != nil { + return nil, fmt.Errorf("failed to open pdpservice.json: %v", err) + } + defer file.Close() + var serviceSecret map[string]string + decoder := json.NewDecoder(file) + if err := decoder.Decode(&serviceSecret); err != nil { + return nil, fmt.Errorf("failed to read pdpservice.json: %v", err) + } + + privPEM := serviceSecret["private_key"] + block, _ := pem.Decode([]byte(privPEM)) + if block == nil { + return nil, fmt.Errorf("failed to parse private key PEM") + } + + // Parse the private key + privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse private key: %v", err) + } + ecdsaPrivKey, ok := privKey.(*ecdsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("private key is not ECDSA") + } + + return ecdsaPrivKey, nil +} + +var piecePrepareCmd = &cli.Command{ + Name: "prepare-piece", + Usage: "Compute the PieceCID of a file", + ArgsUsage: "", + Flags: []cli.Flag{}, + Action: func(cctx *cli.Context) error { + inputFile := cctx.Args().Get(0) + if inputFile == "" { + return fmt.Errorf("input file is required") + } + + // Open input file + file, err := os.Open(inputFile) + if err != nil { + return fmt.Errorf("failed to open input file: %v", err) + } + defer file.Close() + + // Get the piece size from flag or use file size + fi, err := file.Stat() + if err != nil { + return fmt.Errorf("failed to stat input file: %v", err) + } + + pieceSize := fi.Size() + + // Create commp calculator + cp := &commp.Calc{} + + // Copy data into commp calculator + _, err = io.Copy(cp, file) + if err != nil { + return fmt.Errorf("failed to read input file: %v", err) + } + + // Finalize digest + digest, paddedPieceSize, err := cp.Digest() + if err != nil { + return fmt.Errorf("failed to compute digest: %v", err) + } + + // Convert digest to CID + pieceCIDComputed, err := commcid.DataCommitmentV1ToCID(digest) + if err != nil { + return fmt.Errorf("failed to compute piece CID: %v", err) + } + + // now compute sha256 + if _, err := file.Seek(0, io.SeekStart); err != nil { + return fmt.Errorf("failed to seek file: %v", err) + } + + // Create commp calculator + h := sha256.New() + _, err = io.Copy(h, file) + if err != nil { + return fmt.Errorf("failed to read input file: %v", err) + } + + // Finalize digest + shadigest := h.Sum(nil) + + // Output the piece CID and size + fmt.Printf("Piece CID: %s\n", pieceCIDComputed) + fmt.Printf("SHA256: %x\n", shadigest) + fmt.Printf("Padded Piece Size: %d bytes\n", paddedPieceSize) + fmt.Printf("Raw Piece Size: %d bytes\n", pieceSize) + + return nil + }, +} + +var pieceUploadCmd = &cli.Command{ + Name: "upload-piece", + Usage: "Upload a piece to a PDP service", + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "service-url", + Usage: "URL of the PDP service", + Required: true, + }, + &cli.StringFlag{ + Name: "jwt-token", + Usage: "JWT token for authentication (optional if --service-name is provided)", + }, + &cli.StringFlag{ + Name: "service-name", + Usage: "Service Name to include in the JWT token (used if --jwt-token is not provided)", + }, + &cli.StringFlag{ + Name: "notify-url", + Usage: "Notification URL", + Required: false, + }, + &cli.StringFlag{ + Name: "hash-type", + Usage: "Hash type to use for verification (sha256 or commp)", + Value: "sha256", + }, + &cli.BoolFlag{ + Name: "local-notif-wait", + Usage: "Wait for server notification by spawning a temporary local HTTP server", + }, + }, + Action: func(cctx *cli.Context) error { + inputFile := cctx.Args().Get(0) + if inputFile == "" { + return fmt.Errorf("input file is required") + } + + serviceURL := cctx.String("service-url") + jwtToken := cctx.String("jwt-token") + notifyURL := cctx.String("notify-url") + serviceName := cctx.String("service-name") + hashType := cctx.String("hash-type") + localNotifWait := cctx.Bool("local-notif-wait") + + if jwtToken == "" { + if serviceName == "" { + return fmt.Errorf("either --jwt-token or --service-name must be provided") + } + privKey, err := loadPrivateKey() + if err != nil { + return err + } + var errCreateToken error + jwtToken, errCreateToken = createJWTToken(serviceName, privKey) + if errCreateToken != nil { + return errCreateToken + } + } + + if hashType != "sha256" && hashType != "commp" { + return fmt.Errorf("invalid hash type: %s", hashType) + } + + if localNotifWait && notifyURL != "" { + return fmt.Errorf("cannot specify both --notify-url and --local-notif-wait") + } + + var notifyReceived chan struct{} + var server *http.Server + var ln net.Listener + + if localNotifWait { + notifyReceived = make(chan struct{}) + var err error + ln, err = net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return fmt.Errorf("failed to start local HTTP server: %v", err) + } + serverAddr := fmt.Sprintf("http://%s/notify", ln.Addr().String()) + notifyURL = serverAddr + + mux := http.NewServeMux() + mux.HandleFunc("/notify", func(w http.ResponseWriter, r *http.Request) { + fmt.Println("Received notification from server.") + b, err := io.ReadAll(r.Body) + if err != nil { + fmt.Printf("Failed to read notification body: %v\n", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + fmt.Printf("Notification body: %s\n", string(b)) + w.WriteHeader(http.StatusOK) + // Signal that notification was received + close(notifyReceived) + }) + + server = &http.Server{Handler: mux} + + go func() { + if err := server.Serve(ln); err != nil && err != http.ErrServerClosed { + fmt.Printf("HTTP server error: %v\n", err) + } + }() + + defer func() { + server.Close() + ln.Close() + }() + } + + // Open input file + file, err := os.Open(inputFile) + if err != nil { + return fmt.Errorf("failed to open input file: %v", err) + } + defer file.Close() + + // Get the piece size + fi, err := file.Stat() + if err != nil { + return fmt.Errorf("failed to stat input file: %v", err) + } + pieceSize := fi.Size() + + // Compute CommP (PieceCID) + cp := &commp.Calc{} + _, err = io.Copy(cp, file) + if err != nil { + return fmt.Errorf("failed to read input file: %v", err) + } + + commpDigest, _, err := cp.Digest() + if err != nil { + return fmt.Errorf("failed to compute digest: %v", err) + } + + // Compute SHA256 + if _, err := file.Seek(0, io.SeekStart); err != nil { + return fmt.Errorf("failed to seek file: %v", err) + } + + h := sha256.New() + _, err = io.Copy(h, file) + if err != nil { + return fmt.Errorf("failed to read input file: %v", err) + } + + shadigest := h.Sum(nil) + + // Prepare the check data + var checkData map[string]interface{} + + switch hashType { + case "sha256": + checkData = map[string]interface{}{ + "name": "sha2-256", + "hash": hex.EncodeToString(shadigest), + "size": pieceSize, + } + case "commp": + hashHex := hex.EncodeToString(commpDigest) + checkData = map[string]interface{}{ + "name": "sha2-256-trunc254-padded", + "hash": hashHex, + "size": pieceSize, + } + default: + return fmt.Errorf("unsupported hash type: %s", hashType) + } + + // Prepare the request data + reqData := map[string]interface{}{ + "check": checkData, + } + if notifyURL != "" { + reqData["notify"] = notifyURL + } + reqBody, err := json.Marshal(reqData) + if err != nil { + return fmt.Errorf("failed to marshal request data: %v", err) + } + + client := &http.Client{} + + req, err := http.NewRequest("POST", serviceURL+"/pdp/piece", bytes.NewReader(reqBody)) + 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") + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + // Piece already exists, get the pieceCID from the response + var respData map[string]string + err = json.NewDecoder(resp.Body).Decode(&respData) + if err != nil { + return fmt.Errorf("failed to parse response: %v", err) + } + pieceCID := respData["pieceCID"] + fmt.Printf("Piece already exists on the server. Piece CID: %s\n", pieceCID) + return nil + } else if resp.StatusCode == http.StatusCreated { + // Get the upload URL from the Location header + uploadURL := resp.Header.Get("Location") + if uploadURL == "" { + return fmt.Errorf("server did not provide upload URL in Location header") + } + + // Upload the piece data via PUT + if _, err := file.Seek(0, io.SeekStart); err != nil { + return fmt.Errorf("failed to seek file: %v", err) + } + uploadReq, err := http.NewRequest("PUT", serviceURL+uploadURL, file) + if err != nil { + return fmt.Errorf("failed to create upload request: %v", err) + } + // Set the Content-Length header + uploadReq.ContentLength = pieceSize + // Set the Content-Type header + uploadReq.Header.Set("Content-Type", "application/octet-stream") + + uploadResp, err := client.Do(uploadReq) + if err != nil { + return fmt.Errorf("failed to upload piece data: %v", err) + } + defer uploadResp.Body.Close() + + if uploadResp.StatusCode != http.StatusNoContent { + body, _ := io.ReadAll(uploadResp.Body) + return fmt.Errorf("upload failed with status code %d: %s", uploadResp.StatusCode, string(body)) + } + + fmt.Println("Piece uploaded successfully.") + + if localNotifWait { + fmt.Println("Waiting for server notification...") + <-notifyReceived + } + + return nil + } else { + body, _ := io.ReadAll(resp.Body) + return fmt.Errorf("server returned status code %d: %s", resp.StatusCode, string(body)) + } + }, +} + +var createProofSetCmd = &cli.Command{ + Name: "create-proof-set", + Usage: "Create a new proof set on the PDP service", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "service-url", + Usage: "URL of the PDP service", + Required: true, + }, + &cli.StringFlag{ + Name: "recordkeeper", + Usage: "Address of the record keeper contract", + Required: true, + }, + &cli.StringFlag{ + Name: "service-name", + Usage: "Service Name to include in the JWT token", + Required: true, + }, + }, + Action: func(cctx *cli.Context) error { + serviceURL := cctx.String("service-url") + serviceName := cctx.String("service-name") + recordKeeper := cctx.String("recordkeeper") + + // 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 request payload + requestBody := map[string]string{ + "recordKeeper": recordKeeper, + } + requestBodyBytes, err := json.Marshal(requestBody) + if err != nil { + return fmt.Errorf("failed to marshal request body: %v", err) + } + + // Append /pdp/proof-sets to the service URL + postURL := serviceURL + "/pdp/proof-sets" + + // Create the POST request + req, err := http.NewRequest("POST", postURL, bytes.NewBuffer(requestBodyBytes)) + 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) + } + defer resp.Body.Close() + + // Read and display the response + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %v", err) + } + bodyString := string(bodyBytes) + + if resp.StatusCode == http.StatusCreated { + location := resp.Header.Get("Location") + fmt.Printf("Proof set creation initiated successfully.\n") + fmt.Printf("Location: %s\n", location) + fmt.Printf("Response: %s\n", bodyString) + } else { + return fmt.Errorf("failed to create proof set, status code %d: %s", resp.StatusCode, bodyString) + } + + return nil + }, +} + +var getProofSetStatusCmd = &cli.Command{ + Name: "get-proof-set-create-status", + Usage: "Get the status of a proof set creation on the PDP service", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "service-url", + Usage: "URL of the PDP service", + Required: true, + }, + &cli.StringFlag{ + Name: "tx-hash", + Usage: "Transaction hash of the proof set creation", + Required: true, + }, + &cli.StringFlag{ + Name: "service-name", + Usage: "Service Name to include in the JWT token", + Required: true, + }, + }, + Action: func(cctx *cli.Context) error { + serviceURL := cctx.String("service-url") + serviceName := cctx.String("service-name") + txHash := cctx.String("tx-hash") + + // Load the private key + privKey, err := loadPrivateKey() + if err != nil { + return fmt.Errorf("failed to load private key: %v", err) + } + + // Create the JWT token + jwtToken, err := createJWTToken(serviceName, privKey) + if err != nil { + return fmt.Errorf("failed to create JWT token: %v", err) + } + + // Ensure txHash starts with '0x' + if !strings.HasPrefix(txHash, "0x") { + txHash = "0x" + txHash + } + txHash = strings.ToLower(txHash) // Ensure txHash is in lowercase + + // Construct the request URL + getURL := fmt.Sprintf("%s/pdp/proof-sets/created/%s", serviceURL, txHash) + + // Create the GET request + req, err := http.NewRequest("GET", getURL, nil) + if err != nil { + return fmt.Errorf("failed to create request: %v", err) + } + req.Header.Set("Authorization", "Bearer "+jwtToken) + + // Send the request + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + // Read and process the response + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %v", err) + } + + if resp.StatusCode == http.StatusOK { + // Decode the JSON response + var response struct { + CreateMessageHash string `json:"createMessageHash"` + ProofsetCreated bool `json:"proofsetCreated"` + Service string `json:"service"` + TxStatus string `json:"txStatus"` + OK *bool `json:"ok"` + ProofSetId *uint64 `json:"proofSetId,omitempty"` + } + err = json.Unmarshal(bodyBytes, &response) + if err != nil { + return fmt.Errorf("failed to parse JSON response: %v", err) + } + + // Display the status + fmt.Printf("Proof Set Creation Status:\n") + fmt.Printf("Transaction Hash: %s\n", response.CreateMessageHash) + fmt.Printf("Transaction Status: %s\n", response.TxStatus) + if response.OK != nil { + fmt.Printf("Transaction Successful: %v\n", *response.OK) + } else { + fmt.Printf("Transaction Successful: Pending\n") + } + fmt.Printf("Proofset Created: %v\n", response.ProofsetCreated) + if response.ProofSetId != nil { + fmt.Printf("ProofSet ID: %d\n", *response.ProofSetId) + } + } else { + return fmt.Errorf("failed to get proof set status, status code %d: %s", resp.StatusCode, string(bodyBytes)) + } + + return nil + }, +} + +var getProofSetCmd = &cli.Command{ + Name: "get-proof-set", + Usage: "Retrieve the details of a proof set from the PDP service", + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "service-url", + Usage: "URL of the PDP service", + Required: true, + }, + &cli.StringFlag{ + Name: "service-name", + Usage: "Service Name to include in the JWT token", + Required: true, + }, + }, + Action: func(cctx *cli.Context) error { + // Parse arguments + setIDStr := cctx.Args().Get(0) + if setIDStr == "" { + return fmt.Errorf("set-id argument is required") + } + + // Parse setID to uint64 + setID, err := strconv.ParseUint(setIDStr, 10, 64) + if err != nil { + return fmt.Errorf("invalid set-id format: %v", err) + } + + serviceURL := cctx.String("service-url") + serviceName := cctx.String("service-name") + + // Load the private key + privKey, err := loadPrivateKey() + if err != nil { + return fmt.Errorf("failed to load private key: %v", err) + } + + // Create the JWT token + jwtToken, err := createJWTToken(serviceName, privKey) + if err != nil { + return fmt.Errorf("failed to create JWT token: %v", err) + } + + // Construct the request URL + getURL := fmt.Sprintf("%s/pdp/proof-sets/%d", serviceURL, setID) + + // Create the GET request + req, err := http.NewRequest("GET", getURL, nil) + if err != nil { + return fmt.Errorf("failed to create request: %v", err) + } + req.Header.Set("Authorization", "Bearer "+jwtToken) + + // Send the request + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + // Read and process the response + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %v", err) + } + + if resp.StatusCode == http.StatusOK { + // Decode the JSON response + var response struct { + ID uint64 `json:"id"` + NextChallengeEpoch int64 `json:"nextChallengeEpoch"` + Roots []struct { + RootID uint64 `json:"rootId"` + RootCID string `json:"rootCid"` + SubrootCID string `json:"subrootCid"` + SubrootOffset int64 `json:"subrootOffset"` + } `json:"roots"` + } + err = json.Unmarshal(bodyBytes, &response) + if err != nil { + return fmt.Errorf("failed to parse JSON response: %v", err) + } + + // Display the proof set details + fmt.Printf("Proof Set ID: %d\n", response.ID) + fmt.Printf("Next Challenge Epoch: %d\n", response.NextChallengeEpoch) + fmt.Printf("Roots:\n") + for _, root := range response.Roots { + fmt.Printf(" - Root ID: %d\n", root.RootID) + fmt.Printf(" Root CID: %s\n", root.RootCID) + fmt.Printf(" Subroot CID: %s\n", root.SubrootCID) + fmt.Printf(" Subroot Offset: %d\n", root.SubrootOffset) + fmt.Println() + } + } else { + return fmt.Errorf("failed to get proof set, status code %d: %s", resp.StatusCode, string(bodyBytes)) + } + + return nil + }, +} + +var addRootsCmd = &cli.Command{ + Name: "add-roots", + Usage: "Add roots to a proof set on the PDP service", + 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.StringSliceFlag{ + Name: "root", + Usage: "Root CID and its subroots. Format: rootCID:subrootCID1+subrootCID2,...", + Required: true, + }, + }, + Action: func(cctx *cli.Context) error { + serviceURL := cctx.String("service-url") + serviceName := cctx.String("service-name") + proofSetID := cctx.Uint64("proof-set-id") + rootInputs := cctx.StringSlice("root") + + // 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) + } + + // Parse the root inputs to construct the request payload + type SubrootEntry struct { + SubrootCID string `json:"subrootCid"` + } + + type AddRootRequest struct { + RootCID string `json:"rootCid"` + Subroots []SubrootEntry `json:"subroots"` + } + + var addRootRequests []AddRootRequest + + for _, rootInput := range rootInputs { + // Expected format: rootCID:subrootCID1,subrootCID2,... + parts := strings.SplitN(rootInput, ":", 2) + if len(parts) != 2 { + return fmt.Errorf("invalid root input format: %s (%d)", rootInput, len(parts)) + } + rootCID := parts[0] + subrootsStr := parts[1] + subrootCIDStrs := strings.Split(subrootsStr, "+") + + if rootCID == "" || len(subrootCIDStrs) == 0 { + return fmt.Errorf("rootCID and at least one subrootCID are required") + } + + var subroots []SubrootEntry + for _, subrootCID := range subrootCIDStrs { + subroots = append(subroots, SubrootEntry{SubrootCID: subrootCID}) + } + + addRootRequests = append(addRootRequests, AddRootRequest{ + RootCID: rootCID, + Subroots: subroots, + }) + } + + // Construct the request payload + requestBodyBytes, err := json.Marshal(addRootRequests) + if err != nil { + return fmt.Errorf("failed to marshal request body: %v", err) + } + + // Construct the POST URL + postURL := fmt.Sprintf("%s/pdp/proof-sets/%d/roots", serviceURL, proofSetID) + + // Create the POST request + req, err := http.NewRequest("POST", postURL, bytes.NewBuffer(requestBodyBytes)) + 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) + } + defer resp.Body.Close() + + // Read and display the response + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %v", err) + } + bodyString := string(bodyBytes) + + if resp.StatusCode == http.StatusCreated { + fmt.Printf("Roots added to proof set ID %d successfully.\n", proofSetID) + fmt.Printf("Response: %s\n", bodyString) + } else { + return fmt.Errorf("failed to add roots, status code %d: %s", resp.StatusCode, bodyString) + } + + 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 + }, +} diff --git a/cmd/pdptool/pdp_cleanup.sql b/cmd/pdptool/pdp_cleanup.sql new file mode 100644 index 000000000..7d7b0073a --- /dev/null +++ b/cmd/pdptool/pdp_cleanup.sql @@ -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; + diff --git a/cuhttp/server.go b/cuhttp/server.go index fc880ed67..de3477667 100644 --- a/cuhttp/server.go +++ b/cuhttp/server.go @@ -13,6 +13,7 @@ import ( "github.com/go-chi/chi/v5/middleware" "github.com/gorilla/handlers" logging "github.com/ipfs/go-log/v2" + "github.com/snadrus/must" "github.com/yugabyte/pgx/v5" "golang.org/x/crypto/acme/autocert" "golang.org/x/xerrors" @@ -23,6 +24,8 @@ import ( ipni_provider "github.com/filecoin-project/curio/market/ipni/ipni-provider" "github.com/filecoin-project/curio/market/libp2p" "github.com/filecoin-project/curio/market/retrieval" + "github.com/filecoin-project/curio/pdp" + "github.com/filecoin-project/curio/tasks/message" ) var log = logging.Logger("cu-http") @@ -102,7 +105,11 @@ func isWebSocketUpgrade(r *http.Request) bool { return true } -func StartHTTPServer(ctx context.Context, d *deps.Deps) error { +type ServiceDeps struct { + EthSender *message.SenderETH +} + +func StartHTTPServer(ctx context.Context, d *deps.Deps, sd *ServiceDeps) error { cfg := d.Cfg.HTTP // Setup the Chi router for more complex routing (if needed in the future) @@ -143,7 +150,7 @@ func StartHTTPServer(ctx context.Context, d *deps.Deps) error { fmt.Fprintf(w, "Service is up and running") }) - chiRouter, err = attachRouters(ctx, chiRouter, d) + chiRouter, err = attachRouters(ctx, chiRouter, d, sd) if err != nil { return xerrors.Errorf("failed to attach routers: %w", err) } @@ -222,6 +229,7 @@ func (c cache) Put(ctx context.Context, key string, data []byte) error { _, err := c.db.Exec(ctx, `INSERT INTO autocert_cache (k, v) VALUES ($1, $2) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v`, key, data) if err != nil { + log.Warnf("failed to inset key value pair in DB: %s", err) return xerrors.Errorf("failed to inset key value pair in DB: %w", err) } return nil @@ -230,6 +238,7 @@ func (c cache) Put(ctx context.Context, key string, data []byte) error { func (c cache) Delete(ctx context.Context, key string) error { _, err := c.db.Exec(ctx, `DELETE FROM autocert_cache WHERE k = $1`, key) if err != nil { + log.Warnf("failed to delete key value pair from DB: %s", err) return xerrors.Errorf("failed to delete key value pair from DB: %w", err) } return nil @@ -237,7 +246,7 @@ func (c cache) Delete(ctx context.Context, key string) error { var _ autocert.Cache = cache{} -func attachRouters(ctx context.Context, r *chi.Mux, d *deps.Deps) (*chi.Mux, error) { +func attachRouters(ctx context.Context, r *chi.Mux, d *deps.Deps, sd *ServiceDeps) (*chi.Mux, error) { // Attach retrievals rp := retrieval.NewRetrievalProvider(ctx, d.DB, d.IndexStore, d.CachedPieceReader) retrieval.Router(r, rp) @@ -255,5 +264,10 @@ func attachRouters(ctx context.Context, r *chi.Mux, d *deps.Deps) (*chi.Mux, err rd := libp2p.NewRedirector(d.DB) libp2p.Router(r, rd) + if sd.EthSender != nil { + pdsvc := pdp.NewPDPService(d.DB, d.LocalStore, must.One(d.EthClient.Get()), d.Chain, sd.EthSender) + pdp.Routes(r, pdsvc) + } + return r, nil } diff --git a/deps/config/doc_gen.go b/deps/config/doc_gen.go index 2cd96a3e5..73be7d177 100644 --- a/deps/config/doc_gen.go +++ b/deps/config/doc_gen.go @@ -759,6 +759,14 @@ also be bounded by resources available on the machine.`, Comment: `EnableDealMarket enabled the deal market on the node. This would also enable libp2p on the node, if configured.`, }, + { + Name: "EnablePDP", + Type: "bool", + + Comment: `Enable handling for PDP (proof-of-data possession) deals / proving on this node. +PDP deals allow the node to directly store and prove unsealed data with "PDP Services" like Storacha. +This feature is BETA and should only be enabled on nodes which are part of a PDP network.`, + }, { Name: "EnableCommP", Type: "bool", diff --git a/deps/config/types.go b/deps/config/types.go index 13191d80d..6b71fb5e1 100644 --- a/deps/config/types.go +++ b/deps/config/types.go @@ -330,6 +330,11 @@ type CurioSubsystemsConfig struct { // EnableDealMarket enabled the deal market on the node. This would also enable libp2p on the node, if configured. EnableDealMarket bool + // Enable handling for PDP (proof-of-data possession) deals / proving on this node. + // PDP deals allow the node to directly store and prove unsealed data with "PDP Services" like Storacha. + // This feature is BETA and should only be enabled on nodes which are part of a PDP network. + EnablePDP bool + // EnableCommP enables the commP task on te node. CommP is calculated before sending PublishDealMessage for a Mk12 deal // Must have EnableDealMarket = True EnableCommP bool diff --git a/deps/deps.go b/deps/deps.go index 7613cff13..a3dda4ab2 100644 --- a/deps/deps.go +++ b/deps/deps.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/BurntSushi/toml" + "github.com/ethereum/go-ethereum/ethclient" "github.com/gbrlsnchs/jwt/v3" logging "github.com/ipfs/go-log/v2" "github.com/samber/lo" @@ -47,6 +48,7 @@ import ( lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/lazy" "github.com/filecoin-project/lotus/node/modules/dtypes" lrepo "github.com/filecoin-project/lotus/node/repo" "github.com/filecoin-project/lotus/storage/sealer" @@ -189,9 +191,10 @@ type Deps struct { MachineID *int64 Alert *alertmanager.AlertNow IndexStore *indexstore.IndexStore - PieceProvider *pieceprovider.PieceProvider + SectorReader *pieceprovider.SectorReader CachedPieceReader *cachedreader.CachedPieceReader ServeChunker *chunker.ServeChunker + EthClient *lazy.Lazy[*ethclient.Client] } const ( @@ -276,6 +279,13 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, }() } + if deps.EthClient == nil { + deps.EthClient = lazy.MakeLazy[*ethclient.Client](func() (*ethclient.Client, error) { + // todo: this is a hack, just use the lotus chain api client above + return ethclient.Dial("https://api.calibration.node.glif.io/rpc/v1") + }) + } + if deps.Bstore == nil { deps.Bstore = curiochain.NewChainBlockstore(deps.Chain) } @@ -309,7 +319,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, deps.Cfg.Subsystems.GuiAddress = cctx.String("gui-listen") } if deps.LocalStore == nil { - deps.LocalStore, err = paths.NewLocal(ctx, deps.LocalPaths, deps.Si, []string{"http://" + deps.ListenAddr + "/remote"}) + deps.LocalStore, err = paths.NewLocal(ctx, deps.LocalPaths, deps.Si, "http://"+deps.ListenAddr+"/remote") if err != nil { return err } @@ -370,16 +380,17 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, } } - if deps.PieceProvider == nil { - deps.PieceProvider = pieceprovider.NewPieceProvider(deps.Stor, deps.Si) + if deps.SectorReader == nil { + deps.SectorReader = pieceprovider.NewSectorReader(deps.Stor, deps.Si) } if deps.CachedPieceReader == nil { - deps.CachedPieceReader = cachedreader.NewCachedPieceReader(deps.DB, deps.PieceProvider) + ppr := pieceprovider.NewPieceParkReader(deps.Stor, deps.Si) + deps.CachedPieceReader = cachedreader.NewCachedPieceReader(deps.DB, deps.SectorReader, ppr) } if deps.ServeChunker == nil { - deps.ServeChunker = chunker.NewServeChunker(deps.DB, deps.PieceProvider, deps.IndexStore, deps.CachedPieceReader) + deps.ServeChunker = chunker.NewServeChunker(deps.DB, deps.SectorReader, deps.IndexStore, deps.CachedPieceReader) } if deps.Prover == nil { diff --git a/documentation/en/configuration/default-curio-configuration.md b/documentation/en/configuration/default-curio-configuration.md index 290055782..339d4c227 100644 --- a/documentation/en/configuration/default-curio-configuration.md +++ b/documentation/en/configuration/default-curio-configuration.md @@ -234,6 +234,13 @@ description: The default curio configuration # type: bool #EnableDealMarket = false + # Enable handling for PDP (proof-of-data possession) deals / proving on this node. + # PDP deals allow the node to directly store and prove unsealed data with "PDP Services" like Storacha. + # This feature is BETA and should only be enabled on nodes which are part of a PDP network. + # + # type: bool + #EnablePDP = false + # EnableCommP enables the commP task on te node. CommP is calculated before sending PublishDealMessage for a Mk12 deal # Must have EnableDealMarket = True # diff --git a/go.mod b/go.mod index c40e5d5e1..72e005753 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/elastic/go-sysinfo v1.7.0 github.com/etclabscore/go-openrpc-reflect v0.0.36 + github.com/ethereum/go-ethereum v1.14.10 github.com/fatih/color v1.16.0 github.com/filecoin-project/filecoin-ffi v1.31.0 github.com/filecoin-project/go-address v1.2.0 @@ -41,6 +42,7 @@ require ( github.com/gbrlsnchs/jwt/v3 v3.0.1 github.com/georgysavva/scany/v2 v2.1.3 github.com/go-chi/chi/v5 v5.1.0 + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 @@ -87,7 +89,7 @@ require ( github.com/puzpuzpuz/xsync/v2 v2.4.0 github.com/raulk/clock v1.1.0 github.com/samber/lo v1.39.0 - github.com/sirupsen/logrus v1.9.2 + github.com/sirupsen/logrus v1.9.3 github.com/snadrus/must v0.0.0-20240605044437-98cedd57f8eb github.com/stretchr/testify v1.10.0 github.com/triplewz/poseidon v0.0.2 @@ -114,6 +116,7 @@ require ( github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee // indirect github.com/Jorropo/jsync v1.0.1 // indirect github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect @@ -126,6 +129,7 @@ require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -137,10 +141,13 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/cskr/pubsub v1.0.2 // indirect github.com/daaku/go.zipexe v1.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect @@ -152,6 +159,8 @@ require ( github.com/elastic/go-windows v1.0.0 // indirect github.com/elastic/gosigar v0.14.3 // indirect github.com/etclabscore/go-jsonschema-walk v0.0.6 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/filecoin-project/go-amt-ipld/v2 v2.1.0 // indirect github.com/filecoin-project/go-amt-ipld/v3 v3.1.0 // indirect @@ -171,13 +180,14 @@ require ( github.com/filecoin-project/specs-actors/v8 v8.0.1 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/gdamore/tcell/v2 v2.2.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.3 // indirect github.com/go-openapi/jsonreference v0.19.4 // indirect github.com/go-openapi/spec v0.19.11 // indirect @@ -188,7 +198,7 @@ require ( github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect @@ -198,6 +208,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect + github.com/holiman/uint256 v1.3.1 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/iancoleman/orderedmap v0.1.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect @@ -310,9 +321,10 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect - github.com/tklauser/go-sysconf v0.3.5 // indirect - github.com/tklauser/numcpus v0.2.2 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/twmb/murmur3 v1.1.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.0.1 // indirect diff --git a/go.sum b/go.sum index 79f9b7d5c..34b771223 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/CAFxX/httpcompression v0.0.9 h1:0ue2X8dOLEpxTm8tt+OdHcgA+gbDge0OqFQWGKSqgrg= github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOgYygkFhizVPilM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.3 h1:k5viR+xGtIhF61125vCE1cmJ5957RQGXG6dmbaWZSmI= @@ -64,6 +66,8 @@ github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa/go.mod h1:WUmMvh9wMtq github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -75,6 +79,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= @@ -124,8 +130,8 @@ github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcug github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= github.com/btcsuite/btcd v0.24.0/go.mod h1:K4IDc1593s8jKXIF7yS7yCTSxrknB9z0STzc2j6XgE4= -github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= -github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= @@ -146,6 +152,8 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -172,6 +180,18 @@ github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs= github.com/cockroachdb/cockroach-go/v2 v2.2.0/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= @@ -200,6 +220,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lV github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 h1:ZFUue+PNxmHlu7pYv+IYMtqlaO/0VwaGEqKepZf9JpA= github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= github.com/daaku/go.zipexe v1.0.2 h1:Zg55YLYTr7M9wjKn8SY/WcpuuEi+kR2u4E8RhvpyXmk= @@ -210,6 +234,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= @@ -260,6 +286,12 @@ github.com/etclabscore/go-jsonschema-walk v0.0.6 h1:DrNzoKWKd8f8XB5nFGBY00IcjakR github.com/etclabscore/go-jsonschema-walk v0.0.6/go.mod h1:VdfDY72AFAiUhy0ZXEaWSpveGjMT5JcDIm903NGqFwQ= github.com/etclabscore/go-openrpc-reflect v0.0.36 h1:kSqNB2U8RVoW4si+4fsv13NGNkRAQ5j78zTUx1qiehk= github.com/etclabscore/go-openrpc-reflect v0.0.36/go.mod h1:0404Ky3igAasAOpyj1eESjstTyneBAIk5PgJFbK4s5E= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.14.10 h1:kC24WjYeRjDy86LVo6MfF5Xs7nnUu+XG4AjaYIaZYko= +github.com/ethereum/go-ethereum v1.14.10/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= @@ -373,6 +405,8 @@ github.com/gammazero/channelqueue v0.2.2 h1:ufNzIbeDBxNfHj0m5uwUfOwvTmHF/O40hu2Z github.com/gammazero/channelqueue v0.2.2/go.mod h1:824o5HHE+yO1xokh36BIuSv8YWwXW0364ku91eRMFS4= github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gbrlsnchs/jwt/v3 v3.0.1 h1:lbUmgAKpxnClrKloyIwpxm4OuWeDl5wLk52G91ODPw4= github.com/gbrlsnchs/jwt/v3 v3.0.1/go.mod h1:AncDcjXz18xetI3A6STfXq2w+LuTx8pQ8bGEwRN8zVM= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= @@ -381,6 +415,8 @@ github.com/gdamore/tcell/v2 v2.2.0 h1:vSyEgKwraXPSOkvCk7IwOSyX+Pv3V2cV9CikJMXg4U github.com/gdamore/tcell/v2 v2.2.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= github.com/georgysavva/scany/v2 v2.1.3 h1:Zd4zm/ej79Den7tBSU2kaTDPAH64suq4qlQdhiBeGds= github.com/georgysavva/scany/v2 v2.1.3/go.mod h1:fqp9yHZzM/PFVa3/rYEC57VmDx+KDch0LoqrJzkvtos= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= @@ -410,8 +446,8 @@ github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -446,6 +482,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= @@ -486,8 +524,9 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k= github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -509,6 +548,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -566,6 +607,8 @@ github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e/go.mod h1:I github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -583,6 +626,12 @@ github.com/hashicorp/golang-lru/arc/v2 v2.0.7/go.mod h1:Pe7gBlGdc8clY5LJ0LpJXMt5 github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= @@ -999,6 +1048,10 @@ github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= @@ -1073,6 +1126,8 @@ github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOW github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -1220,6 +1275,8 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -1260,8 +1317,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= -github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= @@ -1288,6 +1345,8 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -1310,6 +1369,8 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= @@ -1321,14 +1382,16 @@ github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/triplewz/poseidon v0.0.2 h1:s5QMVYnUfqvgM1eIqp7O9hHjZLVrKnkhx0E7EQTf9Nk= github.com/triplewz/poseidon v0.0.2/go.mod h1:fmoxtMcbtMUjlSJmpuS3Wk/oKSvdJpIp9YWRbsOu3T0= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= @@ -1693,7 +1756,6 @@ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1708,7 +1770,9 @@ golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1928,6 +1992,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/harmony/harmonydb/sql/20240228-piece-park.sql b/harmony/harmonydb/sql/20240228-piece-park.sql index b24ba1b9d..b4fbfeffa 100644 --- a/harmony/harmonydb/sql/20240228-piece-park.sql +++ b/harmony/harmonydb/sql/20240228-piece-park.sql @@ -2,7 +2,7 @@ create table parked_pieces ( id bigserial primary key, created_at timestamp default current_timestamp, - piece_cid text not null, + piece_cid text not null, -- v1 piece_padded_size bigint not null, piece_raw_size bigint not null, @@ -11,11 +11,14 @@ create table parked_pieces ( cleanup_task_id bigint default null, + -- long_term boolean not null default false, -- Added in 20240930-pdp.sql + -- NOTE: Following keys were dropped in 20240507-sdr-pipeline-fk-drop.sql foreign key (task_id) references harmony_task (id) on delete set null, -- dropped foreign key (cleanup_task_id) references harmony_task (id) on delete set null, -- dropped - unique (piece_cid) + unique (piece_cid) -- dropped in 20240930-pdp.sql + -- unique (piece_cid, piece_padded_size, long_term, cleanup_task_id) -- Added in 20240930-pdp.sql ); /* @@ -41,5 +44,7 @@ create table parked_piece_refs ( data_url text, data_headers jsonb not null default '{}', + -- long_term boolean not null default false, -- Added in 20240930-pdp.sql + foreign key (piece_id) references parked_pieces(id) on delete cascade ); diff --git a/harmony/harmonydb/sql/20240929-chain-sends-eth.sql b/harmony/harmonydb/sql/20240929-chain-sends-eth.sql new file mode 100644 index 000000000..2d3dc7cbf --- /dev/null +++ b/harmony/harmonydb/sql/20240929-chain-sends-eth.sql @@ -0,0 +1,75 @@ +CREATE TABLE eth_keys ( + address TEXT NOT NULL PRIMARY KEY, + private_key BYTEA NOT NULL, + role TEXT NOT NULL +); + +CREATE TABLE message_sends_eth +( + from_address TEXT NOT NULL, + to_address TEXT NOT NULL, + send_reason TEXT NOT NULL, + send_task_id SERIAL PRIMARY KEY, + + unsigned_tx BYTEA NOT NULL, + unsigned_hash TEXT NOT NULL, + + nonce BIGINT, + signed_tx BYTEA, + signed_hash TEXT, + + send_time TIMESTAMP DEFAULT NULL, + send_success BOOLEAN DEFAULT NULL, + send_error TEXT +); + +COMMENT ON COLUMN message_sends_eth.from_address IS 'Ethereum 0x... address'; +COMMENT ON COLUMN message_sends_eth.to_address IS 'Ethereum 0x... address'; +COMMENT ON COLUMN message_sends_eth.send_reason IS 'Optional description of send reason'; +COMMENT ON COLUMN message_sends_eth.send_task_id IS 'Task ID of the send task'; + +COMMENT ON COLUMN message_sends_eth.unsigned_tx IS 'Unsigned transaction data'; +COMMENT ON COLUMN message_sends_eth.unsigned_hash IS 'Hash of the unsigned transaction'; + +COMMENT ON COLUMN message_sends_eth.nonce IS 'Assigned transaction nonce, set while the send task is executing'; +COMMENT ON COLUMN message_sends_eth.signed_tx IS 'Signed transaction data, set while the send task is executing'; +COMMENT ON COLUMN message_sends_eth.signed_hash IS 'Hash of the signed transaction'; + +COMMENT ON COLUMN message_sends_eth.send_time IS 'Time when the send task was executed, set after pushing the transaction to the network'; +COMMENT ON COLUMN message_sends_eth.send_success IS 'Whether this transaction was broadcasted to the network already, NULL if not yet attempted, TRUE if successful, FALSE if failed'; +COMMENT ON COLUMN message_sends_eth.send_error IS 'Error message if send_success is FALSE'; + +CREATE UNIQUE INDEX message_sends_eth_success_index + ON message_sends_eth (from_address, nonce) + WHERE send_success IS NOT FALSE; + +COMMENT ON INDEX message_sends_eth_success_index IS + 'message_sends_eth_success_index enforces sender/nonce uniqueness, it is a conditional index that only indexes rows where send_success is not false. This allows us to have multiple rows with the same sender/nonce, as long as only one of them was successfully broadcasted (true) to the network or is in the process of being broadcasted (null).'; + +CREATE TABLE message_send_eth_locks +( + from_address TEXT NOT NULL, + task_id BIGINT NOT NULL, + claimed_at TIMESTAMP NOT NULL, + + CONSTRAINT message_send_eth_locks_pk + PRIMARY KEY (from_address) +); + +CREATE TABLE message_waits_eth ( + signed_tx_hash TEXT PRIMARY KEY, + waiter_machine_id INT REFERENCES harmony_machines (id) ON DELETE SET NULL, + + confirmed_block_number BIGINT, + confirmed_tx_hash TEXT, + confirmed_tx_data JSONB, + + tx_status TEXT, -- 'pending', 'confirmed', 'failed' + tx_receipt JSONB, + tx_success BOOLEAN +); + +-- index for UPDATE message_waits_eth SET waiter_machine_id = $1 WHERE waiter_machine_id IS NULL AND tx_status = 'pending' +CREATE INDEX idx_message_waits_eth_pending + ON message_waits_eth (waiter_machine_id) + WHERE waiter_machine_id IS NULL AND tx_status = 'pending'; diff --git a/harmony/harmonydb/sql/20240930-pdp.sql b/harmony/harmonydb/sql/20240930-pdp.sql new file mode 100644 index 000000000..e3904e9e2 --- /dev/null +++ b/harmony/harmonydb/sql/20240930-pdp.sql @@ -0,0 +1,273 @@ +-- Piece Park adjustments + +ALTER TABLE parked_pieces ADD COLUMN long_term BOOLEAN NOT NULL DEFAULT FALSE; + +ALTER TABLE parked_pieces DROP CONSTRAINT IF EXISTS parked_pieces_piece_cid_key; +ALTER TABLE parked_pieces ADD CONSTRAINT parked_pieces_piece_cid_cleanup_task_id_key UNIQUE (piece_cid, piece_padded_size, long_term, cleanup_task_id); + +ALTER TABLE parked_piece_refs ADD COLUMN long_term BOOLEAN NOT NULL DEFAULT FALSE; + +-- PDP tables +-- PDP services authenticate with ecdsa-sha256 keys; Allowed services here +CREATE TABLE pdp_services ( + id BIGSERIAL PRIMARY KEY, + pubkey BYTEA NOT NULL, + + -- service_url TEXT NOT NULL, + service_label TEXT NOT NULL, + + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + + UNIQUE(pubkey), + UNIQUE(service_label) +); + +CREATE TABLE pdp_piece_uploads ( + id UUID PRIMARY KEY NOT NULL, + service TEXT NOT NULL, -- pdp_services.id + + check_hash_codec TEXT NOT NULL, -- hash multicodec used for checking the piece + check_hash BYTEA NOT NULL, -- hash of the piece + check_size BIGINT NOT NULL, -- size of the piece + + piece_cid TEXT, -- piece cid v2 + notify_url TEXT NOT NULL, -- URL to notify when piece is ready + + notify_task_id BIGINT, -- harmonytask task ID, moves to pdp_piecerefs and calls notify_url when piece is ready + + piece_ref BIGINT, -- packed_piece_refs.ref_id + + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (service) REFERENCES pdp_services(service_label) ON DELETE CASCADE, + FOREIGN KEY (piece_ref) REFERENCES parked_piece_refs(ref_id) ON DELETE SET NULL +); + +-- PDP piece references, this table tells Curio which pieces in storage are managed by PDP +CREATE TABLE pdp_piecerefs ( + id BIGSERIAL PRIMARY KEY, + service TEXT NOT NULL, -- pdp_services.id + piece_cid TEXT NOT NULL, -- piece cid v2 + piece_ref BIGINT NOT NULL, -- parked_piece_refs.ref_id + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + + proofset_refcount BIGINT NOT NULL DEFAULT 0, -- maintained by triggers + + UNIQUE(piece_ref), + FOREIGN KEY (service) REFERENCES pdp_services(service_label) ON DELETE CASCADE, + FOREIGN KEY (piece_ref) REFERENCES parked_piece_refs(ref_id) ON DELETE CASCADE +); + +-- PDP hash to piece cid mapping +CREATE TABLE pdp_piece_mh_to_commp ( + mhash BYTEA PRIMARY KEY, + size BIGINT NOT NULL, + commp TEXT NOT NULL +); + +CREATE INDEX pdp_piecerefs_piece_cid_idx ON pdp_piecerefs(piece_cid); + +-- PDP proofsets we maintain + +CREATE TABLE pdp_proof_sets ( + id BIGINT PRIMARY KEY, -- on-chain proofset id + + -- updated when a challenge is requested (either by first proofset add or by invokes of nextProvingPeriod) + -- initially NULL on fresh proofsets. + prev_challenge_request_epoch BIGINT, + + -- task invoking nextProvingPeriod, the task should be spawned any time prove_at_epoch+challenge_window is in the past + challenge_request_task_id BIGINT REFERENCES harmony_task(id) ON DELETE SET NULL, + + -- nextProvingPeriod message hash, when the message lands prove_task_id will be spawned and + -- this value will be set to NULL + challenge_request_msg_hash TEXT, + + -- the proving period for this proofset and the challenge window duration + proving_period BIGINT, + challenge_window BIGINT, + + -- the epoch at which the next challenge window starts and proofs can be submitted + -- initialized to NULL indicating a special proving period init task handles challenge generation + prove_at_epoch BIGINT, + + -- flag indicating that the proving period is ready for init. Currently set after first add + -- Set to true after first root add + init_ready BOOLEAN NOT NULL DEFAULT FALSE, + + create_message_hash TEXT NOT NULL, + service TEXT NOT NULL REFERENCES pdp_services(service_label) ON DELETE RESTRICT +); + +CREATE TABLE pdp_prove_tasks ( + proofset BIGINT NOT NULL, -- pdp_proof_sets.id + task_id BIGINT NOT NULL, -- harmonytask task ID + + PRIMARY KEY (proofset, task_id), + FOREIGN KEY (proofset) REFERENCES pdp_proof_sets(id) ON DELETE CASCADE, + FOREIGN KEY (task_id) REFERENCES harmony_task(id) ON DELETE CASCADE +); + +-- proofset creation requests +CREATE TABLE pdp_proofset_creates ( + create_message_hash TEXT PRIMARY KEY REFERENCES message_waits_eth(signed_tx_hash) ON DELETE CASCADE, + + -- NULL if not yet processed, TRUE if processed and successful, FALSE if processed and failed + -- NOTE: ok is maintained by a trigger below + ok BOOLEAN DEFAULT NULL, + + proofset_created BOOLEAN NOT NULL DEFAULT FALSE, -- set to true when the proofset is created + + service TEXT NOT NULL REFERENCES pdp_services(service_label) ON DELETE CASCADE, -- service that requested the proofset + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +-- proofset roots +CREATE TABLE pdp_proofset_roots ( + proofset BIGINT NOT NULL, -- pdp_proof_sets.id + root TEXT NOT NULL, -- root cid (piececid v2) + + add_message_hash TEXT NOT NULL REFERENCES message_waits_eth(signed_tx_hash) ON DELETE CASCADE, + add_message_index BIGINT NOT NULL, -- index of root in the add message + + root_id BIGINT NOT NULL, -- on-chain index of the root in the rootCids sub-array + + -- aggregation roots (aggregated like pieces in filecoin sectors) + subroot TEXT NOT NULL, -- subroot cid (piececid v2), with no aggregation this == root + subroot_offset BIGINT NOT NULL, -- offset of the subroot in the root + subroot_size BIGINT NOT NULL, -- size of the subroot (padded piece size) + + pdp_pieceref BIGINT NOT NULL, -- pdp_piecerefs.id + + CONSTRAINT pdp_proofset_roots_root_id_unique PRIMARY KEY (proofset, root_id, subroot_offset), + + FOREIGN KEY (proofset) REFERENCES pdp_proof_sets(id) ON DELETE CASCADE, -- cascade, if we drop a proofset, we no longer care about the roots + FOREIGN KEY (pdp_pieceref) REFERENCES pdp_piecerefs(id) ON DELETE SET NULL -- sets null on delete so that it's easy to notice and clean up +); + +-- proofset root adds - tracking add-root messages which didn't land yet, so don't have a known root_id +CREATE TABLE pdp_proofset_root_adds ( + proofset BIGINT NOT NULL, -- pdp_proof_sets.id + root TEXT NOT NULL, -- root cid (piececid v2) + + add_message_hash TEXT NOT NULL REFERENCES message_waits_eth(signed_tx_hash) ON DELETE CASCADE, + add_message_ok BOOLEAN, -- set to true when the add message is processed + add_message_index BIGINT NOT NULL, -- index of root in the add message + + -- aggregation roots (aggregated like pieces in filecoin sectors) + subroot TEXT NOT NULL, -- subroot cid (piececid v2), with no aggregation this == root + subroot_offset BIGINT NOT NULL, -- offset of the subroot in the root (padded byte offset) + subroot_size BIGINT NOT NULL, -- size of the subroot (padded piece size) + + pdp_pieceref BIGINT NOT NULL, -- pdp_piecerefs.id + + CONSTRAINT pdp_proofset_root_adds_root_id_unique PRIMARY KEY (proofset, add_message_hash, subroot_offset), + + FOREIGN KEY (proofset) REFERENCES pdp_proof_sets(id) ON DELETE CASCADE, -- cascade, if we drop a proofset, we no longer care about the roots + FOREIGN KEY (pdp_pieceref) REFERENCES pdp_piecerefs(id) ON DELETE SET NULL -- sets null on delete so that it's easy to notice and clean up +); + +-- proofset_refcount tracking +CREATE OR REPLACE FUNCTION increment_proofset_refcount() + RETURNS TRIGGER AS $$ +BEGIN + UPDATE pdp_piecerefs + SET proofset_refcount = proofset_refcount + 1 + WHERE id = NEW.pdp_pieceref; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER pdp_proofset_root_insert + AFTER INSERT ON pdp_proofset_roots + FOR EACH ROW + WHEN (NEW.pdp_pieceref IS NOT NULL) +EXECUTE FUNCTION increment_proofset_refcount(); + +CREATE OR REPLACE FUNCTION decrement_proofset_refcount() + RETURNS TRIGGER AS $$ +BEGIN + UPDATE pdp_piecerefs + SET proofset_refcount = proofset_refcount - 1 + WHERE id = OLD.pdp_pieceref; + RETURN OLD; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER pdp_proofset_root_delete + AFTER DELETE ON pdp_proofset_roots + FOR EACH ROW + WHEN (OLD.pdp_pieceref IS NOT NULL) +EXECUTE FUNCTION decrement_proofset_refcount(); + +CREATE OR REPLACE FUNCTION adjust_proofset_refcount_on_update() + RETURNS TRIGGER AS $$ +BEGIN + IF OLD.pdp_pieceref IS DISTINCT FROM NEW.pdp_pieceref THEN + -- Decrement count for old reference if not null + IF OLD.pdp_pieceref IS NOT NULL THEN + UPDATE pdp_piecerefs + SET proofset_refcount = proofset_refcount - 1 + WHERE id = OLD.pdp_pieceref; + END IF; + -- Increment count for new reference if not null + IF NEW.pdp_pieceref IS NOT NULL THEN + UPDATE pdp_piecerefs + SET proofset_refcount = proofset_refcount + 1 + WHERE id = NEW.pdp_pieceref; + END IF; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER pdp_proofset_root_update + AFTER UPDATE ON pdp_proofset_roots + FOR EACH ROW +EXECUTE FUNCTION adjust_proofset_refcount_on_update(); + +-- proofset creation request trigger +CREATE OR REPLACE FUNCTION update_pdp_proofset_creates() + RETURNS TRIGGER AS $$ +BEGIN + IF OLD.tx_status = 'pending' AND (NEW.tx_status = 'confirmed' OR NEW.tx_status = 'failed') THEN + -- Update the ok field in pdp_proofset_creates if a matching entry exists + UPDATE pdp_proofset_creates + SET ok = CASE + WHEN NEW.tx_status = 'failed' OR NEW.tx_success = FALSE THEN FALSE + WHEN NEW.tx_status = 'confirmed' AND NEW.tx_success = TRUE THEN TRUE + ELSE ok + END + WHERE create_message_hash = NEW.signed_tx_hash AND proofset_created = FALSE; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER pdp_proofset_create_message_status_change + AFTER UPDATE OF tx_status, tx_success ON message_waits_eth + FOR EACH ROW +EXECUTE PROCEDURE update_pdp_proofset_creates(); + +-- add proofset add message trigger +CREATE OR REPLACE FUNCTION update_pdp_proofset_roots() + RETURNS TRIGGER AS $$ +BEGIN + IF OLD.tx_status = 'pending' AND (NEW.tx_status = 'confirmed' OR NEW.tx_status = 'failed') THEN + -- Update the add_message_ok field in pdp_proofset_root_adds if a matching entry exists + UPDATE pdp_proofset_root_adds + SET add_message_ok = CASE + WHEN NEW.tx_status = 'failed' OR NEW.tx_success = FALSE THEN FALSE + WHEN NEW.tx_status = 'confirmed' AND NEW.tx_success = TRUE THEN TRUE + ELSE add_message_ok + END + WHERE add_message_hash = NEW.signed_tx_hash; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER pdp_proofset_add_message_status_change + AFTER UPDATE OF tx_status, tx_success ON message_waits_eth + FOR EACH ROW +EXECUTE PROCEDURE update_pdp_proofset_roots(); diff --git a/harmony/taskhelp/retry.go b/harmony/taskhelp/retry.go new file mode 100644 index 000000000..913cf9e25 --- /dev/null +++ b/harmony/taskhelp/retry.go @@ -0,0 +1,22 @@ +package taskhelp + +import ( + "math" + "time" +) + +type RetryWaitFunc func(retries int) time.Duration + +// RetryWaitLinear returns a function that calculates a linearly increasing duration based on retries. +func RetryWaitLinear(initial, increment time.Duration) RetryWaitFunc { + return func(retries int) time.Duration { + return initial + time.Duration(retries)*increment + } +} + +// RetryWaitExp returns a function that calculates an exponentially increasing duration based on retries. +func RetryWaitExp(initial time.Duration, factor float64) RetryWaitFunc { + return func(retries int) time.Duration { + return time.Duration(float64(initial) * math.Pow(factor, float64(retries))) + } +} diff --git a/lib/cachedreader/cachedreader.go b/lib/cachedreader/cachedreader.go index c020648e9..12e34393b 100644 --- a/lib/cachedreader/cachedreader.go +++ b/lib/cachedreader/cachedreader.go @@ -27,20 +27,24 @@ var log = logging.Logger("cached-reader") const MaxCachedReaders = 128 type CachedPieceReader struct { - db *harmonydb.DB - pp *pieceprovider.PieceProvider + db *harmonydb.DB + + sectorReader *pieceprovider.SectorReader + pieceParkReader *pieceprovider.PieceParkReader + pieceReaderCacheMu sync.Mutex pieceReaderCache *ttlcache.Cache } -func NewCachedPieceReader(db *harmonydb.DB, pp *pieceprovider.PieceProvider) *CachedPieceReader { +func NewCachedPieceReader(db *harmonydb.DB, sectorReader *pieceprovider.SectorReader, pieceParkReader *pieceprovider.PieceParkReader) *CachedPieceReader { prCache := ttlcache.NewCache() _ = prCache.SetTTL(time.Minute * 10) prCache.SetCacheSizeLimit(MaxCachedReaders) cpr := &CachedPieceReader{ db: db, - pp: pp, + sectorReader: sectorReader, + pieceParkReader: pieceParkReader, pieceReaderCache: prCache, } @@ -71,7 +75,7 @@ type cachedSectionReader struct { reader storiface.Reader cpr *CachedPieceReader pieceCid cid.Cid - pieceSize abi.PaddedPieceSize + pieceSize abi.UnpaddedPieceSize // Signals when the underlying piece reader is ready ready chan struct{} // err is non-nil if there's an error getting the underlying piece reader @@ -97,7 +101,7 @@ func (r *cachedSectionReader) Close() error { return nil } -func (cpr *CachedPieceReader) getPieceReader(ctx context.Context, pieceCid cid.Cid) (storiface.Reader, abi.PaddedPieceSize, error) { +func (cpr *CachedPieceReader) getPieceReaderFromSector(ctx context.Context, pieceCid cid.Cid) (storiface.Reader, abi.UnpaddedPieceSize, error) { // Get all deals containing this piece var deals []struct { @@ -143,20 +147,52 @@ func (cpr *CachedPieceReader) getPieceReader(ctx context.Context, pieceCid cid.C ProofType: dl.Proof, } - reader, err := cpr.pp.ReadPiece(ctx, sr, storiface.UnpaddedByteIndex(dl.Offset.Unpadded()), dl.Length.Unpadded(), pieceCid) + reader, err := cpr.sectorReader.ReadPiece(ctx, sr, storiface.UnpaddedByteIndex(dl.Offset.Unpadded()), dl.Length.Unpadded(), pieceCid) if err != nil { merr = multierror.Append(merr, err) continue } - return reader, dl.Length, nil + return reader, dl.Length.Unpadded(), nil } return nil, 0, merr } -func (cpr *CachedPieceReader) GetSharedPieceReader(ctx context.Context, pieceCid cid.Cid) (storiface.Reader, abi.PaddedPieceSize, error) { +func (cpr *CachedPieceReader) getPieceReaderFromPiecePark(ctx context.Context, pieceCid cid.Cid) (storiface.Reader, abi.UnpaddedPieceSize, error) { + // Query parked_pieces and parked_piece_refs in one go + var pieceData []struct { + ID int64 `db:"id"` + PieceRawSize int64 `db:"piece_raw_size"` + } + + err := cpr.db.Select(ctx, &pieceData, ` + SELECT + pp.id, + pp.piece_raw_size + FROM + parked_pieces pp + WHERE + pp.piece_cid = $1 AND pp.complete = TRUE AND pp.long_term = TRUE + LIMIT 1; + `, pieceCid.String()) + if err != nil { + return nil, 0, fmt.Errorf("failed to query parked_pieces and parked_piece_refs for piece cid %s: %w", pieceCid.String(), err) + } + + if len(pieceData) == 0 { + return nil, 0, fmt.Errorf("failed to find piece in parked_pieces for piece cid %s", pieceCid.String()) + } + + reader, err := cpr.pieceParkReader.ReadPiece(ctx, storiface.PieceNumber(pieceData[0].ID), pieceData[0].PieceRawSize, pieceCid) + if err != nil { + return nil, 0, fmt.Errorf("failed to read piece from piece park: %w", err) + } + + return reader, abi.UnpaddedPieceSize(pieceData[0].PieceRawSize), nil +} +func (cpr *CachedPieceReader) GetSharedPieceReader(ctx context.Context, pieceCid cid.Cid) (storiface.Reader, abi.UnpaddedPieceSize, error) { var r *cachedSectionReader // Check if there is already a piece reader in the cache @@ -176,17 +212,31 @@ func (cpr *CachedPieceReader) GetSharedPieceReader(ctx context.Context, pieceCid // We just added a cached reader, so get its underlying piece reader readerCtx, readerCtxCancel := context.WithCancel(context.Background()) - sr, size, err := cpr.getPieceReader(readerCtx, pieceCid) + defer close(r.ready) + + reader, size, err := cpr.getPieceReaderFromSector(readerCtx, pieceCid) + if err != nil { + log.Warnw("failed to get piece reader from sector", "piececid", pieceCid, "err", err) + + serr := err + + // Try getPieceReaderFromPiecePark + reader, size, err = cpr.getPieceReaderFromPiecePark(readerCtx, pieceCid) + if err != nil { + log.Errorw("failed to get piece reader from piece park", "piececid", pieceCid, "err", err) - r.reader = sr - r.err = err + r.err = fmt.Errorf("failed to get piece reader from sector or piece park: %w, %w", err, serr) + readerCtxCancel() + + return nil, 0, r.err + } + } + + r.reader = reader + r.err = nil r.cancel = readerCtxCancel r.pieceSize = size - - // Inform any waiting threads that the cached reader is ready - close(r.ready) } else { - r = rr.(*cachedSectionReader) r.refs++ @@ -195,7 +245,7 @@ func (cpr *CachedPieceReader) GetSharedPieceReader(ctx context.Context, pieceCid // We already had a cached reader, wait for it to be ready select { case <-ctx.Done(): - // The context timed out. Deference the cached piece reader and + // The context timed out. Dereference the cached piece reader and // return an error. _ = r.Close() return nil, 0, ctx.Err() diff --git a/lib/dealdata/dealdata.go b/lib/dealdata/dealdata.go index 0f73c49d8..6b3e8ecc1 100644 --- a/lib/dealdata/dealdata.go +++ b/lib/dealdata/dealdata.go @@ -218,7 +218,7 @@ func getDealMetadata(ctx context.Context, db *harmonydb.DB, sc *ffi.SealCalls, s reader, _ := padreader.New(pr, uint64(*p.DataRawSize)) pieceReaders = append(pieceReaders, reader) } else { - reader, _ := padreader.New(NewUrlReader(dataUrl, hdrs, *p.DataRawSize), uint64(*p.DataRawSize)) + reader, _ := padreader.New(NewUrlReader(nil, dataUrl, hdrs, *p.DataRawSize), uint64(*p.DataRawSize)) pieceReaders = append(pieceReaders, reader) } diff --git a/lib/dealdata/urlpiecereader.go b/lib/dealdata/urlpiecereader.go index dee81fefa..8169969c0 100644 --- a/lib/dealdata/urlpiecereader.go +++ b/lib/dealdata/urlpiecereader.go @@ -1,6 +1,7 @@ package dealdata import ( + "context" "fmt" "io" "net/http" @@ -8,23 +9,32 @@ import ( "strings" "golang.org/x/xerrors" + + "github.com/filecoin-project/curio/lib/paths" ) +// CustoreScheme is a special url scheme indicating that a data URL is an http url withing the curio storage system +const CustoreScheme = "custore" + type UrlPieceReader struct { Url string Headers http.Header RawSize int64 // the exact number of bytes read, if we read more or less that's an error + RemoteEndpointReader *paths.Remote // Only used for .ReadRemote which issues http requests for internal /remote endpoints + readSoFar int64 closed bool active io.ReadCloser // auto-closed on EOF } -func NewUrlReader(p string, h http.Header, rs int64) *UrlPieceReader { +func NewUrlReader(rmt *paths.Remote, p string, h http.Header, rs int64) *UrlPieceReader { return &UrlPieceReader{ Url: p, RawSize: rs, Headers: h, + + RemoteEndpointReader: rmt, } } @@ -34,6 +44,19 @@ func (u *UrlPieceReader) initiateRequest() error { return xerrors.Errorf("failed to parse the URL: %w", err) } + if goUrl.Scheme == CustoreScheme { + if u.RemoteEndpointReader == nil { + return xerrors.New("RemoteEndpoint is nil") + } + + goUrl.Scheme = "http" + u.active, err = u.RemoteEndpointReader.ReadRemote(context.Background(), goUrl.String(), 0, 0) + if err != nil { + return xerrors.Errorf("error reading remote (%s): %w", goUrl.String(), err) + } + return nil + } + if goUrl.Scheme != "https" && goUrl.Scheme != "http" { return xerrors.Errorf("URL scheme %s not supported", goUrl.Scheme) } diff --git a/lib/ffi/piece_funcs.go b/lib/ffi/piece_funcs.go index f09a3c26b..c719b1a12 100644 --- a/lib/ffi/piece_funcs.go +++ b/lib/ffi/piece_funcs.go @@ -12,10 +12,9 @@ import ( storiface "github.com/filecoin-project/curio/lib/storiface" ) -func (sb *SealCalls) WritePiece(ctx context.Context, taskID *harmonytask.TaskID, pieceID storiface.PieceNumber, size int64, data io.Reader) error { - // todo: config(?): allow setting PathStorage for this - // todo storage reservations - paths, _, done, err := sb.sectors.AcquireSector(ctx, taskID, pieceID.Ref(), storiface.FTNone, storiface.FTPiece, storiface.PathSealing) +func (sb *SealCalls) WritePiece(ctx context.Context, taskID *harmonytask.TaskID, pieceID storiface.PieceNumber, size int64, data io.Reader, storageType storiface.PathType) error { + // Use storageType in AcquireSector + paths, _, done, err := sb.sectors.AcquireSector(ctx, taskID, pieceID.Ref(), storiface.FTNone, storiface.FTPiece, storageType) if err != nil { return err } @@ -57,7 +56,7 @@ func (sb *SealCalls) WritePiece(ctx context.Context, taskID *harmonytask.TaskID, copyEnd := time.Now() - log.Infow("wrote parked piece", "piece", pieceID, "size", size, "duration", copyEnd.Sub(copyStart), "dest", dest, "MiB/s", float64(size)/(1<<20)/copyEnd.Sub(copyStart).Seconds()) + log.Infow("wrote piece", "piece", pieceID, "size", size, "duration", copyEnd.Sub(copyStart), "dest", dest, "MiB/s", float64(size)/(1<<20)/copyEnd.Sub(copyStart).Seconds()) if err := os.Rename(tempDest, dest); err != nil { return xerrors.Errorf("rename temp piece to dest %s -> %s: %w", tempDest, dest, err) diff --git a/lib/paths/http_handler.go b/lib/paths/http_handler.go index c308dedf5..74921837e 100644 --- a/lib/paths/http_handler.go +++ b/lib/paths/http_handler.go @@ -9,6 +9,7 @@ import ( "strconv" "time" + "github.com/google/uuid" "github.com/gorilla/mux" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" @@ -46,7 +47,10 @@ func (d *DefaultPartialFileHandler) Close(pf *partialfile.PartialFile) error { } type FetchHandler struct { - Local Store + Local interface { + Store + StashStore + } PfHandler PartialFileHandler } @@ -57,6 +61,7 @@ func (handler *FetchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/remote/vanilla/single", handler.generateSingleVanillaProof).Methods("POST") mux.HandleFunc("/remote/vanilla/porep", handler.generatePoRepVanillaProof).Methods("POST") mux.HandleFunc("/remote/vanilla/snap", handler.readSnapVanillaProof).Methods("POST") + mux.HandleFunc("/remote/stash/{id}", handler.remoteGetStash).Methods("GET") mux.HandleFunc("/remote/{type}/{id}/{spt}/allocated/{offset}/{size}", handler.remoteGetAllocated).Methods("GET") mux.HandleFunc("/remote/{type}/{id}", handler.remoteGetSector).Methods("GET") mux.HandleFunc("/remote/{type}/{id}", handler.remoteDeleteSector).Methods("DELETE") @@ -296,6 +301,36 @@ func (handler *FetchHandler) remoteGetAllocated(w http.ResponseWriter, r *http.R w.WriteHeader(http.StatusRequestedRangeNotSatisfiable) } +func (handler *FetchHandler) remoteGetStash(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + idStr := vars["id"] + + id, err := uuid.Parse(idStr) + if err != nil { + http.Error(w, "invalid UUID", http.StatusBadRequest) + return + } + + readCloser, err := handler.Local.ServeAndRemove(r.Context(), id) + if err != nil { + if os.IsNotExist(err) { + http.Error(w, "stash not found", http.StatusNotFound) + } else { + http.Error(w, err.Error(), 500) + } + return + } + defer readCloser.Close() + + w.Header().Set("Content-Type", "application/octet-stream") + _, err = io.Copy(w, readCloser) + if err != nil { + log.Errorf("error copying stash data: %+v", err) + // If the read was incomplete, ServeAndRemove won't remove the file + return + } +} + type SingleVanillaParams struct { Miner abi.ActorID Sector storiface.PostSectorChallenge diff --git a/lib/paths/http_handler_test.go b/lib/paths/http_handler_test.go index e5d067a35..831bed9fd 100644 --- a/lib/paths/http_handler_test.go +++ b/lib/paths/http_handler_test.go @@ -207,7 +207,12 @@ func TestRemoteGetAllocated(t *testing.T) { pfhandler := mocks.NewMockPartialFileHandler(mockCtrl) handler := &paths.FetchHandler{ - Local: lstore, + Local: struct { + paths.Store + paths.StashStore + }{ + Store: lstore, + }, PfHandler: pfhandler, } @@ -406,7 +411,12 @@ func TestRemoteGetSector(t *testing.T) { } handler := &paths.FetchHandler{ - lstore, + struct { + paths.Store + paths.StashStore + }{ + Store: lstore, + }, pfhandler, } diff --git a/lib/paths/interface.go b/lib/paths/interface.go index 3b04225ce..859079491 100644 --- a/lib/paths/interface.go +++ b/lib/paths/interface.go @@ -3,7 +3,10 @@ package paths import ( "context" "io" + "net/url" + "os" + "github.com/google/uuid" "github.com/ipfs/go-cid" "github.com/filecoin-project/go-state-types/abi" @@ -54,3 +57,61 @@ type Store interface { GeneratePoRepVanillaProof(ctx context.Context, sr storiface.SectorRef, sealed, unsealed cid.Cid, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness) ([]byte, error) ReadSnapVanillaProof(ctx context.Context, sr storiface.SectorRef) ([]byte, error) } + +// StashStore provides methods for managing stashes within storage paths. +// Stashes are temporary files located in the "stash/" subdirectory of sealing paths. +// They are removed on startup and are not indexed. Stashes are used to store +// arbitrary data and can be served or removed as needed. +type StashStore interface { + // StashCreate creates a new stash file with the specified maximum size. + // It selects a sealing path with the most available space and creates a file + // named [uuid].tmp in the stash directory. + // + // The provided writeFunc is called with an *os.File pointing to the newly + // created stash file, allowing the caller to write data into it. + // + // Parameters: + // - ctx: Context for cancellation and timeout. + // - maxSize: The maximum size of the stash file in bytes. + // - writeFunc: A function that writes data to the stash file. + // + // Returns: + // - uuid.UUID: A unique identifier for the created stash. + // - error: An error if the stash could not be created. + StashCreate(ctx context.Context, maxSize int64, writeFunc func(f *os.File) error) (uuid.UUID, error) + + // ServeAndRemove serves the stash file identified by the given UUID as an io.ReadCloser. + // Once the stash has been fully read (i.e., the last byte has been read), + // the stash file is automatically removed. + // If the read is incomplete (e.g., due to an error or premature closure), + // the stash file remains on disk. + // + // Parameters: + // - ctx: Context for cancellation and timeout. + // - id: The UUID of the stash to serve. + // + // Returns: + // - io.ReadCloser: A reader for the stash file. + // - error: An error if the stash could not be served. + ServeAndRemove(ctx context.Context, id uuid.UUID) (io.ReadCloser, error) + + // StashRemove removes the stash file identified by the given UUID. + // + // Parameters: + // - ctx: Context for cancellation and timeout. + // - id: The UUID of the stash to remove. + // + // Returns: + // - error: An error if the stash could not be removed. + StashRemove(ctx context.Context, id uuid.UUID) error + + // StashURL generates a URL for accessing the stash identified by the given UUID. + // + // Parameters: + // - id: The UUID of the stash. + // + // Returns: + // - url.URL: The URL where the stash can be accessed. + // - error: An error if the URL could not be generated. + StashURL(id uuid.UUID) (url.URL, error) +} diff --git a/lib/paths/local.go b/lib/paths/local.go index 44ca04fba..43a985083 100644 --- a/lib/paths/local.go +++ b/lib/paths/local.go @@ -69,7 +69,10 @@ type BatchMeta struct { type Local struct { localStorage LocalStorage index SectorIndex - urls []string + + // URL which serves this storage, pointing at /remote + // http://[...]/remote + url string paths map[storiface.ID]*path @@ -101,6 +104,8 @@ type path struct { Reserved int64 Reservations map[string]int64 + + CanSeal bool } // statExistingSectorForReservation is optional parameter for stat method @@ -247,11 +252,11 @@ func init() { })) } -func NewLocal(ctx context.Context, ls LocalStorage, index SectorIndex, urls []string) (*Local, error) { +func NewLocal(ctx context.Context, ls LocalStorage, index SectorIndex, url string) (*Local, error) { l := &Local{ localStorage: newCachedLocalStorage(ls), index: index, - urls: urls, + url: url, paths: map[storiface.ID]*path{}, } @@ -293,6 +298,20 @@ func (st *Local) OpenPath(ctx context.Context, p string) error { MaxStorage: meta.MaxStorage, Reserved: 0, Reservations: map[string]int64{}, + CanSeal: meta.CanSeal, + } + + // Remove all stashes on startup + if meta.CanSeal { + stashDir := filepath.Join(p, StashDirName) + err := os.RemoveAll(stashDir) + if err != nil && !os.IsNotExist(err) { + return xerrors.Errorf("removing stash directory %s: %w", stashDir, err) + } + // Re-create stash directory + if err := os.MkdirAll(stashDir, 0755); err != nil { + return xerrors.Errorf("creating stash directory %s: %w", stashDir, err) + } } fst, _, err := out.stat(st.localStorage) @@ -302,7 +321,7 @@ func (st *Local) OpenPath(ctx context.Context, p string) error { err = st.index.StorageAttach(ctx, storiface.StorageInfo{ ID: meta.ID, - URLs: st.urls, + URLs: []string{st.url}, Weight: meta.Weight, MaxStorage: meta.MaxStorage, CanSeal: meta.CanSeal, @@ -335,10 +354,8 @@ func (st *Local) ClosePath(ctx context.Context, id storiface.ID) error { return xerrors.Errorf("path with ID %s isn't opened", id) } - for _, url := range st.urls { - if err := st.index.StorageDetach(ctx, id, url); err != nil { - return xerrors.Errorf("dropping path (id='%s' url='%s'): %w", id, url, err) - } + if err := st.index.StorageDetach(ctx, id, st.url); err != nil { + return xerrors.Errorf("dropping path (id='%s' url='%s'): %w", id, st.url, err) } delete(st.paths, id) @@ -403,7 +420,7 @@ func (st *Local) Redeclare(ctx context.Context, filterId *storiface.ID, dropMiss err = st.index.StorageAttach(ctx, storiface.StorageInfo{ ID: id, - URLs: st.urls, + URLs: []string{st.url}, Weight: meta.Weight, MaxStorage: meta.MaxStorage, CanSeal: meta.CanSeal, diff --git a/lib/paths/local_stash.go b/lib/paths/local_stash.go new file mode 100644 index 000000000..d4a7a36d9 --- /dev/null +++ b/lib/paths/local_stash.go @@ -0,0 +1,183 @@ +package paths + +import ( + "context" + "io" + "net/url" + "os" + "path/filepath" + + "github.com/google/uuid" + "golang.org/x/xerrors" +) + +const StashDirName = "stash" + +func (st *Local) StashCreate(ctx context.Context, maxSize int64, writeFunc func(f *os.File) error) (uuid.UUID, error) { + st.localLk.RLock() + + var selectedPath *path + var maxAvailable int64 + + for _, p := range st.paths { + if !p.CanSeal { + continue + } + + stat, _, err := p.stat(st.localStorage) + if err != nil { + // Skip path if error getting stat + continue + } + + if stat.Available < maxSize { + // Not enough space + continue + } + + if stat.Available > maxAvailable { + maxAvailable = stat.Available + selectedPath = p + } + } + + st.localLk.RUnlock() + + if selectedPath == nil { + return uuid.Nil, xerrors.Errorf("no sealing paths have enough space (%d bytes)", maxSize) + } + + stashDir := filepath.Join(selectedPath.Local, StashDirName) + err := os.MkdirAll(stashDir, 0755) + if err != nil { + return uuid.Nil, xerrors.Errorf("creating stash directory %s: %w", stashDir, err) + } + + fileUUID := uuid.New() + stashFileName := fileUUID.String() + ".tmp" + stashFilePath := filepath.Join(stashDir, stashFileName) + + f, err := os.OpenFile(stashFilePath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644) + if err != nil { + return uuid.Nil, xerrors.Errorf("creating stash file %s: %w", stashFilePath, err) + } + + writeErr := writeFunc(f) + closeErr := f.Close() + + if writeErr != nil { + // Remove the stash file since writing failed + if removeErr := os.Remove(stashFilePath); removeErr != nil { + log.Errorf("failed to remove stash file %s after write error: %v", stashFilePath, removeErr) + } + return uuid.Nil, xerrors.Errorf("writing to stash file %s: %w", stashFilePath, writeErr) + } + + if closeErr != nil { + return uuid.Nil, xerrors.Errorf("closing stash file %s: %w", stashFilePath, closeErr) + } + + return fileUUID, nil +} + +func (st *Local) StashURL(id uuid.UUID) (url.URL, error) { + u, err := url.Parse(st.url) + if err != nil { + return url.URL{}, xerrors.Errorf("parsing url %s: %w", st.url, err) + } + + u.Path = filepath.Join(u.Path, "stash", id.String()) + return *u, nil +} + +func (st *Local) StashRemove(ctx context.Context, id uuid.UUID) error { + st.localLk.RLock() + + fileName := id.String() + ".tmp" + + for _, p := range st.paths { + if !p.CanSeal { + continue + } + + st.localLk.RUnlock() + + stashDir := filepath.Join(p.Local, StashDirName) + stashFilePath := filepath.Join(stashDir, fileName) + if _, err := os.Stat(stashFilePath); err == nil { + if err := os.Remove(stashFilePath); err != nil { + return xerrors.Errorf("removing stash file %s: %w", stashFilePath, err) + } + return nil + } else if !os.IsNotExist(err) { + return xerrors.Errorf("stat stash file %s: %w", stashFilePath, err) + } + } + + st.localLk.RUnlock() + + return xerrors.Errorf("stash file %s not found", fileName) +} + +func (st *Local) ServeAndRemove(ctx context.Context, id uuid.UUID) (io.ReadCloser, error) { + st.localLk.RLock() + + fileName := id.String() + ".tmp" + + for _, p := range st.paths { + if !p.CanSeal { + continue + } + + stashDir := filepath.Join(p.Local, StashDirName) + stashFilePath := filepath.Join(stashDir, fileName) + f, err := os.Open(stashFilePath) + if err == nil { + st.localLk.RUnlock() + + // Wrap the file in a custom ReadCloser + return &stashFileReadCloser{ + File: f, + path: stashFilePath, + eofReached: false, + }, nil + } else if !os.IsNotExist(err) { + return nil, xerrors.Errorf("opening stash file %s: %w", stashFilePath, err) + } + } + + st.localLk.RUnlock() + + return nil, xerrors.Errorf("stash file %s not found", fileName) +} + +type stashFileReadCloser struct { + File *os.File + path string + eofReached bool +} + +func (sfrc *stashFileReadCloser) Read(p []byte) (int, error) { + n, err := sfrc.File.Read(p) + if err == io.EOF { + sfrc.eofReached = true + } + return n, err +} + +func (sfrc *stashFileReadCloser) Close() error { + err := sfrc.File.Close() + if err != nil { + return err + } + if sfrc.eofReached { + // Remove the file + if err := os.Remove(sfrc.path); err != nil { + return xerrors.Errorf("removing stash file %s: %w", sfrc.path, err) + } + } + // Else, do not remove the file + return nil +} + +var _ StashStore = &Local{} diff --git a/lib/paths/local_test.go b/lib/paths/local_test.go index 4edcef6a9..24282c4df 100644 --- a/lib/paths/local_test.go +++ b/lib/paths/local_test.go @@ -89,7 +89,7 @@ func TestLocalStorage(t *testing.T) { index := NewDBIndex(nil, db) - st, err := NewLocal(ctx, tstor, index, nil) + st, err := NewLocal(ctx, tstor, index, "") require.NoError(t, err) p1 := "1" diff --git a/lib/paths/remote.go b/lib/paths/remote.go index c0ff6322c..38c4e919c 100644 --- a/lib/paths/remote.go +++ b/lib/paths/remote.go @@ -464,7 +464,7 @@ func (r *Remote) StatUrl(ctx context.Context, urlStr string, id storiface.ID) (f return fsutil.FsStat{}, xerrors.Errorf("endpoint failed %s: %d %s", rl.String(), resp.StatusCode, string(b)) } -func (r *Remote) readRemote(ctx context.Context, url string, offset, size abi.PaddedPieceSize) (io.ReadCloser, error) { +func (r *Remote) ReadRemote(ctx context.Context, url string, offset, size int64) (io.ReadCloser, error) { if len(r.limit) >= cap(r.limit) { log.Infof("Throttling remote read, %d already running", len(r.limit)) } @@ -758,9 +758,141 @@ func (r *Remote) Reader(ctx context.Context, s storiface.SectorRef, offset, size } return func(startOffsetAligned, endOffsetAligned storiface.PaddedByteIndex) (io.ReadCloser, error) { - // readRemote fetches a reader that we can use to read the unsealed piece from the remote worker. + // ReadRemote fetches a reader that we can use to read the unsealed piece from the remote worker. // It uses a ranged HTTP query to ensure we ONLY read the unsealed piece and not the entire unsealed file. - rd, err := r.readRemote(ctx, url, offset+abi.PaddedPieceSize(startOffsetAligned), offset+abi.PaddedPieceSize(endOffsetAligned)) + rd, err := r.ReadRemote(ctx, url, int64(offset+abi.PaddedPieceSize(startOffsetAligned)), int64(offset+abi.PaddedPieceSize(endOffsetAligned))) + if err != nil { + log.Warnw("reading from remote", "url", url, "error", err) + return nil, err + } + + return rd, err + }, nil + + } + } + + // we couldn't find a unsealed file with the unsealed piece, will return a nil reader. + log.Debugf("returning nil reader, did not find unsealed piece for %+v (+%d,%d), last error=%s", s, offset, size, lastErr) + return nil, nil +} + +// ReaderPiece returns a reader for a piece at the given offset in the given file. +// NOTE: This method could also be used in place of Reader, but that one also handles PartialFile logic specfic to unsealed sectors. +// +// Hopefully when PartialFile is removed, this method will be used instead of Reader. +func (r *Remote) ReaderPiece(ctx context.Context, s storiface.SectorRef, ft storiface.SectorFileType, offset, size int64) (func(startOffset, endOffset int64) (io.ReadCloser, error), error) { + // check if we have the unsealed sector file locally + paths, _, err := r.local.AcquireSector(ctx, s, ft, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove) + if err != nil { + return nil, xerrors.Errorf("acquire local: %w", err) + } + + path := storiface.PathByType(paths, ft) + + if path != "" { + log.Infof("returning piece reader for local unsealed piece sector=%+v, (offset=%d, size=%d)", s.ID, offset, size) + + // refs keep track of the currently opened pf + // if they drop to 0 for longer than LocalReaderTimeout, pf will be closed + var refsLk sync.Mutex + refs := 0 + + var f *os.File + + cleanupIdle := func() { + lastRefs := 1 + + for range time.After(LocalReaderTimeout) { + refsLk.Lock() + if refs == 0 && lastRefs == 0 && f != nil { + log.Infow("closing idle partial file", "path", path) + err := f.Close() + if err != nil { + log.Errorw("closing idle partial file", "path", path, "error", err) + } + + f = nil + refsLk.Unlock() + return + } + lastRefs = refs + refsLk.Unlock() + } + } + + getFile := func() (*os.File, func() error, error) { + refsLk.Lock() + defer refsLk.Unlock() + + if f == nil { + // got closed in the meantime, reopen + + var err error + f, err = os.Open(path) + if err != nil { + return nil, nil, xerrors.Errorf("reopening file: %w", err) + } + log.Debugf("local file reopened %s (+%d,%d)", path, offset, size) + + go cleanupIdle() + } + + refs++ + + return f, func() error { + refsLk.Lock() + defer refsLk.Unlock() + + refs-- + return nil + }, nil + } + + return func(startOffset, endOffset int64) (io.ReadCloser, error) { + f, done, err := getFile() + if err != nil { + return nil, xerrors.Errorf("getting partialfile handle: %w", err) + } + + r := io.NewSectionReader(f, offset+startOffset, endOffset-startOffset) + + return struct { + io.Reader + io.Closer + }{ + Reader: r, + Closer: funcCloser(done), + }, nil + }, nil + } + + // --- We don't have the piece file locally + + // if we don't have the unsealed sector file locally, we'll first lookup the Miner Sector Store Index + // to determine which workers have the unsealed file and then query those workers to know + // if they have the unsealed piece in the unsealed sector file. + si, err := r.index.StorageFindSector(ctx, s.ID, ft, 0, false) + if err != nil { + log.Debugf("Reader, did not find unsealed file on any of the workers %s (+%d,%d)", path, offset, size) + return nil, err + } + + if len(si) == 0 { + return nil, xerrors.Errorf("failed to read sector %v from remote(%d): %w", s, ft, storiface.ErrSectorNotFound) + } + + sort.Slice(si, func(i, j int) bool { + return si[i].Weight > si[j].Weight + }) + + var lastErr error + for _, info := range si { + for _, url := range info.URLs { + return func(startOffset, endOffset int64) (io.ReadCloser, error) { + // ReadRemote fetches a reader that we can use to read the unsealed piece from the remote worker. + // It uses a ranged HTTP query to ensure we ONLY read the unsealed piece and not the entire unsealed file. + rd, err := r.ReadRemote(ctx, url, offset+startOffset, offset+endOffset) if err != nil { log.Warnw("reading from remote", "url", url, "error", err) return nil, err @@ -806,7 +938,7 @@ func (r *Remote) ReaderSeq(ctx context.Context, s storiface.SectorRef, ft storif for _, info := range si { for _, url := range info.URLs { - rd, err := r.readRemote(ctx, url, 0, 0) + rd, err := r.ReadRemote(ctx, url, 0, 0) if err != nil { log.Warnw("reading from remote", "url", url, "error", err) continue diff --git a/lib/paths/remote_prove.go b/lib/paths/remote_prove.go index edb83b9aa..0ebb0a7e5 100644 --- a/lib/paths/remote_prove.go +++ b/lib/paths/remote_prove.go @@ -58,7 +58,7 @@ func (r *Remote) ReadMinCacheInto(ctx context.Context, s storiface.SectorRef, ft u = purl.String() - rd, err := r.readRemote(ctx, u, 0, 0) + rd, err := r.ReadRemote(ctx, u, 0, 0) if err != nil { log.Warnw("reading from remote", "url", u, "error", err) continue diff --git a/lib/paths/remote_test.go b/lib/paths/remote_test.go index af397d00f..4bd3274ea 100644 --- a/lib/paths/remote_test.go +++ b/lib/paths/remote_test.go @@ -87,9 +87,9 @@ func TestMoveShared(t *testing.T) { hs1 := httptest.NewServer(mux1) hs2 := httptest.NewServer(mux2) - ls1, err := paths.NewLocal(ctx, lr1, index, []string{hs1.URL + "/remote"}) + ls1, err := paths.NewLocal(ctx, lr1, index, hs1.URL+"/remote") require.NoError(t, err) - ls2, err := paths.NewLocal(ctx, lr2, index, []string{hs2.URL + "/remote"}) + ls2, err := paths.NewLocal(ctx, lr2, index, hs2.URL+"/remote") require.NoError(t, err) dirStor := filepath.Join(dir, "stor") diff --git a/lib/pieceprovider/piece_provider.go b/lib/pieceprovider/cached_piecereader.go similarity index 61% rename from lib/pieceprovider/piece_provider.go rename to lib/pieceprovider/cached_piecereader.go index 2e8ccbebf..5278a783d 100644 --- a/lib/pieceprovider/piece_provider.go +++ b/lib/pieceprovider/cached_piecereader.go @@ -8,152 +8,17 @@ import ( lru "github.com/hashicorp/golang-lru/v2" "github.com/ipfs/go-cid" - logging "github.com/ipfs/go-log/v2" - pool "github.com/libp2p/go-buffer-pool" "go.opencensus.io/stats" "go.opencensus.io/tag" "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/curio/lib/paths" "github.com/filecoin-project/curio/lib/storiface" "github.com/filecoin-project/lotus/metrics" - "github.com/filecoin-project/lotus/storage/sealer/fr32" ) -var log = logging.Logger("piece-provider") - -type PieceProvider struct { - storage *paths.Remote - index paths.SectorIndex -} - -func NewPieceProvider(storage *paths.Remote, index paths.SectorIndex) *PieceProvider { - return &PieceProvider{ - storage: storage, - index: index, - } -} - -// tryReadUnsealedPiece will try to read the unsealed piece from an existing unsealed sector file for the given sector from any worker that has it. -// It will NOT try to schedule an Unseal of a sealed sector file for the read. -// -// Returns a nil reader if the piece does NOT exist in any unsealed file or there is no unsealed file for the given sector on any of the workers. -func (p *PieceProvider) tryReadUnsealedPiece(ctx context.Context, pc cid.Cid, sector storiface.SectorRef, pieceOffset storiface.UnpaddedByteIndex, pieceSize abi.UnpaddedPieceSize) (storiface.Reader, error) { - // acquire a lock purely for reading unsealed sectors - ctx, cancel := context.WithCancel(ctx) - if err := p.index.StorageLock(ctx, sector.ID, storiface.FTUnsealed, storiface.FTNone); err != nil { - cancel() - return nil, xerrors.Errorf("acquiring read sector lock: %w", err) - } - - // Reader returns a reader getter for an unsealed piece at the given offset in the given sector. - // The returned reader will be nil if none of the workers has an unsealed sector file containing - // the unsealed piece. - readerGetter, err := p.storage.Reader(ctx, sector, abi.PaddedPieceSize(pieceOffset.Padded()), pieceSize.Padded()) - if err != nil { - cancel() - log.Debugf("did not get storage reader;sector=%+v, err:%s", sector.ID, err) - return nil, err - } - if readerGetter == nil { - cancel() - return nil, nil - } - - pr, err := (&pieceReader{ - getReader: func(startOffset, readSize uint64) (io.ReadCloser, error) { - // The request is for unpadded bytes, at any offset. - // storage.Reader readers give us fr32-padded bytes, so we need to - // do the unpadding here. - - startOffsetAligned := storiface.UnpaddedFloor(startOffset) - startOffsetDiff := int(startOffset - uint64(startOffsetAligned)) - - endOffset := startOffset + readSize - endOffsetAligned := storiface.UnpaddedCeil(endOffset) - - r, err := readerGetter(startOffsetAligned.Padded(), endOffsetAligned.Padded()) - if err != nil { - return nil, xerrors.Errorf("getting reader at +%d: %w", startOffsetAligned, err) - } - - buf := pool.Get(fr32.BufSize(pieceSize.Padded())) - - upr, err := fr32.NewUnpadReaderBuf(r, pieceSize.Padded(), buf) - if err != nil { - r.Close() // nolint - return nil, xerrors.Errorf("creating unpadded reader: %w", err) - } - - bir := bufio.NewReaderSize(upr, 127) - if startOffset > uint64(startOffsetAligned) { - if _, err := bir.Discard(startOffsetDiff); err != nil { - r.Close() // nolint - return nil, xerrors.Errorf("discarding bytes for startOffset: %w", err) - } - } - - var closeOnce sync.Once - - return struct { - io.Reader - io.Closer - }{ - Reader: bir, - Closer: funcCloser(func() error { - closeOnce.Do(func() { - pool.Put(buf) - }) - return r.Close() - }), - }, nil - }, - len: pieceSize, - onClose: cancel, - pieceCid: pc, - }).init(ctx) - if err != nil || pr == nil { // pr == nil to make sure we don't return typed nil - cancel() - return nil, err - } - - return pr, err -} - -type funcCloser func() error - -func (f funcCloser) Close() error { - return f() -} - -var _ io.Closer = funcCloser(nil) - -// ReadPiece is used to read an Unsealed piece at the given offset and of the given size from a Sector -// If an Unsealed sector file exists with the Piece Unsealed in it, we'll use that for the read. -// Otherwise, an error is returned -func (p *PieceProvider) ReadPiece(ctx context.Context, sector storiface.SectorRef, pieceOffset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize, pieceCid cid.Cid) (storiface.Reader, error) { - if err := pieceOffset.Valid(); err != nil { - return nil, xerrors.Errorf("pieceOffset is not valid: %w", err) - } - if err := size.Validate(); err != nil { - return nil, xerrors.Errorf("size is not a valid piece size: %w", err) - } - - r, err := p.tryReadUnsealedPiece(ctx, pieceCid, sector, pieceOffset, size) - - if err != nil { - log.Errorf("error getting reading piece:%s", err) - return nil, err - } - - log.Debugf("returning reader to read unsealed piece, sector=%+v, pieceOffset=%d, size=%d", sector, pieceOffset, size) - - return r, nil -} - var _ storiface.Reader = &pieceReader{} // MaxPieceReaderBurnBytes - For small read skips, it's faster to "burn" some bytes than to set up new sector reader. diff --git a/lib/pieceprovider/piecepark_reader.go b/lib/pieceprovider/piecepark_reader.go new file mode 100644 index 000000000..531e66d43 --- /dev/null +++ b/lib/pieceprovider/piecepark_reader.go @@ -0,0 +1,64 @@ +package pieceprovider + +import ( + "context" + "io" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/curio/lib/paths" + "github.com/filecoin-project/curio/lib/storiface" +) + +type PieceParkReader struct { + storage *paths.Remote + index paths.SectorIndex +} + +func NewPieceParkReader(storage *paths.Remote, index paths.SectorIndex) *PieceParkReader { + return &PieceParkReader{ + storage: storage, + index: index, + } +} + +func (p *PieceParkReader) ReadPiece(ctx context.Context, pieceParkID storiface.PieceNumber, pieceSize int64, pc cid.Cid) (storiface.Reader, error) { + ctx, cancel := context.WithCancel(ctx) + + // Reader returns a reader getter for an unsealed piece at the given offset in the given sector. + // The returned reader will be nil if none of the workers has an unsealed sector file containing + // the unsealed piece. + readerGetter, err := p.storage.ReaderPiece(ctx, pieceParkID.Ref(), storiface.FTPiece, 0, pieceSize) + if err != nil { + cancel() + log.Debugw("failed to get reader for piece", "pieceParkID", pieceParkID, "error", err) + return nil, err + } + if readerGetter == nil { + cancel() + return nil, nil + } + + pr, err := (&pieceReader{ + getReader: func(startOffset, readSize uint64) (io.ReadCloser, error) { + r, err := readerGetter(int64(startOffset), int64(startOffset+readSize)) + if err != nil { + return nil, xerrors.Errorf("getting reader at +%d: %w", startOffset, err) + } + return r, nil + }, + len: abi.UnpaddedPieceSize(pieceSize), + onClose: cancel, + pieceCid: pc, + }).init(ctx) + if err != nil || pr == nil { // pr == nil to make sure we don't return typed nil + cancel() + return nil, err + } + + log.Debugw("got reader for piece", "pieceParkID", pieceParkID, "pieceSize", pieceSize, "pieceCID", pc) + return pr, err +} diff --git a/lib/pieceprovider/sector_reader.go b/lib/pieceprovider/sector_reader.go new file mode 100644 index 000000000..f8ef2d9bb --- /dev/null +++ b/lib/pieceprovider/sector_reader.go @@ -0,0 +1,157 @@ +package pieceprovider + +import ( + "bufio" + "context" + "io" + "sync" + + "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" + pool "github.com/libp2p/go-buffer-pool" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/curio/lib/paths" + "github.com/filecoin-project/curio/lib/storiface" + + "github.com/filecoin-project/lotus/storage/sealer/fr32" +) + +var log = logging.Logger("piece-provider") + +type SectorReader struct { + storage *paths.Remote + index paths.SectorIndex +} + +func NewSectorReader(storage *paths.Remote, index paths.SectorIndex) *SectorReader { + return &SectorReader{ + storage: storage, + index: index, + } +} + +// ReadPiece will try to read the unsealed piece from an existing unsealed sector file for the given sector from any worker that has it. +// It will NOT try to schedule an Unseal of a sealed sector file for the read. +// +// Returns a nil reader if the piece does NOT exist in any unsealed file or there is no unsealed file for the given sector on any of the workers. +func (p *SectorReader) ReadPiece(ctx context.Context, sector storiface.SectorRef, pieceOffset storiface.UnpaddedByteIndex, pieceSize abi.UnpaddedPieceSize, pc cid.Cid) (storiface.Reader, error) { + if err := pieceOffset.Valid(); err != nil { + return nil, xerrors.Errorf("pieceOffset is not valid: %w", err) + } + if err := pieceSize.Validate(); err != nil { + return nil, xerrors.Errorf("size is not a valid piece size: %w", err) + } + + // acquire a lock purely for reading unsealed sectors + ctx, cancel := context.WithCancel(ctx) + if err := p.index.StorageLock(ctx, sector.ID, storiface.FTUnsealed, storiface.FTNone); err != nil { + cancel() + return nil, xerrors.Errorf("acquiring read sector lock: %w", err) + } + + // Reader returns a reader getter for an unsealed piece at the given offset in the given sector. + // The returned reader will be nil if none of the workers has an unsealed sector file containing + // the unsealed piece. + readerGetter, err := p.storage.Reader(ctx, sector, abi.PaddedPieceSize(pieceOffset.Padded()), pieceSize.Padded()) + if err != nil { + cancel() + log.Debugf("did not get storage reader;sector=%+v, err:%s", sector.ID, err) + return nil, err + } + if readerGetter == nil { + cancel() + return nil, nil + } + + pr, err := (&pieceReader{ + getReader: func(startOffset, readSize uint64) (io.ReadCloser, error) { + // The request is for unpadded bytes, at any offset. + // storage.Reader readers give us fr32-padded bytes, so we need to + // do the unpadding here. + + startOffsetAligned := storiface.UnpaddedFloor(startOffset) + startOffsetDiff := int(startOffset - uint64(startOffsetAligned)) + + endOffset := startOffset + readSize + endOffsetAligned := storiface.UnpaddedCeil(endOffset) + + r, err := readerGetter(startOffsetAligned.Padded(), endOffsetAligned.Padded()) + if err != nil { + return nil, xerrors.Errorf("getting reader at +%d: %w", startOffsetAligned, err) + } + + buf := pool.Get(fr32.BufSize(pieceSize.Padded())) + + upr, err := fr32.NewUnpadReaderBuf(r, pieceSize.Padded(), buf) + if err != nil { + r.Close() // nolint + return nil, xerrors.Errorf("creating unpadded reader: %w", err) + } + + bir := bufio.NewReaderSize(upr, 127) + if startOffset > uint64(startOffsetAligned) { + if _, err := bir.Discard(startOffsetDiff); err != nil { + r.Close() // nolint + return nil, xerrors.Errorf("discarding bytes for startOffset: %w", err) + } + } + + var closeOnce sync.Once + + return struct { + io.Reader + io.Closer + }{ + Reader: bir, + Closer: funcCloser(func() error { + closeOnce.Do(func() { + pool.Put(buf) + }) + return r.Close() + }), + }, nil + }, + len: pieceSize, + onClose: cancel, + pieceCid: pc, + }).init(ctx) + if err != nil || pr == nil { // pr == nil to make sure we don't return typed nil + cancel() + return nil, err + } + + log.Debugf("returning reader to read unsealed piece, sector=%+v, pieceOffset=%d, size=%d", sector, pieceOffset, pieceSize) + + return pr, err +} + +// IsUnsealed checks if we have the unsealed piece at the given offset in an already +// existing unsealed file either locally or on any of the workers. +func (p *SectorReader) IsUnsealed(ctx context.Context, sector storiface.SectorRef, offset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize) (bool, error) { + if err := offset.Valid(); err != nil { + return false, xerrors.Errorf("offset is not valid: %w", err) + } + if err := size.Validate(); err != nil { + return false, xerrors.Errorf("size is not a valid piece size: %w", err) + } + + ctxLock, cancel := context.WithCancel(ctx) + defer cancel() + + if err := p.index.StorageLock(ctxLock, sector.ID, storiface.FTUnsealed, storiface.FTNone); err != nil { + return false, xerrors.Errorf("acquiring read sector lock: %w", err) + } + + return p.storage.CheckIsUnsealed(ctxLock, sector, abi.PaddedPieceSize(offset.Padded()), size.Padded()) +} + +type funcCloser func() error + +func (f funcCloser) Close() error { + return f() +} + +var _ io.Closer = funcCloser(nil) diff --git a/lib/proof/merkle_proof_memtree.go b/lib/proof/merkle_proof_memtree.go new file mode 100644 index 000000000..50c0b6b5b --- /dev/null +++ b/lib/proof/merkle_proof_memtree.go @@ -0,0 +1,85 @@ +package proof + +import "golang.org/x/xerrors" + +type RawMerkleProof struct { + Leaf [32]byte + Proof [][32]byte + Root [32]byte +} + +// MemtreeProof generates a Merkle proof for the given leaf index from the memtree. +// The memtree is a byte slice containing all the nodes of the Merkle tree, including leaves and internal nodes. +func MemtreeProof(memtree []byte, leafIndex int64) (*RawMerkleProof, error) { + // Currently, the implementation supports only binary trees (arity == 2) + const arity = 2 + + // Calculate the total number of nodes in the memtree + totalNodes := int64(len(memtree)) / NODE_SIZE + + // Reconstruct level sizes from the total number of nodes + // Starting from the number of leaves, compute the number of nodes at each level + nLeaves := (totalNodes + 1) / 2 + + currLevelCount := nLeaves + levelSizes := []int64{} + totalNodesCheck := int64(0) + + for { + levelSizes = append(levelSizes, currLevelCount) + totalNodesCheck += currLevelCount + + if currLevelCount == 1 { + break + } + // Compute the number of nodes in the next level + currLevelCount = (currLevelCount + int64(arity) - 1) / int64(arity) + } + + // Verify that the reconstructed total nodes match the actual total nodes + if totalNodesCheck != totalNodes { + return nil, xerrors.New("invalid memtree size; reconstructed total nodes do not match") + } + + // Compute the starting byte offset for each level in memtree + levelStarts := make([]int64, len(levelSizes)) + var offset int64 = 0 + for i, size := range levelSizes { + levelStarts[i] = offset + offset += size * NODE_SIZE + } + + // Validate the leaf index + if leafIndex < 0 || leafIndex >= levelSizes[0] { + return nil, xerrors.Errorf("invalid leaf index %d for %d leaves", leafIndex, levelSizes[0]) + } + + // Initialize the proof structure + proof := &RawMerkleProof{ + Proof: make([][NODE_SIZE]byte, 0, len(levelSizes)-1), + } + + // Extract the leaf hash from the memtree + leafOffset := levelStarts[0] + leafIndex*NODE_SIZE + copy(proof.Leaf[:], memtree[leafOffset:leafOffset+NODE_SIZE]) + + // Build the proof by collecting sibling hashes at each level + index := leafIndex + for level := 0; level < len(levelSizes)-1; level++ { + siblingIndex := index ^ 1 // Toggle the last bit to get the sibling index + + siblingOffset := levelStarts[level] + siblingIndex*NODE_SIZE + var siblingHash [NODE_SIZE]byte + copy(siblingHash[:], memtree[siblingOffset:siblingOffset+NODE_SIZE]) + proof.Proof = append(proof.Proof, siblingHash) + + // Move up to the parent index + index /= int64(arity) + } + + // Extract the root hash from the memtree + rootOffset := levelStarts[len(levelSizes)-1] + copy(proof.Root[:], memtree[rootOffset:rootOffset+NODE_SIZE]) + + return proof, nil +} diff --git a/lib/proof/merkle_sha254_memtree.go b/lib/proof/merkle_sha254_memtree.go new file mode 100644 index 000000000..fec3daecd --- /dev/null +++ b/lib/proof/merkle_sha254_memtree.go @@ -0,0 +1,74 @@ +package proof + +import ( + "io" + + pool "github.com/libp2p/go-buffer-pool" + "github.com/minio/sha256-simd" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/storage/sealer/fr32" +) + +const MaxMemtreeSize = 256 << 20 + +// BuildSha254Memtree builds a sha256 memtree from the input data +// Returned slice should be released to the pool after use +func BuildSha254Memtree(rawIn io.Reader, size abi.UnpaddedPieceSize) ([]byte, error) { + if size.Padded() > MaxMemtreeSize { + return nil, xerrors.Errorf("piece too large for memtree: %d", size) + } + + unpadBuf := pool.Get(int(size)) + // read into unpadBuf + _, err := io.ReadFull(rawIn, unpadBuf) + if err != nil { + pool.Put(unpadBuf) + return nil, xerrors.Errorf("failed to read into unpadBuf: %w", err) + } + + nLeaves := int64(size.Padded()) / NODE_SIZE + totalNodes, levelSizes := computeTotalNodes(nLeaves, 2) + memtreeBuf := pool.Get(int(totalNodes * NODE_SIZE)) + + fr32.Pad(unpadBuf, memtreeBuf[:size.Padded()]) + pool.Put(unpadBuf) + + d := sha256.New() + + levelStarts := make([]int64, len(levelSizes)) + levelStarts[0] = 0 + for i := 1; i < len(levelSizes); i++ { + levelStarts[i] = levelStarts[i-1] + levelSizes[i-1]*NODE_SIZE + } + + for level := 1; level < len(levelSizes); level++ { + levelNodes := levelSizes[level] + prevLevelStart := levelStarts[level-1] + currLevelStart := levelStarts[level] + + for i := int64(0); i < levelNodes; i++ { + leftOffset := prevLevelStart + (2*i)*NODE_SIZE + + d.Reset() + d.Write(memtreeBuf[leftOffset : leftOffset+(NODE_SIZE*2)]) + + outOffset := currLevelStart + i*NODE_SIZE + // sum calls append, so we give it a zero len slice at the correct offset + d.Sum(memtreeBuf[outOffset:outOffset]) + + // set top bits to 00 + memtreeBuf[outOffset+NODE_SIZE-1] &= 0x3F + } + } + + return memtreeBuf, nil +} + +func ComputeBinShaParent(left, right [NODE_SIZE]byte) [NODE_SIZE]byte { + out := sha256.Sum256(append(left[:], right[:]...)) + out[NODE_SIZE-1] &= 0x3F + return out +} diff --git a/lib/proof/tree_size.go b/lib/proof/tree_size.go new file mode 100644 index 000000000..46658e47d --- /dev/null +++ b/lib/proof/tree_size.go @@ -0,0 +1,28 @@ +package proof + +func computeTotalNodes(nLeaves, arity int64) (int64, []int64) { + totalNodes := int64(0) + levelCounts := []int64{} + currLevelCount := nLeaves + for currLevelCount > 0 { + levelCounts = append(levelCounts, currLevelCount) + totalNodes += currLevelCount + if currLevelCount == 1 { + break + } + currLevelCount = (currLevelCount + arity - 1) / arity + } + return totalNodes, levelCounts +} + +func NodeLevel(leaves, arity int64) int { + if leaves == 0 { + return 0 + } + level := 0 + for leaves > 1 { + leaves = (leaves + arity - 1) / arity + level++ + } + return level + 1 +} diff --git a/lib/proof/tree_size_test.go b/lib/proof/tree_size_test.go new file mode 100644 index 000000000..765168c89 --- /dev/null +++ b/lib/proof/tree_size_test.go @@ -0,0 +1,36 @@ +package proof + +import "testing" + +func TestNodeLevel(t *testing.T) { + tests := []struct { + leaves int64 + arity int64 + expected int + }{ + {0, 2, 0}, + {1, 2, 1}, + {2, 2, 2}, + {3, 2, 3}, + {4, 2, 3}, + {5, 2, 4}, + {8, 2, 4}, + {16, 2, 5}, + {1, 3, 1}, + {3, 3, 2}, + {4, 3, 3}, + {9, 3, 3}, + {10, 3, 4}, + {27, 3, 4}, + {28, 3, 5}, + {100, 10, 3}, + {1000, 10, 4}, + } + + for _, test := range tests { + result := NodeLevel(test.leaves, test.arity) + if result != test.expected { + t.Errorf("NodeLevel(%d, %d) = %d; expected %d", test.leaves, test.arity, result, test.expected) + } + } +} diff --git a/lib/proof/treed_build.go b/lib/proof/treed_build.go index 7145c9257..b5997756c 100644 --- a/lib/proof/treed_build.go +++ b/lib/proof/treed_build.go @@ -72,7 +72,7 @@ func BuildTreeD(data io.Reader, unpaddedData bool, outPath string, size abi.Padd } }() - outSize := treeSize(size) + outSize := binTreeSize(size) // allocate space for the tree err = out.Truncate(int64(outSize)) @@ -268,15 +268,9 @@ func BuildTreeD(data io.Reader, unpaddedData bool, outPath string, size abi.Padd return commCid, nil } -func treeSize(data abi.PaddedPieceSize) uint64 { - bytesToAlloc := uint64(data) - - // append bytes until we get to nodeSize - for todo := bytesToAlloc; todo > nodeSize; todo /= 2 { - bytesToAlloc += todo / 2 - } - - return bytesToAlloc +func binTreeSize(data abi.PaddedPieceSize) uint64 { + nodes, _ := computeTotalNodes(int64(data)/NODE_SIZE, 2) + return uint64(nodes) * NODE_SIZE } func layerOffset(size uint64, layer int) uint64 { diff --git a/lib/proof/treed_build_test.go b/lib/proof/treed_build_test.go index f69e98322..6d839cd97 100644 --- a/lib/proof/treed_build_test.go +++ b/lib/proof/treed_build_test.go @@ -20,10 +20,10 @@ import ( ) func TestTreeSize(t *testing.T) { - require.Equal(t, uint64(32), treeSize(abi.PaddedPieceSize(32))) - require.Equal(t, uint64(64+32), treeSize(abi.PaddedPieceSize(64))) - require.Equal(t, uint64(128+64+32), treeSize(abi.PaddedPieceSize(128))) - require.Equal(t, uint64(256+128+64+32), treeSize(abi.PaddedPieceSize(256))) + require.Equal(t, uint64(32), binTreeSize(abi.PaddedPieceSize(32))) + require.Equal(t, uint64(64+32), binTreeSize(abi.PaddedPieceSize(64))) + require.Equal(t, uint64(128+64+32), binTreeSize(abi.PaddedPieceSize(128))) + require.Equal(t, uint64(256+128+64+32), binTreeSize(abi.PaddedPieceSize(256))) } func TestTreeLayerOffset(t *testing.T) { diff --git a/market/ipni/chunker/serve-chunker.go b/market/ipni/chunker/serve-chunker.go index c2de9909f..fb775f28d 100644 --- a/market/ipni/chunker/serve-chunker.go +++ b/market/ipni/chunker/serve-chunker.go @@ -43,7 +43,7 @@ type ipniEntry struct { type ServeChunker struct { db *harmonydb.DB - pieceProvider *pieceprovider.PieceProvider + pieceProvider *pieceprovider.SectorReader indexStore *indexstore.IndexStore cpr *cachedreader.CachedPieceReader @@ -57,7 +57,7 @@ type ServeChunker struct { // This cache is only useful in the edge case when entry reads are very slow and time out - this makes retried reads faster const EntryCacheSize = 20 -func NewServeChunker(db *harmonydb.DB, pieceProvider *pieceprovider.PieceProvider, indexStore *indexstore.IndexStore, cpr *cachedreader.CachedPieceReader) *ServeChunker { +func NewServeChunker(db *harmonydb.DB, pieceProvider *pieceprovider.SectorReader, indexStore *indexstore.IndexStore, cpr *cachedreader.CachedPieceReader) *ServeChunker { return &ServeChunker{ db: db, pieceProvider: pieceProvider, diff --git a/market/ipni/ipni-provider/ipni-provider.go b/market/ipni/ipni-provider/ipni-provider.go index 8c33ae20b..004170675 100644 --- a/market/ipni/ipni-provider/ipni-provider.go +++ b/market/ipni/ipni-provider/ipni-provider.go @@ -30,7 +30,6 @@ import ( "github.com/filecoin-project/curio/deps" "github.com/filecoin-project/curio/harmony/harmonydb" - "github.com/filecoin-project/curio/lib/pieceprovider" "github.com/filecoin-project/curio/market/indexstore" "github.com/filecoin-project/curio/market/ipni/chunker" "github.com/filecoin-project/curio/market/ipni/ipniculib" @@ -59,11 +58,10 @@ type peerInfo struct { // Provider represents a provider for IPNI. type Provider struct { - db *harmonydb.DB - pieceProvider *pieceprovider.PieceProvider - indexStore *indexstore.IndexStore - sc *chunker.ServeChunker - keys map[string]*peerInfo // map[peerID String]Private_Key + db *harmonydb.DB + indexStore *indexstore.IndexStore + sc *chunker.ServeChunker + keys map[string]*peerInfo // map[peerID String]Private_Key // announceURLs enables sending direct announcements via HTTP. This is // the list of indexer URLs to send direct HTTP announce messages to. announceURLs []*url.URL @@ -155,7 +153,6 @@ func NewProvider(d *deps.Deps) (*Provider, error) { return &Provider{ db: d.DB, - pieceProvider: d.PieceProvider, indexStore: d.IndexStore, sc: d.ServeChunker, keys: keyMap, diff --git a/market/retrieval/piecehandler.go b/market/retrieval/piecehandler.go index f65aa1054..9f981b60e 100644 --- a/market/retrieval/piecehandler.go +++ b/market/retrieval/piecehandler.go @@ -73,7 +73,7 @@ func setHeaders(w http.ResponseWriter, pieceCid cid.Cid) { w.Header().Set("Cache-Control", "public, max-age=29030400, immutable") } -func serveContent(res http.ResponseWriter, req *http.Request, size abi.PaddedPieceSize, content io.ReadSeeker) { +func serveContent(res http.ResponseWriter, req *http.Request, size abi.UnpaddedPieceSize, content io.ReadSeeker) { // Note that the last modified time is a constant value because the data // in a piece identified by a cid will never change. @@ -84,6 +84,6 @@ func serveContent(res http.ResponseWriter, req *http.Request, size abi.PaddedPie } // Send the content - res.Header().Set("Content-Length", fmt.Sprintf("%d", size.Unpadded())) + res.Header().Set("Content-Length", fmt.Sprintf("%d", size)) http.ServeContent(res, req, "", lastModified, content) } diff --git a/pdp/README.md b/pdp/README.md new file mode 100644 index 000000000..9cb873e05 --- /dev/null +++ b/pdp/README.md @@ -0,0 +1,695 @@ +# 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:** + +```json +{ + "check": { + "name": "", + "hash": "", + "size": + }, + "notify": "" +} +``` + +- **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:** + + ```json + { + "pieceCID": "" + } + ``` + +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:** + +```json +{ + "id": "", + "service": "", + "pieceCID": "", + "notify_url": "", + "check_hash_codec": "", + "check_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 Proof Set + +- **Endpoint:** `POST /pdp/proof-sets` +- **Description:** Create a new proof set. +- **Authentication:** Requires a valid JWT token in the `Authorization` header. +- **Request Body:** + +```json +{ + "recordKeeper": "" +} +``` + +- **Fields:** + - `recordKeeper`: The Ethereum address of the record keeper. + +#### Response + +- **Status Code:** `201 Created` +- **Headers:** + - `Location`: The URL to check the status of the proof 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 Proof Set Creation Status + +- **Endpoint:** `GET /pdp/proof-sets/created/{txHash}` +- **Description:** Retrieve the status of a proof set creation. +- **Authentication:** Requires a valid JWT token in the `Authorization` header. +- **URL Parameters:** + - `txHash`: The transaction hash returned when creating the proof set. + +#### Response + +- **Status Code:** `200 OK` +- **Response Body:** + +```json +{ + "createMessageHash": "", + "proofsetCreated": , + "service": "", + "txStatus": "", + "ok": , + "proofSetId": +} +``` + +- **Fields:** + - `createMessageHash`: The transaction hash used to create the proof set. + - `proofsetCreated`: Whether the proof 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. + - `proofSetId`: The ID of the created proof 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`: Proof set creation not found for the given `txHash`. + +--- + +### 6. Get Proof Set Details + +- **Endpoint:** `GET /pdp/proof-sets/{proofSetID}` +- **Description:** Retrieve the details of a proof set, including its roots. +- **Authentication:** Requires a valid JWT token in the `Authorization` header. +- **URL Parameters:** + - `proofSetID`: The ID of the proof set. + +#### Response + +- **Status Code:** `200 OK` +- **Response Body:** + +```json +{ + "id": , + "roots": [ + { + "rootId": , + "rootCid": "", + "subrootCid": "", + "subrootOffset": + }, + // ... + ] +} +``` + +- **Fields:** + - `id`: The ID of the proof set. + - `roots`: An array of root entries. + - `rootId`: The ID of the root. + - `rootCid`: The CID of the root. + - `subrootCid`: The CID of the subroot. + - `subrootOffset`: The offset of the subroot. + +#### Errors + +- `400 Bad Request`: Missing or invalid `proofSetID`. +- `401 Unauthorized`: Missing or invalid JWT token, or proof set does not belong to the service. +- `404 Not Found`: Proof set not found. + +--- + +### 7. Delete a Proof Set *(To be implemented)* + +- **Endpoint:** `DELETE /pdp/proof-sets/{proofSetID}` +- **Description:** Remove the specified proof set entirely. +- **Authentication:** Requires a valid JWT token in the `Authorization` header. +- **URL Parameters:** + - `proofSetID`: The ID of the proof set. + +#### Response + +- **Status Code:** `204 No Content` + +#### Errors + +- `400 Bad Request`: Invalid request. +- `401 Unauthorized`: Missing or invalid JWT token. +- `404 Not Found`: Proof set not found. + +--- + +### 8. Add Roots to a Proof Set + +- **Endpoint:** `POST /pdp/proof-sets/{proofSetID}/roots` +- **Description:** Add roots to a proof set. +- **Authentication:** Requires a valid JWT token in the `Authorization` header. +- **URL Parameters:** + - `proofSetID`: The ID of the proof set. +- **Request Body:** An array of root entries. + +```json +[ + { + "rootCid": "", + "subroots": [ + { + "subrootCid": "" + }, + { + "subrootCid": "" + }, + // ... + ] + }, + // ... +] +``` + +- **Fields:** + - Each root entry contains: + - `rootCid`: The root CID. + - `subroots`: An array of subroot entries. + - Each subroot entry contains: + - `subrootCid`: The CID of the subroot. + +#### Constraints and Requirements + +- **Subroots Ordering:** The `subroots` must be provided in order **from largest to smallest size**. This ensures that no padding is required between subroots during the computation of the root CID. +- **Subroots Ownership:** All subroots must belong to the service making the request, and they must be previously uploaded and stored on the PDP service. +- **Subroot Sizes:** + - Each subroot size must be at least 128 bytes. +- **Subroot Alignment:** The subroots are concatenated without padding. Proper ordering ensures that the concatenated data aligns correctly for root computation. +- **Root CID Verification:** + - The provider computes the root CID from the provided subroots and verifies that it matches the `rootCid` specified in the request. + - If the computed root 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 subroots not ordered correctly. +- `401 Unauthorized`: Missing or invalid JWT token. +- `404 Not Found`: Proof set not found or subroots not found. +- `500 Internal Server Error`: Failed to process the request. + +--- + +### 9. Get Proof Set Root Details *(To be implemented)* + +- **Endpoint:** `GET /pdp/proof-sets/{proofSetID}/roots/{rootID}` +- **Description:** Retrieve the details of a root in a proof set. +- **Authentication:** Requires a valid JWT token in the `Authorization` header. +- **URL Parameters:** + - `proofSetID`: The ID of the proof set. + - `rootID`: The ID of the root. + +#### Response + +- **Status Code:** `200 OK` +- **Response Body:** Root details (to be defined). + +#### Errors + +- `400 Bad Request`: Invalid request. +- `401 Unauthorized`: Missing or invalid JWT token. +- `404 Not Found`: Proof set or root not found. + +--- + +### 10. Delete a Root from a Proof Set *(To be implemented)* + +- **Endpoint:** `DELETE /pdp/proof-sets/{proofSetID}/roots/{rootID}` +- **Description:** Remove a root from a proof set. +- **Authentication:** Requires a valid JWT token in the `Authorization` header. +- **URL Parameters:** + - `proofSetID`: The ID of the proof set. + - `rootID`: The ID of the root. + +#### Response + +- **Status Code:** `204 No Content` + +#### Errors + +- `400 Bad Request`: Invalid request. +- `401 Unauthorized`: Missing or invalid JWT token. +- `404 Not Found`: Proof set or root 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 +``` + +### 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` + +--- + +## Root CID Computation from Subroots + +When adding roots to a proof set using the `POST /pdp/proof-sets/{proofSetID}/roots` endpoint, the server performs validation and computation of the root CID from the provided subroots. + +### Root Computation Process + +1. **Validating Subroots:** + - Ensure that all subroots are owned by the requesting service. + - The subroots must have been previously uploaded and stored on the server. + +2. **Ordering Subroots:** + - **Important:** Subroots must be ordered from **largest to smallest size**. + - This ordering ensures that no padding is required between the subroots, aligning them correctly for root computation. + +3. **Piece Sizes and Alignment:** + - Each subroot corresponds to a piece with a size that is a power of two (e.g., 128 bytes, 256 bytes, 512 bytes). + - The concatenation of the subroots must not require padding to align to the sector size used in the computation. + +4. **Computing the Root CID:** + - The server uses the `GenerateUnsealedCID` function to compute the root CID from the subroots. + - This function emulates the computation performed in the Filecoin proofs implementation. + - The process involves stacking the subroots and combining them using a Merkle tree hash function. + +5. **Validation of Computed Root CID:** + - The computed root CID is compared with the `rootCid` provided in the request. + - If the computed root CID does not match the provided `rootCid`, the request is rejected with an error. + +### Constraints and Requirements + +- **Subroots Ownership:** All subroot CIDs must belong to the requesting service. +- **Subroots Existence:** All subroot CIDs must be valid and previously stored on the server. +- **Ordering of Subroots:** Must be ordered from largest to smallest. The sizes must be decreasing or equal; no subroot can be larger than the preceding one. +- **Subroot Sizes:** Each subroot size must be a power of two and at least 128 bytes. +- **Total Size Limit:** The total size of the concatenated subroots must not exceed the maximum allowed sector size. + +### Error Responses + +- **Invalid Subroot Order:** + - **Status Code:** `400 Bad Request` + - **Message:** `Subroots must be in descending order of size` + +- **Subroot Not Found or Unauthorized:** + - **Status Code:** `400 Bad Request` + - **Message:** `subroot CID not found or does not belong to service` + +- **Root CID Mismatch:** + - **Status Code:** `400 Bad Request` + - **Message:** `provided RootCID does not match generated RootCID` + +### Root Computation Function + +```go +func GenerateUnsealedCID(proofType abi.RegisteredSealProof, pieceInfos []abi.PieceInfo) (cid.Cid, error) +``` + +Where: + +- `pieceInfos` is a list of pieces (subroots) with their sizes and CIDs. +- The function builds a CommP tree from the subroots, combining them correctly according to their sizes and alignment. + +--- + +## Data Models + +### PieceHash + +Represents hash information about a piece. + +```json +{ + "name": "", + "hash": "", + "size": +} +``` + +- **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. + +### RootEntry + +Represents a root entry in a proof set. + +```json +{ + "rootId": , + "rootCid": "", + "subrootCid": "", + "subrootOffset": +} +``` + +- **Fields:** + - `rootId`: The ID of the root. + - `rootCid`: The CID of the root. + - `subrootCid`: The CID of the subroot. + - `subrootOffset`: The offset of the subroot. + +--- + +## 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:** + + ```http + POST /pdp/piece HTTP/1.1 + Host: example.com + Authorization: Bearer + Content-Type: application/json + + { + "check": { + "name": "sha2-256", + "hash": "", + "size": 12345 + }, + "notify": "https://example.com/notify" + } + ``` + + **Possible Responses:** + + - **Piece Already Exists:** + + ```http + HTTP/1.1 200 OK + Content-Type: application/json + + { + "pieceCID": "" + } + ``` + + - **Upload Required:** + + ```http + HTTP/1.1 201 Created + Location: /pdp/piece/upload/{uploadUUID} + ``` + +2. **Upload Piece Data:** + + **Request:** + + ```http + PUT /pdp/piece/upload/{uploadUUID} HTTP/1.1 + Host: example.com + Content-Type: application/octet-stream + Content-Length: 12345 + + + ``` + + **Response:** + + ```http + HTTP/1.1 204 No Content + ``` + +3. **Receive Notification (if `notify` was provided):** + + **Server's Notification Request:** + + ```http + POST /notify HTTP/1.1 + Host: example.com + Content-Type: application/json + + { + "id": "", + "service": "", + "pieceCID": "", + "notify_url": "https://example.com/notify", + "check_hash_codec": "sha2-256", + "check_hash": "" + } + ``` + + **Your Response:** + + ```http + HTTP/1.1 200 OK + ``` + +### Creating a Proof Set + +**Request:** + +```http +POST /pdp/proof-sets HTTP/1.1 +Host: example.com +Authorization: Bearer +Content-Type: application/json + +{ + "recordKeeper": "0x1234567890abcdef..." +} +``` + +**Response:** + +```http +HTTP/1.1 201 Created +Location: /pdp/proof-sets/created/0xabc123... +``` + +### Adding Roots to a Proof Set + +**Request:** + +```http +POST /pdp/proof-sets/{proofSetID}/roots HTTP/1.1 +Host: example.com +Authorization: Bearer +Content-Type: application/json + +[ + { + "rootCid": "", + "subroots": [ + { "subrootCid": "" }, + { "subrootCid": "" }, + { "subrootCid": "" } + ] + }, + // ... Additional roots if needed +] +``` + +**Response:** + +```http +HTTP/1.1 201 Created +``` diff --git a/pdp/auth.go b/pdp/auth.go new file mode 100644 index 000000000..e47dbc7d6 --- /dev/null +++ b/pdp/auth.go @@ -0,0 +1,105 @@ +package pdp + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/x509" + "fmt" + "net/http" + "strings" + + "github.com/golang-jwt/jwt/v4" +) + +// verifyJWTToken extracts and verifies the JWT token from the request and returns the serviceID. +func (p *PDPService) verifyJWTToken(r *http.Request) (string, error) { + // Get the Authorization header + authHeader := r.Header.Get("Authorization") + if authHeader == "" { + return "", fmt.Errorf("missing Authorization header") + } + + // Extract the token from the header + bearerTokenPrefix := "Bearer " + if !strings.HasPrefix(authHeader, bearerTokenPrefix) { + return "", fmt.Errorf("invalid Authorization header format") + } + tokenString := strings.TrimSpace(strings.TrimPrefix(authHeader, bearerTokenPrefix)) + if tokenString == "" { + return "", fmt.Errorf("empty token") + } + + // Variable to capture serviceID + var service string + + // Parse and verify the JWT token + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + var isEd25519 bool + // Ensure the signing method is ECDSA or EDDSA + if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok { + if _, ok := token.Method.(*jwt.SigningMethodEd25519); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + isEd25519 = true + } + + // Extract the service ID from the token claims + claims, ok := token.Claims.(jwt.MapClaims) + if !ok { + return nil, fmt.Errorf("invalid token claims") + } + + // Extract service_id from claims + serviceIf, ok := claims["service_name"] + if !ok { + return nil, fmt.Errorf("missing service_name claim") + } + + service, ok = serviceIf.(string) + if !ok { + return nil, fmt.Errorf("invalid service_name claim") + } + + // Query the database for the public key using serviceID + var pubKeyBytes []byte + ctx := r.Context() + err := p.db.QueryRow(ctx, ` + SELECT pubkey FROM pdp_services WHERE service_label=$1 + `, service).Scan(&pubKeyBytes) + if err != nil { + return nil, fmt.Errorf("failed to retrieve public key for service_id %s: %v", service, err) + } + + // Parse the public key + pubKeyInterface, err := x509.ParsePKIXPublicKey(pubKeyBytes) + if err != nil { + return nil, fmt.Errorf("failed to parse public key: %v", err) + } + + if isEd25519 { + pubKey, ok := pubKeyInterface.(ed25519.PublicKey) + if !ok { + return nil, fmt.Errorf("public key is not ED25519") + } + + // Return the public key for signature verification + return pubKey, nil + } + + pubKey, ok := pubKeyInterface.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("public key is not ECDSA") + } + + // Return the public key for signature verification + return pubKey, nil + }) + if err != nil { + return "", err + } + if !token.Valid { + return "", fmt.Errorf("invalid token") + } + + return service, nil +} diff --git a/pdp/contract/IPDPProvingSchedule.abi b/pdp/contract/IPDPProvingSchedule.abi new file mode 100644 index 000000000..df191dd6d --- /dev/null +++ b/pdp/contract/IPDPProvingSchedule.abi @@ -0,0 +1,73 @@ +[ + { + "type": "function", + "name": "challengeWindow", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "getChallengesPerProof", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "getMaxProvingPeriod", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "initChallengeWindowStart", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "nextChallengeWindowStart", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + } +] diff --git a/pdp/contract/IPDPProvingSchedule.json b/pdp/contract/IPDPProvingSchedule.json new file mode 100644 index 000000000..b1fbe4526 --- /dev/null +++ b/pdp/contract/IPDPProvingSchedule.json @@ -0,0 +1 @@ +{"abi":[{"type":"function","name":"challengeWindow","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"pure"},{"type":"function","name":"getChallengesPerProof","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"pure"},{"type":"function","name":"getMaxProvingPeriod","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"pure"},{"type":"function","name":"initChallengeWindowStart","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"pure"},{"type":"function","name":"nextChallengeWindowStart","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"challengeWindow()":"861a1412","getChallengesPerProof()":"47d3dfe7","getMaxProvingPeriod()":"f2f12333","initChallengeWindowStart()":"21918cea","nextChallengeWindowStart(uint256)":"8bf96d28"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.23+commit.f704f362\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"challengeWindow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getChallengesPerProof\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMaxProvingPeriod\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initChallengeWindowStart\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"nextChallengeWindowStart\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"challengeWindow()\":{\"returns\":{\"_0\":\"Challenge window size in epochs\"}},\"getChallengesPerProof()\":{\"returns\":{\"_0\":\"Number of challenges required per proof\"}},\"getMaxProvingPeriod()\":{\"returns\":{\"_0\":\"Maximum proving period in epochs\"}},\"nextChallengeWindowStart(uint256)\":{\"params\":{\"setId\":\"The ID of the proof set\"},\"returns\":{\"_0\":\"The block number when the next challenge window starts\"}}},\"title\":\"IPDPProvingWindow\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"challengeWindow()\":{\"notice\":\"Returns the number of epochs at the end of a proving period during which proofs can be submitted\"},\"getChallengesPerProof()\":{\"notice\":\"Returns the required number of challenges/merkle inclusion proofs per proof set\"},\"getMaxProvingPeriod()\":{\"notice\":\"Returns the number of epochs allowed before challenges must be resampled\"},\"initChallengeWindowStart()\":{\"notice\":\"Value for initializing the challenge window start for any proof set assuming proving period starts now\"},\"nextChallengeWindowStart(uint256)\":{\"notice\":\"Calculates the start of the next challenge window for a given proof set\"}},\"notice\":\"Interface for PDP Service SLA specifications\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/IPDPProvingSchedule.sol\":\"IPDPProvingSchedule\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":@pythnetwork/pyth-sdk-solidity/=node_modules/@pythnetwork/pyth-sdk-solidity/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\"]},\"sources\":{\"src/IPDPProvingSchedule.sol\":{\"keccak256\":\"0x6fc7848345c358a7a18e43ad9d93c1ea5fecf9d3f0daca721576d6de96d797b2\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://ab29f0b39894650cf74b6a771e50bc50c91d54f6ba6e5a1b11c7cb1d7878d0cf\",\"dweb:/ipfs/QmawGZjCfua9dbJsqCzN6J9v3kLsE4oRLwMhbbcE4RYUNh\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.23+commit.f704f362"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"pure","type":"function","name":"challengeWindow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"pure","type":"function","name":"getChallengesPerProof","outputs":[{"internalType":"uint64","name":"","type":"uint64"}]},{"inputs":[],"stateMutability":"pure","type":"function","name":"getMaxProvingPeriod","outputs":[{"internalType":"uint64","name":"","type":"uint64"}]},{"inputs":[],"stateMutability":"pure","type":"function","name":"initChallengeWindowStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"nextChallengeWindowStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{"challengeWindow()":{"returns":{"_0":"Challenge window size in epochs"}},"getChallengesPerProof()":{"returns":{"_0":"Number of challenges required per proof"}},"getMaxProvingPeriod()":{"returns":{"_0":"Maximum proving period in epochs"}},"nextChallengeWindowStart(uint256)":{"params":{"setId":"The ID of the proof set"},"returns":{"_0":"The block number when the next challenge window starts"}}},"version":1},"userdoc":{"kind":"user","methods":{"challengeWindow()":{"notice":"Returns the number of epochs at the end of a proving period during which proofs can be submitted"},"getChallengesPerProof()":{"notice":"Returns the required number of challenges/merkle inclusion proofs per proof set"},"getMaxProvingPeriod()":{"notice":"Returns the number of epochs allowed before challenges must be resampled"},"initChallengeWindowStart()":{"notice":"Value for initializing the challenge window start for any proof set assuming proving period starts now"},"nextChallengeWindowStart(uint256)":{"notice":"Calculates the start of the next challenge window for a given proof set"}},"version":1}},"settings":{"remappings":["@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","@pythnetwork/pyth-sdk-solidity/=node_modules/@pythnetwork/pyth-sdk-solidity/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/IPDPProvingSchedule.sol":"IPDPProvingSchedule"},"evmVersion":"shanghai","libraries":{}},"sources":{"src/IPDPProvingSchedule.sol":{"keccak256":"0x6fc7848345c358a7a18e43ad9d93c1ea5fecf9d3f0daca721576d6de96d797b2","urls":["bzz-raw://ab29f0b39894650cf74b6a771e50bc50c91d54f6ba6e5a1b11c7cb1d7878d0cf","dweb:/ipfs/QmawGZjCfua9dbJsqCzN6J9v3kLsE4oRLwMhbbcE4RYUNh"],"license":"UNLICENSED"}},"version":1},"id":0} \ No newline at end of file diff --git a/pdp/contract/PDPVerifier.abi b/pdp/contract/PDPVerifier.abi new file mode 100644 index 000000000..ba75e0195 --- /dev/null +++ b/pdp/contract/PDPVerifier.abi @@ -0,0 +1,1088 @@ +[ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "BURN_ACTOR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "EXTRA_DATA_MAX_SIZE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FIL_USD_PRICE_FEED_ID", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "LEAF_SIZE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MAX_ENQUEUED_REMOVALS", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MAX_ROOT_SIZE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "NO_CHALLENGE_SCHEDULED", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "PYTH", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IPyth" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "RANDOMNESS_PRECOMPILE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "SECONDS_IN_DAY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "UPGRADE_INTERFACE_VERSION", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "addRoots", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rootData", + "type": "tuple[]", + "internalType": "struct PDPVerifier.RootData[]", + "components": [ + { + "name": "root", + "type": "tuple", + "internalType": "struct Cids.Cid", + "components": [ + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "rawSize", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "extraData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "calculateProofFee", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "estimatedGasFee", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "claimProofSetOwnership", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "createProofSet", + "inputs": [ + { + "name": "listenerAddr", + "type": "address", + "internalType": "address" + }, + { + "name": "extraData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "deleteProofSet", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "extraData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "findRootIds", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "leafIndexs", + "type": "uint256[]", + "internalType": "uint256[]" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple[]", + "internalType": "struct PDPVerifier.RootIdAndOffset[]", + "components": [ + { + "name": "rootId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "offset", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getChallengeFinality", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getChallengeRange", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getFILUSDPrice", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "", + "type": "int32", + "internalType": "int32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getNextChallengeEpoch", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getNextProofSetId", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getNextRootId", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getProofSetLastProvenEpoch", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getProofSetLeafCount", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getProofSetListener", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getProofSetOwner", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + }, + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRandomness", + "inputs": [ + { + "name": "epoch", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRootCid", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rootId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct Cids.Cid", + "components": [ + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRootLeafCount", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rootId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getScheduledRemovals", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256[]", + "internalType": "uint256[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "_challengeFinality", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "nextProvingPeriod", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "challengeEpoch", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "extraData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "proofSetLive", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "proposeProofSetOwner", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "provePossession", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "proofs", + "type": "tuple[]", + "internalType": "struct PDPVerifier.Proof[]", + "components": [ + { + "name": "leaf", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "proof", + "type": "bytes32[]", + "internalType": "bytes32[]" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "proxiableUUID", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "rootChallengable", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rootId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "rootLive", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rootId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "scheduleRemovals", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rootIds", + "type": "uint256[]", + "internalType": "uint256[]" + }, + { + "name": "extraData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "upgradeToAndCall", + "inputs": [ + { + "name": "newImplementation", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "event", + "name": "Debug", + "inputs": [ + { + "name": "message", + "type": "string", + "indexed": false, + "internalType": "string" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ProofFeePaid", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "fee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "price", + "type": "uint64", + "indexed": false, + "internalType": "uint64" + }, + { + "name": "expo", + "type": "int32", + "indexed": false, + "internalType": "int32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ProofSetCreated", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ProofSetDeleted", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "deletedLeafCount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ProofSetEmpty", + "inputs": [ + { + "name": "setId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RootsAdded", + "inputs": [ + { + "name": "firstAdded", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RootsRemoved", + "inputs": [ + { + "name": "rootIds", + "type": "uint256[]", + "indexed": true, + "internalType": "uint256[]" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Upgraded", + "inputs": [ + { + "name": "implementation", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AddressEmptyCode", + "inputs": [ + { + "name": "target", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "ERC1967InvalidImplementation", + "inputs": [ + { + "name": "implementation", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "ERC1967NonPayable", + "inputs": [] + }, + { + "type": "error", + "name": "FailedCall", + "inputs": [] + }, + { + "type": "error", + "name": "IndexedError", + "inputs": [ + { + "name": "idx", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "msg", + "type": "string", + "internalType": "string" + } + ] + }, + { + "type": "error", + "name": "InvalidInitialization", + "inputs": [] + }, + { + "type": "error", + "name": "NotInitializing", + "inputs": [] + }, + { + "type": "error", + "name": "OwnableInvalidOwner", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "OwnableUnauthorizedAccount", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "UUPSUnauthorizedCallContext", + "inputs": [] + }, + { + "type": "error", + "name": "UUPSUnsupportedProxiableUUID", + "inputs": [ + { + "name": "slot", + "type": "bytes32", + "internalType": "bytes32" + } + ] + } +] diff --git a/pdp/contract/PDPVerifier.json b/pdp/contract/PDPVerifier.json new file mode 100644 index 000000000..dc0982fbb --- /dev/null +++ b/pdp/contract/PDPVerifier.json @@ -0,0 +1 @@ +{"abi":[{"type":"constructor","inputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"BURN_ACTOR","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"EXTRA_DATA_MAX_SIZE","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"FIL_USD_PRICE_FEED_ID","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"LEAF_SIZE","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"MAX_ENQUEUED_REMOVALS","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"MAX_ROOT_SIZE","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"NO_CHALLENGE_SCHEDULED","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"PYTH","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IPyth"}],"stateMutability":"view"},{"type":"function","name":"RANDOMNESS_PRECOMPILE","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"SECONDS_IN_DAY","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"UPGRADE_INTERFACE_VERSION","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"addRoots","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"rootData","type":"tuple[]","internalType":"struct PDPVerifier.RootData[]","components":[{"name":"root","type":"tuple","internalType":"struct Cids.Cid","components":[{"name":"data","type":"bytes","internalType":"bytes"}]},{"name":"rawSize","type":"uint256","internalType":"uint256"}]},{"name":"extraData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"calculateProofFee","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"estimatedGasFee","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"claimProofSetOwnership","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"createProofSet","inputs":[{"name":"listenerAddr","type":"address","internalType":"address"},{"name":"extraData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"payable"},{"type":"function","name":"deleteProofSet","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"extraData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"findRootIds","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"leafIndexs","type":"uint256[]","internalType":"uint256[]"}],"outputs":[{"name":"","type":"tuple[]","internalType":"struct PDPVerifier.RootIdAndOffset[]","components":[{"name":"rootId","type":"uint256","internalType":"uint256"},{"name":"offset","type":"uint256","internalType":"uint256"}]}],"stateMutability":"view"},{"type":"function","name":"getChallengeFinality","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getChallengeRange","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getFILUSDPrice","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"},{"name":"","type":"int32","internalType":"int32"}],"stateMutability":"view"},{"type":"function","name":"getNextChallengeEpoch","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getNextProofSetId","inputs":[],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"getNextRootId","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getProofSetLastProvenEpoch","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getProofSetLeafCount","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getProofSetListener","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getProofSetOwner","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getRandomness","inputs":[{"name":"epoch","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getRootCid","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"rootId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"tuple","internalType":"struct Cids.Cid","components":[{"name":"data","type":"bytes","internalType":"bytes"}]}],"stateMutability":"view"},{"type":"function","name":"getRootLeafCount","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"rootId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getScheduledRemovals","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256[]","internalType":"uint256[]"}],"stateMutability":"view"},{"type":"function","name":"initialize","inputs":[{"name":"_challengeFinality","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"nextProvingPeriod","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"challengeEpoch","type":"uint256","internalType":"uint256"},{"name":"extraData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"proofSetLive","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"proposeProofSetOwner","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"provePossession","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"proofs","type":"tuple[]","internalType":"struct PDPVerifier.Proof[]","components":[{"name":"leaf","type":"bytes32","internalType":"bytes32"},{"name":"proof","type":"bytes32[]","internalType":"bytes32[]"}]}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"proxiableUUID","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"renounceOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"rootChallengable","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"rootId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"rootLive","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"rootId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"scheduleRemovals","inputs":[{"name":"setId","type":"uint256","internalType":"uint256"},{"name":"rootIds","type":"uint256[]","internalType":"uint256[]"},{"name":"extraData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"transferOwnership","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"upgradeToAndCall","inputs":[{"name":"newImplementation","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"payable"},{"type":"event","name":"Debug","inputs":[{"name":"message","type":"string","indexed":false,"internalType":"string"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ProofFeePaid","inputs":[{"name":"setId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"fee","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"price","type":"uint64","indexed":false,"internalType":"uint64"},{"name":"expo","type":"int32","indexed":false,"internalType":"int32"}],"anonymous":false},{"type":"event","name":"ProofSetCreated","inputs":[{"name":"setId","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"ProofSetDeleted","inputs":[{"name":"setId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"deletedLeafCount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"ProofSetEmpty","inputs":[{"name":"setId","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"RootsAdded","inputs":[{"name":"firstAdded","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"RootsRemoved","inputs":[{"name":"rootIds","type":"uint256[]","indexed":true,"internalType":"uint256[]"}],"anonymous":false},{"type":"event","name":"Upgraded","inputs":[{"name":"implementation","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AddressEmptyCode","inputs":[{"name":"target","type":"address","internalType":"address"}]},{"type":"error","name":"ERC1967InvalidImplementation","inputs":[{"name":"implementation","type":"address","internalType":"address"}]},{"type":"error","name":"ERC1967NonPayable","inputs":[]},{"type":"error","name":"FailedCall","inputs":[]},{"type":"error","name":"IndexedError","inputs":[{"name":"idx","type":"uint256","internalType":"uint256"},{"name":"msg","type":"string","internalType":"string"}]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"name":"owner","type":"address","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"}]},{"type":"error","name":"UUPSUnauthorizedCallContext","inputs":[]},{"type":"error","name":"UUPSUnsupportedProxiableUUID","inputs":[{"name":"slot","type":"bytes32","internalType":"bytes32"}]}],"bytecode":{"object":"0x60a06040523060805234801562000014575f80fd5b506200001f62000025565b620000d9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000765760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b60805161454e620001005f395f8181612d5a01528181612d830152612ec2015261454e5ff3fe60806040526004361061025f575f3560e01c806367e406d51161013f5780639f8cb3bd116100b3578063f2fde38b11610078578063f2fde38b14610786578063f58f952b146107a5578063f5cac1ba146107b8578063f83758fe146107d7578063faa67163146107ea578063fe4b84df14610809575f80fd5b80639f8cb3bd146106e2578063ad3cb1cc146106f7578063c0e1594914610734578063d49245c114610748578063ee3dac6514610767575f80fd5b806371cf2a161161010457806371cf2a1614610603578063847d1d061461062257806389208ba9146106415780638da5cb5b146106605780638ea417e51461069c5780639153e64b146106c3575f80fd5b806367e406d51461055e5780636ba4608f146105855780636cb55c16146105a45780636fa44692146105c3578063715018a6146105ef575f80fd5b80633f84135f116101d6578063473310501161019b578063473310501461049d5780634903704a146104cc5780634f1ef286146104eb5780634fa27920146104fe57806352d1902d1461053457806361a52a3614610548575f80fd5b80633f84135f146103ee578063453f4f621461040d57806345c0b92d1461042c578063462dd4491461044b5780634726075b1461045e575f80fd5b806315b175701161022757806315b175701461031b57806316e2bcd51461033557806319c759501461034f57806331601226146103825780633b68e4e9146103a15780633b7ae913146103c2575f80fd5b8063029b4646146102635780630528a55b1461028b5780630a4d7932146102b75780630a6a63f1146102ca57806311c0ee4a146102fc575b5f80fd5b34801561026e575f80fd5b5061027861080081565b6040519081526020015b60405180910390f35b348015610296575f80fd5b506102aa6102a5366004613939565b610828565b6040516102829190613980565b6102786102c5366004613a26565b61090f565b3480156102d5575f80fd5b506102e4606360ff60981b0181565b6040516001600160a01b039091168152602001610282565b348015610307575f80fd5b50610278610316366004613a67565b610b06565b348015610326575f80fd5b506102e46006607f60991b0181565b348015610340575f80fd5b50610278660400000000000081565b34801561035a575f80fd5b506102787f150ac9b959aee0051e4091f0ef5216d941f590e1c5e7f91cf7635b5c11628c0e81565b34801561038d575f80fd5b506102e461039c366004613ada565b610d3a565b3480156103ac575f80fd5b506103c06103bb366004613a67565b610d7b565b005b3480156103cd575f80fd5b506103e16103dc366004613af1565b61102e565b6040516102829190613b5e565b3480156103f9575f80fd5b50610278610408366004613ada565b61111e565b348015610418575f80fd5b50610278610427366004613ada565b611156565b348015610437575f80fd5b506103c0610446366004613b78565b61124c565b348015610456575f80fd5b506102785f81565b348015610469575f80fd5b5061047d610478366004613ada565b6115c4565b604080516001600160a01b03938416815292909116602083015201610282565b3480156104a8575f80fd5b506104bc6104b7366004613af1565b611618565b6040519015158152602001610282565b3480156104d7575f80fd5b506102786104e6366004613af1565b611660565b6103c06104f9366004613c0a565b6116bf565b348015610509575f80fd5b506105126116de565b604080516001600160401b03909316835260039190910b602083015201610282565b34801561053f575f80fd5b506102786117ff565b348015610553575f80fd5b506102786201518081565b348015610569575f80fd5b506102e473a2aa501b19aff244d90cc15a4cf739d2725b572981565b348015610590575f80fd5b5061027861059f366004613ada565b61181a565b3480156105af575f80fd5b506103c06105be366004613ca9565b611852565b3480156105ce575f80fd5b506105e26105dd366004613ada565b611958565b6040516102829190613cd3565b3480156105fa575f80fd5b506103c0611a27565b34801561060e575f80fd5b506104bc61061d366004613af1565b611a3a565b34801561062d575f80fd5b506103c061063c366004613d16565b611b49565b34801561064c575f80fd5b5061027861065b366004613ada565b611d2e565b34801561066b575f80fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166102e4565b3480156106a7575f80fd5b506001546040516001600160401b039091168152602001610282565b3480156106ce575f80fd5b506102786106dd366004613af1565b611d66565b3480156106ed575f80fd5b506102786107d081565b348015610702575f80fd5b50610727604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516102829190613d44565b34801561073f575f80fd5b50610278602081565b348015610753575f80fd5b50610278610762366004613ada565b611da9565b348015610772575f80fd5b506103c0610781366004613ada565b611de1565b348015610791575f80fd5b506103c06107a0366004613d56565b611eb3565b6103c06107b3366004613939565b611ef0565b3480156107c3575f80fd5b506104bc6107d2366004613ada565b6122d4565b3480156107e2575f80fd5b505f54610278565b3480156107f5575f80fd5b50610278610804366004613ada565b612308565b348015610814575f80fd5b506103c0610823366004613ada565b612340565b5f838152600560205260408120546060919061084390612457565b61084f90610100613d83565b90505f836001600160401b0381111561086a5761086a613bc6565b6040519080825280602002602001820160405280156108ae57816020015b604080518082019091525f80825260208201528152602001906001900390816108885790505b5090505f5b84811015610903576108de878787848181106108d1576108d1613d96565b9050602002013585612546565b8282815181106108f0576108f0613d96565b60209081029190910101526001016108b3565b509150505b9392505050565b5f61080082111561093b5760405162461bcd60e51b815260040161093290613daa565b60405180910390fd5b5f610944612726565b90508034101561098a5760405162461bcd60e51b81526020600482015260116024820152701cde589a5b08199959481b9bdd081b595d607a1b6044820152606401610932565b6109938161274b565b803411156109d057336108fc6109a98334613d83565b6040518115909202915f818181858888f193505050501580156109ce573d5f803e3d5ffd5b505b600180545f916001600160401b0390911690826109ec83613dd8565b82546001600160401b039182166101009390930a928302928202191691909117909155165f81815260066020908152604080832083905560078252808320839055600b825280832080546001600160a01b031990811633179091556008835281842080546001600160a01b038d16921682179055600d90925282209190915590915015610ad357604051634a6a0d9b60e11b81526001600160a01b038716906394d41b3690610aa590849033908a908a90600401613e25565b5f604051808303815f87803b158015610abc575f80fd5b505af1158015610ace573d5f803e3d5ffd5b505050505b60405181907f5979d495e336598dba8459e44f8eb2a1c957ce30fcc10cabea4bb0ffe969df6a905f90a295945050505050565b5f610800821115610b295760405162461bcd60e51b815260040161093290613daa565b610b32866122d4565b610b4e5760405162461bcd60e51b815260040161093290613e4f565b83610b9b5760405162461bcd60e51b815260206004820152601a60248201527f4d75737420616464206174206c65617374206f6e6520726f6f740000000000006044820152606401610932565b5f868152600b60205260409020546001600160a01b03163314610c005760405162461bcd60e51b815260206004820152601c60248201527f4f6e6c7920746865206f776e65722063616e2061646420726f6f7473000000006044820152606401610932565b5f86815260056020526040812054905b85811015610c8157610c788882898985818110610c2f57610c2f613d96565b9050602002810190610c419190613e7b565b610c4b9080613e99565b8a8a86818110610c5d57610c5d613d96565b9050602002810190610c6f9190613e7b565b6020013561281c565b50600101610c10565b505f878152600860205260409020546001600160a01b03168015610d03576040516312d5d66f60e01b81526001600160a01b038216906312d5d66f90610cd5908b9086908c908c908c908c90600401613ead565b5f604051808303815f87803b158015610cec575f80fd5b505af1158015610cfe573d5f803e3d5ffd5b505050505b60405182907fde16f8a35fc44238b21a493b7267f7a5064f2bc1b3a1af51deeb4b5a47281d47905f90a25090505b95945050505050565b5f610d44826122d4565b610d605760405162461bcd60e51b815260040161093290613e4f565b505f908152600860205260409020546001600160a01b031690565b610800811115610d9d5760405162461bcd60e51b815260040161093290613daa565b610da6856122d4565b610dc25760405162461bcd60e51b815260040161093290613e4f565b5f858152600b60205260409020546001600160a01b03163314610e3c5760405162461bcd60e51b815260206004820152602c60248201527f4f6e6c7920746865206f776e65722063616e207363686564756c652072656d6f60448201526b76616c206f6620726f6f747360a01b6064820152608401610932565b5f858152600a60205260409020546107d090610e589085613fb2565b1115610ecc5760405162461bcd60e51b815260206004820152603a60248201527f546f6f206d616e792072656d6f76616c73207761697420666f72206e6578742060448201527f70726f76696e6720706572696f6420746f207363686564756c650000000000006064820152608401610932565b5f5b83811015610fa6575f86815260056020526040902054858583818110610ef657610ef6613d96565b9050602002013510610f5e5760405162461bcd60e51b815260206004820152602b60248201527f43616e206f6e6c79207363686564756c652072656d6f76616c206f662065786960448201526a7374696e6720726f6f747360a81b6064820152608401610932565b5f868152600a60205260409020858583818110610f7d57610f7d613d96565b8354600180820186555f9586526020958690209290950293909301359201919091555001610ece565b505f858152600860205260409020546001600160a01b031680156110265760405163257be8e960e11b81526001600160a01b03821690634af7d1d290610ff89089908990899089908990600401613fc5565b5f604051808303815f87803b15801561100f575f80fd5b505af1158015611021573d5f803e3d5ffd5b505050505b505050505050565b604080516020810190915260608152611046836122d4565b6110625760405162461bcd60e51b815260040161093290613e4f565b5f83815260026020908152604080832085845282529182902082519182019092528154909190829082906110959061401d565b80601f01602080910402602001604051908101604052809291908181526020018280546110c19061401d565b801561110c5780601f106110e35761010080835404028352916020019161110c565b820191905f5260205f20905b8154815290600101906020018083116110ef57829003601f168201915b50505050508152505090505b92915050565b5f611128826122d4565b6111445760405162461bcd60e51b815260040161093290613e4f565b505f9081526006602052604090205490565b5f805f6006607f60991b016001600160a01b03168460405160200161117d91815260200190565b60408051601f198184030181529082905261119791614055565b5f60405180830381855afa9150503d805f81146111cf576040519150601f19603f3d011682016040523d82523d5f602084013e6111d4565b606091505b5091509150816112305760405162461bcd60e51b815260206004820152602160248201527f52616e646f6d6e65737320707265636f6d70696c652063616c6c206661696c656044820152601960fa1b6064820152608401610932565b808060200190518101906112449190614066565b949350505050565b61080081111561126e5760405162461bcd60e51b815260040161093290613daa565b5f848152600b60205260409020546001600160a01b031633146112a35760405162461bcd60e51b81526004016109329061407d565b5f848152600660205260409020546113125760405162461bcd60e51b815260206004820152602c60248201527f63616e206f6e6c792073746172742070726f76696e67206f6e6365206c65617660448201526b195cc8185c9948185919195960a21b6064820152608401610932565b5f848152600d6020526040902054611335575f848152600d602052604090204390555b5f848152600a6020526040812080549091906001600160401b0381111561135e5761135e613bc6565b604051908082528060200260200182016040528015611387578160200160208202803683370190505b5090505f5b815181101561140a57825483906113a590600190613d83565b815481106113b5576113b5613d96565b905f5260205f2001548282815181106113d0576113d0613d96565b602002602001018181525050828054806113ec576113ec6140cb565b5f8281526020812082015f199081019190915501905560010161138c565b5061141586826129df565b5f868152600660209081526040808320546009909252822055546114399043613fb2565b8510156114be5760405162461bcd60e51b815260206004820152604760248201527f6368616c6c656e67652065706f6368206d757374206265206174206c6561737460448201527f206368616c6c656e676546696e616c6974792065706f63687320696e207468656064820152662066757475726560c81b608482015260a401610932565b5f868152600760209081526040808320889055600690915281205490036115255760405186907f323c29bc8d678a5d987b90a321982d10b9a91bcad071a9e445879497bf0e68e7905f90a25f868152600d6020908152604080832083905560079091528120555b5f868152600860205260409020546001600160a01b031680156115bb575f87815260076020908152604080832054600690925291829020549151632a89faf360e21b81526001600160a01b0384169263aa27ebcc9261158d928c92908b908b906004016140df565b5f604051808303815f87803b1580156115a4575f80fd5b505af11580156115b6573d5f803e3d5ffd5b505050505b50505050505050565b5f806115cf836122d4565b6115eb5760405162461bcd60e51b815260040161093290613e4f565b50505f908152600b6020908152604080832054600c909252909120546001600160a01b0391821692911690565b5f611622836122d4565b801561163a57505f8381526005602052604090205482105b80156109085750505f918252600360209081526040808420928452919052902054151590565b5f828152600960209081526040822054829161167c919061410f565b90505f806116886116de565b915091506116b585838386600d5f8c81526020019081526020015f2054436116b09190613d83565b612a6f565b9695505050505050565b6116c7612d4f565b6116d082612df3565b6116da8282612dfb565b5050565b60405163052571af60e51b81527f150ac9b959aee0051e4091f0ef5216d941f590e1c5e7f91cf7635b5c11628c0e60048201526201518060248201525f908190819073a2aa501b19aff244d90cc15a4cf739d2725b57299063a4ae35e090604401608060405180830381865afa15801561175a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061177e9190614126565b90505f815f015160070b136117ee5760405162461bcd60e51b815260206004820152603060248201527f6661696c656420746f2076616c69646174653a207072696365206d757374206260448201526f0652067726561746572207468616e20360841b6064820152608401610932565b805160409091015190939092509050565b5f611808612eb7565b505f805160206144f983398151915290565b5f611824826122d4565b6118405760405162461bcd60e51b815260040161093290613e4f565b505f9081526007602052604090205490565b61185b826122d4565b6118775760405162461bcd60e51b815260040161093290613e4f565b5f828152600b60205260409020546001600160a01b03163381146118f45760405162461bcd60e51b815260206004820152602e60248201527f4f6e6c79207468652063757272656e74206f776e65722063616e2070726f706f60448201526d39b29030903732bb9037bbb732b960911b6064820152608401610932565b816001600160a01b0316816001600160a01b03160361192c5750505f908152600c6020526040902080546001600160a01b0319169055565b5f838152600c6020526040902080546001600160a01b0319166001600160a01b0384161790555b505050565b6060611963826122d4565b61197f5760405162461bcd60e51b815260040161093290613e4f565b5f828152600a6020526040812080549091906001600160401b038111156119a8576119a8613bc6565b6040519080825280602002602001820160405280156119d1578160200160208202803683370190505b5090505f5b8254811015611a1f578281815481106119f1576119f1613d96565b905f5260205f200154828281518110611a0c57611a0c613d96565b60209081029190910101526001016119d6565b509392505050565b611a2f612f00565b611a385f612f5b565b565b5f828152600560205260408120548190611a5390612457565b611a5f90610100613d83565b5f8581526009602052604081205491925090611a89908690611a8390600190613d83565b84612546565b5f86815260036020908152604080832084518452909152902054909150611ab290600190613d83565b816020015114611b2c576040805162461bcd60e51b81526020600482015260248101919091527f6368616c6c656e676552616e6765202d312073686f756c6420616c69676e207760448201527f697468207468652076657279206c617374206c656166206f66206120726f6f746064820152608401610932565b611b368585611618565b8015610d31575051909211159392505050565b610800811115611b6b5760405162461bcd60e51b815260040161093290613daa565b6001546001600160401b03168310611bc55760405162461bcd60e51b815260206004820152601a60248201527f70726f6f6620736574206964206f7574206f6620626f756e64730000000000006044820152606401610932565b5f838152600b60205260409020546001600160a01b03163314611c365760405162461bcd60e51b8152602060048201526024808201527f4f6e6c7920746865206f776e65722063616e2064656c6574652070726f6f66206044820152637365747360e01b6064820152608401610932565b5f838152600660209081526040808320805490849055600b835281842080546001600160a01b031916905560078352818420849055600d83528184208490556008909252909120546001600160a01b03168015611ced576040516326c249e360e01b81526001600160a01b038216906326c249e390611cbf9088908690899089906004016141b7565b5f604051808303815f87803b158015611cd6575f80fd5b505af1158015611ce8573d5f803e3d5ffd5b505050505b847f589e9a441b5bddda77c4ab647b0108764a9cc1a7f655aa9b7bc50b8bdfab867383604051611d1f91815260200190565b60405180910390a25050505050565b5f611d38826122d4565b611d545760405162461bcd60e51b815260040161093290613e4f565b505f9081526009602052604090205490565b5f611d70836122d4565b611d8c5760405162461bcd60e51b815260040161093290613e4f565b505f91825260036020908152604080842092845291905290205490565b5f611db3826122d4565b611dcf5760405162461bcd60e51b815260040161093290613e4f565b505f9081526005602052604090205490565b611dea816122d4565b611e065760405162461bcd60e51b815260040161093290613e4f565b5f818152600c60205260409020546001600160a01b03163314611e7f5760405162461bcd60e51b815260206004820152602b60248201527f4f6e6c79207468652070726f706f736564206f776e65722063616e20636c616960448201526a06d206f776e6572736869760ac1b6064820152608401610932565b5f908152600b6020908152604080832080546001600160a01b03199081163317909155600c90925290912080549091169055565b611ebb612f00565b6001600160a01b038116611ee457604051631e4fbdf760e01b81525f6004820152602401610932565b611eed81612f5b565b50565b5f5a5f858152600b60205260409020549091506001600160a01b03163314611f2a5760405162461bcd60e51b81526004016109329061407d565b5f8481526007602052604090205443811115611f7a5760405162461bcd60e51b815260206004820152600f60248201526e383932b6b0ba3ab93290383937b7b360891b6044820152606401610932565b82611fb55760405162461bcd60e51b815260206004820152600b60248201526a32b6b83a3c90383937b7b360a91b6044820152606401610932565b80611ffb5760405162461bcd60e51b81526020600482015260166024820152751b9bc818da185b1b195b99d9481cd8da19591d5b195960521b6044820152606401610932565b5f61200586612fcb565b5f8781526009602090815260408083205460059092528220549293509161202b90612457565b61203790610100613d83565b90505f5b6001600160401b0381168711156121dc5760408051602081018690529081018a90526001600160c01b031960c083901b1660608201525f9060680160405160208183030381529060405290505f8482805190602001205f1c61209d91906141ea565b90505f6120ab8c8387612546565b90505f6120c36120be8e845f015161102e565b612fe2565b90505f61217e8d8d886001600160401b03168181106120e4576120e4613d96565b90506020028101906120f69190613e7b565b6121049060208101906141fd565b808060200260200160405190810160405280939291908181526020018383602002808284375f81840152601f19601f82011690508083019250505050505050838f8f8a6001600160401b031681811061215f5761215f613d96565b90506020028101906121719190613e7b565b5f013586602001516130cc565b9050806121c45760405162461bcd60e51b815260206004820152601460248201527370726f6f6620646964206e6f742076657269667960601b6044820152606401610932565b505050505080806121d490613dd8565b91505061203b565b505f6121e888886130e3565b6121f3906020613fb2565b6121ff9061051461410f565b5a61220a9088613d83565b6122149190613fb2565b9050612220898261314c565b5f898152600860205260409020546001600160a01b031680156122b8575f8a8152600660205260409081902054905163356de02b60e01b8152600481018c9052602481019190915260448101869052606481018990526001600160a01b0382169063356de02b906084015f604051808303815f87803b1580156122a1575f80fd5b505af11580156122b3573d5f803e3d5ffd5b505050505b5050505f9687525050600d602052505060409092204390555050565b6001545f906001600160401b0316821080156111185750505f908152600b60205260409020546001600160a01b0316151590565b5f612312826122d4565b61232e5760405162461bcd60e51b815260040161093290613e4f565b505f908152600d602052604090205490565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156123845750825b90505f826001600160401b0316600114801561239f5750303b155b9050811580156123ad575080155b156123cb5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156123f557845460ff60401b1916600160401b1785555b6123fe33613211565b612406613222565b5f869055831561102657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a1505050505050565b5f610100608083901c801561247757612471608083613d83565b91508093505b50604083901c80156124945761248e604083613d83565b91508093505b50602083901c80156124b1576124ab602083613d83565b91508093505b50601083901c80156124ce576124c8601083613d83565b91508093505b50600883901c80156124eb576124e5600883613d83565b91508093505b50600483901c801561250857612502600483613d83565b91508093505b50600283901c80156125255761251f600283613d83565b91508093505b50600183901c801561253c57611244600283613d83565b6112448483613d83565b604080518082019091525f80825260208201525f8481526006602052604090205483106125b55760405162461bcd60e51b815260206004820152601860248201527f4c65616620696e646578206f7574206f6620626f756e647300000000000000006044820152606401610932565b5f6125c3600180851b613d83565b90505f80845b801561269b575f888152600560205260409020548410612603576125ee600182613d83565b6125fc906001901b85613d83565b9350612689565b5f8881526004602090815260408083208784529091529020546126269084613fb2565b915086821161266d575f8881526004602090815260408083208784529091529020546126529084613fb2565b925061265f600182613d83565b6125fc906001901b85613fb2565b612678600182613d83565b612686906001901b85613d83565b93505b8061269381614242565b9150506125c9565b505f8781526004602090815260408083208684529091529020546126bf9083613fb2565b90508581116126fc5760405180604001604052808460016126e09190613fb2565b81526020016126ef8389613d83565b8152509350505050610908565b604051806040016040528084815260200183886127199190613d83565b9052979650505050505050565b5f600a61273c6001670de0b6b3a764000061410f565b6127469190614257565b905090565b803410156127925760405162461bcd60e51b8152602060048201526014602482015273125b98dbdc9c9958dd0819995948185b5bdd5b9d60621b6044820152606401610932565b6040515f90606360ff60981b019083908381818185875af1925050503d805f81146127d8576040519150601f19603f3d011682016040523d82523d5f602084013e6127dd565b606091505b50509050806116da5760405162461bcd60e51b815260206004820152600b60248201526a109d5c9b8819985a5b195960aa1b6044820152606401610932565b5f6128286020836141ea565b1561287f578360405163c7b67cf360e01b8152600401610932918152604060208201819052601d908201527f53697a65206d7573742062652061206d756c7469706c65206f66203332000000606082015260800190565b815f036128d8578360405163c7b67cf360e01b8152600401610932918152604060208201819052601b908201527f53697a65206d7573742062652067726561746572207468616e20300000000000606082015260800190565b6604000000000000821115612938578360405163c7b67cf360e01b815260040161093291815260406020808301829052908201527f526f6f742073697a65206d757374206265206c657373207468616e20325e3530606082015260800190565b5f612944602084614257565b5f8781526005602052604081208054929350909190826129638361426a565b91905055905061297487838361322a565b5f8781526002602090815260408083208484529091529020859061299882826142c6565b50505f87815260036020908152604080832084845282528083208590558983526006909152812080548492906129cf908490613fb2565b9091555090979650505050505050565b6129e8826122d4565b612a045760405162461bcd60e51b815260040161093290613e4f565b5f805b8251811015612a4757612a3384848381518110612a2657612a26613d96565b60200260200101516132a5565b612a3d9083613fb2565b9150600101612a07565b505f8381526006602052604081208054839290612a65908490613d83565b9091555050505050565b5f80861180612a7c575048155b612aee5760405162461bcd60e51b815260206004820152603c60248201527f6661696c656420746f2076616c69646174653a20657374696d6174656420676160448201527f7320666565206d7573742062652067726561746572207468616e2030000000006064820152608401610932565b5f856001600160401b031611612b6c5760405162461bcd60e51b815260206004820152603860248201527f6661696c656420746f2076616c69646174653a204174746f46494c207072696360448201527f65206d7573742062652067726561746572207468616e203000000000000000006064820152608401610932565b5f8311612bd75760405162461bcd60e51b815260206004820152603360248201527f6661696c656420746f2076616c69646174653a207261772073697a65206d75736044820152720742062652067726561746572207468616e203606c1b6064820152608401610932565b5f808560030b12612c4d57612bed85600a614489565b6001600160401b038716612c0a620151806501000000000061410f565b612c14919061410f565b612c1e919061410f565b612c316001670de0b6b3a764000061410f565b612c3c90600261410f565b612c469190614257565b9050612cbd565b6001600160401b038616612c6a620151806501000000000061410f565b612c74919061410f565b612c7d8661449a565b612c8890600a614489565b612c9b6001670de0b6b3a764000061410f565b612ca690600261410f565b612cb0919061410f565b612cba9190614257565b90505b5f84612cc9858461410f565b612cd3919061410f565b90505f6064612ce360058461410f565b612ced9190614257565b90505f6064612cfd60048561410f565b612d079190614257565b9050818a10612d1c575f945050505050610d31565b808a10612d3857612d2d8a83613d83565b945050505050610d31565b6064612d4560018561410f565b612d2d9190614257565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480612dd557507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612dc95f805160206144f9833981519152546001600160a01b031690565b6001600160a01b031614155b15611a385760405163703e46dd60e11b815260040160405180910390fd5b611eed612f00565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612e55575060408051601f3d908101601f19168201909252612e5291810190614066565b60015b612e7d57604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610932565b5f805160206144f98339815191528114612ead57604051632a87526960e21b815260048101829052602401610932565b611953838361330b565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611a385760405163703e46dd60e11b815260040160405180910390fd5b33612f327f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614611a385760405163118cdaa760e01b8152336004820152602401610932565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f8181526007602052604081205461111890611156565b5f6020825f01515110156130305760405162461bcd60e51b815260206004820152601560248201527410da590819185d18481a5cc81d1bdbc81cda1bdc9d605a1b6044820152606401610932565b6040805160208082528183019092525f916020820181803683370190505090505f5b60208110156130c25783518051829061306d90602090613d83565b6130779190613fb2565b8151811061308757613087613d96565b602001015160f81c60f81b8282815181106130a4576130a4613d96565b60200101906001600160f81b03191690815f1a905350600101613052565b50610908816144bb565b5f836130d9868585613360565b1495945050505050565b5f80805b83811015611a1f5784848281811061310157613101613d96565b90506020028101906131139190613e7b565b6131219060208101906141fd565b61312d9150602061410f565b613138906040613fb2565b6131429083613fb2565b91506001016130e7565b5f613157488361410f565b90505f6131648483611660565b905061316f8161274b565b803411156131ac57336108fc6131858334613d83565b6040518115909202915f818181858888f193505050501580156131aa573d5f803e3d5ffd5b505b5f806131b66116de565b604080518681526001600160401b0384166020820152600383900b81830152905192945090925087917f928bbf5188022bf8b9a0e59f5e81e179d0a4c729bdba2856ac971af2063fbf2b9181900360600190a2505050505050565b6132196133da565b611eed81613423565b611a386133da565b805f6132358261342b565b9050835f5b82811015613281575f6132506001831b86613d83565b5f8981526004602090815260408083208484529091529020549091506132769084613fb2565b92505060010161323a565b505f9586526004602090815260408088209588529490529290942091909155505050565b5f8281526003602090815260408083208484529091528120546132c984848361343f565b5f84815260036020908152604080832086845282528083208390558683526002825280832086845290915281209061330182826138a8565b5090949350505050565b613314826134e6565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115613358576119538282613549565b6116da6135b2565b5f82815b85518110156133d1575f86828151811061338057613380613d96565b6020026020010151905060028561339791906141ea565b5f036133ae576133a783826135d1565b92506133bb565b6133b881846135d1565b92505b6133c6600286614257565b945050600101613364565b50949350505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16611a3857604051631afcd79f60e31b815260040160405180910390fd5b611ebb6133da565b5f61111861343a836001613fb2565b6135dc565b5f8381526005602052604081205461345690612457565b61346290610100613d83565b90505f61346e8461342b565b90505b81811115801561348d57505f8581526005602052604090205484105b156134df575f858152600460209081526040808320878452909152812080548592906134ba908490613d83565b909155506134cd90506001821b85613fb2565b93506134d88461342b565b9050613471565b5050505050565b806001600160a01b03163b5f0361351b57604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610932565b5f805160206144f983398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516135659190614055565b5f60405180830381855af49150503d805f811461359d576040519150601f19603f3d011682016040523d82523d5f602084013e6135a2565b606091505b5091509150610d318583836137fc565b3415611a385760405163b398979f60e01b815260040160405180910390fd5b5f6109088383613858565b5f6001600160ff1b0382111561363f5760405162461bcd60e51b815260206004820152602260248201527f496e7075742065786365656473206d6178696d756d20696e743235362076616c604482015261756560f01b6064820152608401610932565b6101005f61364c846144de565b841690508015613664578161366081614242565b9250505b6fffffffffffffffffffffffffffffffff81161561368a57613687608083613d83565b91505b77ffffffffffffffff0000000000000000ffffffffffffffff8116156136b8576136b5604083613d83565b91505b7bffffffff00000000ffffffff00000000ffffffff00000000ffffffff8116156136ea576136e7602083613d83565b91505b7dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff81161561371e5761371b601083613d83565b91505b7eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff81161561375357613750600883613d83565b91505b7f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f81161561378957613786600483613d83565b91505b7f33333333333333333333333333333333333333333333333333333333333333338116156137bf576137bc600283613d83565b91505b7f55555555555555555555555555555555555555555555555555555555555555558116156137f5576137f2600183613d83565b91505b5092915050565b6060826138115761380c8261387f565b610908565b815115801561382857506001600160a01b0384163b155b1561385157604051639996b31560e01b81526001600160a01b0385166004820152602401610932565b5080610908565b5f825f528160205260205f60405f60025afa613872575f80fd5b50505f5160c01916919050565b80511561388f5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b5080546138b49061401d565b5f825580601f106138c3575050565b601f0160209004905f5260205f2090810190611eed91905b808211156138ee575f81556001016138db565b5090565b5f8083601f840112613902575f80fd5b5081356001600160401b03811115613918575f80fd5b6020830191508360208260051b8501011115613932575f80fd5b9250929050565b5f805f6040848603121561394b575f80fd5b8335925060208401356001600160401b03811115613967575f80fd5b613973868287016138f2565b9497909650939450505050565b602080825282518282018190525f919060409081850190868401855b828110156139c15781518051855286015186850152928401929085019060010161399c565b5091979650505050505050565b80356001600160a01b03811681146139e4575f80fd5b919050565b5f8083601f8401126139f9575f80fd5b5081356001600160401b03811115613a0f575f80fd5b602083019150836020828501011115613932575f80fd5b5f805f60408486031215613a38575f80fd5b613a41846139ce565b925060208401356001600160401b03811115613a5b575f80fd5b613973868287016139e9565b5f805f805f60608688031215613a7b575f80fd5b8535945060208601356001600160401b0380821115613a98575f80fd5b613aa489838a016138f2565b90965094506040880135915080821115613abc575f80fd5b50613ac9888289016139e9565b969995985093965092949392505050565b5f60208284031215613aea575f80fd5b5035919050565b5f8060408385031215613b02575f80fd5b50508035926020909101359150565b5f5b83811015613b2b578181015183820152602001613b13565b50505f910152565b5f8151808452613b4a816020860160208601613b11565b601f01601f19169290920160200192915050565b602081525f82516020808401526112446040840182613b33565b5f805f8060608587031215613b8b575f80fd5b843593506020850135925060408501356001600160401b03811115613bae575f80fd5b613bba878288016139e9565b95989497509550505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613c0257613c02613bc6565b604052919050565b5f8060408385031215613c1b575f80fd5b613c24836139ce565b91506020808401356001600160401b0380821115613c40575f80fd5b818601915086601f830112613c53575f80fd5b813581811115613c6557613c65613bc6565b613c77601f8201601f19168501613bda565b91508082528784828501011115613c8c575f80fd5b80848401858401375f848284010152508093505050509250929050565b5f8060408385031215613cba575f80fd5b82359150613cca602084016139ce565b90509250929050565b602080825282518282018190525f9190848201906040850190845b81811015613d0a57835183529284019291840191600101613cee565b50909695505050505050565b5f805f60408486031215613d28575f80fd5b8335925060208401356001600160401b03811115613a5b575f80fd5b602081525f6109086020830184613b33565b5f60208284031215613d66575f80fd5b610908826139ce565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561111857611118613d6f565b634e487b7160e01b5f52603260045260245ffd5b6020808252601490820152734578747261206461746120746f6f206c6172676560601b604082015260600190565b5f6001600160401b03808316818103613df357613df3613d6f565b6001019392505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b8481526001600160a01b03841660208201526060604082018190525f906116b59083018486613dfd565b60208082526012908201527150726f6f6620736574206e6f74206c69766560701b604082015260600190565b5f8235603e19833603018112613e8f575f80fd5b9190910192915050565b5f8235601e19833603018112613e8f575f80fd5b5f60808201888352602088818501526040608060408601528288845260a08601905060a08960051b8701019350895f5b8a811015613f8d57878603609f190183528135368d9003603e19018112613f02575f80fd5b8c018035601e193683900381018212613f19575f80fd5b8282019150868952813581833603018112613f32575f80fd5b9091018781019150356001600160401b03811115613f4e575f80fd5b803603821315613f5c575f80fd5b87878a0152613f6f60608a018284613dfd565b92880135988801989098525095509184019190840190600101613edd565b50505050508281036060840152613fa5818587613dfd565b9998505050505050505050565b8082018082111561111857611118613d6f565b85815260606020820181905281018490525f6001600160fb1b03851115613fea575f80fd5b8460051b808760808501378201828103608090810160408501526140119082018587613dfd565b98975050505050505050565b600181811c9082168061403157607f821691505b60208210810361404f57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f8251613e8f818460208701613b11565b5f60208284031215614076575f80fd5b5051919050565b6020808252602e908201527f6f6e6c7920746865206f776e65722063616e206d6f766520746f206e6578742060408201526d1c1c9bdd9a5b99c81c195c9a5bd960921b606082015260800190565b634e487b7160e01b5f52603160045260245ffd5b858152846020820152836040820152608060608201525f614104608083018486613dfd565b979650505050505050565b808202811582820484141761111857611118613d6f565b5f60808284031215614136575f80fd5b604051608081016001600160401b03828210818311171561415957614159613bc6565b81604052845191508160070b821461416f575f80fd5b9082526020840151908082168214614185575f80fd5b5060208201526040830151600381900b811461419f575f80fd5b60408201526060928301519281019290925250919050565b848152836020820152606060408201525f6116b5606083018486613dfd565b634e487b7160e01b5f52601260045260245ffd5b5f826141f8576141f86141d6565b500690565b5f808335601e19843603018112614212575f80fd5b8301803591506001600160401b0382111561422b575f80fd5b6020019150600581901b3603821315613932575f80fd5b5f8161425057614250613d6f565b505f190190565b5f82614265576142656141d6565b500490565b5f6001820161427b5761427b613d6f565b5060010190565b601f82111561195357805f5260205f20601f840160051c810160208510156142a75750805b601f840160051c820191505b818110156134df575f81556001016142b3565b8135601e198336030181126142d9575f80fd5b820180356001600160401b038111156142f0575f80fd5b60208136038184011315614302575f80fd5b61431682614310865461401d565b86614282565b5f601f831160018114614349575f841561433257508482018301355b5f19600386901b1c1916600185901b1786556115bb565b5f86815260208120601f198616915b82811015614379578785018601358255938501936001909101908501614358565b5085821015614397575f1960f88760031b161c198585890101351681555b5050505050600190811b019091555050565b600181815b808511156143e357815f19048211156143c9576143c9613d6f565b808516156143d657918102915b93841c93908002906143ae565b509250929050565b5f826143f957506001611118565b8161440557505f611118565b816001811461441b576002811461442557614441565b6001915050611118565b60ff84111561443657614436613d6f565b50506001821b611118565b5060208310610133831016604e8410600b8410161715614464575081810a611118565b61446e83836143a9565b805f190482111561448157614481613d6f565b029392505050565b5f61090863ffffffff8416836143eb565b5f8160030b637fffffff1981036144b3576144b3613d6f565b5f0392915050565b8051602080830151919081101561404f575f1960209190910360031b1b16919050565b5f600160ff1b82016144f2576144f2613d6f565b505f039056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220e92968eac9add433073ddd4eef326e4ca6e8afc42954363cd26021502729a74f64736f6c63430008170033","sourceMap":"1452:30380:43:-:0;;;1171:4:25;1128:48;;6622:50:43;;;;;;;;;-1:-1:-1;6643:22:43;:20;:22::i;:::-;1452:30380;;7711:422:24;8870:21;7900:15;;;;;;;7896:76;;;7938:23;;-1:-1:-1;;;7938:23:24;;;;;;;;;;;7896:76;7985:14;;-1:-1:-1;;;;;7985:14:24;;;:34;7981:146;;8035:33;;-1:-1:-1;;;;;;8035:33:24;-1:-1:-1;;;;;8035:33:24;;;;;8087:29;;158:50:50;;;8087:29:24;;146:2:50;131:18;8087:29:24;;;;;;;7981:146;7760:373;7711:422::o;14:200:50:-;1452:30380:43;;;;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x60806040526004361061025f575f3560e01c806367e406d51161013f5780639f8cb3bd116100b3578063f2fde38b11610078578063f2fde38b14610786578063f58f952b146107a5578063f5cac1ba146107b8578063f83758fe146107d7578063faa67163146107ea578063fe4b84df14610809575f80fd5b80639f8cb3bd146106e2578063ad3cb1cc146106f7578063c0e1594914610734578063d49245c114610748578063ee3dac6514610767575f80fd5b806371cf2a161161010457806371cf2a1614610603578063847d1d061461062257806389208ba9146106415780638da5cb5b146106605780638ea417e51461069c5780639153e64b146106c3575f80fd5b806367e406d51461055e5780636ba4608f146105855780636cb55c16146105a45780636fa44692146105c3578063715018a6146105ef575f80fd5b80633f84135f116101d6578063473310501161019b578063473310501461049d5780634903704a146104cc5780634f1ef286146104eb5780634fa27920146104fe57806352d1902d1461053457806361a52a3614610548575f80fd5b80633f84135f146103ee578063453f4f621461040d57806345c0b92d1461042c578063462dd4491461044b5780634726075b1461045e575f80fd5b806315b175701161022757806315b175701461031b57806316e2bcd51461033557806319c759501461034f57806331601226146103825780633b68e4e9146103a15780633b7ae913146103c2575f80fd5b8063029b4646146102635780630528a55b1461028b5780630a4d7932146102b75780630a6a63f1146102ca57806311c0ee4a146102fc575b5f80fd5b34801561026e575f80fd5b5061027861080081565b6040519081526020015b60405180910390f35b348015610296575f80fd5b506102aa6102a5366004613939565b610828565b6040516102829190613980565b6102786102c5366004613a26565b61090f565b3480156102d5575f80fd5b506102e4606360ff60981b0181565b6040516001600160a01b039091168152602001610282565b348015610307575f80fd5b50610278610316366004613a67565b610b06565b348015610326575f80fd5b506102e46006607f60991b0181565b348015610340575f80fd5b50610278660400000000000081565b34801561035a575f80fd5b506102787f150ac9b959aee0051e4091f0ef5216d941f590e1c5e7f91cf7635b5c11628c0e81565b34801561038d575f80fd5b506102e461039c366004613ada565b610d3a565b3480156103ac575f80fd5b506103c06103bb366004613a67565b610d7b565b005b3480156103cd575f80fd5b506103e16103dc366004613af1565b61102e565b6040516102829190613b5e565b3480156103f9575f80fd5b50610278610408366004613ada565b61111e565b348015610418575f80fd5b50610278610427366004613ada565b611156565b348015610437575f80fd5b506103c0610446366004613b78565b61124c565b348015610456575f80fd5b506102785f81565b348015610469575f80fd5b5061047d610478366004613ada565b6115c4565b604080516001600160a01b03938416815292909116602083015201610282565b3480156104a8575f80fd5b506104bc6104b7366004613af1565b611618565b6040519015158152602001610282565b3480156104d7575f80fd5b506102786104e6366004613af1565b611660565b6103c06104f9366004613c0a565b6116bf565b348015610509575f80fd5b506105126116de565b604080516001600160401b03909316835260039190910b602083015201610282565b34801561053f575f80fd5b506102786117ff565b348015610553575f80fd5b506102786201518081565b348015610569575f80fd5b506102e473a2aa501b19aff244d90cc15a4cf739d2725b572981565b348015610590575f80fd5b5061027861059f366004613ada565b61181a565b3480156105af575f80fd5b506103c06105be366004613ca9565b611852565b3480156105ce575f80fd5b506105e26105dd366004613ada565b611958565b6040516102829190613cd3565b3480156105fa575f80fd5b506103c0611a27565b34801561060e575f80fd5b506104bc61061d366004613af1565b611a3a565b34801561062d575f80fd5b506103c061063c366004613d16565b611b49565b34801561064c575f80fd5b5061027861065b366004613ada565b611d2e565b34801561066b575f80fd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166102e4565b3480156106a7575f80fd5b506001546040516001600160401b039091168152602001610282565b3480156106ce575f80fd5b506102786106dd366004613af1565b611d66565b3480156106ed575f80fd5b506102786107d081565b348015610702575f80fd5b50610727604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516102829190613d44565b34801561073f575f80fd5b50610278602081565b348015610753575f80fd5b50610278610762366004613ada565b611da9565b348015610772575f80fd5b506103c0610781366004613ada565b611de1565b348015610791575f80fd5b506103c06107a0366004613d56565b611eb3565b6103c06107b3366004613939565b611ef0565b3480156107c3575f80fd5b506104bc6107d2366004613ada565b6122d4565b3480156107e2575f80fd5b505f54610278565b3480156107f5575f80fd5b50610278610804366004613ada565b612308565b348015610814575f80fd5b506103c0610823366004613ada565b612340565b5f838152600560205260408120546060919061084390612457565b61084f90610100613d83565b90505f836001600160401b0381111561086a5761086a613bc6565b6040519080825280602002602001820160405280156108ae57816020015b604080518082019091525f80825260208201528152602001906001900390816108885790505b5090505f5b84811015610903576108de878787848181106108d1576108d1613d96565b9050602002013585612546565b8282815181106108f0576108f0613d96565b60209081029190910101526001016108b3565b509150505b9392505050565b5f61080082111561093b5760405162461bcd60e51b815260040161093290613daa565b60405180910390fd5b5f610944612726565b90508034101561098a5760405162461bcd60e51b81526020600482015260116024820152701cde589a5b08199959481b9bdd081b595d607a1b6044820152606401610932565b6109938161274b565b803411156109d057336108fc6109a98334613d83565b6040518115909202915f818181858888f193505050501580156109ce573d5f803e3d5ffd5b505b600180545f916001600160401b0390911690826109ec83613dd8565b82546001600160401b039182166101009390930a928302928202191691909117909155165f81815260066020908152604080832083905560078252808320839055600b825280832080546001600160a01b031990811633179091556008835281842080546001600160a01b038d16921682179055600d90925282209190915590915015610ad357604051634a6a0d9b60e11b81526001600160a01b038716906394d41b3690610aa590849033908a908a90600401613e25565b5f604051808303815f87803b158015610abc575f80fd5b505af1158015610ace573d5f803e3d5ffd5b505050505b60405181907f5979d495e336598dba8459e44f8eb2a1c957ce30fcc10cabea4bb0ffe969df6a905f90a295945050505050565b5f610800821115610b295760405162461bcd60e51b815260040161093290613daa565b610b32866122d4565b610b4e5760405162461bcd60e51b815260040161093290613e4f565b83610b9b5760405162461bcd60e51b815260206004820152601a60248201527f4d75737420616464206174206c65617374206f6e6520726f6f740000000000006044820152606401610932565b5f868152600b60205260409020546001600160a01b03163314610c005760405162461bcd60e51b815260206004820152601c60248201527f4f6e6c7920746865206f776e65722063616e2061646420726f6f7473000000006044820152606401610932565b5f86815260056020526040812054905b85811015610c8157610c788882898985818110610c2f57610c2f613d96565b9050602002810190610c419190613e7b565b610c4b9080613e99565b8a8a86818110610c5d57610c5d613d96565b9050602002810190610c6f9190613e7b565b6020013561281c565b50600101610c10565b505f878152600860205260409020546001600160a01b03168015610d03576040516312d5d66f60e01b81526001600160a01b038216906312d5d66f90610cd5908b9086908c908c908c908c90600401613ead565b5f604051808303815f87803b158015610cec575f80fd5b505af1158015610cfe573d5f803e3d5ffd5b505050505b60405182907fde16f8a35fc44238b21a493b7267f7a5064f2bc1b3a1af51deeb4b5a47281d47905f90a25090505b95945050505050565b5f610d44826122d4565b610d605760405162461bcd60e51b815260040161093290613e4f565b505f908152600860205260409020546001600160a01b031690565b610800811115610d9d5760405162461bcd60e51b815260040161093290613daa565b610da6856122d4565b610dc25760405162461bcd60e51b815260040161093290613e4f565b5f858152600b60205260409020546001600160a01b03163314610e3c5760405162461bcd60e51b815260206004820152602c60248201527f4f6e6c7920746865206f776e65722063616e207363686564756c652072656d6f60448201526b76616c206f6620726f6f747360a01b6064820152608401610932565b5f858152600a60205260409020546107d090610e589085613fb2565b1115610ecc5760405162461bcd60e51b815260206004820152603a60248201527f546f6f206d616e792072656d6f76616c73207761697420666f72206e6578742060448201527f70726f76696e6720706572696f6420746f207363686564756c650000000000006064820152608401610932565b5f5b83811015610fa6575f86815260056020526040902054858583818110610ef657610ef6613d96565b9050602002013510610f5e5760405162461bcd60e51b815260206004820152602b60248201527f43616e206f6e6c79207363686564756c652072656d6f76616c206f662065786960448201526a7374696e6720726f6f747360a81b6064820152608401610932565b5f868152600a60205260409020858583818110610f7d57610f7d613d96565b8354600180820186555f9586526020958690209290950293909301359201919091555001610ece565b505f858152600860205260409020546001600160a01b031680156110265760405163257be8e960e11b81526001600160a01b03821690634af7d1d290610ff89089908990899089908990600401613fc5565b5f604051808303815f87803b15801561100f575f80fd5b505af1158015611021573d5f803e3d5ffd5b505050505b505050505050565b604080516020810190915260608152611046836122d4565b6110625760405162461bcd60e51b815260040161093290613e4f565b5f83815260026020908152604080832085845282529182902082519182019092528154909190829082906110959061401d565b80601f01602080910402602001604051908101604052809291908181526020018280546110c19061401d565b801561110c5780601f106110e35761010080835404028352916020019161110c565b820191905f5260205f20905b8154815290600101906020018083116110ef57829003601f168201915b50505050508152505090505b92915050565b5f611128826122d4565b6111445760405162461bcd60e51b815260040161093290613e4f565b505f9081526006602052604090205490565b5f805f6006607f60991b016001600160a01b03168460405160200161117d91815260200190565b60408051601f198184030181529082905261119791614055565b5f60405180830381855afa9150503d805f81146111cf576040519150601f19603f3d011682016040523d82523d5f602084013e6111d4565b606091505b5091509150816112305760405162461bcd60e51b815260206004820152602160248201527f52616e646f6d6e65737320707265636f6d70696c652063616c6c206661696c656044820152601960fa1b6064820152608401610932565b808060200190518101906112449190614066565b949350505050565b61080081111561126e5760405162461bcd60e51b815260040161093290613daa565b5f848152600b60205260409020546001600160a01b031633146112a35760405162461bcd60e51b81526004016109329061407d565b5f848152600660205260409020546113125760405162461bcd60e51b815260206004820152602c60248201527f63616e206f6e6c792073746172742070726f76696e67206f6e6365206c65617660448201526b195cc8185c9948185919195960a21b6064820152608401610932565b5f848152600d6020526040902054611335575f848152600d602052604090204390555b5f848152600a6020526040812080549091906001600160401b0381111561135e5761135e613bc6565b604051908082528060200260200182016040528015611387578160200160208202803683370190505b5090505f5b815181101561140a57825483906113a590600190613d83565b815481106113b5576113b5613d96565b905f5260205f2001548282815181106113d0576113d0613d96565b602002602001018181525050828054806113ec576113ec6140cb565b5f8281526020812082015f199081019190915501905560010161138c565b5061141586826129df565b5f868152600660209081526040808320546009909252822055546114399043613fb2565b8510156114be5760405162461bcd60e51b815260206004820152604760248201527f6368616c6c656e67652065706f6368206d757374206265206174206c6561737460448201527f206368616c6c656e676546696e616c6974792065706f63687320696e207468656064820152662066757475726560c81b608482015260a401610932565b5f868152600760209081526040808320889055600690915281205490036115255760405186907f323c29bc8d678a5d987b90a321982d10b9a91bcad071a9e445879497bf0e68e7905f90a25f868152600d6020908152604080832083905560079091528120555b5f868152600860205260409020546001600160a01b031680156115bb575f87815260076020908152604080832054600690925291829020549151632a89faf360e21b81526001600160a01b0384169263aa27ebcc9261158d928c92908b908b906004016140df565b5f604051808303815f87803b1580156115a4575f80fd5b505af11580156115b6573d5f803e3d5ffd5b505050505b50505050505050565b5f806115cf836122d4565b6115eb5760405162461bcd60e51b815260040161093290613e4f565b50505f908152600b6020908152604080832054600c909252909120546001600160a01b0391821692911690565b5f611622836122d4565b801561163a57505f8381526005602052604090205482105b80156109085750505f918252600360209081526040808420928452919052902054151590565b5f828152600960209081526040822054829161167c919061410f565b90505f806116886116de565b915091506116b585838386600d5f8c81526020019081526020015f2054436116b09190613d83565b612a6f565b9695505050505050565b6116c7612d4f565b6116d082612df3565b6116da8282612dfb565b5050565b60405163052571af60e51b81527f150ac9b959aee0051e4091f0ef5216d941f590e1c5e7f91cf7635b5c11628c0e60048201526201518060248201525f908190819073a2aa501b19aff244d90cc15a4cf739d2725b57299063a4ae35e090604401608060405180830381865afa15801561175a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061177e9190614126565b90505f815f015160070b136117ee5760405162461bcd60e51b815260206004820152603060248201527f6661696c656420746f2076616c69646174653a207072696365206d757374206260448201526f0652067726561746572207468616e20360841b6064820152608401610932565b805160409091015190939092509050565b5f611808612eb7565b505f805160206144f983398151915290565b5f611824826122d4565b6118405760405162461bcd60e51b815260040161093290613e4f565b505f9081526007602052604090205490565b61185b826122d4565b6118775760405162461bcd60e51b815260040161093290613e4f565b5f828152600b60205260409020546001600160a01b03163381146118f45760405162461bcd60e51b815260206004820152602e60248201527f4f6e6c79207468652063757272656e74206f776e65722063616e2070726f706f60448201526d39b29030903732bb9037bbb732b960911b6064820152608401610932565b816001600160a01b0316816001600160a01b03160361192c5750505f908152600c6020526040902080546001600160a01b0319169055565b5f838152600c6020526040902080546001600160a01b0319166001600160a01b0384161790555b505050565b6060611963826122d4565b61197f5760405162461bcd60e51b815260040161093290613e4f565b5f828152600a6020526040812080549091906001600160401b038111156119a8576119a8613bc6565b6040519080825280602002602001820160405280156119d1578160200160208202803683370190505b5090505f5b8254811015611a1f578281815481106119f1576119f1613d96565b905f5260205f200154828281518110611a0c57611a0c613d96565b60209081029190910101526001016119d6565b509392505050565b611a2f612f00565b611a385f612f5b565b565b5f828152600560205260408120548190611a5390612457565b611a5f90610100613d83565b5f8581526009602052604081205491925090611a89908690611a8390600190613d83565b84612546565b5f86815260036020908152604080832084518452909152902054909150611ab290600190613d83565b816020015114611b2c576040805162461bcd60e51b81526020600482015260248101919091527f6368616c6c656e676552616e6765202d312073686f756c6420616c69676e207760448201527f697468207468652076657279206c617374206c656166206f66206120726f6f746064820152608401610932565b611b368585611618565b8015610d31575051909211159392505050565b610800811115611b6b5760405162461bcd60e51b815260040161093290613daa565b6001546001600160401b03168310611bc55760405162461bcd60e51b815260206004820152601a60248201527f70726f6f6620736574206964206f7574206f6620626f756e64730000000000006044820152606401610932565b5f838152600b60205260409020546001600160a01b03163314611c365760405162461bcd60e51b8152602060048201526024808201527f4f6e6c7920746865206f776e65722063616e2064656c6574652070726f6f66206044820152637365747360e01b6064820152608401610932565b5f838152600660209081526040808320805490849055600b835281842080546001600160a01b031916905560078352818420849055600d83528184208490556008909252909120546001600160a01b03168015611ced576040516326c249e360e01b81526001600160a01b038216906326c249e390611cbf9088908690899089906004016141b7565b5f604051808303815f87803b158015611cd6575f80fd5b505af1158015611ce8573d5f803e3d5ffd5b505050505b847f589e9a441b5bddda77c4ab647b0108764a9cc1a7f655aa9b7bc50b8bdfab867383604051611d1f91815260200190565b60405180910390a25050505050565b5f611d38826122d4565b611d545760405162461bcd60e51b815260040161093290613e4f565b505f9081526009602052604090205490565b5f611d70836122d4565b611d8c5760405162461bcd60e51b815260040161093290613e4f565b505f91825260036020908152604080842092845291905290205490565b5f611db3826122d4565b611dcf5760405162461bcd60e51b815260040161093290613e4f565b505f9081526005602052604090205490565b611dea816122d4565b611e065760405162461bcd60e51b815260040161093290613e4f565b5f818152600c60205260409020546001600160a01b03163314611e7f5760405162461bcd60e51b815260206004820152602b60248201527f4f6e6c79207468652070726f706f736564206f776e65722063616e20636c616960448201526a06d206f776e6572736869760ac1b6064820152608401610932565b5f908152600b6020908152604080832080546001600160a01b03199081163317909155600c90925290912080549091169055565b611ebb612f00565b6001600160a01b038116611ee457604051631e4fbdf760e01b81525f6004820152602401610932565b611eed81612f5b565b50565b5f5a5f858152600b60205260409020549091506001600160a01b03163314611f2a5760405162461bcd60e51b81526004016109329061407d565b5f8481526007602052604090205443811115611f7a5760405162461bcd60e51b815260206004820152600f60248201526e383932b6b0ba3ab93290383937b7b360891b6044820152606401610932565b82611fb55760405162461bcd60e51b815260206004820152600b60248201526a32b6b83a3c90383937b7b360a91b6044820152606401610932565b80611ffb5760405162461bcd60e51b81526020600482015260166024820152751b9bc818da185b1b195b99d9481cd8da19591d5b195960521b6044820152606401610932565b5f61200586612fcb565b5f8781526009602090815260408083205460059092528220549293509161202b90612457565b61203790610100613d83565b90505f5b6001600160401b0381168711156121dc5760408051602081018690529081018a90526001600160c01b031960c083901b1660608201525f9060680160405160208183030381529060405290505f8482805190602001205f1c61209d91906141ea565b90505f6120ab8c8387612546565b90505f6120c36120be8e845f015161102e565b612fe2565b90505f61217e8d8d886001600160401b03168181106120e4576120e4613d96565b90506020028101906120f69190613e7b565b6121049060208101906141fd565b808060200260200160405190810160405280939291908181526020018383602002808284375f81840152601f19601f82011690508083019250505050505050838f8f8a6001600160401b031681811061215f5761215f613d96565b90506020028101906121719190613e7b565b5f013586602001516130cc565b9050806121c45760405162461bcd60e51b815260206004820152601460248201527370726f6f6620646964206e6f742076657269667960601b6044820152606401610932565b505050505080806121d490613dd8565b91505061203b565b505f6121e888886130e3565b6121f3906020613fb2565b6121ff9061051461410f565b5a61220a9088613d83565b6122149190613fb2565b9050612220898261314c565b5f898152600860205260409020546001600160a01b031680156122b8575f8a8152600660205260409081902054905163356de02b60e01b8152600481018c9052602481019190915260448101869052606481018990526001600160a01b0382169063356de02b906084015f604051808303815f87803b1580156122a1575f80fd5b505af11580156122b3573d5f803e3d5ffd5b505050505b5050505f9687525050600d602052505060409092204390555050565b6001545f906001600160401b0316821080156111185750505f908152600b60205260409020546001600160a01b0316151590565b5f612312826122d4565b61232e5760405162461bcd60e51b815260040161093290613e4f565b505f908152600d602052604090205490565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156123845750825b90505f826001600160401b0316600114801561239f5750303b155b9050811580156123ad575080155b156123cb5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156123f557845460ff60401b1916600160401b1785555b6123fe33613211565b612406613222565b5f869055831561102657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a1505050505050565b5f610100608083901c801561247757612471608083613d83565b91508093505b50604083901c80156124945761248e604083613d83565b91508093505b50602083901c80156124b1576124ab602083613d83565b91508093505b50601083901c80156124ce576124c8601083613d83565b91508093505b50600883901c80156124eb576124e5600883613d83565b91508093505b50600483901c801561250857612502600483613d83565b91508093505b50600283901c80156125255761251f600283613d83565b91508093505b50600183901c801561253c57611244600283613d83565b6112448483613d83565b604080518082019091525f80825260208201525f8481526006602052604090205483106125b55760405162461bcd60e51b815260206004820152601860248201527f4c65616620696e646578206f7574206f6620626f756e647300000000000000006044820152606401610932565b5f6125c3600180851b613d83565b90505f80845b801561269b575f888152600560205260409020548410612603576125ee600182613d83565b6125fc906001901b85613d83565b9350612689565b5f8881526004602090815260408083208784529091529020546126269084613fb2565b915086821161266d575f8881526004602090815260408083208784529091529020546126529084613fb2565b925061265f600182613d83565b6125fc906001901b85613fb2565b612678600182613d83565b612686906001901b85613d83565b93505b8061269381614242565b9150506125c9565b505f8781526004602090815260408083208684529091529020546126bf9083613fb2565b90508581116126fc5760405180604001604052808460016126e09190613fb2565b81526020016126ef8389613d83565b8152509350505050610908565b604051806040016040528084815260200183886127199190613d83565b9052979650505050505050565b5f600a61273c6001670de0b6b3a764000061410f565b6127469190614257565b905090565b803410156127925760405162461bcd60e51b8152602060048201526014602482015273125b98dbdc9c9958dd0819995948185b5bdd5b9d60621b6044820152606401610932565b6040515f90606360ff60981b019083908381818185875af1925050503d805f81146127d8576040519150601f19603f3d011682016040523d82523d5f602084013e6127dd565b606091505b50509050806116da5760405162461bcd60e51b815260206004820152600b60248201526a109d5c9b8819985a5b195960aa1b6044820152606401610932565b5f6128286020836141ea565b1561287f578360405163c7b67cf360e01b8152600401610932918152604060208201819052601d908201527f53697a65206d7573742062652061206d756c7469706c65206f66203332000000606082015260800190565b815f036128d8578360405163c7b67cf360e01b8152600401610932918152604060208201819052601b908201527f53697a65206d7573742062652067726561746572207468616e20300000000000606082015260800190565b6604000000000000821115612938578360405163c7b67cf360e01b815260040161093291815260406020808301829052908201527f526f6f742073697a65206d757374206265206c657373207468616e20325e3530606082015260800190565b5f612944602084614257565b5f8781526005602052604081208054929350909190826129638361426a565b91905055905061297487838361322a565b5f8781526002602090815260408083208484529091529020859061299882826142c6565b50505f87815260036020908152604080832084845282528083208590558983526006909152812080548492906129cf908490613fb2565b9091555090979650505050505050565b6129e8826122d4565b612a045760405162461bcd60e51b815260040161093290613e4f565b5f805b8251811015612a4757612a3384848381518110612a2657612a26613d96565b60200260200101516132a5565b612a3d9083613fb2565b9150600101612a07565b505f8381526006602052604081208054839290612a65908490613d83565b9091555050505050565b5f80861180612a7c575048155b612aee5760405162461bcd60e51b815260206004820152603c60248201527f6661696c656420746f2076616c69646174653a20657374696d6174656420676160448201527f7320666565206d7573742062652067726561746572207468616e2030000000006064820152608401610932565b5f856001600160401b031611612b6c5760405162461bcd60e51b815260206004820152603860248201527f6661696c656420746f2076616c69646174653a204174746f46494c207072696360448201527f65206d7573742062652067726561746572207468616e203000000000000000006064820152608401610932565b5f8311612bd75760405162461bcd60e51b815260206004820152603360248201527f6661696c656420746f2076616c69646174653a207261772073697a65206d75736044820152720742062652067726561746572207468616e203606c1b6064820152608401610932565b5f808560030b12612c4d57612bed85600a614489565b6001600160401b038716612c0a620151806501000000000061410f565b612c14919061410f565b612c1e919061410f565b612c316001670de0b6b3a764000061410f565b612c3c90600261410f565b612c469190614257565b9050612cbd565b6001600160401b038616612c6a620151806501000000000061410f565b612c74919061410f565b612c7d8661449a565b612c8890600a614489565b612c9b6001670de0b6b3a764000061410f565b612ca690600261410f565b612cb0919061410f565b612cba9190614257565b90505b5f84612cc9858461410f565b612cd3919061410f565b90505f6064612ce360058461410f565b612ced9190614257565b90505f6064612cfd60048561410f565b612d079190614257565b9050818a10612d1c575f945050505050610d31565b808a10612d3857612d2d8a83613d83565b945050505050610d31565b6064612d4560018561410f565b612d2d9190614257565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480612dd557507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612dc95f805160206144f9833981519152546001600160a01b031690565b6001600160a01b031614155b15611a385760405163703e46dd60e11b815260040160405180910390fd5b611eed612f00565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612e55575060408051601f3d908101601f19168201909252612e5291810190614066565b60015b612e7d57604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610932565b5f805160206144f98339815191528114612ead57604051632a87526960e21b815260048101829052602401610932565b611953838361330b565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611a385760405163703e46dd60e11b815260040160405180910390fd5b33612f327f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614611a385760405163118cdaa760e01b8152336004820152602401610932565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f8181526007602052604081205461111890611156565b5f6020825f01515110156130305760405162461bcd60e51b815260206004820152601560248201527410da590819185d18481a5cc81d1bdbc81cda1bdc9d605a1b6044820152606401610932565b6040805160208082528183019092525f916020820181803683370190505090505f5b60208110156130c25783518051829061306d90602090613d83565b6130779190613fb2565b8151811061308757613087613d96565b602001015160f81c60f81b8282815181106130a4576130a4613d96565b60200101906001600160f81b03191690815f1a905350600101613052565b50610908816144bb565b5f836130d9868585613360565b1495945050505050565b5f80805b83811015611a1f5784848281811061310157613101613d96565b90506020028101906131139190613e7b565b6131219060208101906141fd565b61312d9150602061410f565b613138906040613fb2565b6131429083613fb2565b91506001016130e7565b5f613157488361410f565b90505f6131648483611660565b905061316f8161274b565b803411156131ac57336108fc6131858334613d83565b6040518115909202915f818181858888f193505050501580156131aa573d5f803e3d5ffd5b505b5f806131b66116de565b604080518681526001600160401b0384166020820152600383900b81830152905192945090925087917f928bbf5188022bf8b9a0e59f5e81e179d0a4c729bdba2856ac971af2063fbf2b9181900360600190a2505050505050565b6132196133da565b611eed81613423565b611a386133da565b805f6132358261342b565b9050835f5b82811015613281575f6132506001831b86613d83565b5f8981526004602090815260408083208484529091529020549091506132769084613fb2565b92505060010161323a565b505f9586526004602090815260408088209588529490529290942091909155505050565b5f8281526003602090815260408083208484529091528120546132c984848361343f565b5f84815260036020908152604080832086845282528083208390558683526002825280832086845290915281209061330182826138a8565b5090949350505050565b613314826134e6565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115613358576119538282613549565b6116da6135b2565b5f82815b85518110156133d1575f86828151811061338057613380613d96565b6020026020010151905060028561339791906141ea565b5f036133ae576133a783826135d1565b92506133bb565b6133b881846135d1565b92505b6133c6600286614257565b945050600101613364565b50949350505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16611a3857604051631afcd79f60e31b815260040160405180910390fd5b611ebb6133da565b5f61111861343a836001613fb2565b6135dc565b5f8381526005602052604081205461345690612457565b61346290610100613d83565b90505f61346e8461342b565b90505b81811115801561348d57505f8581526005602052604090205484105b156134df575f858152600460209081526040808320878452909152812080548592906134ba908490613d83565b909155506134cd90506001821b85613fb2565b93506134d88461342b565b9050613471565b5050505050565b806001600160a01b03163b5f0361351b57604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610932565b5f805160206144f983398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516135659190614055565b5f60405180830381855af49150503d805f811461359d576040519150601f19603f3d011682016040523d82523d5f602084013e6135a2565b606091505b5091509150610d318583836137fc565b3415611a385760405163b398979f60e01b815260040160405180910390fd5b5f6109088383613858565b5f6001600160ff1b0382111561363f5760405162461bcd60e51b815260206004820152602260248201527f496e7075742065786365656473206d6178696d756d20696e743235362076616c604482015261756560f01b6064820152608401610932565b6101005f61364c846144de565b841690508015613664578161366081614242565b9250505b6fffffffffffffffffffffffffffffffff81161561368a57613687608083613d83565b91505b77ffffffffffffffff0000000000000000ffffffffffffffff8116156136b8576136b5604083613d83565b91505b7bffffffff00000000ffffffff00000000ffffffff00000000ffffffff8116156136ea576136e7602083613d83565b91505b7dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff81161561371e5761371b601083613d83565b91505b7eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff81161561375357613750600883613d83565b91505b7f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f81161561378957613786600483613d83565b91505b7f33333333333333333333333333333333333333333333333333333333333333338116156137bf576137bc600283613d83565b91505b7f55555555555555555555555555555555555555555555555555555555555555558116156137f5576137f2600183613d83565b91505b5092915050565b6060826138115761380c8261387f565b610908565b815115801561382857506001600160a01b0384163b155b1561385157604051639996b31560e01b81526001600160a01b0385166004820152602401610932565b5080610908565b5f825f528160205260205f60405f60025afa613872575f80fd5b50505f5160c01916919050565b80511561388f5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b5080546138b49061401d565b5f825580601f106138c3575050565b601f0160209004905f5260205f2090810190611eed91905b808211156138ee575f81556001016138db565b5090565b5f8083601f840112613902575f80fd5b5081356001600160401b03811115613918575f80fd5b6020830191508360208260051b8501011115613932575f80fd5b9250929050565b5f805f6040848603121561394b575f80fd5b8335925060208401356001600160401b03811115613967575f80fd5b613973868287016138f2565b9497909650939450505050565b602080825282518282018190525f919060409081850190868401855b828110156139c15781518051855286015186850152928401929085019060010161399c565b5091979650505050505050565b80356001600160a01b03811681146139e4575f80fd5b919050565b5f8083601f8401126139f9575f80fd5b5081356001600160401b03811115613a0f575f80fd5b602083019150836020828501011115613932575f80fd5b5f805f60408486031215613a38575f80fd5b613a41846139ce565b925060208401356001600160401b03811115613a5b575f80fd5b613973868287016139e9565b5f805f805f60608688031215613a7b575f80fd5b8535945060208601356001600160401b0380821115613a98575f80fd5b613aa489838a016138f2565b90965094506040880135915080821115613abc575f80fd5b50613ac9888289016139e9565b969995985093965092949392505050565b5f60208284031215613aea575f80fd5b5035919050565b5f8060408385031215613b02575f80fd5b50508035926020909101359150565b5f5b83811015613b2b578181015183820152602001613b13565b50505f910152565b5f8151808452613b4a816020860160208601613b11565b601f01601f19169290920160200192915050565b602081525f82516020808401526112446040840182613b33565b5f805f8060608587031215613b8b575f80fd5b843593506020850135925060408501356001600160401b03811115613bae575f80fd5b613bba878288016139e9565b95989497509550505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613c0257613c02613bc6565b604052919050565b5f8060408385031215613c1b575f80fd5b613c24836139ce565b91506020808401356001600160401b0380821115613c40575f80fd5b818601915086601f830112613c53575f80fd5b813581811115613c6557613c65613bc6565b613c77601f8201601f19168501613bda565b91508082528784828501011115613c8c575f80fd5b80848401858401375f848284010152508093505050509250929050565b5f8060408385031215613cba575f80fd5b82359150613cca602084016139ce565b90509250929050565b602080825282518282018190525f9190848201906040850190845b81811015613d0a57835183529284019291840191600101613cee565b50909695505050505050565b5f805f60408486031215613d28575f80fd5b8335925060208401356001600160401b03811115613a5b575f80fd5b602081525f6109086020830184613b33565b5f60208284031215613d66575f80fd5b610908826139ce565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561111857611118613d6f565b634e487b7160e01b5f52603260045260245ffd5b6020808252601490820152734578747261206461746120746f6f206c6172676560601b604082015260600190565b5f6001600160401b03808316818103613df357613df3613d6f565b6001019392505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b8481526001600160a01b03841660208201526060604082018190525f906116b59083018486613dfd565b60208082526012908201527150726f6f6620736574206e6f74206c69766560701b604082015260600190565b5f8235603e19833603018112613e8f575f80fd5b9190910192915050565b5f8235601e19833603018112613e8f575f80fd5b5f60808201888352602088818501526040608060408601528288845260a08601905060a08960051b8701019350895f5b8a811015613f8d57878603609f190183528135368d9003603e19018112613f02575f80fd5b8c018035601e193683900381018212613f19575f80fd5b8282019150868952813581833603018112613f32575f80fd5b9091018781019150356001600160401b03811115613f4e575f80fd5b803603821315613f5c575f80fd5b87878a0152613f6f60608a018284613dfd565b92880135988801989098525095509184019190840190600101613edd565b50505050508281036060840152613fa5818587613dfd565b9998505050505050505050565b8082018082111561111857611118613d6f565b85815260606020820181905281018490525f6001600160fb1b03851115613fea575f80fd5b8460051b808760808501378201828103608090810160408501526140119082018587613dfd565b98975050505050505050565b600181811c9082168061403157607f821691505b60208210810361404f57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f8251613e8f818460208701613b11565b5f60208284031215614076575f80fd5b5051919050565b6020808252602e908201527f6f6e6c7920746865206f776e65722063616e206d6f766520746f206e6578742060408201526d1c1c9bdd9a5b99c81c195c9a5bd960921b606082015260800190565b634e487b7160e01b5f52603160045260245ffd5b858152846020820152836040820152608060608201525f614104608083018486613dfd565b979650505050505050565b808202811582820484141761111857611118613d6f565b5f60808284031215614136575f80fd5b604051608081016001600160401b03828210818311171561415957614159613bc6565b81604052845191508160070b821461416f575f80fd5b9082526020840151908082168214614185575f80fd5b5060208201526040830151600381900b811461419f575f80fd5b60408201526060928301519281019290925250919050565b848152836020820152606060408201525f6116b5606083018486613dfd565b634e487b7160e01b5f52601260045260245ffd5b5f826141f8576141f86141d6565b500690565b5f808335601e19843603018112614212575f80fd5b8301803591506001600160401b0382111561422b575f80fd5b6020019150600581901b3603821315613932575f80fd5b5f8161425057614250613d6f565b505f190190565b5f82614265576142656141d6565b500490565b5f6001820161427b5761427b613d6f565b5060010190565b601f82111561195357805f5260205f20601f840160051c810160208510156142a75750805b601f840160051c820191505b818110156134df575f81556001016142b3565b8135601e198336030181126142d9575f80fd5b820180356001600160401b038111156142f0575f80fd5b60208136038184011315614302575f80fd5b61431682614310865461401d565b86614282565b5f601f831160018114614349575f841561433257508482018301355b5f19600386901b1c1916600185901b1786556115bb565b5f86815260208120601f198616915b82811015614379578785018601358255938501936001909101908501614358565b5085821015614397575f1960f88760031b161c198585890101351681555b5050505050600190811b019091555050565b600181815b808511156143e357815f19048211156143c9576143c9613d6f565b808516156143d657918102915b93841c93908002906143ae565b509250929050565b5f826143f957506001611118565b8161440557505f611118565b816001811461441b576002811461442557614441565b6001915050611118565b60ff84111561443657614436613d6f565b50506001821b611118565b5060208310610133831016604e8410600b8410161715614464575081810a611118565b61446e83836143a9565b805f190482111561448157614481613d6f565b029392505050565b5f61090863ffffffff8416836143eb565b5f8160030b637fffffff1981036144b3576144b3613d6f565b5f0392915050565b8051602080830151919081101561404f575f1960209190910360031b1b16919050565b5f600160ff1b82016144f2576144f2613d6f565b505f039056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca2646970667358221220e92968eac9add433073ddd4eef326e4ca6e8afc42954363cd26021502729a74f64736f6c63430008170033","sourceMap":"1452:30380:43:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1886:50;;;;;;;;;;;;1932:4;1886:50;;;;;160:25:50;;;148:2;133:18;1886:50:43;;;;;;;;30553:511;;;;;;;;;;-1:-1:-1;30553:511:43;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;12456:1015::-;;;;;;:::i;:::-;;:::i;1550:79::-;;;;;;;;;;;;-1:-1:-1;;;;;1550:79:43;;;;;-1:-1:-1;;;;;3071:32:50;;;3053:51;;3041:2;3026:18;1550:79:43;2907:203:50;14696:868:43;;;;;;;;;;-1:-1:-1;14696:868:43;;;;;:::i;:::-;;:::i;1790:90::-;;;;;;;;;;;;-1:-1:-1;;;;;1790:90:43;;1679:47;;;;;;;;;;;;1719:7;1679:47;;2134:114;;;;;;;;;;-1:-1:-1;2134:114:43;2182:66;2134:114;;9301:181;;;;;;;;;;-1:-1:-1;9301:181:43;;;;;:::i;:::-;;:::i;16589:922::-;;;;;;;;;;-1:-1:-1;16589:922:43;;;;;:::i;:::-;;:::i;:::-;;10043:196;;;;;;;;;;-1:-1:-1;10043:196:43;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;8590:183::-;;;;;;;;;;-1:-1:-1;8590:183:43;;;;;:::i;:::-;;:::i;22342:406::-;;;;;;;;;;-1:-1:-1;22342:406:43;;;;;:::i;:::-;;:::i;23793:1870::-;;;;;;;;;;-1:-1:-1;23793:1870:43;;;;;:::i;:::-;;:::i;2254:50::-;;;;;;;;;;;;2303:1;2254:50;;9558:216;;;;;;;;;;-1:-1:-1;9558:216:43;;;;;:::i;:::-;;:::i;:::-;;;;-1:-1:-1;;;;;7045:15:50;;;7027:34;;7097:15;;;;7092:2;7077:18;;7070:43;6962:18;9558:216:43;6815:304:50;7813:186:43;;;;;;;;;;-1:-1:-1;7813:186:43;;;;;:::i;:::-;;:::i;:::-;;;7289:14:50;;7282:22;7264:41;;7252:2;7237:18;7813:186:43;7124:187:50;20956:452:43;;;;;;;;;;-1:-1:-1;20956:452:43;;;;;:::i;:::-;;:::i;4161:214:25:-;;;;;;:::i;:::-;;:::i;31353:477:43:-;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;8756:31:50;;;8738:50;;8835:1;8824:21;;;;8819:2;8804:18;;8797:49;8711:18;31353:477:43;8570:282:50;3708:134:25;;;;;;;;;;;;;:::i;1942:46:43:-;;;;;;;;;;;;1983:5;1942:46;;1994:78;;;;;;;;;;;;2029:42;1994:78;;9058:185;;;;;;;;;;-1:-1:-1;9058:185:43;;;;;:::i;:::-;;:::i;11430:517::-;;;;;;;;;;-1:-1:-1;11430:517:43;;;;;:::i;:::-;;:::i;10915:406::-;;;;;;;;;;-1:-1:-1;10915:406:43;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;3155:101:23:-;;;;;;;;;;;;;:::i;8098:441:43:-;;;;;;;;;;-1:-1:-1;8098:441:43;;;;;:::i;:::-;;:::i;13543:848::-;;;;;;;;;;-1:-1:-1;13543:848:43;;;;;:::i;:::-;;:::i;10629:177::-;;;;;;;;;;-1:-1:-1;10629:177:43;;;;;:::i;:::-;;:::i;2441:144:23:-;;;;;;;;;;-1:-1:-1;1313:22:23;2570:8;-1:-1:-1;;;;;2570:8:23;2441:144;;7385:96:43;;;;;;;;;;-1:-1:-1;7460:14:43;;7385:96;;-1:-1:-1;;;;;7460:14:43;;;10602:50:50;;10590:2;10575:18;7385:96:43;10458:200:50;10314::43;;;;;;;;;;-1:-1:-1;10314:200:43;;;;;:::i;:::-;;:::i;1732:52::-;;;;;;;;;;;;1780:4;1732:52;;1819:58:25;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1819:58:25;;;;;;;;;;;;:::i;1635:38:43:-;;;;;;;;;;;;1671:2;1635:38;;8827:169;;;;;;;;;;-1:-1:-1;8827:169:43;;;;;:::i;:::-;;:::i;11953:317::-;;;;;;;;;;-1:-1:-1;11953:317:43;;;;;:::i;:::-;;:::i;3405:215:23:-;;;;;;;;;;-1:-1:-1;3405:215:23;;;;;:::i;:::-;;:::i;17867:3083:43:-;;;;;;:::i;:::-;;:::i;7558:148::-;;;;;;;;;;-1:-1:-1;7558:148:43;;;;;:::i;:::-;;:::i;7239:103::-;;;;;;;;;;-1:-1:-1;7292:7:43;7318:17;7239:103;;9780:195;;;;;;;;;;-1:-1:-1;9780:195:43;;;;;:::i;:::-;;:::i;6678:192::-;;;;;;;;;;-1:-1:-1;6678:192:43;;;;;:::i;:::-;;:::i;30553:511::-;30767:11;30798:17;;;:10;:17;;;;;;30641:24;;30767:11;30787:29;;:10;:29::i;:::-;30781:35;;:3;:35;:::i;:::-;30767:49;-1:-1:-1;30826:31:43;30882:10;-1:-1:-1;;;;;30860:40:43;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;30860:40:43;;;;;;;;;;;;;;;;30826:74;;30915:9;30910:125;30930:21;;;30910:125;;;30984:40;30998:5;31005:10;;31016:1;31005:13;;;;;;;:::i;:::-;;;;;;;31020:3;30984:13;:40::i;:::-;30972:6;30979:1;30972:9;;;;;;;;:::i;:::-;;;;;;;;;;:52;30953:3;;30910:125;;;-1:-1:-1;31051:6:43;-1:-1:-1;;30553:511:43;;;;;;:::o;12456:1015::-;12552:7;1932:4;12579:39;;;12571:72;;;;-1:-1:-1;;;12571:72:43;;;;;;;:::i;:::-;;;;;;;;;12653:16;12672:18;:16;:18::i;:::-;12653:37;;12721:8;12708:9;:21;;12700:51;;;;-1:-1:-1;;;12700:51:43;;12562:2:50;12700:51:43;;;12544:21:50;12601:2;12581:18;;;12574:30;-1:-1:-1;;;12620:18:50;;;12613:47;12677:18;;12700:51:43;12360:341:50;12700:51:43;12761:17;12769:8;12761:7;:17::i;:::-;12804:8;12792:9;:20;12788:139;;;12874:10;12866:50;12895:20;12907:8;12895:9;:20;:::i;:::-;12866:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12788:139;12953:14;:16;;12937:13;;-1:-1:-1;;;;;12953:16:43;;;;12937:13;12953:16;;;:::i;:::-;;;-1:-1:-1;;;;;12953:16:43;;;;;;;;;;;;;;;;;;;;;;;12937:32;-1:-1:-1;12979:24:43;;;:17;:24;;;;;;;;:28;;;13017:18;:25;;;;;:50;;;13128:13;:20;;;;;:33;;-1:-1:-1;;;;;;13128:33:43;;;13151:10;13128:33;;;;13171:16;:23;;;;;:38;;-1:-1:-1;;;;;13171:38:43;;;;;;;;13219:23;:30;;;;;:48;;;;12937:32;;-1:-1:-1;13282:26:43;13278:128;;13324:71;;-1:-1:-1;;;13324:71:43;;-1:-1:-1;;;;;13324:41:43;;;;;:71;;13366:5;;13373:10;;13385:9;;;;13324:71;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13278:128;13420:22;;13436:5;;13420:22;;;;;13459:5;12456:1015;-1:-1:-1;;;;;12456:1015:43:o;14696:868::-;14801:7;1932:4;14828:39;;;14820:72;;;;-1:-1:-1;;;14820:72:43;;;;;;;:::i;:::-;14910:19;14923:5;14910:12;:19::i;:::-;14902:50;;;;-1:-1:-1;;;14902:50:43;;;;;;;:::i;:::-;14970:19;14962:58;;;;-1:-1:-1;;;14962:58:43;;14157:2:50;14962:58:43;;;14139:21:50;14196:2;14176:18;;;14169:30;14235:28;14215:18;;;14208:56;14281:18;;14962:58:43;13955:350:50;14962:58:43;15038:20;;;;:13;:20;;;;;;-1:-1:-1;;;;;15038:20:43;15062:10;15038:34;15030:75;;;;-1:-1:-1;;;15030:75:43;;14512:2:50;15030:75:43;;;14494:21:50;14551:2;14531:18;;;14524:30;14590;14570:18;;;14563:58;14638:18;;15030:75:43;14310:352:50;15030:75:43;15115:18;15136:17;;;:10;:17;;;;;;;15164:130;15184:19;;;15164:130;;;15224:59;15235:5;15242:1;15245:8;;15254:1;15245:11;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;:16;;;;:::i;:::-;15263:8;;15272:1;15263:11;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;:19;;;15224:10;:59::i;:::-;-1:-1:-1;15205:3:43;;15164:130;;;-1:-1:-1;15304:20:43;15327:23;;;:16;:23;;;;;;-1:-1:-1;;;;;15327:23:43;15364:26;;15360:133;;15406:76;;-1:-1:-1;;;15406:76:43;;-1:-1:-1;;;;;15406:36:43;;;;;:76;;15443:5;;15450:10;;15462:8;;;;15472:9;;;;15406:76;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15360:133;15507:22;;15518:10;;15507:22;;;;;-1:-1:-1;15547:10:43;-1:-1:-1;14696:868:43;;;;;;;;:::o;9301:181::-;9366:7;9393:19;9406:5;9393:12;:19::i;:::-;9385:50;;;;-1:-1:-1;;;9385:50:43;;;;;;;:::i;:::-;-1:-1:-1;9452:23:43;;;;:16;:23;;;;;;-1:-1:-1;;;;;9452:23:43;;9301:181::o;16589:922::-;1932:4;16709:39;;;16701:72;;;;-1:-1:-1;;;16701:72:43;;;;;;;:::i;:::-;16791:19;16804:5;16791:12;:19::i;:::-;16783:50;;;;-1:-1:-1;;;16783:50:43;;;;;;;:::i;:::-;16851:20;;;;:13;:20;;;;;;-1:-1:-1;;;;;16851:20:43;16875:10;16851:34;16843:91;;;;-1:-1:-1;;;16843:91:43;;17778:2:50;16843:91:43;;;17760:21:50;17817:2;17797:18;;;17790:30;17856:34;17836:18;;;17829:62;-1:-1:-1;;;17907:18:50;;;17900:42;17959:19;;16843:91:43;17576:408:50;16843:91:43;16969:24;;;;:17;:24;;;;;:31;1780:4;;16952:48;;:7;:48;:::i;:::-;:73;;16944:144;;;;-1:-1:-1;;;16944:144:43;;18321:2:50;16944:144:43;;;18303:21:50;18360:2;18340:18;;;18333:30;18399:34;18379:18;;;18372:62;18470:28;18450:18;;;18443:56;18516:19;;16944:144:43;18119:422:50;16944:144:43;17104:9;17099:210;17119:18;;;17099:210;;;17178:17;;;;:10;:17;;;;;;17165:7;;17173:1;17165:10;;;;;;;:::i;:::-;;;;;;;:30;17157:86;;;;-1:-1:-1;;;17157:86:43;;18748:2:50;17157:86:43;;;18730:21:50;18787:2;18767:18;;;18760:30;18826:34;18806:18;;;18799:62;-1:-1:-1;;;18877:18:50;;;18870:41;18928:19;;17157:86:43;18546:407:50;17157:86:43;17257:24;;;;:17;:24;;;;;17287:7;;17295:1;17287:10;;;;;;;:::i;:::-;17257:41;;;;;;;;-1:-1:-1;17257:41:43;;;17287:10;17257:41;;;;17287:10;;;;;;;;;17257:41;;;;;;-1:-1:-1;17139:3:43;17099:210;;;-1:-1:-1;17319:20:43;17342:23;;;:16;:23;;;;;;-1:-1:-1;;;;;17342:23:43;17379:26;;17375:130;;17421:73;;-1:-1:-1;;;17421:73:43;;-1:-1:-1;;;;;17421:46:43;;;;;:73;;17468:5;;17475:7;;;;17484:9;;;;17421:73;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17375:130;16691:820;16589:922;;;;;:::o;10043:196::-;-1:-1:-1;;;;;;;;;;;;10150:19:43;10163:5;10150:12;:19::i;:::-;10142:50;;;;-1:-1:-1;;;10142:50:43;;;;;;;:::i;:::-;10209:15;;;;:8;:15;;;;;;;;:23;;;;;;;;;10202:30;;;;;;;;;;;;10209:23;10202:30;;10209:23;;10202:30;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10043:196;;;;;:::o;8590:183::-;8656:7;8683:19;8696:5;8683:12;:19::i;:::-;8675:50;;;;-1:-1:-1;;;8675:50:43;;;;;;;:::i;:::-;-1:-1:-1;8742:24:43;;;;:17;:24;;;;;;;8590:183::o;22342:406::-;22401:7;22452:12;22466:19;-1:-1:-1;;;;;;;;;;22489:32:43;22539:5;22522:23;;;;;;20178:19:50;;20222:2;20213:12;;20049:182;22522:23:43;;;;-1:-1:-1;;22522:23:43;;;;;;;;;;22489:57;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22451:95;;;;22609:7;22601:53;;;;-1:-1:-1;;;22601:53:43;;20730:2:50;22601:53:43;;;20712:21:50;20769:2;20749:18;;;20742:30;20808:34;20788:18;;;20781:62;-1:-1:-1;;;20859:18:50;;;20852:31;20900:19;;22601:53:43;20528:397:50;22601:53:43;22723:6;22712:29;;;;;;;;;;;;:::i;:::-;22705:36;22342:406;-1:-1:-1;;;;22342:406:43:o;23793:1870::-;1932:4;23910:39;;;23902:72;;;;-1:-1:-1;;;23902:72:43;;;;;;;:::i;:::-;24006:20;;;;:13;:20;;;;;;-1:-1:-1;;;;;24006:20:43;23992:10;:34;23984:93;;;;-1:-1:-1;;;23984:93:43;;;;;;;:::i;:::-;24122:1;24095:24;;;:17;:24;;;;;;24087:85;;;;-1:-1:-1;;;24087:85:43;;21736:2:50;24087:85:43;;;21718:21:50;21775:2;21755:18;;;21748:30;21814:34;21794:18;;;21787:62;-1:-1:-1;;;21865:18:50;;;21858:42;21917:19;;24087:85:43;21534:408:50;24087:85:43;6488:1;24195:30;;;:23;:30;;;;;;24191:125;;24260:30;;;;:23;:30;;;;;24293:12;24260:45;;24191:125;24375:26;24404:24;;;:17;:24;;;;;24489:15;;24404:24;;24375:26;-1:-1:-1;;;;;24475:30:43;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;24475:30:43;;24438:67;;24521:9;24516:160;24540:17;:24;24536:1;:28;24516:160;;;24617:15;;24608:8;;24617:19;;24635:1;;24617:19;:::i;:::-;24608:29;;;;;;;;:::i;:::-;;;;;;;;;24585:17;24603:1;24585:20;;;;;;;;:::i;:::-;;;;;;:52;;;;;24651:8;:14;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;24651:14:43;;;;;;;;;;;24566:3;24516:160;;;;24686:37;24698:5;24705:17;24686:11;:37::i;:::-;24803:24;;;;:17;:24;;;;;;;;;24779:14;:21;;;;;:48;24873:17;24858:32;;:12;:32;:::i;:::-;24841:14;:49;24837:161;;;24906:81;;-1:-1:-1;;;24906:81:43;;22281:2:50;24906:81:43;;;22263:21:50;22320:2;22300:18;;;22293:30;22359:34;22339:18;;;22332:62;22430:34;22410:18;;;22403:62;-1:-1:-1;;;22481:19:50;;;22474:38;22529:19;;24906:81:43;22079:475:50;24837:161:43;25007:25;;;;:18;:25;;;;;;;;:42;;;25213:17;:24;;;;;;:29;;25209:211;;25263:20;;25277:5;;25263:20;;;;;6488:1;25297:30;;;:23;:30;;;;;;;;:48;;;25359:18;:25;;;;;:50;25209:211;25430:20;25453:23;;;:16;:23;;;;;;-1:-1:-1;;;;;25453:23:43;25490:26;;25486:171;;25583:25;;;;:18;:25;;;;;;;;;25610:17;:24;;;;;;;;25532:114;;-1:-1:-1;;;25532:114:43;;-1:-1:-1;;;;;25532:43:43;;;;;:114;;25576:5;;25610:24;25636:9;;;;25532:114;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;25486:171;23892:1771;;;23793:1870;;;;:::o;9558:216::-;9620:7;9629;9656:19;9669:5;9656:12;:19::i;:::-;9648:50;;;;-1:-1:-1;;;9648:50:43;;;;;;;:::i;:::-;-1:-1:-1;;9716:20:43;;;;:13;:20;;;;;;;;;9738:21;:28;;;;;;;-1:-1:-1;;;;;9716:20:43;;;;9738:28;;;9558:216::o;7813:186::-;7883:4;7906:19;7919:5;7906:12;:19::i;:::-;:49;;;;-1:-1:-1;7938:17:43;;;;:10;:17;;;;;;7929:26;;7906:49;:86;;;;-1:-1:-1;;7991:1:43;7959:21;;;:14;:21;;;;;;;;:29;;;;;;;;;:33;;;7813:186::o;20956:452::-;21044:7;21086:21;;;:14;:21;;;;;;;;21044:7;;21081:26;;21086:21;21081:26;:::i;:::-;21063:44;;21118:18;21138:21;21163:16;:14;:16::i;:::-;21117:62;;;;21197:204;21242:15;21271:11;21296:15;21325:7;21361:23;:30;21385:5;21361:30;;;;;;;;;;;;21346:12;:45;;;;:::i;:::-;21197:31;:204::i;:::-;21190:211;20956:452;-1:-1:-1;;;;;;20956:452:43:o;4161:214:25:-;2655:13;:11;:13::i;:::-;4276:36:::1;4294:17;4276;:36::i;:::-;4322:46;4344:17;4363:4;4322:21;:46::i;:::-;4161:214:::0;;:::o;31353:477:43:-;31511:97;;-1:-1:-1;;;31511:97:43;;2182:66;31511:97;;;23370:25:50;1983:5:43;23411:18:50;;;23404:34;31400:6:43;;;;;;2029:42;;31511:24;;23343:18:50;;31511:97:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;31474:134;;31644:1;31626:9;:15;;;:19;;;31618:80;;;;-1:-1:-1;;;31618:80:43;;24592:2:50;31618:80:43;;;24574:21:50;24631:2;24611:18;;;24604:30;24670:34;24650:18;;;24643:62;-1:-1:-1;;;24721:18:50;;;24714:46;24777:19;;31618:80:43;24390:412:50;31618:80:43;31790:15;;31808:14;;;;;31790:15;;31808:14;;-1:-1:-1;31353:477:43;-1:-1:-1;31353:477:43:o;3708:134:25:-;3777:7;2926:20;:18;:20::i;:::-;-1:-1:-1;;;;;;;;;;;;3708:134:25;:::o;9058:185:43:-;9125:7;9152:19;9165:5;9152:12;:19::i;:::-;9144:50;;;;-1:-1:-1;;;9144:50:43;;;;;;;:::i;:::-;-1:-1:-1;9211:25:43;;;;:18;:25;;;;;;;9058:185::o;11430:517::-;11518:19;11531:5;11518:12;:19::i;:::-;11510:50;;;;-1:-1:-1;;;11510:50:43;;;;;;;:::i;:::-;11570:13;11586:20;;;:13;:20;;;;;;-1:-1:-1;;;;;11586:20:43;11633:10;11624:19;;11616:78;;;;-1:-1:-1;;;11616:78:43;;25009:2:50;11616:78:43;;;24991:21:50;25048:2;25028:18;;;25021:30;25087:34;25067:18;;;25060:62;-1:-1:-1;;;25138:18:50;;;25131:44;25192:19;;11616:78:43;24807:410:50;11616:78:43;11717:8;-1:-1:-1;;;;;11708:17:43;:5;-1:-1:-1;;;;;11708:17:43;;11704:237;;-1:-1:-1;;11832:28:43;;;;:21;:28;;;;;11825:35;;-1:-1:-1;;;;;;11825:35:43;;;11430:517::o;11704:237::-;11891:28;;;;:21;:28;;;;;:39;;-1:-1:-1;;;;;;11891:39:43;-1:-1:-1;;;;;11891:39:43;;;;;11704:237;11500:447;11430:517;;:::o;10915:406::-;10981:16;11017:19;11030:5;11017:12;:19::i;:::-;11009:50;;;;-1:-1:-1;;;11009:50:43;;;;;;;:::i;:::-;11069:26;11098:24;;;:17;:24;;;;;11172:15;;11098:24;;11069:26;-1:-1:-1;;;;;11158:30:43;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11158:30:43;;11132:56;;11203:9;11198:94;11222:15;;11218:19;;11198:94;;;11270:8;11279:1;11270:11;;;;;;;;:::i;:::-;;;;;;;;;11258:6;11265:1;11258:9;;;;;;;;:::i;:::-;;;;;;;;;;:23;11239:3;;11198:94;;;-1:-1:-1;11308:6:43;10915:406;-1:-1:-1;;;10915:406:43:o;3155:101:23:-;2334:13;:11;:13::i;:::-;3219:30:::1;3246:1;3219:18;:30::i;:::-;3155:101::o:0;8098:441:43:-;8176:4;8223:17;;;:10;:17;;;;;;8176:4;;8212:29;;:10;:29::i;:::-;8206:35;;:3;:35;:::i;:::-;8251:26;8301:21;;;:14;:21;;;;;;8192:49;;-1:-1:-1;8251:26:43;8280:50;;8294:5;;8301:23;;8323:1;;8301:23;:::i;:::-;8326:3;8280:13;:50::i;:::-;8362:21;;;;:14;:21;;;;;;;;8384:10;;8362:33;;;;;;;;8251:79;;-1:-1:-1;8362:37:43;;8398:1;;8362:37;:::i;:::-;8348:3;:10;;;:51;8340:128;;;;;-1:-1:-1;;;8340:128:43;;25424:2:50;8340:128:43;;;25406:21:50;25443:18;;;25436:30;;;;25502:34;25482:18;;;25475:62;25573:34;25553:18;;;25546:62;25625:19;;8340:128:43;25222:428:50;8340:128:43;8485:23;8494:5;8501:6;8485:8;:23::i;:::-;:47;;;;-1:-1:-1;8522:10:43;8512:20;;;;;8478:54;-1:-1:-1;;;8098:441:43:o;13543:848::-;1932:4;13633:39;;;13625:72;;;;-1:-1:-1;;;13625:72:43;;;;;;;:::i;:::-;13720:14;;-1:-1:-1;;;;;13720:14:43;13711:23;;13707:90;;13750:36;;-1:-1:-1;;;13750:36:43;;25857:2:50;13750:36:43;;;25839:21:50;25896:2;25876:18;;;25869:30;25935:28;25915:18;;;25908:56;25981:18;;13750:36:43;25655:350:50;13707:90:43;13815:20;;;;:13;:20;;;;;;-1:-1:-1;;;;;13815:20:43;13839:10;13815:34;13807:83;;;;-1:-1:-1;;;13807:83:43;;26212:2:50;13807:83:43;;;26194:21:50;26251:2;26231:18;;;26224:30;26290:34;26270:18;;;26263:62;-1:-1:-1;;;26341:18:50;;;26334:34;26385:19;;13807:83:43;26010:400:50;13807:83:43;13900:24;13927;;;:17;:24;;;;;;;;;;13961:28;;;;13999:13;:20;;;;;:33;;-1:-1:-1;;;;;;13999:33:43;;;14042:18;:25;;;;;:29;;;14081:23;:30;;;;;:48;;;14163:16;:23;;;;;;;-1:-1:-1;;;;;14163:23:43;14200:26;;14196:134;;14242:77;;-1:-1:-1;;;14242:77:43;;-1:-1:-1;;;;;14242:41:43;;;;;:77;;14284:5;;14291:16;;14309:9;;;;14242:77;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14196:134;14360:5;14344:40;14367:16;14344:40;;;;160:25:50;;148:2;133:18;;14:177;14344:40:43;;;;;;;;13615:776;;13543:848;;;:::o;10629:177::-;10692:7;10719:19;10732:5;10719:12;:19::i;:::-;10711:50;;;;-1:-1:-1;;;10711:50:43;;;;;;;:::i;:::-;-1:-1:-1;10778:21:43;;;;:14;:21;;;;;;;10629:177::o;10314:200::-;10392:7;10419:19;10432:5;10419:12;:19::i;:::-;10411:50;;;;-1:-1:-1;;;10411:50:43;;;;;;;:::i;:::-;-1:-1:-1;10478:21:43;;;;:14;:21;;;;;;;;:29;;;;;;;;;;10314:200::o;8827:169::-;8886:7;8913:19;8926:5;8913:12;:19::i;:::-;8905:50;;;;-1:-1:-1;;;8905:50:43;;;;;;;:::i;:::-;-1:-1:-1;8972:17:43;;;;:10;:17;;;;;;;8827:169::o;11953:317::-;12025:19;12038:5;12025:12;:19::i;:::-;12017:50;;;;-1:-1:-1;;;12017:50:43;;;;;;;:::i;:::-;12085:28;;;;:21;:28;;;;;;-1:-1:-1;;;;;12085:28:43;12117:10;12085:42;12077:98;;;;-1:-1:-1;;;12077:98:43;;27008:2:50;12077:98:43;;;26990:21:50;27047:2;27027:18;;;27020:30;27086:34;27066:18;;;27059:62;-1:-1:-1;;;27137:18:50;;;27130:41;27188:19;;12077:98:43;26806:407:50;12077:98:43;12185:20;;;;:13;:20;;;;;;;;:33;;-1:-1:-1;;;;;;12185:33:43;;;12208:10;12185:33;;;;12235:21;:28;;;;;;12228:35;;;;;;;11953:317::o;3405:215:23:-;2334:13;:11;:13::i;:::-;-1:-1:-1;;;;;3489:22:23;::::1;3485:91;;3534:31;::::0;-1:-1:-1;;;3534:31:23;;3562:1:::1;3534:31;::::0;::::1;3053:51:50::0;3026:18;;3534:31:23::1;2907:203:50::0;3485:91:23::1;3585:28;3604:8;3585:18;:28::i;:::-;3405:215:::0;:::o;17867:3083:43:-;17956:18;17977:9;18018:20;;;;:13;:20;;;;;;17956:30;;-1:-1:-1;;;;;;18018:20:43;18004:10;:34;17996:93;;;;-1:-1:-1;;;17996:93:43;;;;;;;:::i;:::-;18099:22;18124:25;;;:18;:25;;;;;;18167:12;:30;-1:-1:-1;18167:30:43;18159:58;;;;-1:-1:-1;;;18159:58:43;;27420:2:50;18159:58:43;;;27402:21:50;27459:2;27439:18;;;27432:30;-1:-1:-1;;;27478:18:50;;;27471:45;27533:18;;18159:58:43;27218:339:50;18159:58:43;18235:17;18227:41;;;;-1:-1:-1;;;18227:41:43;;27764:2:50;18227:41:43;;;27746:21:50;27803:2;27783:18;;;27776:30;-1:-1:-1;;;27822:18:50;;;27815:41;27873:18;;18227:41:43;27562:335:50;18227:41:43;18286:14;18278:75;;;;-1:-1:-1;;;18278:75:43;;28104:2:50;18278:75:43;;;28086:21:50;28143:2;28123:18;;;28116:30;-1:-1:-1;;;28162:18:50;;;28155:52;28224:18;;18278:75:43;27902:346:50;18278:75:43;18372:12;18387:24;18405:5;18387:17;:24::i;:::-;18421:17;18441:21;;;:14;:21;;;;;;;;;18510:10;:17;;;;;;18372:39;;-1:-1:-1;18441:21:43;18499:29;;:10;:29::i;:::-;18493:35;;:3;:35;:::i;:::-;18472:56;;18543:8;18538:1617;-1:-1:-1;;;;;18557:17:43;;;-1:-1:-1;18538:1617:43;;;19620:32;;;;;;28436:19:50;;;28471:12;;;28464:28;;;-1:-1:-1;;;;;;28548:3:50;28526:16;;;28522:51;28508:12;;;28501:73;19597:20:43;;28590:12:50;;19620:32:43;;;;;;;;;;;;19597:55;;19666:20;19719:9;19707:7;19697:18;;;;;;19689:27;;:39;;;;:::i;:::-;19666:62;;19837:27;19867:46;19881:5;19888:12;19902:10;19867:13;:46::i;:::-;19837:76;;19927:16;19946:50;19965:30;19976:5;19983:4;:11;;;19965:10;:30::i;:::-;19946:18;:50::i;:::-;19927:69;;20010:7;20020:75;20040:6;;20047:1;-1:-1:-1;;;;;20040:9:43;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;:15;;;;;;;:::i;:::-;20020:75;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20057:8;20067:6;;20074:1;-1:-1:-1;;;;;20067:9:43;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;:14;;;20083:4;:11;;;20020:19;:75::i;:::-;20010:85;;20117:2;20109:35;;;;-1:-1:-1;;;20109:35:43;;29942:2:50;20109:35:43;;;29924:21:50;29981:2;29961:18;;;29954:30;-1:-1:-1;;;30000:18:50;;;29993:50;30060:18;;20109:35:43;29740:344:50;20109:35:43;18581:1574;;;;;18576:3;;;;;:::i;:::-;;;;18538:1617;;;;20529:15;20576:29;20598:6;;20576:21;:29::i;:::-;:34;;20608:2;20576:34;:::i;:::-;20575:43;;20614:4;20575:43;:::i;:::-;20561:9;20548:22;;:10;:22;:::i;:::-;20547:72;;;;:::i;:::-;20529:90;;20629:40;20654:5;20661:7;20629:24;:40::i;:::-;20680:20;20703:23;;;:16;:23;;;;;;-1:-1:-1;;;;;20703:23:43;20740:26;;20736:153;;20832:24;;;;:17;:24;;;;;;;;20782:96;;-1:-1:-1;;;20782:96:43;;;;;30320:25:50;;;30361:18;;;30354:34;;;;30404:18;;;30397:34;;;30447:18;;;30440:34;;;-1:-1:-1;;;;;20782:42:43;;;;;30292:19:50;;20782:96:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20736:153;-1:-1:-1;;;20898:30:43;;;;-1:-1:-1;;20898:23:43;:30;;-1:-1:-1;;20898:30:43;;;;20931:12;20898:45;;-1:-1:-1;;17867:3083:43:o;7558:148::-;7647:14;;7616:4;;-1:-1:-1;;;;;7647:14:43;7639:22;;:60;;;;-1:-1:-1;;7697:1:43;7665:20;;;:13;:20;;;;;;-1:-1:-1;;;;;7665:20:43;:34;;;7558:148::o;9780:195::-;9852:7;9879:19;9892:5;9879:12;:19::i;:::-;9871:50;;;;-1:-1:-1;;;9871:50:43;;;;;;;:::i;:::-;-1:-1:-1;9938:30:43;;;;:23;:30;;;;;;;9780:195::o;6678:192::-;8870:21:24;4302:15;;-1:-1:-1;;;4302:15:24;;;;4301:16;;-1:-1:-1;;;;;4348:14:24;4158:30;4726:16;;:34;;;;;4746:14;4726:34;4706:54;;4770:17;4790:11;-1:-1:-1;;;;;4790:16:24;4805:1;4790:16;:50;;;;-1:-1:-1;4818:4:24;4810:25;:30;4790:50;4770:70;;4856:12;4855:13;:30;;;;;4873:12;4872:13;4855:30;4851:91;;;4908:23;;-1:-1:-1;;;4908:23:24;;;;;;;;;;;4851:91;4951:18;;-1:-1:-1;;4951:18:24;4968:1;4951:18;;;4979:67;;;;5013:22;;-1:-1:-1;;;;5013:22:24;-1:-1:-1;;;5013:22:24;;;4979:67;6755:26:43::1;6770:10;6755:14;:26::i;:::-;6791:24;:22;:24::i;:::-;6825:17;:38:::0;;;5066:101:24;;;;5100:23;;-1:-1:-1;;;;5100:23:24;;;5142:14;;-1:-1:-1;10602:50:50;;5142:14:24;;10590:2:50;10575:18;5142:14:24;;;;;;;4092:1081;;;;;6678:192:43;:::o;189:563:39:-;236:7;267:3;309;304:8;;;318:6;;314:32;;328:8;333:3;328:8;;:::i;:::-;;;342:1;338:5;;314:32;-1:-1:-1;364:2:39;359:7;;;373:6;;369:32;;383:7;388:2;383:7;;:::i;:::-;;;397:1;393:5;;369:32;-1:-1:-1;419:2:39;414:7;;;428:6;;424:32;;438:7;443:2;438:7;;:::i;:::-;;;452:1;448:5;;424:32;-1:-1:-1;474:2:39;469:7;;;483:6;;479:32;;493:7;498:2;493:7;;:::i;:::-;;;507:1;503:5;;479:32;-1:-1:-1;529:1:39;524:6;;;538;;534:32;;548:6;553:1;548:6;;:::i;:::-;;;562:1;558:5;;534:32;-1:-1:-1;584:1:39;579:6;;;593;;589:32;;603:6;608:1;603:6;;:::i;:::-;;;617:1;613:5;;589:32;-1:-1:-1;639:1:39;634:6;;;648;;644:32;;658:6;663:1;658:6;;:::i;:::-;;;672:1;668:5;;644:32;-1:-1:-1;694:1:39;689:6;;;703;;699:24;;718:5;722:1;718;:5;:::i;699:24::-;740:5;744:1;740;:5;:::i;29183:1307:43:-;-1:-1:-1;;;;;;;;;;;;;;;;;29330:24:43;;;;:17;:24;;;;;;29318:36;;29310:73;;;;-1:-1:-1;;;29310:73:43;;30901:2:50;29310:73:43;;;30883:21:50;30940:2;30920:18;;;30913:30;30979:26;30959:18;;;30952:54;31023:18;;29310:73:43;30699:348:50;29310:73:43;29393:17;29413:14;29426:1;29414:8;;;29413:14;:::i;:::-;29393:34;-1:-1:-1;29437:11:43;;29601:3;29584:616;29606:5;;29584:616;;29753:17;;;;:10;:17;;;;;;29740:30;;29736:120;;29809:5;29813:1;29809;:5;:::i;:::-;29790:25;;29803:1;:12;;29790:25;;:::i;:::-;;;29833:8;;29736:120;29888:20;;;;:13;:20;;;;;;;;:31;;;;;;;;;29882:37;;:3;:37;:::i;:::-;29870:49;;29974:9;29961;:22;29957:233;;30010:20;;;;:13;:20;;;;;;;;:31;;;;;;;;;30003:38;;;;:::i;:::-;;-1:-1:-1;30078:5:43;30082:1;30078;:5;:::i;:::-;30059:25;;30072:1;:12;;30059:25;;:::i;29957:233::-;30169:5;30173:1;30169;:5;:::i;:::-;30150:25;;30163:1;:12;;30150:25;;:::i;:::-;;;29957:233;29613:3;;;;:::i;:::-;;;;29584:616;;;-1:-1:-1;30227:20:43;;;;:13;:20;;;;;;;;:31;;;;;;;;;30221:37;;:3;:37;:::i;:::-;30209:49;;30285:9;30272;:22;30268:141;;30345:53;;;;;;;;30361:9;30373:1;30361:13;;;;:::i;:::-;30345:53;;;;30376:21;30388:9;30376;:21;:::i;:::-;30345:53;;;30338:60;;;;;;;30268:141;30440:43;;;;;;;;30456:9;30440:43;;;;30479:3;30467:9;:15;;;;:::i;:::-;30440:43;;30433:50;29183:1307;-1:-1:-1;;;;;;;29183:1307:43:o;2953:85:42:-;2996:7;279:2;195:15;153:1;195:4;:15;:::i;:::-;261:20;;;;:::i;:::-;3015:16;;2953:85;:::o;6966:215:43:-;7039:6;7026:9;:19;;7018:52;;;;-1:-1:-1;;;7018:52:43;;31520:2:50;7018:52:43;;;31502:21:50;31559:2;31539:18;;;31532:30;-1:-1:-1;;;31578:18:50;;;31571:50;31638:18;;7018:52:43;31318:344:50;7018:52:43;7099:34;;7081:12;;-1:-1:-1;;;;;1587:42:43;7122:6;;7081:12;7099:34;7081:12;7099:34;7122:6;1587:42;7099:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7080:53;;;7151:7;7143:31;;;;-1:-1:-1;;;7143:31:43;;32079:2:50;7143:31:43;;;32061:21:50;32118:2;32098:18;;;32091:30;-1:-1:-1;;;32137:18:50;;;32130:41;32188:18;;7143:31:43;31877:335:50;15620:793:43;15731:7;15754:19;1671:2;15754:7;:19;:::i;:::-;:24;15750:116;;15814:7;15801:54;;-1:-1:-1;;;15801:54:43;;;;;;32429:25:50;;32490:2;32485;32470:18;;32463:30;;;32529:2;32509:18;;;32502:30;32568:31;32563:2;32548:18;;32541:59;32632:3;32617:19;;32217:425;15750:116:43;15879:7;15890:1;15879:12;15875:102;;15927:7;15914:52;;-1:-1:-1;;;15914:52:43;;;;;;32859:25:50;;32920:2;32915;32900:18;;32893:30;;;32959:2;32939:18;;;32932:30;32998:29;32993:2;32978:18;;32971:57;33060:3;33045:19;;32647:423;15875:102:43;1719:7;15990;:23;15986:118;;;16049:7;16036:57;;-1:-1:-1;;;16036:57:43;;;;;;33287:25:50;;33348:2;33343;33328:18;;;33321:30;;;33367:18;;;33360:30;33426:34;33421:2;33406:18;;33399:62;33493:3;33478:19;;33075:428;15986:118:43;16114:17;16134:19;1671:2;16134:7;:19;:::i;:::-;16163:14;16180:17;;;:10;:17;;;;;:19;;16114:39;;-1:-1:-1;16163:14:43;;16180:19;16163:14;16180:19;;;:::i;:::-;;;;;16163:36;;16209;16220:5;16227:9;16238:6;16209:10;:36::i;:::-;16255:15;;;;:8;:15;;;;;;;;:23;;;;;;;;16281:4;;16255:30;16281:4;16255:23;:30;:::i;:::-;-1:-1:-1;;16295:21:43;;;;:14;:21;;;;;;;;:29;;;;;;;;:41;;;16346:24;;;:17;:24;;;;;:37;;16327:9;;16295:21;16346:37;;16327:9;;16346:37;:::i;:::-;;;;-1:-1:-1;16400:6:43;;15620:793;-1:-1:-1;;;;;;;15620:793:43:o;25716:342::-;25805:19;25818:5;25805:12;:19::i;:::-;25797:50;;;;-1:-1:-1;;;25797:50:43;;;;;;;:::i;:::-;25857:18;25894:9;25889:115;25913:7;:14;25909:1;:18;25889:115;;;25961:32;25975:5;25982:7;25990:1;25982:10;;;;;;;;:::i;:::-;;;;;;;25961:13;:32::i;:::-;25947:46;;;;:::i;:::-;;-1:-1:-1;25929:3:43;;25889:115;;;-1:-1:-1;26013:24:43;;;;:17;:24;;;;;:38;;26041:10;;26013:24;:38;;26041:10;;26013:38;:::i;:::-;;;;-1:-1:-1;;;;;25716:342:43:o;992:1834:42:-;1215:7;1268:1;1250:15;:19;:41;;;-1:-1:-1;1273:13:42;:18;1250:41;1242:114;;;;-1:-1:-1;;;1242:114:42;;36287:2:50;1242:114:42;;;36269:21:50;36326:2;36306:18;;;36299:30;36365:34;36345:18;;;36338:62;36436:30;36416:18;;;36409:58;36484:19;;1242:114:42;36085:424:50;1242:114:42;1388:1;1374:11;-1:-1:-1;;;;;1374:15:42;;1366:84;;;;-1:-1:-1;;;1366:84:42;;36716:2:50;1366:84:42;;;36698:21:50;36755:2;36735:18;;;36728:30;36794:34;36774:18;;;36767:62;36865:26;36845:18;;;36838:54;36909:19;;1366:84:42;36514:420:50;1366:84:42;1478:1;1468:7;:11;1460:75;;;;-1:-1:-1;;;1460:75:42;;37141:2:50;1460:75:42;;;37123:21:50;37180:2;37160:18;;;37153:30;37219:34;37199:18;;;37192:62;-1:-1:-1;;;37270:18:50;;;37263:49;37329:19;;1460:75:42;36939:415:50;1460:75:42;1606:29;1668:1;1649:15;:20;;;1645:452;;1839:29;1852:15;1839:2;:29;:::i;:::-;-1:-1:-1;;;;;1790:45:42;;:31;943:5;828:7;1790:31;:::i;:::-;:45;;;;:::i;:::-;:79;;;;:::i;:::-;195:15;153:1;195:4;:15;:::i;:::-;1710:58;;421:1;1710:58;:::i;:::-;1709:161;;;;:::i;:::-;1685:185;;1645:452;;;-1:-1:-1;;;;;2040:45:42;;:31;943:5;828:7;2040:31;:::i;:::-;:45;;;;:::i;:::-;2001:16;2002:15;2001:16;:::i;:::-;1988:30;;:2;:30;:::i;:::-;195:15;153:1;195:4;:15;:::i;:::-;1926:58;;421:1;1926:58;:::i;:::-;:93;;;;:::i;:::-;1925:161;;;;:::i;:::-;1901:185;;1645:452;2164:30;2236:7;2197:36;2221:12;2197:21;:36;:::i;:::-;:46;;;;:::i;:::-;2164:79;-1:-1:-1;2286:21:42;2366:3;2311:51;717:1;2164:79;2311:51;:::i;:::-;2310:59;;;;:::i;:::-;2286:83;-1:-1:-1;2379:20:42;2457:3;2403:50;607:1;2403:22;:50;:::i;:::-;2402:58;;;;:::i;:::-;2379:81;;2494:13;2475:15;:32;2471:349;;2530:1;2523:8;;;;;;;;2471:349;2619:12;2600:15;:31;2596:224;;2654:31;2670:15;2654:13;:31;:::i;:::-;2647:38;;;;;;;;2596:224;2806:3;2757:45;499:1;2757:22;:45;:::i;:::-;2756:53;;;;:::i;4603:312:25:-;4683:4;-1:-1:-1;;;;;4692:6:25;4675:23;;;:120;;;4789:6;-1:-1:-1;;;;;4753:42:25;:32;-1:-1:-1;;;;;;;;;;;1519:53:30;-1:-1:-1;;;;;1519:53:30;;1441:138;4753:32:25;-1:-1:-1;;;;;4753:42:25;;;4675:120;4658:251;;;4869:29;;-1:-1:-1;;;4869:29:25;;;;;;;;;;;6876:84:43;2334:13:23;:11;:13::i;6057:538:25:-;6174:17;-1:-1:-1;;;;;6156:50:25;;:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;6156:52:25;;;;;;;;-1:-1:-1;;6156:52:25;;;;;;;;;;;;:::i;:::-;;;6152:437;;6518:60;;-1:-1:-1;;;6518:60:25;;-1:-1:-1;;;;;3071:32:50;;6518:60:25;;;3053:51:50;3026:18;;6518:60:25;2907:203:50;6152:437:25;-1:-1:-1;;;;;;;;;;;6250:40:25;;6246:120;;6317:34;;-1:-1:-1;;;6317:34:25;;;;;160:25:50;;;133:18;;6317:34:25;14:177:50;6246:120:25;6379:54;6409:17;6428:4;6379:29;:54::i;5032:213::-;5106:4;-1:-1:-1;;;;;5115:6:25;5098:23;;5094:145;;5199:29;;-1:-1:-1;;;5199:29:25;;;;;;;;;;;2658:162:23;966:10:26;2717:7:23;1313:22;2570:8;-1:-1:-1;;;;;2570:8:23;;2441:144;2717:7;-1:-1:-1;;;;;2717:23:23;;2713:101;;2763:40;;-1:-1:-1;;;2763:40:23;;966:10:26;2763:40:23;;;3053:51:50;3026:18;;2763:40:23;2907:203:50;3774:248:23;1313:22;3923:8;;-1:-1:-1;;;;;;3941:19:23;;-1:-1:-1;;;;;3941:19:23;;;;;;;;3975:40;;3923:8;;;;;3975:40;;3847:24;;3975:40;3837:185;;3774:248;:::o;22755:138:43:-;22820:7;22860:25;;;:18;:25;;;;;;22846:40;;:13;:40::i;274:1721:40:-;336:7;382:2;363:3;:8;;;:15;:21;;355:55;;;;-1:-1:-1;;;355:55:40;;39326:2:50;355:55:40;;;39308:21:50;39365:2;39345:18;;;39338:30;-1:-1:-1;;;39384:18:50;;;39377:51;39445:18;;355:55:40;39124:345:50;355:55:40;445:13;;;455:2;445:13;;;;;;;;;420:22;;445:13;;;;;;;;;;-1:-1:-1;445:13:40;420:38;;473:6;468:104;489:2;485:1;:6;468:104;;;527:8;;536:15;;559:1;;536:20;;554:2;;536:20;:::i;:::-;:24;;;;:::i;:::-;527:34;;;;;;;;:::i;:::-;;;;;;;;;512:9;522:1;512:12;;;;;;;;:::i;:::-;;;;:49;-1:-1:-1;;;;;512:49:40;;;;;;;;-1:-1:-1;493:3:40;;468:104;;;-1:-1:-1;588:18:40;596:9;588:18;:::i;951:188:44:-;1060:4;1128;1083:41;1102:5;1109:4;1115:8;1083:18;:41::i;:::-;:49;;951:188;-1:-1:-1;;;;;951:188:44:o;21952:384:43:-;22031:7;;;22084:217;22104:17;;;22084:217;;;22262:6;;22269:1;22262:9;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;:15;;;;;;;:::i;:::-;:27;;-1:-1:-1;22287:2:43;22262:27;:::i;:::-;22256:34;;:2;:34;:::i;:::-;22240:50;;;;:::i;:::-;;-1:-1:-1;22123:3:43;;22084:217;;21414:532;21499:23;21525;21535:13;21525:7;:23;:::i;:::-;21499:49;;21558:16;21577:41;21595:5;21602:15;21577:17;:41::i;:::-;21558:60;;21628:17;21636:8;21628:7;:17::i;:::-;21671:8;21659:9;:20;21655:139;;;21741:10;21733:50;21762:20;21774:8;21762:9;:20;:::i;:::-;21733:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21655:139;21804:18;21824:21;21849:16;:14;:16::i;:::-;21880:59;;;39972:25:50;;;-1:-1:-1;;;;;40033:31:50;;40028:2;40013:18;;40006:59;40112:1;40101:21;;;40081:18;;;40074:49;21880:59:43;;40033:31:50;;-1:-1:-1;40101:21:50;;-1:-1:-1;21893:5:43;;21880:59;;;;;39960:2:50;21880:59:43;;;21489:457;;;;21414:532;;:::o;1847:127:23:-;6931:20:24;:18;:20::i;:::-;1929:38:23::1;1954:12;1929:24;:38::i;2970:67:25:-:0;6931:20:24;:18;:20::i;28064:420:43:-;28165:6;28149:13;28193:22;28165:6;28193:15;:22::i;:::-;28181:34;-1:-1:-1;28240:5:43;28226:11;28305:129;28329:1;28325;:5;28305:129;;;28351:9;28363:16;28372:1;:6;;28363:5;:16;:::i;:::-;28400:20;;;;:13;:20;;;;;;;;:23;;;;;;;;;28351:28;;-1:-1:-1;28393:30:43;;;;:::i;:::-;;-1:-1:-1;;28332:3:43;;28305:129;;;-1:-1:-1;28443:20:43;;;;:13;:20;;;;;;;;:28;;;;;;;;;;:34;;;;-1:-1:-1;;;28064:420:43:o;26232:296::-;26304:7;26339:21;;;:14;:21;;;;;;;;:29;;;;;;;;;26378:35;26354:5;26361:6;26339:29;26378:13;:35::i;:::-;26430:21;;;;:14;:21;;;;;;;;:29;;;;;;;;26423:36;;;26476:15;;;:8;:15;;;;;:23;;;;;;;;;26469:30;26476:23;26430:21;26469:30;:::i;:::-;-1:-1:-1;26516:5:43;;26232:296;-1:-1:-1;;;;26232:296:43:o;2264:344:30:-;2355:37;2374:17;2355:18;:37::i;:::-;2407:36;;-1:-1:-1;;;;;2407:36:30;;;;;;;;2458:11;;:15;2454:148;;2489:53;2518:17;2537:4;2489:28;:53::i;2454:148::-;2573:18;:16;:18::i;1422:633:44:-;1529:7;1571:4;1529:7;1585:435;1609:5;:12;1605:1;:16;1585:435;;;1736:15;1754:5;1760:1;1754:8;;;;;;;;:::i;:::-;;;;;;;1736:26;;1791:1;1780:8;:12;;;;:::i;:::-;1796:1;1780:17;1776:207;;1832:41;1851:12;1865:7;1832:18;:41::i;:::-;1817:56;;1776:207;;;1927:41;1946:7;1955:12;1927:18;:41::i;:::-;1912:56;;1776:207;1996:13;2008:1;1996:13;;:::i;:::-;;-1:-1:-1;;1623:3:44;;1585:435;;;-1:-1:-1;2036:12:44;1422:633;-1:-1:-1;;;;1422:633:44:o;7084:141:24:-;8870:21;8560:40;-1:-1:-1;;;8560:40:24;;;;7146:73;;7191:17;;-1:-1:-1;;;7191:17:24;;;;;;;;;;;1980:235:23;6931:20:24;:18;:20::i;31189:117:43:-;31252:7;31278:21;31289:9;:5;31297:1;31289:9;:::i;:::-;31278:10;:21::i;28528:537::-;28615:11;28654:17;;;:10;:17;;;;;;28643:29;;:10;:29::i;:::-;28637:35;;:3;:35;:::i;:::-;28615:58;;28683:9;28703:22;28719:5;28703:15;:22::i;:::-;28683:43;;28883:176;28895:3;28890:1;:8;;:37;;;;-1:-1:-1;28910:17:43;;;;:10;:17;;;;;;28902:25;;28890:37;28883:176;;;28943:20;;;;:13;:20;;;;;;;;:27;;;;;;;;:36;;28974:5;;28943:20;:36;;28974:5;;28943:36;:::i;:::-;;;;-1:-1:-1;28993:15:43;;-1:-1:-1;29002:1:43;:6;;28993:15;;:::i;:::-;;;29026:22;29042:5;29026:15;:22::i;:::-;29022:26;;28883:176;;;28605:460;;28528:537;;;:::o;1671:281:30:-;1748:17;-1:-1:-1;;;;;1748:29:30;;1781:1;1748:34;1744:119;;1805:47;;-1:-1:-1;;;1805:47:30;;-1:-1:-1;;;;;3071:32:50;;1805:47:30;;;3053:51:50;3026:18;;1805:47:30;2907:203:50;1744:119:30;-1:-1:-1;;;;;;;;;;;1872:73:30;;-1:-1:-1;;;;;;1872:73:30;-1:-1:-1;;;;;1872:73:30;;;;;;;;;;1671:281::o;3900:253:33:-;3983:12;4008;4022:23;4049:6;-1:-1:-1;;;;;4049:19:33;4069:4;4049:25;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4007:67;;;;4091:55;4118:6;4126:7;4135:10;4091:26;:55::i;6113:122:30:-;6163:9;:13;6159:70;;6199:19;;-1:-1:-1;;;6199:19:30;;;;;;;;;;;11407:121:44;11473:7;11499:22;11516:1;11519;11499:16;:22::i;1606:793:39:-;1653:7;-1:-1:-1;;;;;1680:1:39;:30;;1672:77;;;;-1:-1:-1;;;1672:77:39;;40336:2:50;1672:77:39;;;40318:21:50;40375:2;40355:18;;;40348:30;40414:34;40394:18;;;40387:62;-1:-1:-1;;;40465:18:50;;;40458:32;40507:19;;1672:77:39;40134:398:50;1672:77:39;1771:3;1759:9;1797:10;1805:1;1797:10;:::i;:::-;1821:13;;;-1:-1:-1;1848:6:39;;1844:40;;1870:3;;;;:::i;:::-;;;;1844:40;784:66;1897:11;;:16;1893:55;;1929:8;1934:3;1929:8;;:::i;:::-;;;1893:55;881:66;1961:10;;:15;1957:53;;1992:7;1997:2;1992:7;;:::i;:::-;;;1957:53;978:66;2023:10;;:15;2019:53;;2054:7;2059:2;2054:7;;:::i;:::-;;;2019:53;1075:66;2085:10;;:15;2081:53;;2116:7;2121:2;2116:7;;:::i;:::-;;;2081:53;1171:66;2147:9;;:14;2143:51;;2177:6;2182:1;2177:6;;:::i;:::-;;;2143:51;1267:66;2207:9;;:14;2203:51;;2237:6;2242:1;2237:6;;:::i;:::-;;;2203:51;1363:66;2267:9;;:14;2263:51;;2297:6;2302:1;2297:6;;:::i;:::-;;;2263:51;1459:66;2327:9;;:14;2323:51;;2357:6;2362:1;2357:6;;:::i;:::-;;;2323:51;-1:-1:-1;2391:1:39;1606:793;-1:-1:-1;;1606:793:39:o;4421:582:33:-;4565:12;4594:7;4589:408;;4617:19;4625:10;4617:7;:19::i;:::-;4589:408;;;4841:17;;:22;:49;;;;-1:-1:-1;;;;;;4867:18:33;;;:23;4841:49;4837:119;;;4917:24;;-1:-1:-1;;;4917:24:33;;-1:-1:-1;;;;;3071:32:50;;4917:24:33;;;3053:51:50;3026:18;;4917:24:33;2907:203:50;4837:119:33;-1:-1:-1;4976:10:33;4969:17;;11645:532:44;11715:13;11792:1;11786:4;11779:15;11820:1;11814:4;11807:15;11941:4;11935;11929;11923;11918:3;11911:5;11900:46;11890:102;;11976:1;11973;11966:12;11890:102;-1:-1:-1;;12033:4:44;12027:11;-1:-1:-1;;12138:23:44;;11645:532;-1:-1:-1;11645:532:44:o;5543:487:33:-;5674:17;;:21;5670:354;;5871:10;5865:17;5927:15;5914:10;5910:2;5906:19;5899:44;5670:354;5994:19;;-1:-1:-1;;;5994:19:33;;;;;;;;;;;-1:-1:-1;;;;;;;:::i;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;196:367:50:-;259:8;269:6;323:3;316:4;308:6;304:17;300:27;290:55;;341:1;338;331:12;290:55;-1:-1:-1;364:20:50;;-1:-1:-1;;;;;396:30:50;;393:50;;;439:1;436;429:12;393:50;476:4;468:6;464:17;452:29;;536:3;529:4;519:6;516:1;512:14;504:6;500:27;496:38;493:47;490:67;;;553:1;550;543:12;490:67;196:367;;;;;:::o;568:505::-;663:6;671;679;732:2;720:9;711:7;707:23;703:32;700:52;;;748:1;745;738:12;700:52;784:9;771:23;761:33;;845:2;834:9;830:18;817:32;-1:-1:-1;;;;;864:6:50;861:30;858:50;;;904:1;901;894:12;858:50;943:70;1005:7;996:6;985:9;981:22;943:70;:::i;:::-;568:505;;1032:8;;-1:-1:-1;917:96:50;;-1:-1:-1;;;;568:505:50:o;1078:806::-;1317:2;1369:21;;;1439:13;;1342:18;;;1461:22;;;1288:4;;1317:2;1502;;1520:18;;;;1561:15;;;1288:4;1604:254;1618:6;1615:1;1612:13;1604:254;;;1677:13;;1715:9;;1703:22;;1765:11;;1759:18;1745:12;;;1738:40;1798:12;;;;1833:15;;;;1640:1;1633:9;1604:254;;;-1:-1:-1;1875:3:50;;1078:806;-1:-1:-1;;;;;;;1078:806:50:o;1889:173::-;1957:20;;-1:-1:-1;;;;;2006:31:50;;1996:42;;1986:70;;2052:1;2049;2042:12;1986:70;1889:173;;;:::o;2067:347::-;2118:8;2128:6;2182:3;2175:4;2167:6;2163:17;2159:27;2149:55;;2200:1;2197;2190:12;2149:55;-1:-1:-1;2223:20:50;;-1:-1:-1;;;;;2255:30:50;;2252:50;;;2298:1;2295;2288:12;2252:50;2335:4;2327:6;2323:17;2311:29;;2387:3;2380:4;2371:6;2363;2359:19;2355:30;2352:39;2349:59;;;2404:1;2401;2394:12;2419:483;2498:6;2506;2514;2567:2;2555:9;2546:7;2542:23;2538:32;2535:52;;;2583:1;2580;2573:12;2535:52;2606:29;2625:9;2606:29;:::i;:::-;2596:39;;2686:2;2675:9;2671:18;2658:32;-1:-1:-1;;;;;2705:6:50;2702:30;2699:50;;;2745:1;2742;2735:12;2699:50;2784:58;2834:7;2825:6;2814:9;2810:22;2784:58;:::i;3115:842::-;3259:6;3267;3275;3283;3291;3344:2;3332:9;3323:7;3319:23;3315:32;3312:52;;;3360:1;3357;3350:12;3312:52;3396:9;3383:23;3373:33;;3457:2;3446:9;3442:18;3429:32;-1:-1:-1;;;;;3521:2:50;3513:6;3510:14;3507:34;;;3537:1;3534;3527:12;3507:34;3576:70;3638:7;3629:6;3618:9;3614:22;3576:70;:::i;:::-;3665:8;;-1:-1:-1;3550:96:50;-1:-1:-1;3753:2:50;3738:18;;3725:32;;-1:-1:-1;3769:16:50;;;3766:36;;;3798:1;3795;3788:12;3766:36;;3837:60;3889:7;3878:8;3867:9;3863:24;3837:60;:::i;:::-;3115:842;;;;-1:-1:-1;3115:842:50;;-1:-1:-1;3916:8:50;;3811:86;3115:842;-1:-1:-1;;;3115:842:50:o;4144:180::-;4203:6;4256:2;4244:9;4235:7;4231:23;4227:32;4224:52;;;4272:1;4269;4262:12;4224:52;-1:-1:-1;4295:23:50;;4144:180;-1:-1:-1;4144:180:50:o;5147:248::-;5215:6;5223;5276:2;5264:9;5255:7;5251:23;5247:32;5244:52;;;5292:1;5289;5282:12;5244:52;-1:-1:-1;;5315:23:50;;;5385:2;5370:18;;;5357:32;;-1:-1:-1;5147:248:50:o;5400:250::-;5485:1;5495:113;5509:6;5506:1;5503:13;5495:113;;;5585:11;;;5579:18;5566:11;;;5559:39;5531:2;5524:10;5495:113;;;-1:-1:-1;;5642:1:50;5624:16;;5617:27;5400:250::o;5655:270::-;5696:3;5734:5;5728:12;5761:6;5756:3;5749:19;5777:76;5846:6;5839:4;5834:3;5830:14;5823:4;5816:5;5812:16;5777:76;:::i;:::-;5907:2;5886:15;-1:-1:-1;;5882:29:50;5873:39;;;;5914:4;5869:50;;5655:270;-1:-1:-1;;5655:270:50:o;5930:330::-;6103:2;6092:9;6085:21;6066:4;6141:6;6135:13;6184:2;6179;6168:9;6164:18;6157:30;6204:50;6250:2;6239:9;6235:18;6221:12;6204:50;:::i;6265:545::-;6353:6;6361;6369;6377;6430:2;6418:9;6409:7;6405:23;6401:32;6398:52;;;6446:1;6443;6436:12;6398:52;6482:9;6469:23;6459:33;;6539:2;6528:9;6524:18;6511:32;6501:42;;6594:2;6583:9;6579:18;6566:32;-1:-1:-1;;;;;6613:6:50;6610:30;6607:50;;;6653:1;6650;6643:12;6607:50;6692:58;6742:7;6733:6;6722:9;6718:22;6692:58;:::i;:::-;6265:545;;;;-1:-1:-1;6769:8:50;-1:-1:-1;;;;6265:545:50:o;7316:127::-;7377:10;7372:3;7368:20;7365:1;7358:31;7408:4;7405:1;7398:15;7432:4;7429:1;7422:15;7448:275;7519:2;7513:9;7584:2;7565:13;;-1:-1:-1;;7561:27:50;7549:40;;-1:-1:-1;;;;;7604:34:50;;7640:22;;;7601:62;7598:88;;;7666:18;;:::i;:::-;7702:2;7695:22;7448:275;;-1:-1:-1;7448:275:50:o;7728:837::-;7805:6;7813;7866:2;7854:9;7845:7;7841:23;7837:32;7834:52;;;7882:1;7879;7872:12;7834:52;7905:29;7924:9;7905:29;:::i;:::-;7895:39;;7953:2;8006;7995:9;7991:18;7978:32;-1:-1:-1;;;;;8070:2:50;8062:6;8059:14;8056:34;;;8086:1;8083;8076:12;8056:34;8124:6;8113:9;8109:22;8099:32;;8169:7;8162:4;8158:2;8154:13;8150:27;8140:55;;8191:1;8188;8181:12;8140:55;8227:2;8214:16;8249:2;8245;8242:10;8239:36;;;8255:18;;:::i;:::-;8297:53;8340:2;8321:13;;-1:-1:-1;;8317:27:50;8313:36;;8297:53;:::i;:::-;8284:66;;8373:2;8366:5;8359:17;8413:7;8408:2;8403;8399;8395:11;8391:20;8388:33;8385:53;;;8434:1;8431;8424:12;8385:53;8489:2;8484;8480;8476:11;8471:2;8464:5;8460:14;8447:45;8533:1;8528:2;8523;8516:5;8512:14;8508:23;8501:34;;8554:5;8544:15;;;;;7728:837;;;;;:::o;9080:254::-;9148:6;9156;9209:2;9197:9;9188:7;9184:23;9180:32;9177:52;;;9225:1;9222;9215:12;9177:52;9261:9;9248:23;9238:33;;9290:38;9324:2;9313:9;9309:18;9290:38;:::i;:::-;9280:48;;9080:254;;;;;:::o;9339:632::-;9510:2;9562:21;;;9632:13;;9535:18;;;9654:22;;;9481:4;;9510:2;9733:15;;;;9707:2;9692:18;;;9481:4;9776:169;9790:6;9787:1;9784:13;9776:169;;;9851:13;;9839:26;;9920:15;;;;9885:12;;;;9812:1;9805:9;9776:169;;;-1:-1:-1;9962:3:50;;9339:632;-1:-1:-1;;;;;;9339:632:50:o;9976:477::-;10055:6;10063;10071;10124:2;10112:9;10103:7;10099:23;10095:32;10092:52;;;10140:1;10137;10130:12;10092:52;10176:9;10163:23;10153:33;;10237:2;10226:9;10222:18;10209:32;-1:-1:-1;;;;;10256:6:50;10253:30;10250:50;;;10296:1;10293;10286:12;10663:219;10812:2;10801:9;10794:21;10775:4;10832:44;10872:2;10861:9;10857:18;10849:6;10832:44;:::i;10887:186::-;10946:6;10999:2;10987:9;10978:7;10974:23;10970:32;10967:52;;;11015:1;11012;11005:12;10967:52;11038:29;11057:9;11038:29;:::i;11614:127::-;11675:10;11670:3;11666:20;11663:1;11656:31;11706:4;11703:1;11696:15;11730:4;11727:1;11720:15;11746:128;11813:9;;;11834:11;;;11831:37;;;11848:18;;:::i;11879:127::-;11940:10;11935:3;11931:20;11928:1;11921:31;11971:4;11968:1;11961:15;11995:4;11992:1;11985:15;12011:344;12213:2;12195:21;;;12252:2;12232:18;;;12225:30;-1:-1:-1;;;12286:2:50;12271:18;;12264:50;12346:2;12331:18;;12011:344::o;12706:209::-;12744:3;-1:-1:-1;;;;;12825:2:50;12818:5;12814:14;12852:2;12843:7;12840:15;12837:41;;12858:18;;:::i;:::-;12907:1;12894:15;;12706:209;-1:-1:-1;;;12706:209:50:o;12920:266::-;13008:6;13003:3;12996:19;13060:6;13053:5;13046:4;13041:3;13037:14;13024:43;-1:-1:-1;13112:1:50;13087:16;;;13105:4;13083:27;;;13076:38;;;;13168:2;13147:15;;;-1:-1:-1;;13143:29:50;13134:39;;;13130:50;;12920:266::o;13191:412::-;13386:25;;;-1:-1:-1;;;;;13447:32:50;;13442:2;13427:18;;13420:60;13516:2;13511;13496:18;;13489:30;;;-1:-1:-1;;13536:61:50;;13578:18;;13570:6;13562;13536:61;:::i;13608:342::-;13810:2;13792:21;;;13849:2;13829:18;;;13822:30;-1:-1:-1;;;13883:2:50;13868:18;;13861:48;13941:2;13926:18;;13608:342::o;14667:326::-;14762:4;14820:11;14807:25;14914:2;14910:7;14899:8;14883:14;14879:29;14875:43;14855:18;14851:68;14841:96;;14933:1;14930;14923:12;14841:96;14954:33;;;;;14667:326;-1:-1:-1;;14667:326:50:o;14998:321::-;15088:4;15146:11;15133:25;15240:2;15236:7;15225:8;15209:14;15205:29;15201:43;15181:18;15177:68;15167:96;;15259:1;15256;15249:12;15324:2247;15644:4;15692:3;15681:9;15677:19;15723:6;15712:9;15705:25;15749:2;15787:6;15782:2;15771:9;15767:18;15760:34;15813:2;15851:3;15846:2;15835:9;15831:18;15824:31;15875:6;15905;15897;15890:22;15943:3;15932:9;15928:19;15921:26;;16006:3;15996:6;15993:1;15989:14;15978:9;15974:30;15970:40;15956:54;;16033:6;16057:1;16067:1373;16081:6;16078:1;16075:13;16067:1373;;;16146:22;;;-1:-1:-1;;16142:37:50;16130:50;;16219:20;;16294:14;16290:27;;;-1:-1:-1;;16286:41:50;16262:66;;16252:94;;16342:1;16339;16332:12;16252:94;16372:31;;16444:19;;-1:-1:-1;;16550:14:50;16546:26;;;16542:35;;16516:62;;16506:90;;16592:1;16589;16582:12;16506:90;16650:5;16628:20;16624:32;16609:47;;16684:2;16676:6;16669:18;16741:7;16728:21;16832:2;16822:7;16806:14;16802:28;16798:37;16776:20;16772:64;16762:92;;16850:1;16847;16840:12;16762:92;16882:34;;;16992:16;;;;-1:-1:-1;16943:21:50;-1:-1:-1;;;;;17024:30:50;;17021:50;;;17067:1;17064;17057:12;17021:50;17120:6;17104:14;17100:27;17091:7;17087:41;17084:61;;;17141:1;17138;17131:12;17084:61;17182:2;17177;17169:6;17165:15;17158:27;17209:59;17264:2;17256:6;17252:15;17244:6;17235:7;17209:59;:::i;:::-;17318:14;;;17305:28;17288:15;;;17281:53;;;;-1:-1:-1;17198:70:50;-1:-1:-1;17418:12:50;;;;17383:15;;;;16103:1;16096:9;16067:1373;;;16071:3;;;;;17488:9;17480:6;17476:22;17471:2;17460:9;17456:18;17449:50;17516:49;17558:6;17550;17542;17516:49;:::i;:::-;17508:57;15324:2247;-1:-1:-1;;;;;;;;;15324:2247:50:o;17989:125::-;18054:9;;;18075:10;;;18072:36;;;18088:18;;:::i;18958:701::-;19213:25;;;19274:2;19269;19254:18;;19247:30;;;19293:18;;19286:34;;;-1:-1:-1;;;;;;19332:31:50;;19329:51;;;19376:1;19373;19366:12;19329:51;19410:6;19407:1;19403:14;19468:6;19460;19454:3;19443:9;19439:19;19426:49;19494:22;;19556:18;;;19576:3;19552:28;;;19547:2;19532:18;;19525:56;19598:55;;19640:12;;19632:6;19624;19598:55;:::i;:::-;19590:63;18958:701;-1:-1:-1;;;;;;;;18958:701:50:o;19664:380::-;19743:1;19739:12;;;;19786;;;19807:61;;19861:4;19853:6;19849:17;19839:27;;19807:61;19914:2;19906:6;19903:14;19883:18;19880:38;19877:161;;19960:10;19955:3;19951:20;19948:1;19941:31;19995:4;19992:1;19985:15;20023:4;20020:1;20013:15;19877:161;;19664:380;;;:::o;20236:287::-;20365:3;20403:6;20397:13;20419:66;20478:6;20473:3;20466:4;20458:6;20454:17;20419:66;:::i;20930:184::-;21000:6;21053:2;21041:9;21032:7;21028:23;21024:32;21021:52;;;21069:1;21066;21059:12;21021:52;-1:-1:-1;21092:16:50;;20930:184;-1:-1:-1;20930:184:50:o;21119:410::-;21321:2;21303:21;;;21360:2;21340:18;;;21333:30;21399:34;21394:2;21379:18;;21372:62;-1:-1:-1;;;21465:2:50;21450:18;;21443:44;21519:3;21504:19;;21119:410::o;21947:127::-;22008:10;22003:3;21999:20;21996:1;21989:31;22039:4;22036:1;22029:15;22063:4;22060:1;22053:15;22559:459;22800:6;22789:9;22782:25;22843:6;22838:2;22827:9;22823:18;22816:34;22886:6;22881:2;22870:9;22866:18;22859:34;22929:3;22924:2;22913:9;22909:18;22902:31;22763:4;22950:62;23007:3;22996:9;22992:19;22984:6;22976;22950:62;:::i;:::-;22942:70;22559:459;-1:-1:-1;;;;;;;22559:459:50:o;23023:168::-;23096:9;;;23127;;23144:15;;;23138:22;;23124:37;23114:71;;23165:18;;:::i;23449:936::-;23543:6;23596:3;23584:9;23575:7;23571:23;23567:33;23564:53;;;23613:1;23610;23603:12;23564:53;23646:2;23640:9;23688:3;23680:6;23676:16;-1:-1:-1;;;;;23779:6:50;23767:10;23764:22;23759:2;23747:10;23744:18;23741:46;23738:72;;;23790:18;;:::i;:::-;23830:10;23826:2;23819:22;23869:9;23863:16;23850:29;;23922:5;23919:1;23908:20;23901:5;23898:31;23888:59;;23943:1;23940;23933:12;23888:59;23956:21;;;24022:2;24007:18;;24001:25;;24057:16;;;24045:29;;24035:57;;24088:1;24085;24078:12;24035:57;-1:-1:-1;24120:2:50;24108:15;;24101:32;24178:2;24163:18;;24157:25;24224:1;24213:22;;;24201:35;;24191:63;;24250:1;24247;24240:12;24191:63;24282:2;24270:15;;24263:32;24349:2;24334:18;;;24328:25;24311:15;;;24304:50;;;;-1:-1:-1;24274:6:50;23449:936;-1:-1:-1;23449:936:50:o;26415:386::-;26628:6;26617:9;26610:25;26671:6;26666:2;26655:9;26651:18;26644:34;26714:2;26709;26698:9;26694:18;26687:30;26591:4;26734:61;26791:2;26780:9;26776:18;26768:6;26760;26734:61;:::i;28613:127::-;28674:10;28669:3;28665:20;28662:1;28655:31;28705:4;28702:1;28695:15;28729:4;28726:1;28719:15;28745:112;28777:1;28803;28793:35;;28808:18;;:::i;:::-;-1:-1:-1;28842:9:50;;28745:112::o;29190:545::-;29283:4;29289:6;29349:11;29336:25;29443:2;29439:7;29428:8;29412:14;29408:29;29404:43;29384:18;29380:68;29370:96;;29462:1;29459;29452:12;29370:96;29489:33;;29541:20;;;-1:-1:-1;;;;;;29573:30:50;;29570:50;;;29616:1;29613;29606:12;29570:50;29649:4;29637:17;;-1:-1:-1;29700:1:50;29696:14;;;29680;29676:35;29666:46;;29663:66;;;29725:1;29722;29715:12;31052:136;31091:3;31119:5;31109:39;;31128:18;;:::i;:::-;-1:-1:-1;;;31164:18:50;;31052:136::o;31193:120::-;31233:1;31259;31249:35;;31264:18;;:::i;:::-;-1:-1:-1;31298:9:50;;31193:120::o;33508:135::-;33547:3;33568:17;;;33565:43;;33588:18;;:::i;:::-;-1:-1:-1;33635:1:50;33624:13;;33508:135::o;33773:517::-;33874:2;33869:3;33866:11;33863:421;;;33910:5;33907:1;33900:16;33954:4;33951:1;33941:18;34024:2;34012:10;34008:19;34005:1;34001:27;33995:4;33991:38;34060:4;34048:10;34045:20;34042:47;;;-1:-1:-1;34083:4:50;34042:47;34138:2;34133:3;34129:12;34126:1;34122:20;34116:4;34112:31;34102:41;;34193:81;34211:2;34204:5;34201:13;34193:81;;;34270:1;34256:16;;34237:1;34226:13;34193:81;;34466:1614;34636:5;34623:19;34721:2;34717:7;34709:5;34693:14;34689:26;34685:40;34665:18;34661:65;34651:93;;34740:1;34737;34730:12;34651:93;34765:30;;34818:18;;-1:-1:-1;;;;;34848:30:50;;34845:50;;;34891:1;34888;34881:12;34845:50;34914:4;34969:6;34953:14;34949:27;34944:2;34938:4;34934:13;34930:47;34927:67;;;34990:1;34987;34980:12;34927:67;35003:96;35092:6;35052:38;35084:4;35078:11;35052:38;:::i;:::-;35046:4;35003:96;:::i;:::-;35125:1;35153:2;35145:6;35142:14;35170:1;35165:635;;;;35846:1;35863:6;35860:108;;;-1:-1:-1;35928:20:50;;;35924:29;;35911:43;35860:108;-1:-1:-1;;34423:1:50;34419:11;;;34415:24;34411:29;34401:40;34447:1;34443:11;;;34398:57;35981:83;;35135:939;;35165:635;33720:1;33713:14;;;33757:4;33744:18;;-1:-1:-1;;35201:20:50;;;35318:235;35332:7;35329:1;35326:14;35318:235;;;35423:20;;;35419:29;;35406:43;35391:59;;35521:18;;;;35489:1;35477:14;;;;35348:10;;35318:235;;;35322:3;35581:6;35572:7;35569:19;35566:175;;;35721:1;35717:6;35711:3;35702:6;35699:1;35695:14;35691:24;35687:37;35683:42;35677:2;35665:9;35659:4;35655:20;35651:29;35638:43;35634:92;35626:6;35619:108;35566:175;-1:-1:-1;;;;;35787:1:50;35771:14;;;35767:22;35754:36;;;-1:-1:-1;;34466:1614:50:o;37359:416::-;37448:1;37485:5;37448:1;37499:270;37520:7;37510:8;37507:21;37499:270;;;37579:4;37575:1;37571:6;37567:17;37561:4;37558:27;37555:53;;;37588:18;;:::i;:::-;37638:7;37628:8;37624:22;37621:55;;;37658:16;;;;37621:55;37737:22;;;;37697:15;;;;37499:270;;;37503:3;37359:416;;;;;:::o;37780:806::-;37829:5;37859:8;37849:80;;-1:-1:-1;37900:1:50;37914:5;;37849:80;37948:4;37938:76;;-1:-1:-1;37985:1:50;37999:5;;37938:76;38030:4;38048:1;38043:59;;;;38116:1;38111:130;;;;38023:218;;38043:59;38073:1;38064:10;;38087:5;;;38111:130;38148:3;38138:8;38135:17;38132:43;;;38155:18;;:::i;:::-;-1:-1:-1;;38211:1:50;38197:16;;38226:5;;38023:218;;38325:2;38315:8;38312:16;38306:3;38300:4;38297:13;38293:36;38287:2;38277:8;38274:16;38269:2;38263:4;38260:12;38256:35;38253:77;38250:159;;;-1:-1:-1;38362:19:50;;;38394:5;;38250:159;38441:34;38466:8;38460:4;38441:34;:::i;:::-;38511:6;38507:1;38503:6;38499:19;38490:7;38487:32;38484:58;;;38522:18;;:::i;:::-;38560:20;;37780:806;-1:-1:-1;;;37780:806:50:o;38591:147::-;38650:5;38679:53;38720:10;38710:8;38706:25;38700:4;38679:53;:::i;38743:187::-;38777:3;38824:5;38821:1;38810:20;38858:10;38854:15;38845:7;38842:28;38839:54;;38873:18;;:::i;:::-;38913:1;38909:15;;38743:187;-1:-1:-1;;38743:187:50:o;39474:297::-;39592:12;;39639:4;39628:16;;;39622:23;;39592:12;39657:16;;39654:111;;;-1:-1:-1;;39731:4:50;39727:17;;;;39724:1;39720:25;39716:38;39705:50;;39474:297;-1:-1:-1;39474:297:50:o;40537:136::-;40572:3;-1:-1:-1;;;40593:22:50;;40590:48;;40618:18;;:::i;:::-;-1:-1:-1;40658:1:50;40654:13;;40537:136::o","linkReferences":{},"immutableReferences":{"40699":[{"start":11610,"length":32},{"start":11651,"length":32},{"start":11970,"length":32}]}},"methodIdentifiers":{"BURN_ACTOR()":"0a6a63f1","EXTRA_DATA_MAX_SIZE()":"029b4646","FIL_USD_PRICE_FEED_ID()":"19c75950","LEAF_SIZE()":"c0e15949","MAX_ENQUEUED_REMOVALS()":"9f8cb3bd","MAX_ROOT_SIZE()":"16e2bcd5","NO_CHALLENGE_SCHEDULED()":"462dd449","PYTH()":"67e406d5","RANDOMNESS_PRECOMPILE()":"15b17570","SECONDS_IN_DAY()":"61a52a36","UPGRADE_INTERFACE_VERSION()":"ad3cb1cc","addRoots(uint256,((bytes),uint256)[],bytes)":"11c0ee4a","calculateProofFee(uint256,uint256)":"4903704a","claimProofSetOwnership(uint256)":"ee3dac65","createProofSet(address,bytes)":"0a4d7932","deleteProofSet(uint256,bytes)":"847d1d06","findRootIds(uint256,uint256[])":"0528a55b","getChallengeFinality()":"f83758fe","getChallengeRange(uint256)":"89208ba9","getFILUSDPrice()":"4fa27920","getNextChallengeEpoch(uint256)":"6ba4608f","getNextProofSetId()":"8ea417e5","getNextRootId(uint256)":"d49245c1","getProofSetLastProvenEpoch(uint256)":"faa67163","getProofSetLeafCount(uint256)":"3f84135f","getProofSetListener(uint256)":"31601226","getProofSetOwner(uint256)":"4726075b","getRandomness(uint256)":"453f4f62","getRootCid(uint256,uint256)":"3b7ae913","getRootLeafCount(uint256,uint256)":"9153e64b","getScheduledRemovals(uint256)":"6fa44692","initialize(uint256)":"fe4b84df","nextProvingPeriod(uint256,uint256,bytes)":"45c0b92d","owner()":"8da5cb5b","proofSetLive(uint256)":"f5cac1ba","proposeProofSetOwner(uint256,address)":"6cb55c16","provePossession(uint256,(bytes32,bytes32[])[])":"f58f952b","proxiableUUID()":"52d1902d","renounceOwnership()":"715018a6","rootChallengable(uint256,uint256)":"71cf2a16","rootLive(uint256,uint256)":"47331050","scheduleRemovals(uint256,uint256[],bytes)":"3b68e4e9","transferOwnership(address)":"f2fde38b","upgradeToAndCall(address,bytes)":"4f1ef286"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.23+commit.f704f362\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"ERC1967InvalidImplementation\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ERC1967NonPayable\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idx\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"msg\",\"type\":\"string\"}],\"name\":\"IndexedError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UUPSUnauthorizedCallContext\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"slot\",\"type\":\"bytes32\"}],\"name\":\"UUPSUnsupportedProxiableUUID\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Debug\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"price\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"int32\",\"name\":\"expo\",\"type\":\"int32\"}],\"name\":\"ProofFeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"ProofSetCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"deletedLeafCount\",\"type\":\"uint256\"}],\"name\":\"ProofSetDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"ProofSetEmpty\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"firstAdded\",\"type\":\"uint256\"}],\"name\":\"RootsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256[]\",\"name\":\"rootIds\",\"type\":\"uint256[]\"}],\"name\":\"RootsRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BURN_ACTOR\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"EXTRA_DATA_MAX_SIZE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"FIL_USD_PRICE_FEED_ID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LEAF_SIZE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_ENQUEUED_REMOVALS\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_ROOT_SIZE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"NO_CHALLENGE_SCHEDULED\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PYTH\",\"outputs\":[{\"internalType\":\"contract IPyth\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RANDOMNESS_PRECOMPILE\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SECONDS_IN_DAY\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"UPGRADE_INTERFACE_VERSION\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"struct Cids.Cid\",\"name\":\"root\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"rawSize\",\"type\":\"uint256\"}],\"internalType\":\"struct PDPVerifier.RootData[]\",\"name\":\"rootData\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"addRoots\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"estimatedGasFee\",\"type\":\"uint256\"}],\"name\":\"calculateProofFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"claimProofSetOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"listenerAddr\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"createProofSet\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"deleteProofSet\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"leafIndexs\",\"type\":\"uint256[]\"}],\"name\":\"findRootIds\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"rootId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"offset\",\"type\":\"uint256\"}],\"internalType\":\"struct PDPVerifier.RootIdAndOffset[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getChallengeFinality\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"getChallengeRange\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFILUSDPrice\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"getNextChallengeEpoch\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNextProofSetId\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"getNextRootId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"getProofSetLastProvenEpoch\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"getProofSetLeafCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"getProofSetListener\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"getProofSetOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"epoch\",\"type\":\"uint256\"}],\"name\":\"getRandomness\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rootId\",\"type\":\"uint256\"}],\"name\":\"getRootCid\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"struct Cids.Cid\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rootId\",\"type\":\"uint256\"}],\"name\":\"getRootLeafCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"getScheduledRemovals\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_challengeFinality\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"challengeEpoch\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"nextProvingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"}],\"name\":\"proofSetLive\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"proposeProofSetOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"leaf\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"struct PDPVerifier.Proof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"}],\"name\":\"provePossession\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proxiableUUID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rootId\",\"type\":\"uint256\"}],\"name\":\"rootChallengable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rootId\",\"type\":\"uint256\"}],\"name\":\"rootLive\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"setId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"rootIds\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"scheduleRemovals\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"AddressEmptyCode(address)\":[{\"details\":\"There's no code at `target` (it is not a contract).\"}],\"ERC1967InvalidImplementation(address)\":[{\"details\":\"The `implementation` of the proxy is invalid.\"}],\"ERC1967NonPayable()\":[{\"details\":\"An upgrade function sees `msg.value > 0` that may be lost.\"}],\"FailedCall()\":[{\"details\":\"A call to an address target failed. The target may have reverted.\"}],\"InvalidInitialization()\":[{\"details\":\"The contract is already initialized.\"}],\"NotInitializing()\":[{\"details\":\"The contract is not initializing.\"}],\"OwnableInvalidOwner(address)\":[{\"details\":\"The owner is not a valid owner account. (eg. `address(0)`)\"}],\"OwnableUnauthorizedAccount(address)\":[{\"details\":\"The caller account is not authorized to perform an operation.\"}],\"UUPSUnauthorizedCallContext()\":[{\"details\":\"The call is from an unauthorized context.\"}],\"UUPSUnsupportedProxiableUUID(bytes32)\":[{\"details\":\"The storage `slot` is unsupported as a UUID.\"}]},\"events\":{\"Initialized(uint64)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"},\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"custom:oz-upgrades-unsafe-allow\":\"constructor\"},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"proxiableUUID()\":{\"details\":\"Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the implementation. It is used to validate the implementation's compatibility when performing an upgrade. IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"},\"upgradeToAndCall(address,bytes)\":{\"custom:oz-upgrades-unsafe-allow-reachable\":\"delegatecall\",\"details\":\"Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call encoded in `data`. Calls {_authorizeUpgrade}. Emits an {Upgraded} event.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/PDPVerifier.sol\":\"PDPVerifier\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":@pythnetwork/pyth-sdk-solidity/=node_modules/@pythnetwork/pyth-sdk-solidity/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\"]},\"sources\":{\"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol\":{\"keccak256\":\"0xc163fcf9bb10138631a9ba5564df1fa25db9adff73bd9ee868a8ae1858fe093a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9706d43a0124053d9880f6e31a59f31bc0a6a3dc1acd66ce0a16e1111658c5f6\",\"dweb:/ipfs/QmUFmfowzkRwGtDu36cXV9SPTBHJ3n7dG9xQiK5B28jTf2\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol\":{\"keccak256\":\"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609\",\"dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol\":{\"keccak256\":\"0x8816653b632f8f634b78885c35112232b44acbf6033ec9e5065d2dd94946b15a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6c16be456b19a1dbaaff7e89b9f6f5c92a02544d5d5f89222a9f57b5a8cfc2f0\",\"dweb:/ipfs/QmS4aeG6paPRwAM1puekhkyGR4mHuMUzFz3riVDv7fbvvB\"]},\"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol\":{\"keccak256\":\"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9\",\"dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV\"]},\"lib/openzeppelin-contracts/contracts/interfaces/IERC1967.sol\":{\"keccak256\":\"0xb25a4f11fa80c702bf5cd85adec90e6f6f507f32f4a8e6f5dbc31e8c10029486\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6917f8a323e7811f041aecd4d9fd6e92455a6fba38a797ac6f6e208c7912b79d\",\"dweb:/ipfs/QmShuYv55wYHGi4EFkDB8QfF7ZCHoKk2efyz3AWY1ExSq7\"]},\"lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol\":{\"keccak256\":\"0xc42facb5094f2f35f066a7155bda23545e39a3156faef3ddc00185544443ba7d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d3b36282ab029b46bd082619a308a2ea11c309967b9425b7b7a6eb0b0c1c3196\",\"dweb:/ipfs/QmP2YVfDB2FoREax3vJu7QhDnyYRMw52WPrCD4vdT2kuDA\"]},\"lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sol\":{\"keccak256\":\"0x02caa0e5f7bade9a0d8ad6058467d641cb67697cd4678c7b1c170686bafe9128\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://33b42a434f5d5fdc5071be05238059b9d8938bdab510071a5c300a975abc405a\",\"dweb:/ipfs/QmaThmoD3JMdHGhn4GUJbEGnKcojUG8PWMFoC7DFcQoeCw\"]},\"lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol\":{\"keccak256\":\"0xc59a78b07b44b2cf2e8ab4175fca91e8eca1eee2df7357b8d2a8833e5ea1f64c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5aa4f07e65444784c29cd7bfcc2341b34381e4e5b5da9f0c5bd00d7f430e66fa\",\"dweb:/ipfs/QmWRMh4Q9DpaU9GvsiXmDdoNYMyyece9if7hnfLz7uqzWM\"]},\"lib/openzeppelin-contracts/contracts/utils/Address.sol\":{\"keccak256\":\"0x9d8da059267bac779a2dbbb9a26c2acf00ca83085e105d62d5d4ef96054a47f5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c78e2aa4313323cecd1ef12a8d6265b96beee1a199923abf55d9a2a9e291ad23\",\"dweb:/ipfs/QmUTs2KStXucZezzFo3EYeqYu47utu56qrF7jj1Gue65vb\"]},\"lib/openzeppelin-contracts/contracts/utils/Errors.sol\":{\"keccak256\":\"0x6afa713bfd42cf0f7656efa91201007ac465e42049d7de1d50753a373648c123\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://ba1d02f4847670a1b83dec9f7d37f0b0418d6043447b69f3a29a5f9efc547fcf\",\"dweb:/ipfs/QmQ7iH2keLNUKgq2xSWcRmuBE5eZ3F5whYAkAGzCNNoEWB\"]},\"lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol\":{\"keccak256\":\"0xcf74f855663ce2ae00ed8352666b7935f6cddea2932fdf2c3ecd30a9b1cd0e97\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9f660b1f351b757dfe01438e59888f31f33ded3afcf5cb5b0d9bf9aa6f320a8b\",\"dweb:/ipfs/QmarDJ5hZEgBtCmmrVzEZWjub9769eD686jmzb2XpSU1cM\"]},\"node_modules/@pythnetwork/pyth-sdk-solidity/IPyth.sol\":{\"keccak256\":\"0x217532ece69b8e472a6260b740c34aebfb5a299bbfed6392cf0458ed368be7ab\",\"license\":\"Apache-2.0\",\"urls\":[\"bzz-raw://02d1b71006ccdfd6402a2b72ea197babbd1b54c26a70ebb76a114f0ae8352f08\",\"dweb:/ipfs/QmbqfuvwriG3AEwYEwupUaQKgfxRYK6Qui99o6wQysPoP3\"]},\"node_modules/@pythnetwork/pyth-sdk-solidity/IPythEvents.sol\":{\"keccak256\":\"0x7ca8e03315d4516d6833c425a52c43e8cacf2077492074d2d36ae5c17899c9c8\",\"license\":\"Apache-2.0\",\"urls\":[\"bzz-raw://ad1c69d157eccb09ce248e1ec021f2e58b61dd36160f5be3973a7bea4a899f64\",\"dweb:/ipfs/QmW1yXsDrMsuQKxtZanSZXpyUW2QwnCKVoCjS5fC3NoSVY\"]},\"node_modules/@pythnetwork/pyth-sdk-solidity/PythStructs.sol\":{\"keccak256\":\"0xade221177dda98ebd194c363f264ceea125bde0e6a7a72f7b54da3ac60316894\",\"license\":\"Apache-2.0\",\"urls\":[\"bzz-raw://a404dbbc64183995326c345cae27601d37c783b3d9030c8dc0ab4943fa2bf1da\",\"dweb:/ipfs/QmfNFesQffYisafmJFbKHxVFSD8fY49X1z9f8N7qtfW8AX\"]},\"src/BitOps.sol\":{\"keccak256\":\"0x55fc8272df01302eba6fde6174e691ec86f791c39ac9b1c6a5e4ca1792439ca4\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://1e4de6ed5f6e6180261728a590eeb629de65db443f4f279801c03a1bc14201d7\",\"dweb:/ipfs/QmeCcCjy88QJwCkZoGbeZVjxksePwTcmhKevtA2F3kRXaT\"]},\"src/Cids.sol\":{\"keccak256\":\"0x0f875f54659ac1b4e6e0d564217018054d9dca52b5c4cff38bcf110cb0195340\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://9ccc64bb38106848fffcaf932865dcde6f97bbcd76917f8a4465f052bdb48e4a\",\"dweb:/ipfs/QmRph46aWRhVqoLQvZg5jzZcLyE7GcuSiBRKozQHUc8NtB\"]},\"src/Fees.sol\":{\"keccak256\":\"0x49a86f10a8b4fcf7c17a1394badafc1f5ca701d5d7867c9fb109c7789e22331f\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://8e7ac5a8e060b0908d010e8a5a6bb9007ee42fa4ff7ddc870fa4b602407517be\",\"dweb:/ipfs/QmS7bVhLo9Q9BVJFBqy1A4hwFdDmrNZywn36obMSvk7nq4\"]},\"src/PDPVerifier.sol\":{\"keccak256\":\"0x9e8cec076550541ec040b2ea98638696a9fc99c6ccfece735954c34c78102f24\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://d740fcf9e84aa99eb72150aba82e1b003585d7b0e5819d1eedf9cbf489a33162\",\"dweb:/ipfs/QmUgbfXsaM6p1UnboKin8b16Kg7YX9krDpwW29swiWhGEb\"]},\"src/Proofs.sol\":{\"keccak256\":\"0xf8d27dd91086ba2b4521f36227d92aae35c9f8dfcb117c775e2417166e15a737\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8db46f122470a14d2c084655c6fada18c966accca05feae92923b1ace7a9f86b\",\"dweb:/ipfs/QmQWGxWUcpejzJt28gwbKfq5C3LLiB5HrHdXMja6HHYxbj\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.23+commit.f704f362"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"type":"error","name":"AddressEmptyCode"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"type":"error","name":"ERC1967InvalidImplementation"},{"inputs":[],"type":"error","name":"ERC1967NonPayable"},{"inputs":[],"type":"error","name":"FailedCall"},{"inputs":[{"internalType":"uint256","name":"idx","type":"uint256"},{"internalType":"string","name":"msg","type":"string"}],"type":"error","name":"IndexedError"},{"inputs":[],"type":"error","name":"InvalidInitialization"},{"inputs":[],"type":"error","name":"NotInitializing"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"type":"error","name":"OwnableInvalidOwner"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"type":"error","name":"OwnableUnauthorizedAccount"},{"inputs":[],"type":"error","name":"UUPSUnauthorizedCallContext"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"type":"error","name":"UUPSUnsupportedProxiableUUID"},{"inputs":[{"internalType":"string","name":"message","type":"string","indexed":false},{"internalType":"uint256","name":"value","type":"uint256","indexed":false}],"type":"event","name":"Debug","anonymous":false},{"inputs":[{"internalType":"uint64","name":"version","type":"uint64","indexed":false}],"type":"event","name":"Initialized","anonymous":false},{"inputs":[{"internalType":"address","name":"previousOwner","type":"address","indexed":true},{"internalType":"address","name":"newOwner","type":"address","indexed":true}],"type":"event","name":"OwnershipTransferred","anonymous":false},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256","indexed":true},{"internalType":"uint256","name":"fee","type":"uint256","indexed":false},{"internalType":"uint64","name":"price","type":"uint64","indexed":false},{"internalType":"int32","name":"expo","type":"int32","indexed":false}],"type":"event","name":"ProofFeePaid","anonymous":false},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256","indexed":true}],"type":"event","name":"ProofSetCreated","anonymous":false},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256","indexed":true},{"internalType":"uint256","name":"deletedLeafCount","type":"uint256","indexed":false}],"type":"event","name":"ProofSetDeleted","anonymous":false},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256","indexed":true}],"type":"event","name":"ProofSetEmpty","anonymous":false},{"inputs":[{"internalType":"uint256","name":"firstAdded","type":"uint256","indexed":true}],"type":"event","name":"RootsAdded","anonymous":false},{"inputs":[{"internalType":"uint256[]","name":"rootIds","type":"uint256[]","indexed":true}],"type":"event","name":"RootsRemoved","anonymous":false},{"inputs":[{"internalType":"address","name":"implementation","type":"address","indexed":true}],"type":"event","name":"Upgraded","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"BURN_ACTOR","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"EXTRA_DATA_MAX_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"FIL_USD_PRICE_FEED_ID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"LEAF_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MAX_ENQUEUED_REMOVALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MAX_ROOT_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"NO_CHALLENGE_SCHEDULED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"PYTH","outputs":[{"internalType":"contract IPyth","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"RANDOMNESS_PRECOMPILE","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"SECONDS_IN_DAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"struct PDPVerifier.RootData[]","name":"rootData","type":"tuple[]","components":[{"internalType":"struct Cids.Cid","name":"root","type":"tuple","components":[{"internalType":"bytes","name":"data","type":"bytes"}]},{"internalType":"uint256","name":"rawSize","type":"uint256"}]},{"internalType":"bytes","name":"extraData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"addRoots","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"uint256","name":"estimatedGasFee","type":"uint256"}],"stateMutability":"view","type":"function","name":"calculateProofFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"claimProofSetOwnership"},{"inputs":[{"internalType":"address","name":"listenerAddr","type":"address"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"stateMutability":"payable","type":"function","name":"createProofSet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"deleteProofSet"},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"uint256[]","name":"leafIndexs","type":"uint256[]"}],"stateMutability":"view","type":"function","name":"findRootIds","outputs":[{"internalType":"struct PDPVerifier.RootIdAndOffset[]","name":"","type":"tuple[]","components":[{"internalType":"uint256","name":"rootId","type":"uint256"},{"internalType":"uint256","name":"offset","type":"uint256"}]}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getChallengeFinality","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getChallengeRange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getFILUSDPrice","outputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"int32","name":"","type":"int32"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getNextChallengeEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getNextProofSetId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getNextRootId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getProofSetLastProvenEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getProofSetLeafCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getProofSetListener","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getProofSetOwner","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function","name":"getRandomness","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"uint256","name":"rootId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getRootCid","outputs":[{"internalType":"struct Cids.Cid","name":"","type":"tuple","components":[{"internalType":"bytes","name":"data","type":"bytes"}]}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"uint256","name":"rootId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getRootLeafCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"getScheduledRemovals","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}]},{"inputs":[{"internalType":"uint256","name":"_challengeFinality","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"initialize"},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"uint256","name":"challengeEpoch","type":"uint256"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"nextProvingPeriod"},{"inputs":[],"stateMutability":"view","type":"function","name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"}],"stateMutability":"view","type":"function","name":"proofSetLive","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"address","name":"newOwner","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"proposeProofSetOwner"},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"struct PDPVerifier.Proof[]","name":"proofs","type":"tuple[]","components":[{"internalType":"bytes32","name":"leaf","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}]}],"stateMutability":"payable","type":"function","name":"provePossession"},{"inputs":[],"stateMutability":"view","type":"function","name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"renounceOwnership"},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"uint256","name":"rootId","type":"uint256"}],"stateMutability":"view","type":"function","name":"rootChallengable","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"uint256","name":"rootId","type":"uint256"}],"stateMutability":"view","type":"function","name":"rootLive","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"uint256","name":"setId","type":"uint256"},{"internalType":"uint256[]","name":"rootIds","type":"uint256[]"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"scheduleRemovals"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"transferOwnership"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"payable","type":"function","name":"upgradeToAndCall"}],"devdoc":{"kind":"dev","methods":{"constructor":{"custom:oz-upgrades-unsafe-allow":"constructor"},"owner()":{"details":"Returns the address of the current owner."},"proxiableUUID()":{"details":"Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the implementation. It is used to validate the implementation's compatibility when performing an upgrade. IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier."},"renounceOwnership()":{"details":"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner."},"transferOwnership(address)":{"details":"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner."},"upgradeToAndCall(address,bytes)":{"custom:oz-upgrades-unsafe-allow-reachable":"delegatecall","details":"Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call encoded in `data`. Calls {_authorizeUpgrade}. Emits an {Upgraded} event."}},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","@pythnetwork/pyth-sdk-solidity/=node_modules/@pythnetwork/pyth-sdk-solidity/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/PDPVerifier.sol":"PDPVerifier"},"evmVersion":"shanghai","libraries":{}},"sources":{"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol":{"keccak256":"0xc163fcf9bb10138631a9ba5564df1fa25db9adff73bd9ee868a8ae1858fe093a","urls":["bzz-raw://9706d43a0124053d9880f6e31a59f31bc0a6a3dc1acd66ce0a16e1111658c5f6","dweb:/ipfs/QmUFmfowzkRwGtDu36cXV9SPTBHJ3n7dG9xQiK5B28jTf2"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol":{"keccak256":"0x631188737069917d2f909d29ce62c4d48611d326686ba6683e26b72a23bfac0b","urls":["bzz-raw://7a61054ae84cd6c4d04c0c4450ba1d6de41e27e0a2c4f1bcdf58f796b401c609","dweb:/ipfs/QmUvtdp7X1mRVyC3CsHrtPbgoqWaXHp3S1ZR24tpAQYJWM"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol":{"keccak256":"0x8816653b632f8f634b78885c35112232b44acbf6033ec9e5065d2dd94946b15a","urls":["bzz-raw://6c16be456b19a1dbaaff7e89b9f6f5c92a02544d5d5f89222a9f57b5a8cfc2f0","dweb:/ipfs/QmS4aeG6paPRwAM1puekhkyGR4mHuMUzFz3riVDv7fbvvB"],"license":"MIT"},"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol":{"keccak256":"0xdbef5f0c787055227243a7318ef74c8a5a1108ca3a07f2b3a00ef67769e1e397","urls":["bzz-raw://08e39f23d5b4692f9a40803e53a8156b72b4c1f9902a88cd65ba964db103dab9","dweb:/ipfs/QmPKn6EYDgpga7KtpkA8wV2yJCYGMtc9K4LkJfhKX2RVSV"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/interfaces/IERC1967.sol":{"keccak256":"0xb25a4f11fa80c702bf5cd85adec90e6f6f507f32f4a8e6f5dbc31e8c10029486","urls":["bzz-raw://6917f8a323e7811f041aecd4d9fd6e92455a6fba38a797ac6f6e208c7912b79d","dweb:/ipfs/QmShuYv55wYHGi4EFkDB8QfF7ZCHoKk2efyz3AWY1ExSq7"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol":{"keccak256":"0xc42facb5094f2f35f066a7155bda23545e39a3156faef3ddc00185544443ba7d","urls":["bzz-raw://d3b36282ab029b46bd082619a308a2ea11c309967b9425b7b7a6eb0b0c1c3196","dweb:/ipfs/QmP2YVfDB2FoREax3vJu7QhDnyYRMw52WPrCD4vdT2kuDA"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sol":{"keccak256":"0x02caa0e5f7bade9a0d8ad6058467d641cb67697cd4678c7b1c170686bafe9128","urls":["bzz-raw://33b42a434f5d5fdc5071be05238059b9d8938bdab510071a5c300a975abc405a","dweb:/ipfs/QmaThmoD3JMdHGhn4GUJbEGnKcojUG8PWMFoC7DFcQoeCw"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol":{"keccak256":"0xc59a78b07b44b2cf2e8ab4175fca91e8eca1eee2df7357b8d2a8833e5ea1f64c","urls":["bzz-raw://5aa4f07e65444784c29cd7bfcc2341b34381e4e5b5da9f0c5bd00d7f430e66fa","dweb:/ipfs/QmWRMh4Q9DpaU9GvsiXmDdoNYMyyece9if7hnfLz7uqzWM"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/Address.sol":{"keccak256":"0x9d8da059267bac779a2dbbb9a26c2acf00ca83085e105d62d5d4ef96054a47f5","urls":["bzz-raw://c78e2aa4313323cecd1ef12a8d6265b96beee1a199923abf55d9a2a9e291ad23","dweb:/ipfs/QmUTs2KStXucZezzFo3EYeqYu47utu56qrF7jj1Gue65vb"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/Errors.sol":{"keccak256":"0x6afa713bfd42cf0f7656efa91201007ac465e42049d7de1d50753a373648c123","urls":["bzz-raw://ba1d02f4847670a1b83dec9f7d37f0b0418d6043447b69f3a29a5f9efc547fcf","dweb:/ipfs/QmQ7iH2keLNUKgq2xSWcRmuBE5eZ3F5whYAkAGzCNNoEWB"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol":{"keccak256":"0xcf74f855663ce2ae00ed8352666b7935f6cddea2932fdf2c3ecd30a9b1cd0e97","urls":["bzz-raw://9f660b1f351b757dfe01438e59888f31f33ded3afcf5cb5b0d9bf9aa6f320a8b","dweb:/ipfs/QmarDJ5hZEgBtCmmrVzEZWjub9769eD686jmzb2XpSU1cM"],"license":"MIT"},"node_modules/@pythnetwork/pyth-sdk-solidity/IPyth.sol":{"keccak256":"0x217532ece69b8e472a6260b740c34aebfb5a299bbfed6392cf0458ed368be7ab","urls":["bzz-raw://02d1b71006ccdfd6402a2b72ea197babbd1b54c26a70ebb76a114f0ae8352f08","dweb:/ipfs/QmbqfuvwriG3AEwYEwupUaQKgfxRYK6Qui99o6wQysPoP3"],"license":"Apache-2.0"},"node_modules/@pythnetwork/pyth-sdk-solidity/IPythEvents.sol":{"keccak256":"0x7ca8e03315d4516d6833c425a52c43e8cacf2077492074d2d36ae5c17899c9c8","urls":["bzz-raw://ad1c69d157eccb09ce248e1ec021f2e58b61dd36160f5be3973a7bea4a899f64","dweb:/ipfs/QmW1yXsDrMsuQKxtZanSZXpyUW2QwnCKVoCjS5fC3NoSVY"],"license":"Apache-2.0"},"node_modules/@pythnetwork/pyth-sdk-solidity/PythStructs.sol":{"keccak256":"0xade221177dda98ebd194c363f264ceea125bde0e6a7a72f7b54da3ac60316894","urls":["bzz-raw://a404dbbc64183995326c345cae27601d37c783b3d9030c8dc0ab4943fa2bf1da","dweb:/ipfs/QmfNFesQffYisafmJFbKHxVFSD8fY49X1z9f8N7qtfW8AX"],"license":"Apache-2.0"},"src/BitOps.sol":{"keccak256":"0x55fc8272df01302eba6fde6174e691ec86f791c39ac9b1c6a5e4ca1792439ca4","urls":["bzz-raw://1e4de6ed5f6e6180261728a590eeb629de65db443f4f279801c03a1bc14201d7","dweb:/ipfs/QmeCcCjy88QJwCkZoGbeZVjxksePwTcmhKevtA2F3kRXaT"],"license":"UNLICENSED"},"src/Cids.sol":{"keccak256":"0x0f875f54659ac1b4e6e0d564217018054d9dca52b5c4cff38bcf110cb0195340","urls":["bzz-raw://9ccc64bb38106848fffcaf932865dcde6f97bbcd76917f8a4465f052bdb48e4a","dweb:/ipfs/QmRph46aWRhVqoLQvZg5jzZcLyE7GcuSiBRKozQHUc8NtB"],"license":"UNLICENSED"},"src/Fees.sol":{"keccak256":"0x49a86f10a8b4fcf7c17a1394badafc1f5ca701d5d7867c9fb109c7789e22331f","urls":["bzz-raw://8e7ac5a8e060b0908d010e8a5a6bb9007ee42fa4ff7ddc870fa4b602407517be","dweb:/ipfs/QmS7bVhLo9Q9BVJFBqy1A4hwFdDmrNZywn36obMSvk7nq4"],"license":"UNLICENSED"},"src/PDPVerifier.sol":{"keccak256":"0x9e8cec076550541ec040b2ea98638696a9fc99c6ccfece735954c34c78102f24","urls":["bzz-raw://d740fcf9e84aa99eb72150aba82e1b003585d7b0e5819d1eedf9cbf489a33162","dweb:/ipfs/QmUgbfXsaM6p1UnboKin8b16Kg7YX9krDpwW29swiWhGEb"],"license":"UNLICENSED"},"src/Proofs.sol":{"keccak256":"0xf8d27dd91086ba2b4521f36227d92aae35c9f8dfcb117c775e2417166e15a737","urls":["bzz-raw://8db46f122470a14d2c084655c6fada18c966accca05feae92923b1ace7a9f86b","dweb:/ipfs/QmQWGxWUcpejzJt28gwbKfq5C3LLiB5HrHdXMja6HHYxbj"],"license":"MIT"}},"version":1},"id":43} \ No newline at end of file diff --git a/pdp/contract/addresses.go b/pdp/contract/addresses.go new file mode 100644 index 000000000..9dc85a558 --- /dev/null +++ b/pdp/contract/addresses.go @@ -0,0 +1,33 @@ +package contract + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/snadrus/must" + + "github.com/filecoin-project/curio/build" + + "github.com/filecoin-project/lotus/chain/types" +) + +type PDPContracts struct { + PDPVerifier common.Address +} + +func ContractAddresses() PDPContracts { + switch build.BuildType { + case build.BuildCalibnet: + return PDPContracts{ + PDPVerifier: common.HexToAddress("0x38529187C03de8d60C8489af063c675b0892CCD9"), + } + default: + panic("pdp contracts unknown for this network") + } +} + +const NumChallenges = 5 + +func SybilFee() *big.Int { + return must.One(types.ParseFIL("0.1")).Int +} diff --git a/pdp/contract/pdp_proving_schedule.go b/pdp/contract/pdp_proving_schedule.go new file mode 100644 index 000000000..650cc5bdb --- /dev/null +++ b/pdp/contract/pdp_proving_schedule.go @@ -0,0 +1,336 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// IPDPProvingScheduleMetaData contains all meta data concerning the IPDPProvingSchedule contract. +var IPDPProvingScheduleMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"challengeWindow\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"getChallengesPerProof\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"getMaxProvingPeriod\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"initChallengeWindowStart\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"nextChallengeWindowStart\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"}]", +} + +// IPDPProvingScheduleABI is the input ABI used to generate the binding from. +// Deprecated: Use IPDPProvingScheduleMetaData.ABI instead. +var IPDPProvingScheduleABI = IPDPProvingScheduleMetaData.ABI + +// IPDPProvingSchedule is an auto generated Go binding around an Ethereum contract. +type IPDPProvingSchedule struct { + IPDPProvingScheduleCaller // Read-only binding to the contract + IPDPProvingScheduleTransactor // Write-only binding to the contract + IPDPProvingScheduleFilterer // Log filterer for contract events +} + +// IPDPProvingScheduleCaller is an auto generated read-only Go binding around an Ethereum contract. +type IPDPProvingScheduleCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IPDPProvingScheduleTransactor is an auto generated write-only Go binding around an Ethereum contract. +type IPDPProvingScheduleTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IPDPProvingScheduleFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type IPDPProvingScheduleFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IPDPProvingScheduleSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type IPDPProvingScheduleSession struct { + Contract *IPDPProvingSchedule // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// IPDPProvingScheduleCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type IPDPProvingScheduleCallerSession struct { + Contract *IPDPProvingScheduleCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// IPDPProvingScheduleTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type IPDPProvingScheduleTransactorSession struct { + Contract *IPDPProvingScheduleTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// IPDPProvingScheduleRaw is an auto generated low-level Go binding around an Ethereum contract. +type IPDPProvingScheduleRaw struct { + Contract *IPDPProvingSchedule // Generic contract binding to access the raw methods on +} + +// IPDPProvingScheduleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type IPDPProvingScheduleCallerRaw struct { + Contract *IPDPProvingScheduleCaller // Generic read-only contract binding to access the raw methods on +} + +// IPDPProvingScheduleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type IPDPProvingScheduleTransactorRaw struct { + Contract *IPDPProvingScheduleTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewIPDPProvingSchedule creates a new instance of IPDPProvingSchedule, bound to a specific deployed contract. +func NewIPDPProvingSchedule(address common.Address, backend bind.ContractBackend) (*IPDPProvingSchedule, error) { + contract, err := bindIPDPProvingSchedule(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &IPDPProvingSchedule{IPDPProvingScheduleCaller: IPDPProvingScheduleCaller{contract: contract}, IPDPProvingScheduleTransactor: IPDPProvingScheduleTransactor{contract: contract}, IPDPProvingScheduleFilterer: IPDPProvingScheduleFilterer{contract: contract}}, nil +} + +// NewIPDPProvingScheduleCaller creates a new read-only instance of IPDPProvingSchedule, bound to a specific deployed contract. +func NewIPDPProvingScheduleCaller(address common.Address, caller bind.ContractCaller) (*IPDPProvingScheduleCaller, error) { + contract, err := bindIPDPProvingSchedule(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &IPDPProvingScheduleCaller{contract: contract}, nil +} + +// NewIPDPProvingScheduleTransactor creates a new write-only instance of IPDPProvingSchedule, bound to a specific deployed contract. +func NewIPDPProvingScheduleTransactor(address common.Address, transactor bind.ContractTransactor) (*IPDPProvingScheduleTransactor, error) { + contract, err := bindIPDPProvingSchedule(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &IPDPProvingScheduleTransactor{contract: contract}, nil +} + +// NewIPDPProvingScheduleFilterer creates a new log filterer instance of IPDPProvingSchedule, bound to a specific deployed contract. +func NewIPDPProvingScheduleFilterer(address common.Address, filterer bind.ContractFilterer) (*IPDPProvingScheduleFilterer, error) { + contract, err := bindIPDPProvingSchedule(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &IPDPProvingScheduleFilterer{contract: contract}, nil +} + +// bindIPDPProvingSchedule binds a generic wrapper to an already deployed contract. +func bindIPDPProvingSchedule(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := IPDPProvingScheduleMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_IPDPProvingSchedule *IPDPProvingScheduleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IPDPProvingSchedule.Contract.IPDPProvingScheduleCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_IPDPProvingSchedule *IPDPProvingScheduleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IPDPProvingSchedule.Contract.IPDPProvingScheduleTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IPDPProvingSchedule *IPDPProvingScheduleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IPDPProvingSchedule.Contract.IPDPProvingScheduleTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_IPDPProvingSchedule *IPDPProvingScheduleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IPDPProvingSchedule.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_IPDPProvingSchedule *IPDPProvingScheduleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IPDPProvingSchedule.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IPDPProvingSchedule *IPDPProvingScheduleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IPDPProvingSchedule.Contract.contract.Transact(opts, method, params...) +} + +// ChallengeWindow is a free data retrieval call binding the contract method 0x861a1412. +// +// Solidity: function challengeWindow() pure returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleCaller) ChallengeWindow(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IPDPProvingSchedule.contract.Call(opts, &out, "challengeWindow") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// ChallengeWindow is a free data retrieval call binding the contract method 0x861a1412. +// +// Solidity: function challengeWindow() pure returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleSession) ChallengeWindow() (*big.Int, error) { + return _IPDPProvingSchedule.Contract.ChallengeWindow(&_IPDPProvingSchedule.CallOpts) +} + +// ChallengeWindow is a free data retrieval call binding the contract method 0x861a1412. +// +// Solidity: function challengeWindow() pure returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleCallerSession) ChallengeWindow() (*big.Int, error) { + return _IPDPProvingSchedule.Contract.ChallengeWindow(&_IPDPProvingSchedule.CallOpts) +} + +// GetChallengesPerProof is a free data retrieval call binding the contract method 0x47d3dfe7. +// +// Solidity: function getChallengesPerProof() pure returns(uint64) +func (_IPDPProvingSchedule *IPDPProvingScheduleCaller) GetChallengesPerProof(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _IPDPProvingSchedule.contract.Call(opts, &out, "getChallengesPerProof") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// GetChallengesPerProof is a free data retrieval call binding the contract method 0x47d3dfe7. +// +// Solidity: function getChallengesPerProof() pure returns(uint64) +func (_IPDPProvingSchedule *IPDPProvingScheduleSession) GetChallengesPerProof() (uint64, error) { + return _IPDPProvingSchedule.Contract.GetChallengesPerProof(&_IPDPProvingSchedule.CallOpts) +} + +// GetChallengesPerProof is a free data retrieval call binding the contract method 0x47d3dfe7. +// +// Solidity: function getChallengesPerProof() pure returns(uint64) +func (_IPDPProvingSchedule *IPDPProvingScheduleCallerSession) GetChallengesPerProof() (uint64, error) { + return _IPDPProvingSchedule.Contract.GetChallengesPerProof(&_IPDPProvingSchedule.CallOpts) +} + +// GetMaxProvingPeriod is a free data retrieval call binding the contract method 0xf2f12333. +// +// Solidity: function getMaxProvingPeriod() pure returns(uint64) +func (_IPDPProvingSchedule *IPDPProvingScheduleCaller) GetMaxProvingPeriod(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _IPDPProvingSchedule.contract.Call(opts, &out, "getMaxProvingPeriod") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// GetMaxProvingPeriod is a free data retrieval call binding the contract method 0xf2f12333. +// +// Solidity: function getMaxProvingPeriod() pure returns(uint64) +func (_IPDPProvingSchedule *IPDPProvingScheduleSession) GetMaxProvingPeriod() (uint64, error) { + return _IPDPProvingSchedule.Contract.GetMaxProvingPeriod(&_IPDPProvingSchedule.CallOpts) +} + +// GetMaxProvingPeriod is a free data retrieval call binding the contract method 0xf2f12333. +// +// Solidity: function getMaxProvingPeriod() pure returns(uint64) +func (_IPDPProvingSchedule *IPDPProvingScheduleCallerSession) GetMaxProvingPeriod() (uint64, error) { + return _IPDPProvingSchedule.Contract.GetMaxProvingPeriod(&_IPDPProvingSchedule.CallOpts) +} + +// InitChallengeWindowStart is a free data retrieval call binding the contract method 0x21918cea. +// +// Solidity: function initChallengeWindowStart() pure returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleCaller) InitChallengeWindowStart(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IPDPProvingSchedule.contract.Call(opts, &out, "initChallengeWindowStart") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// InitChallengeWindowStart is a free data retrieval call binding the contract method 0x21918cea. +// +// Solidity: function initChallengeWindowStart() pure returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleSession) InitChallengeWindowStart() (*big.Int, error) { + return _IPDPProvingSchedule.Contract.InitChallengeWindowStart(&_IPDPProvingSchedule.CallOpts) +} + +// InitChallengeWindowStart is a free data retrieval call binding the contract method 0x21918cea. +// +// Solidity: function initChallengeWindowStart() pure returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleCallerSession) InitChallengeWindowStart() (*big.Int, error) { + return _IPDPProvingSchedule.Contract.InitChallengeWindowStart(&_IPDPProvingSchedule.CallOpts) +} + +// NextChallengeWindowStart is a free data retrieval call binding the contract method 0x8bf96d28. +// +// Solidity: function nextChallengeWindowStart(uint256 setId) view returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleCaller) NextChallengeWindowStart(opts *bind.CallOpts, setId *big.Int) (*big.Int, error) { + var out []interface{} + err := _IPDPProvingSchedule.contract.Call(opts, &out, "nextChallengeWindowStart", setId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// NextChallengeWindowStart is a free data retrieval call binding the contract method 0x8bf96d28. +// +// Solidity: function nextChallengeWindowStart(uint256 setId) view returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleSession) NextChallengeWindowStart(setId *big.Int) (*big.Int, error) { + return _IPDPProvingSchedule.Contract.NextChallengeWindowStart(&_IPDPProvingSchedule.CallOpts, setId) +} + +// NextChallengeWindowStart is a free data retrieval call binding the contract method 0x8bf96d28. +// +// Solidity: function nextChallengeWindowStart(uint256 setId) view returns(uint256) +func (_IPDPProvingSchedule *IPDPProvingScheduleCallerSession) NextChallengeWindowStart(setId *big.Int) (*big.Int, error) { + return _IPDPProvingSchedule.Contract.NextChallengeWindowStart(&_IPDPProvingSchedule.CallOpts, setId) +} diff --git a/pdp/contract/pdp_verifier.go b/pdp/contract/pdp_verifier.go new file mode 100644 index 000000000..b501e243b --- /dev/null +++ b/pdp/contract/pdp_verifier.go @@ -0,0 +1,2884 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// CidsCid is an auto generated low-level Go binding around an user-defined struct. +type CidsCid struct { + Data []byte +} + +// PDPVerifierProof is an auto generated low-level Go binding around an user-defined struct. +type PDPVerifierProof struct { + Leaf [32]byte + Proof [][32]byte +} + +// PDPVerifierRootData is an auto generated low-level Go binding around an user-defined struct. +type PDPVerifierRootData struct { + Root CidsCid + RawSize *big.Int +} + +// PDPVerifierRootIdAndOffset is an auto generated low-level Go binding around an user-defined struct. +type PDPVerifierRootIdAndOffset struct { + RootId *big.Int + Offset *big.Int +} + +// PDPVerifierMetaData contains all meta data concerning the PDPVerifier contract. +var PDPVerifierMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"BURN_ACTOR\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"EXTRA_DATA_MAX_SIZE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"FIL_USD_PRICE_FEED_ID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"LEAF_SIZE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MAX_ENQUEUED_REMOVALS\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MAX_ROOT_SIZE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"NO_CHALLENGE_SCHEDULED\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"PYTH\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIPyth\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"RANDOMNESS_PRECOMPILE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"SECONDS_IN_DAY\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"UPGRADE_INTERFACE_VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"addRoots\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"rootData\",\"type\":\"tuple[]\",\"internalType\":\"structPDPVerifier.RootData[]\",\"components\":[{\"name\":\"root\",\"type\":\"tuple\",\"internalType\":\"structCids.Cid\",\"components\":[{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"rawSize\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"calculateProofFee\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"estimatedGasFee\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"claimProofSetOwnership\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"createProofSet\",\"inputs\":[{\"name\":\"listenerAddr\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"deleteProofSet\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"findRootIds\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"leafIndexs\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple[]\",\"internalType\":\"structPDPVerifier.RootIdAndOffset[]\",\"components\":[{\"name\":\"rootId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"offset\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getChallengeFinality\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getChallengeRange\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getFILUSDPrice\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"\",\"type\":\"int32\",\"internalType\":\"int32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getNextChallengeEpoch\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getNextProofSetId\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getNextRootId\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getProofSetLastProvenEpoch\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getProofSetLeafCount\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getProofSetListener\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getProofSetOwner\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRandomness\",\"inputs\":[{\"name\":\"epoch\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRootCid\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"rootId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structCids.Cid\",\"components\":[{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getRootLeafCount\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"rootId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getScheduledRemovals\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_challengeFinality\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"nextProvingPeriod\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"challengeEpoch\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proofSetLive\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proposeProofSetOwner\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"provePossession\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"proofs\",\"type\":\"tuple[]\",\"internalType\":\"structPDPVerifier.Proof[]\",\"components\":[{\"name\":\"leaf\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"proxiableUUID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"rootChallengable\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"rootId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"rootLive\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"rootId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"scheduleRemovals\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"rootIds\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"upgradeToAndCall\",\"inputs\":[{\"name\":\"newImplementation\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"event\",\"name\":\"Debug\",\"inputs\":[{\"name\":\"message\",\"type\":\"string\",\"indexed\":false,\"internalType\":\"string\"},{\"name\":\"value\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ProofFeePaid\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"fee\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"price\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"expo\",\"type\":\"int32\",\"indexed\":false,\"internalType\":\"int32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ProofSetCreated\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ProofSetDeleted\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"},{\"name\":\"deletedLeafCount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ProofSetEmpty\",\"inputs\":[{\"name\":\"setId\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RootsAdded\",\"inputs\":[{\"name\":\"firstAdded\",\"type\":\"uint256\",\"indexed\":true,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RootsRemoved\",\"inputs\":[{\"name\":\"rootIds\",\"type\":\"uint256[]\",\"indexed\":true,\"internalType\":\"uint256[]\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Upgraded\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"AddressEmptyCode\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ERC1967InvalidImplementation\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ERC1967NonPayable\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FailedCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"IndexedError\",\"inputs\":[{\"name\":\"idx\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"msg\",\"type\":\"string\",\"internalType\":\"string\"}]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnableInvalidOwner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableUnauthorizedAccount\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"UUPSUnauthorizedCallContext\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnsupportedProxiableUUID\",\"inputs\":[{\"name\":\"slot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]}]", +} + +// PDPVerifierABI is the input ABI used to generate the binding from. +// Deprecated: Use PDPVerifierMetaData.ABI instead. +var PDPVerifierABI = PDPVerifierMetaData.ABI + +// PDPVerifier is an auto generated Go binding around an Ethereum contract. +type PDPVerifier struct { + PDPVerifierCaller // Read-only binding to the contract + PDPVerifierTransactor // Write-only binding to the contract + PDPVerifierFilterer // Log filterer for contract events +} + +// PDPVerifierCaller is an auto generated read-only Go binding around an Ethereum contract. +type PDPVerifierCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PDPVerifierTransactor is an auto generated write-only Go binding around an Ethereum contract. +type PDPVerifierTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PDPVerifierFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type PDPVerifierFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PDPVerifierSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type PDPVerifierSession struct { + Contract *PDPVerifier // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PDPVerifierCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type PDPVerifierCallerSession struct { + Contract *PDPVerifierCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// PDPVerifierTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type PDPVerifierTransactorSession struct { + Contract *PDPVerifierTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PDPVerifierRaw is an auto generated low-level Go binding around an Ethereum contract. +type PDPVerifierRaw struct { + Contract *PDPVerifier // Generic contract binding to access the raw methods on +} + +// PDPVerifierCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type PDPVerifierCallerRaw struct { + Contract *PDPVerifierCaller // Generic read-only contract binding to access the raw methods on +} + +// PDPVerifierTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type PDPVerifierTransactorRaw struct { + Contract *PDPVerifierTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewPDPVerifier creates a new instance of PDPVerifier, bound to a specific deployed contract. +func NewPDPVerifier(address common.Address, backend bind.ContractBackend) (*PDPVerifier, error) { + contract, err := bindPDPVerifier(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &PDPVerifier{PDPVerifierCaller: PDPVerifierCaller{contract: contract}, PDPVerifierTransactor: PDPVerifierTransactor{contract: contract}, PDPVerifierFilterer: PDPVerifierFilterer{contract: contract}}, nil +} + +// NewPDPVerifierCaller creates a new read-only instance of PDPVerifier, bound to a specific deployed contract. +func NewPDPVerifierCaller(address common.Address, caller bind.ContractCaller) (*PDPVerifierCaller, error) { + contract, err := bindPDPVerifier(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &PDPVerifierCaller{contract: contract}, nil +} + +// NewPDPVerifierTransactor creates a new write-only instance of PDPVerifier, bound to a specific deployed contract. +func NewPDPVerifierTransactor(address common.Address, transactor bind.ContractTransactor) (*PDPVerifierTransactor, error) { + contract, err := bindPDPVerifier(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &PDPVerifierTransactor{contract: contract}, nil +} + +// NewPDPVerifierFilterer creates a new log filterer instance of PDPVerifier, bound to a specific deployed contract. +func NewPDPVerifierFilterer(address common.Address, filterer bind.ContractFilterer) (*PDPVerifierFilterer, error) { + contract, err := bindPDPVerifier(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &PDPVerifierFilterer{contract: contract}, nil +} + +// bindPDPVerifier binds a generic wrapper to an already deployed contract. +func bindPDPVerifier(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := PDPVerifierMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PDPVerifier *PDPVerifierRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _PDPVerifier.Contract.PDPVerifierCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PDPVerifier *PDPVerifierRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PDPVerifier.Contract.PDPVerifierTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PDPVerifier *PDPVerifierRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PDPVerifier.Contract.PDPVerifierTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_PDPVerifier *PDPVerifierCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _PDPVerifier.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_PDPVerifier *PDPVerifierTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PDPVerifier.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_PDPVerifier *PDPVerifierTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PDPVerifier.Contract.contract.Transact(opts, method, params...) +} + +// BURNACTOR is a free data retrieval call binding the contract method 0x0a6a63f1. +// +// Solidity: function BURN_ACTOR() view returns(address) +func (_PDPVerifier *PDPVerifierCaller) BURNACTOR(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "BURN_ACTOR") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// BURNACTOR is a free data retrieval call binding the contract method 0x0a6a63f1. +// +// Solidity: function BURN_ACTOR() view returns(address) +func (_PDPVerifier *PDPVerifierSession) BURNACTOR() (common.Address, error) { + return _PDPVerifier.Contract.BURNACTOR(&_PDPVerifier.CallOpts) +} + +// BURNACTOR is a free data retrieval call binding the contract method 0x0a6a63f1. +// +// Solidity: function BURN_ACTOR() view returns(address) +func (_PDPVerifier *PDPVerifierCallerSession) BURNACTOR() (common.Address, error) { + return _PDPVerifier.Contract.BURNACTOR(&_PDPVerifier.CallOpts) +} + +// EXTRADATAMAXSIZE is a free data retrieval call binding the contract method 0x029b4646. +// +// Solidity: function EXTRA_DATA_MAX_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) EXTRADATAMAXSIZE(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "EXTRA_DATA_MAX_SIZE") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// EXTRADATAMAXSIZE is a free data retrieval call binding the contract method 0x029b4646. +// +// Solidity: function EXTRA_DATA_MAX_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) EXTRADATAMAXSIZE() (*big.Int, error) { + return _PDPVerifier.Contract.EXTRADATAMAXSIZE(&_PDPVerifier.CallOpts) +} + +// EXTRADATAMAXSIZE is a free data retrieval call binding the contract method 0x029b4646. +// +// Solidity: function EXTRA_DATA_MAX_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) EXTRADATAMAXSIZE() (*big.Int, error) { + return _PDPVerifier.Contract.EXTRADATAMAXSIZE(&_PDPVerifier.CallOpts) +} + +// FILUSDPRICEFEEDID is a free data retrieval call binding the contract method 0x19c75950. +// +// Solidity: function FIL_USD_PRICE_FEED_ID() view returns(bytes32) +func (_PDPVerifier *PDPVerifierCaller) FILUSDPRICEFEEDID(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "FIL_USD_PRICE_FEED_ID") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// FILUSDPRICEFEEDID is a free data retrieval call binding the contract method 0x19c75950. +// +// Solidity: function FIL_USD_PRICE_FEED_ID() view returns(bytes32) +func (_PDPVerifier *PDPVerifierSession) FILUSDPRICEFEEDID() ([32]byte, error) { + return _PDPVerifier.Contract.FILUSDPRICEFEEDID(&_PDPVerifier.CallOpts) +} + +// FILUSDPRICEFEEDID is a free data retrieval call binding the contract method 0x19c75950. +// +// Solidity: function FIL_USD_PRICE_FEED_ID() view returns(bytes32) +func (_PDPVerifier *PDPVerifierCallerSession) FILUSDPRICEFEEDID() ([32]byte, error) { + return _PDPVerifier.Contract.FILUSDPRICEFEEDID(&_PDPVerifier.CallOpts) +} + +// LEAFSIZE is a free data retrieval call binding the contract method 0xc0e15949. +// +// Solidity: function LEAF_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) LEAFSIZE(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "LEAF_SIZE") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// LEAFSIZE is a free data retrieval call binding the contract method 0xc0e15949. +// +// Solidity: function LEAF_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) LEAFSIZE() (*big.Int, error) { + return _PDPVerifier.Contract.LEAFSIZE(&_PDPVerifier.CallOpts) +} + +// LEAFSIZE is a free data retrieval call binding the contract method 0xc0e15949. +// +// Solidity: function LEAF_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) LEAFSIZE() (*big.Int, error) { + return _PDPVerifier.Contract.LEAFSIZE(&_PDPVerifier.CallOpts) +} + +// MAXENQUEUEDREMOVALS is a free data retrieval call binding the contract method 0x9f8cb3bd. +// +// Solidity: function MAX_ENQUEUED_REMOVALS() view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) MAXENQUEUEDREMOVALS(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "MAX_ENQUEUED_REMOVALS") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// MAXENQUEUEDREMOVALS is a free data retrieval call binding the contract method 0x9f8cb3bd. +// +// Solidity: function MAX_ENQUEUED_REMOVALS() view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) MAXENQUEUEDREMOVALS() (*big.Int, error) { + return _PDPVerifier.Contract.MAXENQUEUEDREMOVALS(&_PDPVerifier.CallOpts) +} + +// MAXENQUEUEDREMOVALS is a free data retrieval call binding the contract method 0x9f8cb3bd. +// +// Solidity: function MAX_ENQUEUED_REMOVALS() view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) MAXENQUEUEDREMOVALS() (*big.Int, error) { + return _PDPVerifier.Contract.MAXENQUEUEDREMOVALS(&_PDPVerifier.CallOpts) +} + +// MAXROOTSIZE is a free data retrieval call binding the contract method 0x16e2bcd5. +// +// Solidity: function MAX_ROOT_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) MAXROOTSIZE(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "MAX_ROOT_SIZE") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// MAXROOTSIZE is a free data retrieval call binding the contract method 0x16e2bcd5. +// +// Solidity: function MAX_ROOT_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) MAXROOTSIZE() (*big.Int, error) { + return _PDPVerifier.Contract.MAXROOTSIZE(&_PDPVerifier.CallOpts) +} + +// MAXROOTSIZE is a free data retrieval call binding the contract method 0x16e2bcd5. +// +// Solidity: function MAX_ROOT_SIZE() view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) MAXROOTSIZE() (*big.Int, error) { + return _PDPVerifier.Contract.MAXROOTSIZE(&_PDPVerifier.CallOpts) +} + +// NOCHALLENGESCHEDULED is a free data retrieval call binding the contract method 0x462dd449. +// +// Solidity: function NO_CHALLENGE_SCHEDULED() view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) NOCHALLENGESCHEDULED(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "NO_CHALLENGE_SCHEDULED") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// NOCHALLENGESCHEDULED is a free data retrieval call binding the contract method 0x462dd449. +// +// Solidity: function NO_CHALLENGE_SCHEDULED() view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) NOCHALLENGESCHEDULED() (*big.Int, error) { + return _PDPVerifier.Contract.NOCHALLENGESCHEDULED(&_PDPVerifier.CallOpts) +} + +// NOCHALLENGESCHEDULED is a free data retrieval call binding the contract method 0x462dd449. +// +// Solidity: function NO_CHALLENGE_SCHEDULED() view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) NOCHALLENGESCHEDULED() (*big.Int, error) { + return _PDPVerifier.Contract.NOCHALLENGESCHEDULED(&_PDPVerifier.CallOpts) +} + +// PYTH is a free data retrieval call binding the contract method 0x67e406d5. +// +// Solidity: function PYTH() view returns(address) +func (_PDPVerifier *PDPVerifierCaller) PYTH(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "PYTH") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// PYTH is a free data retrieval call binding the contract method 0x67e406d5. +// +// Solidity: function PYTH() view returns(address) +func (_PDPVerifier *PDPVerifierSession) PYTH() (common.Address, error) { + return _PDPVerifier.Contract.PYTH(&_PDPVerifier.CallOpts) +} + +// PYTH is a free data retrieval call binding the contract method 0x67e406d5. +// +// Solidity: function PYTH() view returns(address) +func (_PDPVerifier *PDPVerifierCallerSession) PYTH() (common.Address, error) { + return _PDPVerifier.Contract.PYTH(&_PDPVerifier.CallOpts) +} + +// RANDOMNESSPRECOMPILE is a free data retrieval call binding the contract method 0x15b17570. +// +// Solidity: function RANDOMNESS_PRECOMPILE() view returns(address) +func (_PDPVerifier *PDPVerifierCaller) RANDOMNESSPRECOMPILE(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "RANDOMNESS_PRECOMPILE") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// RANDOMNESSPRECOMPILE is a free data retrieval call binding the contract method 0x15b17570. +// +// Solidity: function RANDOMNESS_PRECOMPILE() view returns(address) +func (_PDPVerifier *PDPVerifierSession) RANDOMNESSPRECOMPILE() (common.Address, error) { + return _PDPVerifier.Contract.RANDOMNESSPRECOMPILE(&_PDPVerifier.CallOpts) +} + +// RANDOMNESSPRECOMPILE is a free data retrieval call binding the contract method 0x15b17570. +// +// Solidity: function RANDOMNESS_PRECOMPILE() view returns(address) +func (_PDPVerifier *PDPVerifierCallerSession) RANDOMNESSPRECOMPILE() (common.Address, error) { + return _PDPVerifier.Contract.RANDOMNESSPRECOMPILE(&_PDPVerifier.CallOpts) +} + +// SECONDSINDAY is a free data retrieval call binding the contract method 0x61a52a36. +// +// Solidity: function SECONDS_IN_DAY() view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) SECONDSINDAY(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "SECONDS_IN_DAY") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// SECONDSINDAY is a free data retrieval call binding the contract method 0x61a52a36. +// +// Solidity: function SECONDS_IN_DAY() view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) SECONDSINDAY() (*big.Int, error) { + return _PDPVerifier.Contract.SECONDSINDAY(&_PDPVerifier.CallOpts) +} + +// SECONDSINDAY is a free data retrieval call binding the contract method 0x61a52a36. +// +// Solidity: function SECONDS_IN_DAY() view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) SECONDSINDAY() (*big.Int, error) { + return _PDPVerifier.Contract.SECONDSINDAY(&_PDPVerifier.CallOpts) +} + +// UPGRADEINTERFACEVERSION is a free data retrieval call binding the contract method 0xad3cb1cc. +// +// Solidity: function UPGRADE_INTERFACE_VERSION() view returns(string) +func (_PDPVerifier *PDPVerifierCaller) UPGRADEINTERFACEVERSION(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "UPGRADE_INTERFACE_VERSION") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// UPGRADEINTERFACEVERSION is a free data retrieval call binding the contract method 0xad3cb1cc. +// +// Solidity: function UPGRADE_INTERFACE_VERSION() view returns(string) +func (_PDPVerifier *PDPVerifierSession) UPGRADEINTERFACEVERSION() (string, error) { + return _PDPVerifier.Contract.UPGRADEINTERFACEVERSION(&_PDPVerifier.CallOpts) +} + +// UPGRADEINTERFACEVERSION is a free data retrieval call binding the contract method 0xad3cb1cc. +// +// Solidity: function UPGRADE_INTERFACE_VERSION() view returns(string) +func (_PDPVerifier *PDPVerifierCallerSession) UPGRADEINTERFACEVERSION() (string, error) { + return _PDPVerifier.Contract.UPGRADEINTERFACEVERSION(&_PDPVerifier.CallOpts) +} + +// CalculateProofFee is a free data retrieval call binding the contract method 0x4903704a. +// +// Solidity: function calculateProofFee(uint256 setId, uint256 estimatedGasFee) view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) CalculateProofFee(opts *bind.CallOpts, setId *big.Int, estimatedGasFee *big.Int) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "calculateProofFee", setId, estimatedGasFee) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// CalculateProofFee is a free data retrieval call binding the contract method 0x4903704a. +// +// Solidity: function calculateProofFee(uint256 setId, uint256 estimatedGasFee) view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) CalculateProofFee(setId *big.Int, estimatedGasFee *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.CalculateProofFee(&_PDPVerifier.CallOpts, setId, estimatedGasFee) +} + +// CalculateProofFee is a free data retrieval call binding the contract method 0x4903704a. +// +// Solidity: function calculateProofFee(uint256 setId, uint256 estimatedGasFee) view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) CalculateProofFee(setId *big.Int, estimatedGasFee *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.CalculateProofFee(&_PDPVerifier.CallOpts, setId, estimatedGasFee) +} + +// FindRootIds is a free data retrieval call binding the contract method 0x0528a55b. +// +// Solidity: function findRootIds(uint256 setId, uint256[] leafIndexs) view returns((uint256,uint256)[]) +func (_PDPVerifier *PDPVerifierCaller) FindRootIds(opts *bind.CallOpts, setId *big.Int, leafIndexs []*big.Int) ([]PDPVerifierRootIdAndOffset, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "findRootIds", setId, leafIndexs) + + if err != nil { + return *new([]PDPVerifierRootIdAndOffset), err + } + + out0 := *abi.ConvertType(out[0], new([]PDPVerifierRootIdAndOffset)).(*[]PDPVerifierRootIdAndOffset) + + return out0, err + +} + +// FindRootIds is a free data retrieval call binding the contract method 0x0528a55b. +// +// Solidity: function findRootIds(uint256 setId, uint256[] leafIndexs) view returns((uint256,uint256)[]) +func (_PDPVerifier *PDPVerifierSession) FindRootIds(setId *big.Int, leafIndexs []*big.Int) ([]PDPVerifierRootIdAndOffset, error) { + return _PDPVerifier.Contract.FindRootIds(&_PDPVerifier.CallOpts, setId, leafIndexs) +} + +// FindRootIds is a free data retrieval call binding the contract method 0x0528a55b. +// +// Solidity: function findRootIds(uint256 setId, uint256[] leafIndexs) view returns((uint256,uint256)[]) +func (_PDPVerifier *PDPVerifierCallerSession) FindRootIds(setId *big.Int, leafIndexs []*big.Int) ([]PDPVerifierRootIdAndOffset, error) { + return _PDPVerifier.Contract.FindRootIds(&_PDPVerifier.CallOpts, setId, leafIndexs) +} + +// GetChallengeFinality is a free data retrieval call binding the contract method 0xf83758fe. +// +// Solidity: function getChallengeFinality() view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) GetChallengeFinality(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getChallengeFinality") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetChallengeFinality is a free data retrieval call binding the contract method 0xf83758fe. +// +// Solidity: function getChallengeFinality() view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) GetChallengeFinality() (*big.Int, error) { + return _PDPVerifier.Contract.GetChallengeFinality(&_PDPVerifier.CallOpts) +} + +// GetChallengeFinality is a free data retrieval call binding the contract method 0xf83758fe. +// +// Solidity: function getChallengeFinality() view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) GetChallengeFinality() (*big.Int, error) { + return _PDPVerifier.Contract.GetChallengeFinality(&_PDPVerifier.CallOpts) +} + +// GetChallengeRange is a free data retrieval call binding the contract method 0x89208ba9. +// +// Solidity: function getChallengeRange(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) GetChallengeRange(opts *bind.CallOpts, setId *big.Int) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getChallengeRange", setId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetChallengeRange is a free data retrieval call binding the contract method 0x89208ba9. +// +// Solidity: function getChallengeRange(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) GetChallengeRange(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetChallengeRange(&_PDPVerifier.CallOpts, setId) +} + +// GetChallengeRange is a free data retrieval call binding the contract method 0x89208ba9. +// +// Solidity: function getChallengeRange(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) GetChallengeRange(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetChallengeRange(&_PDPVerifier.CallOpts, setId) +} + +// GetFILUSDPrice is a free data retrieval call binding the contract method 0x4fa27920. +// +// Solidity: function getFILUSDPrice() view returns(uint64, int32) +func (_PDPVerifier *PDPVerifierCaller) GetFILUSDPrice(opts *bind.CallOpts) (uint64, int32, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getFILUSDPrice") + + if err != nil { + return *new(uint64), *new(int32), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + out1 := *abi.ConvertType(out[1], new(int32)).(*int32) + + return out0, out1, err + +} + +// GetFILUSDPrice is a free data retrieval call binding the contract method 0x4fa27920. +// +// Solidity: function getFILUSDPrice() view returns(uint64, int32) +func (_PDPVerifier *PDPVerifierSession) GetFILUSDPrice() (uint64, int32, error) { + return _PDPVerifier.Contract.GetFILUSDPrice(&_PDPVerifier.CallOpts) +} + +// GetFILUSDPrice is a free data retrieval call binding the contract method 0x4fa27920. +// +// Solidity: function getFILUSDPrice() view returns(uint64, int32) +func (_PDPVerifier *PDPVerifierCallerSession) GetFILUSDPrice() (uint64, int32, error) { + return _PDPVerifier.Contract.GetFILUSDPrice(&_PDPVerifier.CallOpts) +} + +// GetNextChallengeEpoch is a free data retrieval call binding the contract method 0x6ba4608f. +// +// Solidity: function getNextChallengeEpoch(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) GetNextChallengeEpoch(opts *bind.CallOpts, setId *big.Int) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getNextChallengeEpoch", setId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetNextChallengeEpoch is a free data retrieval call binding the contract method 0x6ba4608f. +// +// Solidity: function getNextChallengeEpoch(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) GetNextChallengeEpoch(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetNextChallengeEpoch(&_PDPVerifier.CallOpts, setId) +} + +// GetNextChallengeEpoch is a free data retrieval call binding the contract method 0x6ba4608f. +// +// Solidity: function getNextChallengeEpoch(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) GetNextChallengeEpoch(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetNextChallengeEpoch(&_PDPVerifier.CallOpts, setId) +} + +// GetNextProofSetId is a free data retrieval call binding the contract method 0x8ea417e5. +// +// Solidity: function getNextProofSetId() view returns(uint64) +func (_PDPVerifier *PDPVerifierCaller) GetNextProofSetId(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getNextProofSetId") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// GetNextProofSetId is a free data retrieval call binding the contract method 0x8ea417e5. +// +// Solidity: function getNextProofSetId() view returns(uint64) +func (_PDPVerifier *PDPVerifierSession) GetNextProofSetId() (uint64, error) { + return _PDPVerifier.Contract.GetNextProofSetId(&_PDPVerifier.CallOpts) +} + +// GetNextProofSetId is a free data retrieval call binding the contract method 0x8ea417e5. +// +// Solidity: function getNextProofSetId() view returns(uint64) +func (_PDPVerifier *PDPVerifierCallerSession) GetNextProofSetId() (uint64, error) { + return _PDPVerifier.Contract.GetNextProofSetId(&_PDPVerifier.CallOpts) +} + +// GetNextRootId is a free data retrieval call binding the contract method 0xd49245c1. +// +// Solidity: function getNextRootId(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) GetNextRootId(opts *bind.CallOpts, setId *big.Int) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getNextRootId", setId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetNextRootId is a free data retrieval call binding the contract method 0xd49245c1. +// +// Solidity: function getNextRootId(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) GetNextRootId(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetNextRootId(&_PDPVerifier.CallOpts, setId) +} + +// GetNextRootId is a free data retrieval call binding the contract method 0xd49245c1. +// +// Solidity: function getNextRootId(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) GetNextRootId(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetNextRootId(&_PDPVerifier.CallOpts, setId) +} + +// GetProofSetLastProvenEpoch is a free data retrieval call binding the contract method 0xfaa67163. +// +// Solidity: function getProofSetLastProvenEpoch(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) GetProofSetLastProvenEpoch(opts *bind.CallOpts, setId *big.Int) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getProofSetLastProvenEpoch", setId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetProofSetLastProvenEpoch is a free data retrieval call binding the contract method 0xfaa67163. +// +// Solidity: function getProofSetLastProvenEpoch(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) GetProofSetLastProvenEpoch(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetProofSetLastProvenEpoch(&_PDPVerifier.CallOpts, setId) +} + +// GetProofSetLastProvenEpoch is a free data retrieval call binding the contract method 0xfaa67163. +// +// Solidity: function getProofSetLastProvenEpoch(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) GetProofSetLastProvenEpoch(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetProofSetLastProvenEpoch(&_PDPVerifier.CallOpts, setId) +} + +// GetProofSetLeafCount is a free data retrieval call binding the contract method 0x3f84135f. +// +// Solidity: function getProofSetLeafCount(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) GetProofSetLeafCount(opts *bind.CallOpts, setId *big.Int) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getProofSetLeafCount", setId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetProofSetLeafCount is a free data retrieval call binding the contract method 0x3f84135f. +// +// Solidity: function getProofSetLeafCount(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) GetProofSetLeafCount(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetProofSetLeafCount(&_PDPVerifier.CallOpts, setId) +} + +// GetProofSetLeafCount is a free data retrieval call binding the contract method 0x3f84135f. +// +// Solidity: function getProofSetLeafCount(uint256 setId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) GetProofSetLeafCount(setId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetProofSetLeafCount(&_PDPVerifier.CallOpts, setId) +} + +// GetProofSetListener is a free data retrieval call binding the contract method 0x31601226. +// +// Solidity: function getProofSetListener(uint256 setId) view returns(address) +func (_PDPVerifier *PDPVerifierCaller) GetProofSetListener(opts *bind.CallOpts, setId *big.Int) (common.Address, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getProofSetListener", setId) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetProofSetListener is a free data retrieval call binding the contract method 0x31601226. +// +// Solidity: function getProofSetListener(uint256 setId) view returns(address) +func (_PDPVerifier *PDPVerifierSession) GetProofSetListener(setId *big.Int) (common.Address, error) { + return _PDPVerifier.Contract.GetProofSetListener(&_PDPVerifier.CallOpts, setId) +} + +// GetProofSetListener is a free data retrieval call binding the contract method 0x31601226. +// +// Solidity: function getProofSetListener(uint256 setId) view returns(address) +func (_PDPVerifier *PDPVerifierCallerSession) GetProofSetListener(setId *big.Int) (common.Address, error) { + return _PDPVerifier.Contract.GetProofSetListener(&_PDPVerifier.CallOpts, setId) +} + +// GetProofSetOwner is a free data retrieval call binding the contract method 0x4726075b. +// +// Solidity: function getProofSetOwner(uint256 setId) view returns(address, address) +func (_PDPVerifier *PDPVerifierCaller) GetProofSetOwner(opts *bind.CallOpts, setId *big.Int) (common.Address, common.Address, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getProofSetOwner", setId) + + if err != nil { + return *new(common.Address), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + + return out0, out1, err + +} + +// GetProofSetOwner is a free data retrieval call binding the contract method 0x4726075b. +// +// Solidity: function getProofSetOwner(uint256 setId) view returns(address, address) +func (_PDPVerifier *PDPVerifierSession) GetProofSetOwner(setId *big.Int) (common.Address, common.Address, error) { + return _PDPVerifier.Contract.GetProofSetOwner(&_PDPVerifier.CallOpts, setId) +} + +// GetProofSetOwner is a free data retrieval call binding the contract method 0x4726075b. +// +// Solidity: function getProofSetOwner(uint256 setId) view returns(address, address) +func (_PDPVerifier *PDPVerifierCallerSession) GetProofSetOwner(setId *big.Int) (common.Address, common.Address, error) { + return _PDPVerifier.Contract.GetProofSetOwner(&_PDPVerifier.CallOpts, setId) +} + +// GetRandomness is a free data retrieval call binding the contract method 0x453f4f62. +// +// Solidity: function getRandomness(uint256 epoch) view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) GetRandomness(opts *bind.CallOpts, epoch *big.Int) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getRandomness", epoch) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetRandomness is a free data retrieval call binding the contract method 0x453f4f62. +// +// Solidity: function getRandomness(uint256 epoch) view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) GetRandomness(epoch *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetRandomness(&_PDPVerifier.CallOpts, epoch) +} + +// GetRandomness is a free data retrieval call binding the contract method 0x453f4f62. +// +// Solidity: function getRandomness(uint256 epoch) view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) GetRandomness(epoch *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetRandomness(&_PDPVerifier.CallOpts, epoch) +} + +// GetRootCid is a free data retrieval call binding the contract method 0x3b7ae913. +// +// Solidity: function getRootCid(uint256 setId, uint256 rootId) view returns((bytes)) +func (_PDPVerifier *PDPVerifierCaller) GetRootCid(opts *bind.CallOpts, setId *big.Int, rootId *big.Int) (CidsCid, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getRootCid", setId, rootId) + + if err != nil { + return *new(CidsCid), err + } + + out0 := *abi.ConvertType(out[0], new(CidsCid)).(*CidsCid) + + return out0, err + +} + +// GetRootCid is a free data retrieval call binding the contract method 0x3b7ae913. +// +// Solidity: function getRootCid(uint256 setId, uint256 rootId) view returns((bytes)) +func (_PDPVerifier *PDPVerifierSession) GetRootCid(setId *big.Int, rootId *big.Int) (CidsCid, error) { + return _PDPVerifier.Contract.GetRootCid(&_PDPVerifier.CallOpts, setId, rootId) +} + +// GetRootCid is a free data retrieval call binding the contract method 0x3b7ae913. +// +// Solidity: function getRootCid(uint256 setId, uint256 rootId) view returns((bytes)) +func (_PDPVerifier *PDPVerifierCallerSession) GetRootCid(setId *big.Int, rootId *big.Int) (CidsCid, error) { + return _PDPVerifier.Contract.GetRootCid(&_PDPVerifier.CallOpts, setId, rootId) +} + +// GetRootLeafCount is a free data retrieval call binding the contract method 0x9153e64b. +// +// Solidity: function getRootLeafCount(uint256 setId, uint256 rootId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCaller) GetRootLeafCount(opts *bind.CallOpts, setId *big.Int, rootId *big.Int) (*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getRootLeafCount", setId, rootId) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetRootLeafCount is a free data retrieval call binding the contract method 0x9153e64b. +// +// Solidity: function getRootLeafCount(uint256 setId, uint256 rootId) view returns(uint256) +func (_PDPVerifier *PDPVerifierSession) GetRootLeafCount(setId *big.Int, rootId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetRootLeafCount(&_PDPVerifier.CallOpts, setId, rootId) +} + +// GetRootLeafCount is a free data retrieval call binding the contract method 0x9153e64b. +// +// Solidity: function getRootLeafCount(uint256 setId, uint256 rootId) view returns(uint256) +func (_PDPVerifier *PDPVerifierCallerSession) GetRootLeafCount(setId *big.Int, rootId *big.Int) (*big.Int, error) { + return _PDPVerifier.Contract.GetRootLeafCount(&_PDPVerifier.CallOpts, setId, rootId) +} + +// GetScheduledRemovals is a free data retrieval call binding the contract method 0x6fa44692. +// +// Solidity: function getScheduledRemovals(uint256 setId) view returns(uint256[]) +func (_PDPVerifier *PDPVerifierCaller) GetScheduledRemovals(opts *bind.CallOpts, setId *big.Int) ([]*big.Int, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "getScheduledRemovals", setId) + + if err != nil { + return *new([]*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + + return out0, err + +} + +// GetScheduledRemovals is a free data retrieval call binding the contract method 0x6fa44692. +// +// Solidity: function getScheduledRemovals(uint256 setId) view returns(uint256[]) +func (_PDPVerifier *PDPVerifierSession) GetScheduledRemovals(setId *big.Int) ([]*big.Int, error) { + return _PDPVerifier.Contract.GetScheduledRemovals(&_PDPVerifier.CallOpts, setId) +} + +// GetScheduledRemovals is a free data retrieval call binding the contract method 0x6fa44692. +// +// Solidity: function getScheduledRemovals(uint256 setId) view returns(uint256[]) +func (_PDPVerifier *PDPVerifierCallerSession) GetScheduledRemovals(setId *big.Int) ([]*big.Int, error) { + return _PDPVerifier.Contract.GetScheduledRemovals(&_PDPVerifier.CallOpts, setId) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_PDPVerifier *PDPVerifierCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_PDPVerifier *PDPVerifierSession) Owner() (common.Address, error) { + return _PDPVerifier.Contract.Owner(&_PDPVerifier.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_PDPVerifier *PDPVerifierCallerSession) Owner() (common.Address, error) { + return _PDPVerifier.Contract.Owner(&_PDPVerifier.CallOpts) +} + +// ProofSetLive is a free data retrieval call binding the contract method 0xf5cac1ba. +// +// Solidity: function proofSetLive(uint256 setId) view returns(bool) +func (_PDPVerifier *PDPVerifierCaller) ProofSetLive(opts *bind.CallOpts, setId *big.Int) (bool, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "proofSetLive", setId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// ProofSetLive is a free data retrieval call binding the contract method 0xf5cac1ba. +// +// Solidity: function proofSetLive(uint256 setId) view returns(bool) +func (_PDPVerifier *PDPVerifierSession) ProofSetLive(setId *big.Int) (bool, error) { + return _PDPVerifier.Contract.ProofSetLive(&_PDPVerifier.CallOpts, setId) +} + +// ProofSetLive is a free data retrieval call binding the contract method 0xf5cac1ba. +// +// Solidity: function proofSetLive(uint256 setId) view returns(bool) +func (_PDPVerifier *PDPVerifierCallerSession) ProofSetLive(setId *big.Int) (bool, error) { + return _PDPVerifier.Contract.ProofSetLive(&_PDPVerifier.CallOpts, setId) +} + +// ProxiableUUID is a free data retrieval call binding the contract method 0x52d1902d. +// +// Solidity: function proxiableUUID() view returns(bytes32) +func (_PDPVerifier *PDPVerifierCaller) ProxiableUUID(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "proxiableUUID") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// ProxiableUUID is a free data retrieval call binding the contract method 0x52d1902d. +// +// Solidity: function proxiableUUID() view returns(bytes32) +func (_PDPVerifier *PDPVerifierSession) ProxiableUUID() ([32]byte, error) { + return _PDPVerifier.Contract.ProxiableUUID(&_PDPVerifier.CallOpts) +} + +// ProxiableUUID is a free data retrieval call binding the contract method 0x52d1902d. +// +// Solidity: function proxiableUUID() view returns(bytes32) +func (_PDPVerifier *PDPVerifierCallerSession) ProxiableUUID() ([32]byte, error) { + return _PDPVerifier.Contract.ProxiableUUID(&_PDPVerifier.CallOpts) +} + +// RootChallengable is a free data retrieval call binding the contract method 0x71cf2a16. +// +// Solidity: function rootChallengable(uint256 setId, uint256 rootId) view returns(bool) +func (_PDPVerifier *PDPVerifierCaller) RootChallengable(opts *bind.CallOpts, setId *big.Int, rootId *big.Int) (bool, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "rootChallengable", setId, rootId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// RootChallengable is a free data retrieval call binding the contract method 0x71cf2a16. +// +// Solidity: function rootChallengable(uint256 setId, uint256 rootId) view returns(bool) +func (_PDPVerifier *PDPVerifierSession) RootChallengable(setId *big.Int, rootId *big.Int) (bool, error) { + return _PDPVerifier.Contract.RootChallengable(&_PDPVerifier.CallOpts, setId, rootId) +} + +// RootChallengable is a free data retrieval call binding the contract method 0x71cf2a16. +// +// Solidity: function rootChallengable(uint256 setId, uint256 rootId) view returns(bool) +func (_PDPVerifier *PDPVerifierCallerSession) RootChallengable(setId *big.Int, rootId *big.Int) (bool, error) { + return _PDPVerifier.Contract.RootChallengable(&_PDPVerifier.CallOpts, setId, rootId) +} + +// RootLive is a free data retrieval call binding the contract method 0x47331050. +// +// Solidity: function rootLive(uint256 setId, uint256 rootId) view returns(bool) +func (_PDPVerifier *PDPVerifierCaller) RootLive(opts *bind.CallOpts, setId *big.Int, rootId *big.Int) (bool, error) { + var out []interface{} + err := _PDPVerifier.contract.Call(opts, &out, "rootLive", setId, rootId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// RootLive is a free data retrieval call binding the contract method 0x47331050. +// +// Solidity: function rootLive(uint256 setId, uint256 rootId) view returns(bool) +func (_PDPVerifier *PDPVerifierSession) RootLive(setId *big.Int, rootId *big.Int) (bool, error) { + return _PDPVerifier.Contract.RootLive(&_PDPVerifier.CallOpts, setId, rootId) +} + +// RootLive is a free data retrieval call binding the contract method 0x47331050. +// +// Solidity: function rootLive(uint256 setId, uint256 rootId) view returns(bool) +func (_PDPVerifier *PDPVerifierCallerSession) RootLive(setId *big.Int, rootId *big.Int) (bool, error) { + return _PDPVerifier.Contract.RootLive(&_PDPVerifier.CallOpts, setId, rootId) +} + +// AddRoots is a paid mutator transaction binding the contract method 0x11c0ee4a. +// +// Solidity: function addRoots(uint256 setId, ((bytes),uint256)[] rootData, bytes extraData) returns(uint256) +func (_PDPVerifier *PDPVerifierTransactor) AddRoots(opts *bind.TransactOpts, setId *big.Int, rootData []PDPVerifierRootData, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "addRoots", setId, rootData, extraData) +} + +// AddRoots is a paid mutator transaction binding the contract method 0x11c0ee4a. +// +// Solidity: function addRoots(uint256 setId, ((bytes),uint256)[] rootData, bytes extraData) returns(uint256) +func (_PDPVerifier *PDPVerifierSession) AddRoots(setId *big.Int, rootData []PDPVerifierRootData, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.AddRoots(&_PDPVerifier.TransactOpts, setId, rootData, extraData) +} + +// AddRoots is a paid mutator transaction binding the contract method 0x11c0ee4a. +// +// Solidity: function addRoots(uint256 setId, ((bytes),uint256)[] rootData, bytes extraData) returns(uint256) +func (_PDPVerifier *PDPVerifierTransactorSession) AddRoots(setId *big.Int, rootData []PDPVerifierRootData, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.AddRoots(&_PDPVerifier.TransactOpts, setId, rootData, extraData) +} + +// ClaimProofSetOwnership is a paid mutator transaction binding the contract method 0xee3dac65. +// +// Solidity: function claimProofSetOwnership(uint256 setId) returns() +func (_PDPVerifier *PDPVerifierTransactor) ClaimProofSetOwnership(opts *bind.TransactOpts, setId *big.Int) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "claimProofSetOwnership", setId) +} + +// ClaimProofSetOwnership is a paid mutator transaction binding the contract method 0xee3dac65. +// +// Solidity: function claimProofSetOwnership(uint256 setId) returns() +func (_PDPVerifier *PDPVerifierSession) ClaimProofSetOwnership(setId *big.Int) (*types.Transaction, error) { + return _PDPVerifier.Contract.ClaimProofSetOwnership(&_PDPVerifier.TransactOpts, setId) +} + +// ClaimProofSetOwnership is a paid mutator transaction binding the contract method 0xee3dac65. +// +// Solidity: function claimProofSetOwnership(uint256 setId) returns() +func (_PDPVerifier *PDPVerifierTransactorSession) ClaimProofSetOwnership(setId *big.Int) (*types.Transaction, error) { + return _PDPVerifier.Contract.ClaimProofSetOwnership(&_PDPVerifier.TransactOpts, setId) +} + +// CreateProofSet is a paid mutator transaction binding the contract method 0x0a4d7932. +// +// Solidity: function createProofSet(address listenerAddr, bytes extraData) payable returns(uint256) +func (_PDPVerifier *PDPVerifierTransactor) CreateProofSet(opts *bind.TransactOpts, listenerAddr common.Address, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "createProofSet", listenerAddr, extraData) +} + +// CreateProofSet is a paid mutator transaction binding the contract method 0x0a4d7932. +// +// Solidity: function createProofSet(address listenerAddr, bytes extraData) payable returns(uint256) +func (_PDPVerifier *PDPVerifierSession) CreateProofSet(listenerAddr common.Address, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.CreateProofSet(&_PDPVerifier.TransactOpts, listenerAddr, extraData) +} + +// CreateProofSet is a paid mutator transaction binding the contract method 0x0a4d7932. +// +// Solidity: function createProofSet(address listenerAddr, bytes extraData) payable returns(uint256) +func (_PDPVerifier *PDPVerifierTransactorSession) CreateProofSet(listenerAddr common.Address, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.CreateProofSet(&_PDPVerifier.TransactOpts, listenerAddr, extraData) +} + +// DeleteProofSet is a paid mutator transaction binding the contract method 0x847d1d06. +// +// Solidity: function deleteProofSet(uint256 setId, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierTransactor) DeleteProofSet(opts *bind.TransactOpts, setId *big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "deleteProofSet", setId, extraData) +} + +// DeleteProofSet is a paid mutator transaction binding the contract method 0x847d1d06. +// +// Solidity: function deleteProofSet(uint256 setId, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierSession) DeleteProofSet(setId *big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.DeleteProofSet(&_PDPVerifier.TransactOpts, setId, extraData) +} + +// DeleteProofSet is a paid mutator transaction binding the contract method 0x847d1d06. +// +// Solidity: function deleteProofSet(uint256 setId, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierTransactorSession) DeleteProofSet(setId *big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.DeleteProofSet(&_PDPVerifier.TransactOpts, setId, extraData) +} + +// Initialize is a paid mutator transaction binding the contract method 0xfe4b84df. +// +// Solidity: function initialize(uint256 _challengeFinality) returns() +func (_PDPVerifier *PDPVerifierTransactor) Initialize(opts *bind.TransactOpts, _challengeFinality *big.Int) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "initialize", _challengeFinality) +} + +// Initialize is a paid mutator transaction binding the contract method 0xfe4b84df. +// +// Solidity: function initialize(uint256 _challengeFinality) returns() +func (_PDPVerifier *PDPVerifierSession) Initialize(_challengeFinality *big.Int) (*types.Transaction, error) { + return _PDPVerifier.Contract.Initialize(&_PDPVerifier.TransactOpts, _challengeFinality) +} + +// Initialize is a paid mutator transaction binding the contract method 0xfe4b84df. +// +// Solidity: function initialize(uint256 _challengeFinality) returns() +func (_PDPVerifier *PDPVerifierTransactorSession) Initialize(_challengeFinality *big.Int) (*types.Transaction, error) { + return _PDPVerifier.Contract.Initialize(&_PDPVerifier.TransactOpts, _challengeFinality) +} + +// NextProvingPeriod is a paid mutator transaction binding the contract method 0x45c0b92d. +// +// Solidity: function nextProvingPeriod(uint256 setId, uint256 challengeEpoch, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierTransactor) NextProvingPeriod(opts *bind.TransactOpts, setId *big.Int, challengeEpoch *big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "nextProvingPeriod", setId, challengeEpoch, extraData) +} + +// NextProvingPeriod is a paid mutator transaction binding the contract method 0x45c0b92d. +// +// Solidity: function nextProvingPeriod(uint256 setId, uint256 challengeEpoch, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierSession) NextProvingPeriod(setId *big.Int, challengeEpoch *big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.NextProvingPeriod(&_PDPVerifier.TransactOpts, setId, challengeEpoch, extraData) +} + +// NextProvingPeriod is a paid mutator transaction binding the contract method 0x45c0b92d. +// +// Solidity: function nextProvingPeriod(uint256 setId, uint256 challengeEpoch, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierTransactorSession) NextProvingPeriod(setId *big.Int, challengeEpoch *big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.NextProvingPeriod(&_PDPVerifier.TransactOpts, setId, challengeEpoch, extraData) +} + +// ProposeProofSetOwner is a paid mutator transaction binding the contract method 0x6cb55c16. +// +// Solidity: function proposeProofSetOwner(uint256 setId, address newOwner) returns() +func (_PDPVerifier *PDPVerifierTransactor) ProposeProofSetOwner(opts *bind.TransactOpts, setId *big.Int, newOwner common.Address) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "proposeProofSetOwner", setId, newOwner) +} + +// ProposeProofSetOwner is a paid mutator transaction binding the contract method 0x6cb55c16. +// +// Solidity: function proposeProofSetOwner(uint256 setId, address newOwner) returns() +func (_PDPVerifier *PDPVerifierSession) ProposeProofSetOwner(setId *big.Int, newOwner common.Address) (*types.Transaction, error) { + return _PDPVerifier.Contract.ProposeProofSetOwner(&_PDPVerifier.TransactOpts, setId, newOwner) +} + +// ProposeProofSetOwner is a paid mutator transaction binding the contract method 0x6cb55c16. +// +// Solidity: function proposeProofSetOwner(uint256 setId, address newOwner) returns() +func (_PDPVerifier *PDPVerifierTransactorSession) ProposeProofSetOwner(setId *big.Int, newOwner common.Address) (*types.Transaction, error) { + return _PDPVerifier.Contract.ProposeProofSetOwner(&_PDPVerifier.TransactOpts, setId, newOwner) +} + +// ProvePossession is a paid mutator transaction binding the contract method 0xf58f952b. +// +// Solidity: function provePossession(uint256 setId, (bytes32,bytes32[])[] proofs) payable returns() +func (_PDPVerifier *PDPVerifierTransactor) ProvePossession(opts *bind.TransactOpts, setId *big.Int, proofs []PDPVerifierProof) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "provePossession", setId, proofs) +} + +// ProvePossession is a paid mutator transaction binding the contract method 0xf58f952b. +// +// Solidity: function provePossession(uint256 setId, (bytes32,bytes32[])[] proofs) payable returns() +func (_PDPVerifier *PDPVerifierSession) ProvePossession(setId *big.Int, proofs []PDPVerifierProof) (*types.Transaction, error) { + return _PDPVerifier.Contract.ProvePossession(&_PDPVerifier.TransactOpts, setId, proofs) +} + +// ProvePossession is a paid mutator transaction binding the contract method 0xf58f952b. +// +// Solidity: function provePossession(uint256 setId, (bytes32,bytes32[])[] proofs) payable returns() +func (_PDPVerifier *PDPVerifierTransactorSession) ProvePossession(setId *big.Int, proofs []PDPVerifierProof) (*types.Transaction, error) { + return _PDPVerifier.Contract.ProvePossession(&_PDPVerifier.TransactOpts, setId, proofs) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_PDPVerifier *PDPVerifierTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_PDPVerifier *PDPVerifierSession) RenounceOwnership() (*types.Transaction, error) { + return _PDPVerifier.Contract.RenounceOwnership(&_PDPVerifier.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_PDPVerifier *PDPVerifierTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _PDPVerifier.Contract.RenounceOwnership(&_PDPVerifier.TransactOpts) +} + +// ScheduleRemovals is a paid mutator transaction binding the contract method 0x3b68e4e9. +// +// Solidity: function scheduleRemovals(uint256 setId, uint256[] rootIds, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierTransactor) ScheduleRemovals(opts *bind.TransactOpts, setId *big.Int, rootIds []*big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "scheduleRemovals", setId, rootIds, extraData) +} + +// ScheduleRemovals is a paid mutator transaction binding the contract method 0x3b68e4e9. +// +// Solidity: function scheduleRemovals(uint256 setId, uint256[] rootIds, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierSession) ScheduleRemovals(setId *big.Int, rootIds []*big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.ScheduleRemovals(&_PDPVerifier.TransactOpts, setId, rootIds, extraData) +} + +// ScheduleRemovals is a paid mutator transaction binding the contract method 0x3b68e4e9. +// +// Solidity: function scheduleRemovals(uint256 setId, uint256[] rootIds, bytes extraData) returns() +func (_PDPVerifier *PDPVerifierTransactorSession) ScheduleRemovals(setId *big.Int, rootIds []*big.Int, extraData []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.ScheduleRemovals(&_PDPVerifier.TransactOpts, setId, rootIds, extraData) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_PDPVerifier *PDPVerifierTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_PDPVerifier *PDPVerifierSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _PDPVerifier.Contract.TransferOwnership(&_PDPVerifier.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_PDPVerifier *PDPVerifierTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _PDPVerifier.Contract.TransferOwnership(&_PDPVerifier.TransactOpts, newOwner) +} + +// UpgradeToAndCall is a paid mutator transaction binding the contract method 0x4f1ef286. +// +// Solidity: function upgradeToAndCall(address newImplementation, bytes data) payable returns() +func (_PDPVerifier *PDPVerifierTransactor) UpgradeToAndCall(opts *bind.TransactOpts, newImplementation common.Address, data []byte) (*types.Transaction, error) { + return _PDPVerifier.contract.Transact(opts, "upgradeToAndCall", newImplementation, data) +} + +// UpgradeToAndCall is a paid mutator transaction binding the contract method 0x4f1ef286. +// +// Solidity: function upgradeToAndCall(address newImplementation, bytes data) payable returns() +func (_PDPVerifier *PDPVerifierSession) UpgradeToAndCall(newImplementation common.Address, data []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.UpgradeToAndCall(&_PDPVerifier.TransactOpts, newImplementation, data) +} + +// UpgradeToAndCall is a paid mutator transaction binding the contract method 0x4f1ef286. +// +// Solidity: function upgradeToAndCall(address newImplementation, bytes data) payable returns() +func (_PDPVerifier *PDPVerifierTransactorSession) UpgradeToAndCall(newImplementation common.Address, data []byte) (*types.Transaction, error) { + return _PDPVerifier.Contract.UpgradeToAndCall(&_PDPVerifier.TransactOpts, newImplementation, data) +} + +// PDPVerifierDebugIterator is returned from FilterDebug and is used to iterate over the raw logs and unpacked data for Debug events raised by the PDPVerifier contract. +type PDPVerifierDebugIterator struct { + Event *PDPVerifierDebug // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierDebugIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierDebug) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierDebug) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierDebugIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierDebugIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierDebug represents a Debug event raised by the PDPVerifier contract. +type PDPVerifierDebug struct { + Message string + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterDebug is a free log retrieval operation binding the contract event 0x3c5ad147104e56be34a9176a6692f7df8d2f4b29a5af06bc6b98970d329d6577. +// +// Solidity: event Debug(string message, uint256 value) +func (_PDPVerifier *PDPVerifierFilterer) FilterDebug(opts *bind.FilterOpts) (*PDPVerifierDebugIterator, error) { + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "Debug") + if err != nil { + return nil, err + } + return &PDPVerifierDebugIterator{contract: _PDPVerifier.contract, event: "Debug", logs: logs, sub: sub}, nil +} + +// WatchDebug is a free log subscription operation binding the contract event 0x3c5ad147104e56be34a9176a6692f7df8d2f4b29a5af06bc6b98970d329d6577. +// +// Solidity: event Debug(string message, uint256 value) +func (_PDPVerifier *PDPVerifierFilterer) WatchDebug(opts *bind.WatchOpts, sink chan<- *PDPVerifierDebug) (event.Subscription, error) { + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "Debug") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierDebug) + if err := _PDPVerifier.contract.UnpackLog(event, "Debug", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseDebug is a log parse operation binding the contract event 0x3c5ad147104e56be34a9176a6692f7df8d2f4b29a5af06bc6b98970d329d6577. +// +// Solidity: event Debug(string message, uint256 value) +func (_PDPVerifier *PDPVerifierFilterer) ParseDebug(log types.Log) (*PDPVerifierDebug, error) { + event := new(PDPVerifierDebug) + if err := _PDPVerifier.contract.UnpackLog(event, "Debug", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the PDPVerifier contract. +type PDPVerifierInitializedIterator struct { + Event *PDPVerifierInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierInitialized represents a Initialized event raised by the PDPVerifier contract. +type PDPVerifierInitialized struct { + Version uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_PDPVerifier *PDPVerifierFilterer) FilterInitialized(opts *bind.FilterOpts) (*PDPVerifierInitializedIterator, error) { + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &PDPVerifierInitializedIterator{contract: _PDPVerifier.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_PDPVerifier *PDPVerifierFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *PDPVerifierInitialized) (event.Subscription, error) { + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierInitialized) + if err := _PDPVerifier.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_PDPVerifier *PDPVerifierFilterer) ParseInitialized(log types.Log) (*PDPVerifierInitialized, error) { + event := new(PDPVerifierInitialized) + if err := _PDPVerifier.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the PDPVerifier contract. +type PDPVerifierOwnershipTransferredIterator struct { + Event *PDPVerifierOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierOwnershipTransferred represents a OwnershipTransferred event raised by the PDPVerifier contract. +type PDPVerifierOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_PDPVerifier *PDPVerifierFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*PDPVerifierOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &PDPVerifierOwnershipTransferredIterator{contract: _PDPVerifier.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_PDPVerifier *PDPVerifierFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *PDPVerifierOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierOwnershipTransferred) + if err := _PDPVerifier.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_PDPVerifier *PDPVerifierFilterer) ParseOwnershipTransferred(log types.Log) (*PDPVerifierOwnershipTransferred, error) { + event := new(PDPVerifierOwnershipTransferred) + if err := _PDPVerifier.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierProofFeePaidIterator is returned from FilterProofFeePaid and is used to iterate over the raw logs and unpacked data for ProofFeePaid events raised by the PDPVerifier contract. +type PDPVerifierProofFeePaidIterator struct { + Event *PDPVerifierProofFeePaid // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierProofFeePaidIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierProofFeePaid) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierProofFeePaid) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierProofFeePaidIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierProofFeePaidIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierProofFeePaid represents a ProofFeePaid event raised by the PDPVerifier contract. +type PDPVerifierProofFeePaid struct { + SetId *big.Int + Fee *big.Int + Price uint64 + Expo int32 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProofFeePaid is a free log retrieval operation binding the contract event 0x928bbf5188022bf8b9a0e59f5e81e179d0a4c729bdba2856ac971af2063fbf2b. +// +// Solidity: event ProofFeePaid(uint256 indexed setId, uint256 fee, uint64 price, int32 expo) +func (_PDPVerifier *PDPVerifierFilterer) FilterProofFeePaid(opts *bind.FilterOpts, setId []*big.Int) (*PDPVerifierProofFeePaidIterator, error) { + + var setIdRule []interface{} + for _, setIdItem := range setId { + setIdRule = append(setIdRule, setIdItem) + } + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "ProofFeePaid", setIdRule) + if err != nil { + return nil, err + } + return &PDPVerifierProofFeePaidIterator{contract: _PDPVerifier.contract, event: "ProofFeePaid", logs: logs, sub: sub}, nil +} + +// WatchProofFeePaid is a free log subscription operation binding the contract event 0x928bbf5188022bf8b9a0e59f5e81e179d0a4c729bdba2856ac971af2063fbf2b. +// +// Solidity: event ProofFeePaid(uint256 indexed setId, uint256 fee, uint64 price, int32 expo) +func (_PDPVerifier *PDPVerifierFilterer) WatchProofFeePaid(opts *bind.WatchOpts, sink chan<- *PDPVerifierProofFeePaid, setId []*big.Int) (event.Subscription, error) { + + var setIdRule []interface{} + for _, setIdItem := range setId { + setIdRule = append(setIdRule, setIdItem) + } + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "ProofFeePaid", setIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierProofFeePaid) + if err := _PDPVerifier.contract.UnpackLog(event, "ProofFeePaid", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProofFeePaid is a log parse operation binding the contract event 0x928bbf5188022bf8b9a0e59f5e81e179d0a4c729bdba2856ac971af2063fbf2b. +// +// Solidity: event ProofFeePaid(uint256 indexed setId, uint256 fee, uint64 price, int32 expo) +func (_PDPVerifier *PDPVerifierFilterer) ParseProofFeePaid(log types.Log) (*PDPVerifierProofFeePaid, error) { + event := new(PDPVerifierProofFeePaid) + if err := _PDPVerifier.contract.UnpackLog(event, "ProofFeePaid", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierProofSetCreatedIterator is returned from FilterProofSetCreated and is used to iterate over the raw logs and unpacked data for ProofSetCreated events raised by the PDPVerifier contract. +type PDPVerifierProofSetCreatedIterator struct { + Event *PDPVerifierProofSetCreated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierProofSetCreatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierProofSetCreated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierProofSetCreated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierProofSetCreatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierProofSetCreatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierProofSetCreated represents a ProofSetCreated event raised by the PDPVerifier contract. +type PDPVerifierProofSetCreated struct { + SetId *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProofSetCreated is a free log retrieval operation binding the contract event 0x5979d495e336598dba8459e44f8eb2a1c957ce30fcc10cabea4bb0ffe969df6a. +// +// Solidity: event ProofSetCreated(uint256 indexed setId) +func (_PDPVerifier *PDPVerifierFilterer) FilterProofSetCreated(opts *bind.FilterOpts, setId []*big.Int) (*PDPVerifierProofSetCreatedIterator, error) { + + var setIdRule []interface{} + for _, setIdItem := range setId { + setIdRule = append(setIdRule, setIdItem) + } + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "ProofSetCreated", setIdRule) + if err != nil { + return nil, err + } + return &PDPVerifierProofSetCreatedIterator{contract: _PDPVerifier.contract, event: "ProofSetCreated", logs: logs, sub: sub}, nil +} + +// WatchProofSetCreated is a free log subscription operation binding the contract event 0x5979d495e336598dba8459e44f8eb2a1c957ce30fcc10cabea4bb0ffe969df6a. +// +// Solidity: event ProofSetCreated(uint256 indexed setId) +func (_PDPVerifier *PDPVerifierFilterer) WatchProofSetCreated(opts *bind.WatchOpts, sink chan<- *PDPVerifierProofSetCreated, setId []*big.Int) (event.Subscription, error) { + + var setIdRule []interface{} + for _, setIdItem := range setId { + setIdRule = append(setIdRule, setIdItem) + } + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "ProofSetCreated", setIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierProofSetCreated) + if err := _PDPVerifier.contract.UnpackLog(event, "ProofSetCreated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProofSetCreated is a log parse operation binding the contract event 0x5979d495e336598dba8459e44f8eb2a1c957ce30fcc10cabea4bb0ffe969df6a. +// +// Solidity: event ProofSetCreated(uint256 indexed setId) +func (_PDPVerifier *PDPVerifierFilterer) ParseProofSetCreated(log types.Log) (*PDPVerifierProofSetCreated, error) { + event := new(PDPVerifierProofSetCreated) + if err := _PDPVerifier.contract.UnpackLog(event, "ProofSetCreated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierProofSetDeletedIterator is returned from FilterProofSetDeleted and is used to iterate over the raw logs and unpacked data for ProofSetDeleted events raised by the PDPVerifier contract. +type PDPVerifierProofSetDeletedIterator struct { + Event *PDPVerifierProofSetDeleted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierProofSetDeletedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierProofSetDeleted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierProofSetDeleted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierProofSetDeletedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierProofSetDeletedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierProofSetDeleted represents a ProofSetDeleted event raised by the PDPVerifier contract. +type PDPVerifierProofSetDeleted struct { + SetId *big.Int + DeletedLeafCount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProofSetDeleted is a free log retrieval operation binding the contract event 0x589e9a441b5bddda77c4ab647b0108764a9cc1a7f655aa9b7bc50b8bdfab8673. +// +// Solidity: event ProofSetDeleted(uint256 indexed setId, uint256 deletedLeafCount) +func (_PDPVerifier *PDPVerifierFilterer) FilterProofSetDeleted(opts *bind.FilterOpts, setId []*big.Int) (*PDPVerifierProofSetDeletedIterator, error) { + + var setIdRule []interface{} + for _, setIdItem := range setId { + setIdRule = append(setIdRule, setIdItem) + } + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "ProofSetDeleted", setIdRule) + if err != nil { + return nil, err + } + return &PDPVerifierProofSetDeletedIterator{contract: _PDPVerifier.contract, event: "ProofSetDeleted", logs: logs, sub: sub}, nil +} + +// WatchProofSetDeleted is a free log subscription operation binding the contract event 0x589e9a441b5bddda77c4ab647b0108764a9cc1a7f655aa9b7bc50b8bdfab8673. +// +// Solidity: event ProofSetDeleted(uint256 indexed setId, uint256 deletedLeafCount) +func (_PDPVerifier *PDPVerifierFilterer) WatchProofSetDeleted(opts *bind.WatchOpts, sink chan<- *PDPVerifierProofSetDeleted, setId []*big.Int) (event.Subscription, error) { + + var setIdRule []interface{} + for _, setIdItem := range setId { + setIdRule = append(setIdRule, setIdItem) + } + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "ProofSetDeleted", setIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierProofSetDeleted) + if err := _PDPVerifier.contract.UnpackLog(event, "ProofSetDeleted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProofSetDeleted is a log parse operation binding the contract event 0x589e9a441b5bddda77c4ab647b0108764a9cc1a7f655aa9b7bc50b8bdfab8673. +// +// Solidity: event ProofSetDeleted(uint256 indexed setId, uint256 deletedLeafCount) +func (_PDPVerifier *PDPVerifierFilterer) ParseProofSetDeleted(log types.Log) (*PDPVerifierProofSetDeleted, error) { + event := new(PDPVerifierProofSetDeleted) + if err := _PDPVerifier.contract.UnpackLog(event, "ProofSetDeleted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierProofSetEmptyIterator is returned from FilterProofSetEmpty and is used to iterate over the raw logs and unpacked data for ProofSetEmpty events raised by the PDPVerifier contract. +type PDPVerifierProofSetEmptyIterator struct { + Event *PDPVerifierProofSetEmpty // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierProofSetEmptyIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierProofSetEmpty) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierProofSetEmpty) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierProofSetEmptyIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierProofSetEmptyIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierProofSetEmpty represents a ProofSetEmpty event raised by the PDPVerifier contract. +type PDPVerifierProofSetEmpty struct { + SetId *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterProofSetEmpty is a free log retrieval operation binding the contract event 0x323c29bc8d678a5d987b90a321982d10b9a91bcad071a9e445879497bf0e68e7. +// +// Solidity: event ProofSetEmpty(uint256 indexed setId) +func (_PDPVerifier *PDPVerifierFilterer) FilterProofSetEmpty(opts *bind.FilterOpts, setId []*big.Int) (*PDPVerifierProofSetEmptyIterator, error) { + + var setIdRule []interface{} + for _, setIdItem := range setId { + setIdRule = append(setIdRule, setIdItem) + } + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "ProofSetEmpty", setIdRule) + if err != nil { + return nil, err + } + return &PDPVerifierProofSetEmptyIterator{contract: _PDPVerifier.contract, event: "ProofSetEmpty", logs: logs, sub: sub}, nil +} + +// WatchProofSetEmpty is a free log subscription operation binding the contract event 0x323c29bc8d678a5d987b90a321982d10b9a91bcad071a9e445879497bf0e68e7. +// +// Solidity: event ProofSetEmpty(uint256 indexed setId) +func (_PDPVerifier *PDPVerifierFilterer) WatchProofSetEmpty(opts *bind.WatchOpts, sink chan<- *PDPVerifierProofSetEmpty, setId []*big.Int) (event.Subscription, error) { + + var setIdRule []interface{} + for _, setIdItem := range setId { + setIdRule = append(setIdRule, setIdItem) + } + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "ProofSetEmpty", setIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierProofSetEmpty) + if err := _PDPVerifier.contract.UnpackLog(event, "ProofSetEmpty", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseProofSetEmpty is a log parse operation binding the contract event 0x323c29bc8d678a5d987b90a321982d10b9a91bcad071a9e445879497bf0e68e7. +// +// Solidity: event ProofSetEmpty(uint256 indexed setId) +func (_PDPVerifier *PDPVerifierFilterer) ParseProofSetEmpty(log types.Log) (*PDPVerifierProofSetEmpty, error) { + event := new(PDPVerifierProofSetEmpty) + if err := _PDPVerifier.contract.UnpackLog(event, "ProofSetEmpty", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierRootsAddedIterator is returned from FilterRootsAdded and is used to iterate over the raw logs and unpacked data for RootsAdded events raised by the PDPVerifier contract. +type PDPVerifierRootsAddedIterator struct { + Event *PDPVerifierRootsAdded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierRootsAddedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierRootsAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierRootsAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierRootsAddedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierRootsAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierRootsAdded represents a RootsAdded event raised by the PDPVerifier contract. +type PDPVerifierRootsAdded struct { + FirstAdded *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRootsAdded is a free log retrieval operation binding the contract event 0xde16f8a35fc44238b21a493b7267f7a5064f2bc1b3a1af51deeb4b5a47281d47. +// +// Solidity: event RootsAdded(uint256 indexed firstAdded) +func (_PDPVerifier *PDPVerifierFilterer) FilterRootsAdded(opts *bind.FilterOpts, firstAdded []*big.Int) (*PDPVerifierRootsAddedIterator, error) { + + var firstAddedRule []interface{} + for _, firstAddedItem := range firstAdded { + firstAddedRule = append(firstAddedRule, firstAddedItem) + } + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "RootsAdded", firstAddedRule) + if err != nil { + return nil, err + } + return &PDPVerifierRootsAddedIterator{contract: _PDPVerifier.contract, event: "RootsAdded", logs: logs, sub: sub}, nil +} + +// WatchRootsAdded is a free log subscription operation binding the contract event 0xde16f8a35fc44238b21a493b7267f7a5064f2bc1b3a1af51deeb4b5a47281d47. +// +// Solidity: event RootsAdded(uint256 indexed firstAdded) +func (_PDPVerifier *PDPVerifierFilterer) WatchRootsAdded(opts *bind.WatchOpts, sink chan<- *PDPVerifierRootsAdded, firstAdded []*big.Int) (event.Subscription, error) { + + var firstAddedRule []interface{} + for _, firstAddedItem := range firstAdded { + firstAddedRule = append(firstAddedRule, firstAddedItem) + } + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "RootsAdded", firstAddedRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierRootsAdded) + if err := _PDPVerifier.contract.UnpackLog(event, "RootsAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRootsAdded is a log parse operation binding the contract event 0xde16f8a35fc44238b21a493b7267f7a5064f2bc1b3a1af51deeb4b5a47281d47. +// +// Solidity: event RootsAdded(uint256 indexed firstAdded) +func (_PDPVerifier *PDPVerifierFilterer) ParseRootsAdded(log types.Log) (*PDPVerifierRootsAdded, error) { + event := new(PDPVerifierRootsAdded) + if err := _PDPVerifier.contract.UnpackLog(event, "RootsAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierRootsRemovedIterator is returned from FilterRootsRemoved and is used to iterate over the raw logs and unpacked data for RootsRemoved events raised by the PDPVerifier contract. +type PDPVerifierRootsRemovedIterator struct { + Event *PDPVerifierRootsRemoved // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierRootsRemovedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierRootsRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierRootsRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierRootsRemovedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierRootsRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierRootsRemoved represents a RootsRemoved event raised by the PDPVerifier contract. +type PDPVerifierRootsRemoved struct { + RootIds []*big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRootsRemoved is a free log retrieval operation binding the contract event 0x27acad209a3635943e55118deed44ace417c09eff8fb78836bcfa1b3e01e0d6b. +// +// Solidity: event RootsRemoved(uint256[] indexed rootIds) +func (_PDPVerifier *PDPVerifierFilterer) FilterRootsRemoved(opts *bind.FilterOpts, rootIds [][]*big.Int) (*PDPVerifierRootsRemovedIterator, error) { + + var rootIdsRule []interface{} + for _, rootIdsItem := range rootIds { + rootIdsRule = append(rootIdsRule, rootIdsItem) + } + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "RootsRemoved", rootIdsRule) + if err != nil { + return nil, err + } + return &PDPVerifierRootsRemovedIterator{contract: _PDPVerifier.contract, event: "RootsRemoved", logs: logs, sub: sub}, nil +} + +// WatchRootsRemoved is a free log subscription operation binding the contract event 0x27acad209a3635943e55118deed44ace417c09eff8fb78836bcfa1b3e01e0d6b. +// +// Solidity: event RootsRemoved(uint256[] indexed rootIds) +func (_PDPVerifier *PDPVerifierFilterer) WatchRootsRemoved(opts *bind.WatchOpts, sink chan<- *PDPVerifierRootsRemoved, rootIds [][]*big.Int) (event.Subscription, error) { + + var rootIdsRule []interface{} + for _, rootIdsItem := range rootIds { + rootIdsRule = append(rootIdsRule, rootIdsItem) + } + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "RootsRemoved", rootIdsRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierRootsRemoved) + if err := _PDPVerifier.contract.UnpackLog(event, "RootsRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRootsRemoved is a log parse operation binding the contract event 0x27acad209a3635943e55118deed44ace417c09eff8fb78836bcfa1b3e01e0d6b. +// +// Solidity: event RootsRemoved(uint256[] indexed rootIds) +func (_PDPVerifier *PDPVerifierFilterer) ParseRootsRemoved(log types.Log) (*PDPVerifierRootsRemoved, error) { + event := new(PDPVerifierRootsRemoved) + if err := _PDPVerifier.contract.UnpackLog(event, "RootsRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// PDPVerifierUpgradedIterator is returned from FilterUpgraded and is used to iterate over the raw logs and unpacked data for Upgraded events raised by the PDPVerifier contract. +type PDPVerifierUpgradedIterator struct { + Event *PDPVerifierUpgraded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *PDPVerifierUpgradedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(PDPVerifierUpgraded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(PDPVerifierUpgraded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *PDPVerifierUpgradedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *PDPVerifierUpgradedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// PDPVerifierUpgraded represents a Upgraded event raised by the PDPVerifier contract. +type PDPVerifierUpgraded struct { + Implementation common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterUpgraded is a free log retrieval operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address indexed implementation) +func (_PDPVerifier *PDPVerifierFilterer) FilterUpgraded(opts *bind.FilterOpts, implementation []common.Address) (*PDPVerifierUpgradedIterator, error) { + + var implementationRule []interface{} + for _, implementationItem := range implementation { + implementationRule = append(implementationRule, implementationItem) + } + + logs, sub, err := _PDPVerifier.contract.FilterLogs(opts, "Upgraded", implementationRule) + if err != nil { + return nil, err + } + return &PDPVerifierUpgradedIterator{contract: _PDPVerifier.contract, event: "Upgraded", logs: logs, sub: sub}, nil +} + +// WatchUpgraded is a free log subscription operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address indexed implementation) +func (_PDPVerifier *PDPVerifierFilterer) WatchUpgraded(opts *bind.WatchOpts, sink chan<- *PDPVerifierUpgraded, implementation []common.Address) (event.Subscription, error) { + + var implementationRule []interface{} + for _, implementationItem := range implementation { + implementationRule = append(implementationRule, implementationItem) + } + + logs, sub, err := _PDPVerifier.contract.WatchLogs(opts, "Upgraded", implementationRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(PDPVerifierUpgraded) + if err := _PDPVerifier.contract.UnpackLog(event, "Upgraded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseUpgraded is a log parse operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address indexed implementation) +func (_PDPVerifier *PDPVerifierFilterer) ParseUpgraded(log types.Log) (*PDPVerifierUpgraded, error) { + event := new(PDPVerifierUpgraded) + if err := _PDPVerifier.contract.UnpackLog(event, "Upgraded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/pdp/handlers.go b/pdp/handlers.go new file mode 100644 index 000000000..692ada49e --- /dev/null +++ b/pdp/handlers.go @@ -0,0 +1,1151 @@ +package pdp + +import ( + "context" + "database/sql" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "net/http" + "path" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/go-chi/chi/v5" + "github.com/ipfs/go-cid" + "github.com/yugabyte/pgx/v5" + + "github.com/filecoin-project/go-commp-utils/nonffi" + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/lib/paths" + "github.com/filecoin-project/curio/pdp/contract" + "github.com/filecoin-project/curio/tasks/message" + + types2 "github.com/filecoin-project/lotus/chain/types" +) + +// PDPRoutePath is the base path for PDP routes +const PDPRoutePath = "/pdp" + +// PDPService represents the service for managing proof sets and pieces +type PDPService struct { + db *harmonydb.DB + storage paths.StashStore + + sender *message.SenderETH + ethClient *ethclient.Client + filClient PDPServiceNodeApi +} + +type PDPServiceNodeApi interface { + ChainHead(ctx context.Context) (*types2.TipSet, error) +} + +// NewPDPService creates a new instance of PDPService with the provided stores +func NewPDPService(db *harmonydb.DB, stor paths.StashStore, ec *ethclient.Client, fc PDPServiceNodeApi, sn *message.SenderETH) *PDPService { + return &PDPService{ + db: db, + storage: stor, + + sender: sn, + ethClient: ec, + filClient: fc, + } +} + +// Routes registers the HTTP routes with the provided router +func Routes(r *chi.Mux, p *PDPService) { + + // Routes for proof sets + r.Route(path.Join(PDPRoutePath, "/proof-sets"), func(r chi.Router) { + // POST /pdp/proof-sets - Create a new proof set + r.Post("/", p.handleCreateProofSet) + + // GET /pdp/proof-sets/created/{txHash} - Get the status of a proof set creation + r.Get("/created/{txHash}", p.handleGetProofSetCreationStatus) + + // Individual proof set routes + r.Route("/{proofSetID}", func(r chi.Router) { + // GET /pdp/proof-sets/{set-id} + r.Get("/", p.handleGetProofSet) + + // DEL /pdp/proof-sets/{set-id} + r.Delete("/", p.handleDeleteProofSet) + + // Routes for roots within a proof set + r.Route("/roots", func(r chi.Router) { + // POST /pdp/proof-sets/{set-id}/roots + r.Post("/", p.handleAddRootToProofSet) + + // Individual root routes + r.Route("/{rootID}", func(r chi.Router) { + // GET /pdp/proof-sets/{set-id}/roots/{root-id} + r.Get("/", p.handleGetProofSetRoot) + + // DEL /pdp/proof-sets/{set-id}/roots/{root-id} + r.Delete("/", p.handleDeleteProofSetRoot) + }) + }) + }) + }) + + r.Get(path.Join(PDPRoutePath, "/ping"), p.handlePing) + + // Routes for piece storage and retrieval + // POST /pdp/piece + r.Post(path.Join(PDPRoutePath, "/piece"), p.handlePiecePost) + + // GET /pdp/piece/ + r.Get(path.Join(PDPRoutePath, "/piece/"), p.handleFindPiece) + + // PUT /pdp/piece/upload/{uploadUUID} + r.Put(path.Join(PDPRoutePath, "/piece/upload/{uploadUUID}"), p.handlePieceUpload) +} + +// Handler functions + +func (p *PDPService) handlePing(w http.ResponseWriter, r *http.Request) { + // Verify that the request is authorized using ECDSA JWT + _, err := p.verifyJWTToken(r) + if err != nil { + http.Error(w, "Unauthorized: "+err.Error(), http.StatusUnauthorized) + return + } + + // Return 200 OK + w.WriteHeader(http.StatusOK) +} + +// handleCreateProofSet handles the creation of a new proof set +func (p *PDPService) handleCreateProofSet(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: Parse the request body to get the 'recordKeeper' address + type RequestBody struct { + RecordKeeper string `json:"recordKeeper"` + } + + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Failed to read request body: "+err.Error(), http.StatusBadRequest) + return + } + defer r.Body.Close() + + var reqBody RequestBody + if err := json.Unmarshal(body, &reqBody); err != nil { + http.Error(w, "Invalid JSON in request body: "+err.Error(), http.StatusBadRequest) + return + } + + if reqBody.RecordKeeper == "" { + http.Error(w, "recordKeeper address is required", http.StatusBadRequest) + return + } + + recordKeeperAddr := common.HexToAddress(reqBody.RecordKeeper) + if recordKeeperAddr == (common.Address{}) { + http.Error(w, "Invalid recordKeeper address", http.StatusBadRequest) + return + } + + // Step 3: Get the sender address from 'eth_keys' table where role = 'pdp' limit 1 + fromAddress, err := p.getSenderAddress(ctx) + if err != nil { + http.Error(w, "Failed to get sender address: "+err.Error(), http.StatusInternalServerError) + return + } + + // Step 4: Manually create the transaction without requiring a Signer + // Obtain the ABI of the PDPVerifier contract + 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("createProofSet", recordKeeperAddr, []byte{}) + if err != nil { + http.Error(w, "Failed to pack method call: "+err.Error(), http.StatusInternalServerError) + return + } + + // Prepare the transaction (nonce will be set to 0, SenderETH will assign it) + tx := types.NewTransaction( + 0, + contract.ContractAddresses().PDPVerifier, + contract.SybilFee(), + 0, + nil, + data, + ) + + // Step 5: Send the transaction using SenderETH + reason := "pdp-mkproofset" + txHash, err := p.sender.Send(ctx, fromAddress, tx, reason) + if err != nil { + http.Error(w, "Failed to send transaction: "+err.Error(), http.StatusInternalServerError) + log.Errorf("Failed to send transaction: %+v", err) + return + } + + // Step 6: Insert into message_waits_eth and pdp_proofset_creates + err = p.insertMessageWaitsAndProofsetCreate(ctx, txHash.Hex(), serviceLabel) + if err != nil { + log.Errorf("Failed to insert into message_waits_eth and pdp_proofset_creates: %+v", err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + + // Step 7: Respond with 201 Created and Location header + w.Header().Set("Location", path.Join("/pdp/proof-sets/created", txHash.Hex())) + w.WriteHeader(http.StatusCreated) +} + +// getSenderAddress retrieves the sender address from the database where role = 'pdp' limit 1 +func (p *PDPService) getSenderAddress(ctx context.Context) (common.Address, error) { + var addressStr string + err := p.db.QueryRow(ctx, `SELECT address FROM eth_keys WHERE role = 'pdp' LIMIT 1`).Scan(&addressStr) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return common.Address{}, errors.New("no sender address with role 'pdp' found") + } + return common.Address{}, err + } + address := common.HexToAddress(addressStr) + return address, nil +} + +// insertMessageWaitsAndProofsetCreate inserts records into message_waits_eth and pdp_proofset_creates +func (p *PDPService) insertMessageWaitsAndProofsetCreate(ctx context.Context, txHashHex string, serviceLabel string) error { + // Begin a database 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) + `, txHashHex, "pending") + if err != nil { + return false, err // Return false to rollback the transaction + } + + // Insert into pdp_proofset_creates + _, err = tx.Exec(` + INSERT INTO pdp_proofset_creates (create_message_hash, service) + VALUES ($1, $2) + `, txHashHex, serviceLabel) + if err != nil { + return false, err // Return false to rollback the transaction + } + + // Return true to commit the transaction + return true, nil + }, harmonydb.OptionRetry()) + if err != nil { + return err + } + return nil +} + +// handleGetProofSetCreationStatus handles the GET request to retrieve the status of a proof set creation +func (p *PDPService) handleGetProofSetCreationStatus(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 txHash from the URL + txHash := chi.URLParam(r, "txHash") + if txHash == "" { + http.Error(w, "Missing txHash in URL", http.StatusBadRequest) + return + } + + // Clean txHash (ensure it starts with '0x' and is lowercase) + if !strings.HasPrefix(txHash, "0x") { + txHash = "0x" + txHash + } + txHash = strings.ToLower(txHash) + + // Validate txHash is a valid hash + if len(txHash) != 66 { // '0x' + 64 hex chars + http.Error(w, "Invalid txHash length", http.StatusBadRequest) + return + } + if _, err := hex.DecodeString(txHash[2:]); err != nil { + http.Error(w, "Invalid txHash format", http.StatusBadRequest) + return + } + + // Step 3: Lookup pdp_proofset_creates by create_message_hash (which is txHash) + var proofSetCreate struct { + CreateMessageHash string `db:"create_message_hash"` + OK *bool `db:"ok"` // Pointer to handle NULL + ProofSetCreated bool `db:"proofset_created"` + Service string `db:"service"` + } + + err = p.db.QueryRow(ctx, ` + SELECT create_message_hash, ok, proofset_created, service + FROM pdp_proofset_creates + WHERE create_message_hash = $1 + `, txHash).Scan(&proofSetCreate.CreateMessageHash, &proofSetCreate.OK, &proofSetCreate.ProofSetCreated, &proofSetCreate.Service) + if err != nil { + if err == sql.ErrNoRows { + http.Error(w, "Proof set creation not found for given txHash", http.StatusNotFound) + return + } + http.Error(w, "Failed to query proof set creation: "+err.Error(), http.StatusInternalServerError) + return + } + + // Step 4: Check that the service matches the requesting service + if proofSetCreate.Service != serviceLabel { + http.Error(w, "Unauthorized: service label mismatch", http.StatusUnauthorized) + return + } + + // Step 5: Prepare the response + response := struct { + CreateMessageHash string `json:"createMessageHash"` + ProofsetCreated bool `json:"proofsetCreated"` + Service string `json:"service"` + TxStatus string `json:"txStatus"` + OK *bool `json:"ok"` + ProofSetId *uint64 `json:"proofSetId,omitempty"` + }{ + CreateMessageHash: proofSetCreate.CreateMessageHash, + ProofsetCreated: proofSetCreate.ProofSetCreated, + Service: proofSetCreate.Service, + OK: proofSetCreate.OK, + } + + // Now get the tx_status from message_waits_eth + var txStatus string + err = p.db.QueryRow(ctx, ` + SELECT tx_status + FROM message_waits_eth + WHERE signed_tx_hash = $1 + `, txHash).Scan(&txStatus) + if err != nil { + if err == sql.ErrNoRows { + // This should not happen as per foreign key constraints + http.Error(w, "Message status not found for given txHash", http.StatusInternalServerError) + return + } + http.Error(w, "Failed to query message status: "+err.Error(), http.StatusInternalServerError) + return + } + + response.TxStatus = txStatus + + if proofSetCreate.ProofSetCreated { + // The proof set has been created, get the proofSetId from pdp_proof_sets + var proofSetId uint64 + err = p.db.QueryRow(ctx, ` + SELECT id + FROM pdp_proof_sets + WHERE create_message_hash = $1 + `, txHash).Scan(&proofSetId) + if err != nil { + if err == sql.ErrNoRows { + // Should not happen, but handle gracefully + http.Error(w, "Proof set not found despite proofset_created = true", http.StatusInternalServerError) + return + } + http.Error(w, "Failed to query proof set: "+err.Error(), http.StatusInternalServerError) + return + } + response.ProofSetId = &proofSetId + } + + // Step 6: Return the response as JSON + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, "Failed to write response: "+err.Error(), http.StatusInternalServerError) + return + } +} + +// handleGetProofSet handles the GET request to retrieve the details of a proof set +func (p *PDPService) handleGetProofSet(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 proofSetId from the URL + proofSetIdStr := chi.URLParam(r, "proofSetID") + if proofSetIdStr == "" { + http.Error(w, "Missing proof set 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 + } + + // Step 3: Retrieve the proof set from the database + var proofSet struct { + ID uint64 `db:"id"` + Service string `db:"service"` + } + + err = p.db.QueryRow(ctx, ` + SELECT id, service + FROM pdp_proof_sets + WHERE id = $1 + `, proofSetId).Scan(&proofSet.ID, &proofSet.Service) + if err != nil { + if err == sql.ErrNoRows { + http.Error(w, "Proof set not found", http.StatusNotFound) + return + } + http.Error(w, "Failed to retrieve proof set: "+err.Error(), http.StatusInternalServerError) + return + } + + // Step 4: Check that the proof set belongs to the requesting service + if proofSet.Service != serviceLabel { + http.Error(w, "Unauthorized: proof set does not belong to your service", http.StatusUnauthorized) + return + } + + // Step 5: Retrieve the roots associated with the proof set + var roots []struct { + RootID uint64 `db:"root_id"` + RootCID string `db:"root"` + SubrootCID string `db:"subroot"` + SubrootOffset int64 `db:"subroot_offset"` + } + + err = p.db.Select(ctx, &roots, ` + SELECT root_id, root, subroot, subroot_offset + FROM pdp_proofset_roots + WHERE proofset = $1 + ORDER BY root_id, subroot_offset + `, proofSetId) + if err != nil { + http.Error(w, "Failed to retrieve proof set roots: "+err.Error(), http.StatusInternalServerError) + return + } + + // Step 6: Get the next challenge epoch + var nextChallengeEpoch int64 + err = p.db.QueryRow(ctx, ` + SELECT prove_at_epoch + FROM pdp_proof_sets + WHERE id = $1 + `, proofSetId).Scan(&nextChallengeEpoch) + if err != nil { + http.Error(w, "Failed to retrieve next challenge epoch: "+err.Error(), http.StatusInternalServerError) + return + } + + // Step 6: Prepare the response + response := struct { + ID uint64 `json:"id"` + Roots []RootEntry `json:"roots"` + NextChallengeEpoch int64 `json:"nextChallengeEpoch"` + }{ + ID: proofSet.ID, + NextChallengeEpoch: nextChallengeEpoch, + } + + // Convert roots to the desired JSON format + for _, root := range roots { + response.Roots = append(response.Roots, RootEntry{ + RootID: root.RootID, + RootCID: root.RootCID, + SubrootCID: root.SubrootCID, + SubrootOffset: root.SubrootOffset, + }) + } + + // Step 7: Return the response as JSON + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, "Failed to write response: "+err.Error(), http.StatusInternalServerError) + return + } +} + +// RootEntry represents a root in the proof set for JSON serialization +type RootEntry struct { + RootID uint64 `json:"rootId"` + RootCID string `json:"rootCid"` + SubrootCID string `json:"subrootCid"` + SubrootOffset int64 `json:"subrootOffset"` +} + +func (p *PDPService) handleDeleteProofSet(w http.ResponseWriter, r *http.Request) { + // ### DEL /proof-sets/{set id} + // Remove the specified proof set entirely + + http.Error(w, "todo", http.StatusBadRequest) +} + +func (p *PDPService) handleAddRootToProofSet(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 proofSetID from the URL + proofSetIDStr := chi.URLParam(r, "proofSetID") + if proofSetIDStr == "" { + http.Error(w, "Missing proof set ID in URL", http.StatusBadRequest) + return + } + + // Convert proofSetID to uint64 + proofSetIDUint64, err := strconv.ParseUint(proofSetIDStr, 10, 64) + if err != nil { + http.Error(w, "Invalid proof set 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 + `, proofSetIDUint64).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 + } + + // Convert proofSetID to *big.Int + proofSetID := new(big.Int).SetUint64(proofSetIDUint64) + + // Step 3: Parse the request body + type SubrootEntry struct { + SubrootCID string `json:"subrootCid"` + } + + type AddRootRequest struct { + RootCID string `json:"rootCid"` + Subroots []SubrootEntry `json:"subroots"` + } + + var req []AddRootRequest // array because we can add multiple roots in one request + err = json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, "Invalid request body: "+err.Error(), http.StatusBadRequest) + return + } + defer r.Body.Close() + + if len(req) == 0 { + http.Error(w, "At least one root must be provided", http.StatusBadRequest) + return + } + + // Collect all subrootCIDs to fetch their info in a batch + subrootCIDsSet := make(map[string]struct{}) + for _, addRootReq := range req { + if addRootReq.RootCID == "" { + http.Error(w, "RootCID is required for each root", http.StatusBadRequest) + return + } + + if len(addRootReq.Subroots) == 0 { + http.Error(w, "At least one subroot is required per root", http.StatusBadRequest) + return + } + + for _, subrootEntry := range addRootReq.Subroots { + if subrootEntry.SubrootCID == "" { + http.Error(w, "subrootCid is required for each subroot", http.StatusBadRequest) + return + } + if _, exists := subrootCIDsSet[subrootEntry.SubrootCID]; exists { + http.Error(w, "duplicate subrootCid in request", http.StatusBadRequest) + return + } + + subrootCIDsSet[subrootEntry.SubrootCID] = struct{}{} + } + } + + // Convert set to slice + subrootCIDsList := make([]string, 0, len(subrootCIDsSet)) + for cidStr := range subrootCIDsSet { + subrootCIDsList = append(subrootCIDsList, cidStr) + } + + // Map to store subrootCID -> [pieceInfo, pdp_pieceref.id, subrootOffset] + type SubrootInfo struct { + PieceInfo abi.PieceInfo + PDPPieceRefID int64 + SubrootOffset uint64 + } + + subrootInfoMap := make(map[string]*SubrootInfo) + + // Start a DB transaction + _, err = p.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) { + // Step 4: Get pdp_piecerefs matching all subroot cids + make sure those refs belong to serviceLabel + rows, err := tx.Query(` + SELECT ppr.piece_cid, ppr.id AS pdp_pieceref_id, ppr.piece_ref, + pp.piece_padded_size + FROM pdp_piecerefs ppr + JOIN parked_piece_refs pprf ON pprf.ref_id = ppr.piece_ref + JOIN parked_pieces pp ON pp.id = pprf.piece_id + WHERE ppr.service = $1 AND ppr.piece_cid = ANY($2) + `, serviceLabel, subrootCIDsList) + if err != nil { + return false, err + } + defer rows.Close() + + foundSubroots := make(map[string]struct{}) + for rows.Next() { + var pieceCIDStr string + var pdpPieceRefID, pieceRefID int64 + var piecePaddedSize uint64 + + err := rows.Scan(&pieceCIDStr, &pdpPieceRefID, &pieceRefID, &piecePaddedSize) + if err != nil { + return false, err + } + + // Parse the piece CID + pieceCID, err := cid.Decode(pieceCIDStr) + if err != nil { + return false, fmt.Errorf("invalid piece CID in database: %s", pieceCIDStr) + } + + // Create PieceInfo + pieceInfo := abi.PieceInfo{ + Size: abi.PaddedPieceSize(piecePaddedSize), + PieceCID: pieceCID, + } + + subrootInfoMap[pieceCIDStr] = &SubrootInfo{ + PieceInfo: pieceInfo, + PDPPieceRefID: pdpPieceRefID, + SubrootOffset: 0, // Will compute offset later + } + + foundSubroots[pieceCIDStr] = struct{}{} + } + + // Check if all subroot CIDs were found + for _, cidStr := range subrootCIDsList { + if _, found := foundSubroots[cidStr]; !found { + return false, fmt.Errorf("subroot CID %s not found or does not belong to service %s", cidStr, serviceLabel) + } + } + + // Now, for each AddRootRequest, validate RootCID and prepare data for ETH transaction + for _, addRootReq := range req { + // Collect pieceInfos for subroots + pieceInfos := make([]abi.PieceInfo, len(addRootReq.Subroots)) + + var totalOffset uint64 = 0 + for i, subrootEntry := range addRootReq.Subroots { + subrootInfo, exists := subrootInfoMap[subrootEntry.SubrootCID] + if !exists { + return false, fmt.Errorf("subroot CID %s not found in subroot info map", subrootEntry.SubrootCID) + } + + // Update SubrootOffset + subrootInfo.SubrootOffset = totalOffset + subrootInfoMap[subrootEntry.SubrootCID] = subrootInfo // Update the map + + pieceInfos[i] = subrootInfo.PieceInfo + + totalOffset += uint64(subrootInfo.PieceInfo.Size) + } + + // Use GenerateUnsealedCID to generate RootCID from subroots + proofType := abi.RegisteredSealProof_StackedDrg64GiBV1_1 // Proof type sets max piece size, nothing else + generatedRootCID, err := nonffi.GenerateUnsealedCID(proofType, pieceInfos) + if err != nil { + return false, fmt.Errorf("failed to generate RootCID: %v", err) + } + + // Compare generated RootCID with provided RootCID + providedRootCID, err := cid.Decode(addRootReq.RootCID) + if err != nil { + return false, fmt.Errorf("invalid provided RootCID: %v", err) + } + + if !providedRootCID.Equals(generatedRootCID) { + return false, fmt.Errorf("provided RootCID does not match generated RootCID: %s != %s", providedRootCID, generatedRootCID) + } + } + + // All validations passed, commit the transaction + return true, nil + }, harmonydb.OptionRetry()) + if err != nil { + http.Error(w, "Failed to validate subroots: "+err.Error(), http.StatusBadRequest) + return + } + + // Step 5: Prepare the Ethereum transaction data outside the DB transaction + // Obtain the ABI of the PDPVerifier contract + abiData, err := contract.PDPVerifierMetaData.GetAbi() + if err != nil { + http.Error(w, "Failed to get contract ABI: "+err.Error(), http.StatusInternalServerError) + return + } + + // Prepare RootData array for Ethereum transaction + // Define a Struct that matches the Solidity RootData struct + type RootData struct { + Root struct{ Data []byte } + RawSize *big.Int + } + + var rootDataArray []RootData + + for _, addRootReq := range req { + // Convert RootCID to bytes + rootCID, err := cid.Decode(addRootReq.RootCID) + if err != nil { + http.Error(w, "Invalid RootCID: "+err.Error(), http.StatusBadRequest) + return + } + + // Get raw size by summing up the sizes of subroots + var totalSize uint64 = 0 + var prevSubrootSize = subrootInfoMap[addRootReq.Subroots[0].SubrootCID].PieceInfo.Size + for i, subrootEntry := range addRootReq.Subroots { + subrootInfo := subrootInfoMap[subrootEntry.SubrootCID] + if subrootInfo.PieceInfo.Size > prevSubrootSize { + msg := fmt.Sprintf("Subroots must be in descending order of size, root %d %s is larger than prev subroot %s", i, subrootEntry.SubrootCID, addRootReq.Subroots[i-1].SubrootCID) + http.Error(w, msg, http.StatusBadRequest) + return + } + + prevSubrootSize = subrootInfo.PieceInfo.Size + totalSize += uint64(subrootInfo.PieceInfo.Size.Unpadded()) + } + + // Prepare RootData for Ethereum transaction + rootData := RootData{ + Root: struct{ Data []byte }{Data: rootCID.Bytes()}, + RawSize: new(big.Int).SetUint64(totalSize), + } + + rootDataArray = append(rootDataArray, rootData) + } + + // Step 6: Prepare the Ethereum transaction + // Pack the method call data + data, err := abiData.Pack("addRoots", proofSetID, rootDataArray, []byte{}) + if err != nil { + http.Error(w, "Failed to pack method call: "+err.Error(), http.StatusInternalServerError) + return + } + + // Step 7: Get the sender address from 'eth_keys' table where role = 'pdp' limit 1 + fromAddress, err := p.getSenderAddress(ctx) + if err != nil { + http.Error(w, "Failed to get sender address: "+err.Error(), http.StatusInternalServerError) + return + } + + // Prepare the transaction (nonce will be set to 0, SenderETH will assign it) + txEth := types.NewTransaction( + 0, + contract.ContractAddresses().PDPVerifier, + big.NewInt(0), + 0, + nil, + data, + ) + + // Step 8: Send the transaction using SenderETH + reason := "pdp-addroots" + txHash, err := p.sender.Send(ctx, fromAddress, txEth, reason) + if err != nil { + http.Error(w, "Failed to send transaction: "+err.Error(), http.StatusInternalServerError) + log.Errorf("Failed to send transaction: %+v", err) + return + } + + // Step 9: Insert into message_waits_eth and pdp_proofset_roots + _, err = p.db.BeginTransaction(ctx, func(txdb *harmonydb.Tx) (bool, error) { + // Insert into message_waits_eth + _, err := txdb.Exec(` + INSERT INTO message_waits_eth (signed_tx_hash, tx_status) + VALUES ($1, $2) + `, txHash.Hex(), "pending") + if err != nil { + return false, err // Return false to rollback the transaction + } + + // Update proof set for initialization upon first add + _, err = txdb.Exec(` + UPDATE pdp_proof_sets SET init_ready = true + WHERE id = $1 AND prev_challenge_request_epoch IS NULL AND challenge_request_msg_hash IS NULL AND prove_at_epoch IS NULL + `, proofSetIDUint64) + if err != nil { + return false, err + } + + // Insert into pdp_proofset_roots + + for addMessageIndex, addRootReq := range req { + for _, subrootEntry := range addRootReq.Subroots { + subrootInfo := subrootInfoMap[subrootEntry.SubrootCID] + + // Insert into pdp_proofset_roots + _, err = txdb.Exec(` + INSERT INTO pdp_proofset_root_adds ( + proofset, + root, + add_message_hash, + add_message_index, + subroot, + subroot_offset, + subroot_size, + pdp_pieceref + ) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + `, + proofSetIDUint64, + addRootReq.RootCID, + txHash.Hex(), + addMessageIndex, + subrootEntry.SubrootCID, + subrootInfo.SubrootOffset, + subrootInfo.PieceInfo.Size, + subrootInfo.PDPPieceRefID, + ) + if err != nil { + return false, err + } + } + } + + // Return true to commit the transaction + return true, nil + }, harmonydb.OptionRetry()) + if err != nil { + log.Errorw("Failed to insert into database", "error", err, "txHash", txHash.Hex(), "subroots", subrootInfoMap) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + + // Step 10: Respond with 201 Created + 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} + // Response Body: + // { + // "rootId": {root ID}, + // "rootCid": "bafy....root", + // "subroots": [ + // { + // "subrootCid": "bafy...subroot", + // "subrootOffset": 0, + // "pieceCid": "bafy...piece1" + // }, + // //... + // ] + // } + + /* 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 + }*/ + + // Retrieve root from proof set in store + /*rootDetails, err := p.ProofSetStore.GetProofSetRoot(proofSetID, rootID) + if err != nil { + http.Error(w, "Root not found", http.StatusNotFound) + return + }*/ + + // Respond with root details + w.Header().Set("Content-Type", "application/json") + /*err = json.NewEncoder(w).Encode(rootDetails) + if err != nil { + http.Error(w, "Failed to encode response", http.StatusInternalServerError) + return + }*/ +} + +// Data models corresponding to the updated schema + +// PDPOwnerAddress represents the owner address with its private key +type PDPOwnerAddress struct { + OwnerAddress string // PRIMARY KEY + PrivateKey []byte // BYTEA NOT NULL +} + +// PDPServiceEntry represents a PDP service entry +type PDPServiceEntry struct { + ID int64 // PRIMARY KEY + PublicKey []byte // BYTEA NOT NULL + ServiceLabel string // TEXT NOT NULL + CreatedAt time.Time // DEFAULT CURRENT_TIMESTAMP +} + +// PDPPieceRef represents a PDP piece reference +type PDPPieceRef struct { + ID int64 // PRIMARY KEY + ServiceID int64 // pdp_services.id + PieceCID string // TEXT NOT NULL + RefID string // TEXT NOT NULL + ServiceTag string // VARCHAR(64) + ClientTag string // VARCHAR(64) + CreatedAt time.Time // DEFAULT CURRENT_TIMESTAMP +} + +// PDPProofSet represents a proof set +type PDPProofSet struct { + ID int64 // PRIMARY KEY (on-chain proofset id) + NextChallengeEpoch int64 // Cached chain value +} + +// PDPProofSetRoot represents a root in a proof set +type PDPProofSetRoot struct { + ProofSetID int64 // proofset BIGINT NOT NULL + RootID int64 // root_id BIGINT NOT NULL + Root string // root TEXT NOT NULL + Subroot string // subroot TEXT NOT NULL + SubrootOffset int64 // subroot_offset BIGINT NOT NULL + PDPPieceRefID int64 // pdp_piecerefs.id +} + +// PDPProveTask represents a prove task +type PDPProveTask struct { + ProofSetID int64 // proofset + ChallengeEpoch int64 // challenge epoch + TaskID int64 // harmonytask task ID + MessageCID string // text + MessageEthHash string // text +} + +// Interfaces + +// ProofSetStore defines methods to manage proof sets and roots +type ProofSetStore interface { + CreateProofSet(proofSet *PDPProofSet) (int64, error) + GetProofSet(proofSetID int64) (*PDPProofSetDetails, error) + DeleteProofSet(proofSetID int64) error + AddProofSetRoot(proofSetRoot *PDPProofSetRoot) error + DeleteProofSetRoot(proofSetID int64, rootID int64) error +} + +// PieceStore defines methods to manage pieces and piece references +type PieceStore interface { + HasPiece(pieceCID string) (bool, error) + StorePiece(pieceCID string, data []byte) error + GetPiece(pieceCID string) ([]byte, error) + GetPieceRefIDByPieceCID(pieceCID string) (int64, error) +} + +// OwnerAddressStore defines methods to manage owner addresses +type OwnerAddressStore interface { + HasOwnerAddress(ownerAddress string) (bool, error) +} + +// PDPProofSetDetails represents the details of a proof set, including roots +type PDPProofSetDetails struct { + ID int64 `json:"id"` + NextChallengeEpoch int64 `json:"nextChallengeEpoch"` + Roots []PDPProofSetRootDetail `json:"roots"` +} + +// PDPProofSetRootDetail represents the details of a root in a proof set +type PDPProofSetRootDetail struct { + RootID int64 `json:"rootId"` + RootCID string `json:"rootCid"` + Subroots []PDPProofSetSubrootDetail `json:"subroots"` +} + +// PDPProofSetSubrootDetail represents a subroot in a proof set root +type PDPProofSetSubrootDetail struct { + SubrootCID string `json:"subrootCid"` + SubrootOffset int64 `json:"subrootOffset"` + PieceCID string `json:"pieceCid"` +} diff --git a/pdp/handlers_upload.go b/pdp/handlers_upload.go new file mode 100644 index 000000000..fe4cb0f59 --- /dev/null +++ b/pdp/handlers_upload.go @@ -0,0 +1,512 @@ +package pdp + +import ( + "bytes" + "context" + "database/sql" + "encoding/hex" + "encoding/json" + "fmt" + "hash" + "io" + "net/http" + "os" + "path" + "strconv" + + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "github.com/ipfs/go-cid" + logger "github.com/ipfs/go-log/v2" + "github.com/multiformats/go-multihash" + mhreg "github.com/multiformats/go-multihash/core" + "github.com/snadrus/must" + "github.com/yugabyte/pgx/v5" + + commcid "github.com/filecoin-project/go-fil-commcid" + commp "github.com/filecoin-project/go-fil-commp-hashhash" + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/lib/dealdata" + "github.com/filecoin-project/curio/lib/proof" +) + +var log = logger.Logger("pdp") + +// PieceSizeLimit in bytes +var PieceSizeLimit = abi.PaddedPieceSize(proof.MaxMemtreeSize).Unpadded() + +type PieceHash struct { + // Name of the hash function used + // sha2-256-trunc254-padded - CommP + // sha2-256 - Blob sha256 + Name string `json:"name"` + + // hex encoded hash + Hash string `json:"hash"` + + // Size of the piece in bytes + Size int64 `json:"size"` +} + +func (ph *PieceHash) Set() bool { + return ph.Name != "" && ph.Hash != "" && ph.Size > 0 +} + +func (ph *PieceHash) mh() (multihash.Multihash, error) { + _, ok := multihash.Names[ph.Name] + if !ok { + return nil, fmt.Errorf("hash function name not recognized: %s", ph.Name) + } + + hashBytes, err := hex.DecodeString(ph.Hash) + if err != nil { + return nil, fmt.Errorf("failed to decode hash: %w", err) + } + + return multihash.EncodeName(hashBytes, ph.Name) +} + +func (ph *PieceHash) commp(ctx context.Context, db *harmonydb.DB) (cid.Cid, bool, error) { + // commp, known, error + mh, err := ph.mh() + if err != nil { + return cid.Undef, false, fmt.Errorf("failed to decode hash: %w", err) + } + + if ph.Name == multihash.Codes[multihash.SHA2_256_TRUNC254_PADDED] { + return cid.NewCidV1(cid.FilCommitmentUnsealed, mh), true, nil + } + + var commpStr string + err = db.QueryRow(ctx, ` + SELECT commp FROM pdp_piece_mh_to_commp WHERE mhash = $1 AND size = $2 + `, mh, ph.Size).Scan(&commpStr) + if err != nil { + if err == pgx.ErrNoRows { + return cid.Undef, false, nil + } + return cid.Undef, false, fmt.Errorf("failed to query pdp_piece_mh_to_commp: %w", err) + } + + commpCid, err := cid.Parse(commpStr) + if err != nil { + return cid.Undef, false, fmt.Errorf("failed to parse commp CID: %w", err) + } + + return commpCid, true, nil +} + +func (ph *PieceHash) maybeStaticCommp() (cid.Cid, bool) { + mh, err := ph.mh() + if err != nil { + return cid.Undef, false + } + + if ph.Name == multihash.Codes[multihash.SHA2_256_TRUNC254_PADDED] { + return cid.NewCidV1(cid.FilCommitmentUnsealed, mh), true + } + + return cid.Undef, false +} + +func (p *PDPService) handlePiecePost(w http.ResponseWriter, r *http.Request) { + // Verify that the request is authorized using ECDSA JWT + serviceID, err := p.verifyJWTToken(r) + if err != nil { + http.Error(w, "Unauthorized: "+err.Error(), http.StatusUnauthorized) + return + } + + // Parse request body + var req struct { + Check PieceHash `json:"check"` + Notify string `json:"notify,omitempty"` + } + err = json.NewDecoder(r.Body).Decode(&req) + if err != nil || !req.Check.Set() { + http.Error(w, "Invalid request body: missing pieceCid or refId", http.StatusBadRequest) + return + } + + if abi.UnpaddedPieceSize(req.Check.Size) > PieceSizeLimit { + http.Error(w, "Piece size exceeds the maximum allowed size", http.StatusBadRequest) + return + } + + ctx := r.Context() + + pieceCid, havePieceCid, err := req.Check.commp(ctx, p.db) + if err != nil { + http.Error(w, "Failed to process request: "+err.Error(), http.StatusInternalServerError) + return + } + + // Variables to hold information outside the transaction + var uploadUUID uuid.UUID + var uploadURL string + var responseStatus int + + _, err = p.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) { + if havePieceCid { + // Check if a 'parked_pieces' entry exists for the given 'piece_cid' + var parkedPieceID int64 + err := tx.QueryRow(` + SELECT id FROM parked_pieces WHERE piece_cid = $1 AND long_term = TRUE AND complete = TRUE + `, pieceCid).Scan(&parkedPieceID) + if err != nil && err != pgx.ErrNoRows { + return false, fmt.Errorf("failed to query parked_pieces: %w", err) + } + + if err == nil { + // Piece is already stored + // Create a new 'parked_piece_refs' entry + var parkedPieceRefID int64 + err = tx.QueryRow(` + INSERT INTO parked_piece_refs (piece_id, long_term) + VALUES ($1, TRUE) RETURNING ref_id + `, parkedPieceID).Scan(&parkedPieceRefID) + if err != nil { + return false, fmt.Errorf("failed to insert into parked_piece_refs: %w", err) + } + + // Create a new 'pdp_piece_uploads' entry pointing to the 'parked_piece_refs' entry + uploadUUID = uuid.New() + _, err = tx.Exec(` + INSERT INTO pdp_piece_uploads (id, service, piece_cid, notify_url, piece_ref, check_hash_codec, check_hash, check_size) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + `, uploadUUID.String(), serviceID, pieceCid, req.Notify, parkedPieceRefID, req.Check.Name, must.One(hex.DecodeString(req.Check.Hash)), req.Check.Size) + if err != nil { + return false, fmt.Errorf("failed to insert into pdp_piece_uploads: %w", err) + } + + responseStatus = http.StatusOK + return true, nil // Commit the transaction + } + } + + // Piece does not exist, proceed to create a new upload request + uploadUUID = uuid.New() + + // Store the upload request in the database + var pieceCidStr *string + if p, ok := req.Check.maybeStaticCommp(); ok { + ps := p.String() + pieceCidStr = &ps + } + + _, err = tx.Exec(` + INSERT INTO pdp_piece_uploads (id, service, piece_cid, notify_url, check_hash_codec, check_hash, check_size) + VALUES ($1, $2, $3, $4, $5, $6, $7) + `, uploadUUID.String(), serviceID, pieceCidStr, req.Notify, req.Check.Name, must.One(hex.DecodeString(req.Check.Hash)), req.Check.Size) + if err != nil { + return false, fmt.Errorf("Failed to store upload request in database: %w", err) + } + + // Create a location URL where the piece data can be uploaded via PUT + uploadURL = path.Join(PDPRoutePath, "/piece/upload", uploadUUID.String()) + responseStatus = http.StatusCreated + + return true, nil // Commit the transaction + }, harmonydb.OptionRetry()) + + if err != nil { + http.Error(w, "Failed to process request: "+err.Error(), http.StatusInternalServerError) + return + } + + if responseStatus == http.StatusCreated { + // Return 201 Created with Location header + w.Header().Set("Location", uploadURL) + w.WriteHeader(http.StatusCreated) + } else if responseStatus == http.StatusOK { + // Return 200 OK with the pieceCID + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(map[string]string{"pieceCID": pieceCid.String()}) + } else { + // Should not reach here + http.Error(w, "Unexpected error", http.StatusInternalServerError) + } +} + +// handlePieceUpload handles the PUT request to upload the actual bytes of the piece +func (p *PDPService) handlePieceUpload(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPut { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + + // Extract the uploadUUID from the URL + uploadUUIDStr := chi.URLParam(r, "uploadUUID") + uploadUUID, err := uuid.Parse(uploadUUIDStr) + if err != nil { + http.Error(w, "Invalid upload UUID", http.StatusBadRequest) + return + } + + ctx := r.Context() + + // Lookup the expected pieceCID, notify_url, and piece_ref from the database using uploadUUID + var pieceCIDStr *string + var notifyURL, checkHashName string + var checkHash []byte + var checkSize int64 + + var pieceRef sql.NullInt64 + err = p.db.QueryRow(ctx, ` + SELECT piece_cid, notify_url, piece_ref, check_hash_codec, check_hash, check_size FROM pdp_piece_uploads WHERE id = $1 + `, uploadUUID.String()).Scan(&pieceCIDStr, ¬ifyURL, &pieceRef, &checkHashName, &checkHash, &checkSize) + if err != nil { + if err == sql.ErrNoRows { + http.Error(w, "Upload UUID not found", http.StatusNotFound) + } else { + http.Error(w, "Database error", http.StatusInternalServerError) + } + return + } + + // Check that piece_ref is null; non-null means data was already uploaded + if pieceRef.Valid { + http.Error(w, "Data has already been uploaded", http.StatusConflict) + return + } + + ph := PieceHash{ + Name: checkHashName, + Hash: hex.EncodeToString(checkHash), + Size: checkSize, + } + phMh, err := ph.mh() + if err != nil { + http.Error(w, "Failed to decode hash: "+err.Error(), http.StatusInternalServerError) + return + } + + // Limit the size of the piece data + maxPieceSize := checkSize + + // Create a commp.Calc instance for calculating commP + cp := &commp.Calc{} + readSize := int64(0) + + var vhash hash.Hash + if checkHashName != multihash.Codes[multihash.SHA2_256_TRUNC254_PADDED] { + hasher, err := mhreg.GetVariableHasher(multihash.Names[checkHashName], -1) + if err != nil { + http.Error(w, "Failed to get hasher", http.StatusInternalServerError) + return + } + vhash = hasher + } + + // Function to write data into StashStore and calculate commP + writeFunc := func(f *os.File) error { + limitedReader := io.LimitReader(r.Body, maxPieceSize+1) // +1 to detect exceeding the limit + multiWriter := io.MultiWriter(cp, f) + if vhash != nil { + multiWriter = io.MultiWriter(vhash, multiWriter) + } + + // Copy data from limitedReader to multiWriter + n, err := io.Copy(multiWriter, limitedReader) + if err != nil { + return fmt.Errorf("failed to read and write piece data: %w", err) + } + + if n > maxPieceSize { + return fmt.Errorf("piece data exceeds the maximum allowed size") + } + + readSize = n + + return nil + } + + // Upload into StashStore + stashID, err := p.storage.StashCreate(ctx, maxPieceSize, writeFunc) + if err != nil { + if err.Error() == "piece data exceeds the maximum allowed size" { + http.Error(w, err.Error(), http.StatusRequestEntityTooLarge) + return + } else { + log.Errorw("Failed to store piece data in StashStore", "error", err) + http.Error(w, "Failed to store piece data", http.StatusInternalServerError) + return + } + } + + // Finalize the commP calculation + digest, paddedPieceSize, err := cp.Digest() + if err != nil { + // Remove the stash file as the data is invalid + _ = p.storage.StashRemove(ctx, stashID) + http.Error(w, "Failed to finalize commP calculation", http.StatusInternalServerError) + return + } + + if readSize != checkSize { + _ = p.storage.StashRemove(ctx, stashID) + http.Error(w, "Piece size does not match the expected size", http.StatusBadRequest) + return + } + + var outHash = digest + if vhash != nil { + outHash = vhash.Sum(nil) + } + + if !bytes.Equal(outHash, checkHash) { + // Remove the stash file as the data is invalid + _ = p.storage.StashRemove(ctx, stashID) + http.Error(w, "Computed hash does not match expected hash", http.StatusBadRequest) + return + } + + // Convert commP digest into a piece CID + pieceCIDComputed, err := commcid.DataCommitmentV1ToCID(digest) + if err != nil { + // Remove the stash file as the data is invalid + _ = p.storage.StashRemove(ctx, stashID) + http.Error(w, "Failed to convert commP to CID", http.StatusInternalServerError) + return + } + + // Compare the computed piece CID with the expected one from the database + if pieceCIDStr != nil && pieceCIDComputed.String() != *pieceCIDStr { + // Remove the stash file as the data is invalid + _ = p.storage.StashRemove(ctx, stashID) + http.Error(w, "Computed piece CID does not match expected piece CID", http.StatusBadRequest) + return + } + + didCommit, err := p.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) { + + // 1. Create a long-term parked piece entry + var parkedPieceID int64 + err := tx.QueryRow(` + INSERT INTO parked_pieces (piece_cid, piece_padded_size, piece_raw_size, long_term) + VALUES ($1, $2, $3, TRUE) RETURNING id + `, pieceCIDComputed.String(), paddedPieceSize, readSize).Scan(&parkedPieceID) + if err != nil { + return false, fmt.Errorf("failed to create parked_pieces entry: %w", err) + } + + // 2. Create a piece ref with data_url being "stashstore://" + // Get StashURL + stashURL, err := p.storage.StashURL(stashID) + if err != nil { + return false, fmt.Errorf("failed to get stash URL: %w", err) + } + + // Change scheme to "custore" + stashURL.Scheme = dealdata.CustoreScheme + dataURL := stashURL.String() + + var pieceRefID int64 + err = tx.QueryRow(` + INSERT INTO parked_piece_refs (piece_id, data_url, long_term) + VALUES ($1, $2, TRUE) RETURNING ref_id + `, parkedPieceID, dataURL).Scan(&pieceRefID) + if err != nil { + return false, fmt.Errorf("failed to create parked_piece_refs entry: %w", err) + } + + // 3. Update the pdp_piece_uploads entry to contain the created piece_ref + _, err = tx.Exec(` + UPDATE pdp_piece_uploads SET piece_ref = $1, piece_cid = $2 WHERE id = $3 + `, pieceRefID, pieceCIDComputed.String(), uploadUUID.String()) + if err != nil { + return false, fmt.Errorf("failed to update pdp_piece_uploads: %w", err) + } + + if checkHashName != multihash.Codes[multihash.SHA2_256_TRUNC254_PADDED] { + _, err = tx.Exec(` + INSERT INTO pdp_piece_mh_to_commp (mhash, size, commp) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING + `, phMh, checkSize, pieceCIDComputed.String()) + if err != nil { + return false, fmt.Errorf("failed to insert into pdp_piece_mh_to_commp: %w", err) + } + } + + return true, nil // Commit the transaction + }, harmonydb.OptionRetry()) + + if err != nil || !didCommit { + // Remove the stash file as the transaction failed + _ = p.storage.StashRemove(ctx, stashID) + http.Error(w, "Failed to process piece upload", http.StatusInternalServerError) + return + } + + // Respond with 204 No Content + w.WriteHeader(http.StatusNoContent) +} + +// handle find piece allows one to look up a pdp piece by its original post data as +// query parameters +func (p *PDPService) handleFindPiece(w http.ResponseWriter, r *http.Request) { + // Verify that the request is authorized using ECDSA JWT + _, err := p.verifyJWTToken(r) + if err != nil { + http.Error(w, "Unauthorized: "+err.Error(), http.StatusUnauthorized) + return + } + + // Parse query parameters + + sizeString := r.URL.Query().Get("size") + size, err := strconv.ParseInt(sizeString, 10, 64) + if err != nil { + http.Error(w, fmt.Sprintf("errors parsing size: %s", err.Error()), 400) + return + } + req := PieceHash{ + Name: r.URL.Query().Get("name"), + Hash: r.URL.Query().Get("hash"), + Size: size, + } + + ctx := r.Context() + + pieceCid, havePieceCid, err := req.commp(ctx, p.db) + if err != nil { + http.Error(w, "Failed to process request: "+err.Error(), http.StatusInternalServerError) + return + } + + // upload either not complete or does not exist + if !havePieceCid { + http.NotFound(w, r) + return + } + + // Verify that a 'parked_pieces' entry exists for the given 'piece_cid' + var count int + err = p.db.QueryRow(ctx, ` + SELECT count(*) FROM parked_pieces WHERE piece_cid = $1 AND long_term = TRUE AND complete = TRUE + `, pieceCid.String()).Scan(&count) + if err != nil { + http.Error(w, "Database error", http.StatusInternalServerError) + return + } + if count == 0 { + http.NotFound(w, r) + return + } + + response := struct { + PieceCID string `json:"piece_cid"` + }{ + PieceCID: pieceCid.String(), + } + + // encode response + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, "Failed to write response: "+err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/tasks/indexing/task_indexing.go b/tasks/indexing/task_indexing.go index 72d9e1b8d..bdd7b4759 100644 --- a/tasks/indexing/task_indexing.go +++ b/tasks/indexing/task_indexing.go @@ -34,7 +34,7 @@ var log = logging.Logger("indexing") type IndexingTask struct { db *harmonydb.DB indexStore *indexstore.IndexStore - pieceProvider *pieceprovider.PieceProvider + pieceProvider *pieceprovider.SectorReader sc *ffi.SealCalls cfg *config.CurioConfig insertConcurrency int @@ -42,7 +42,7 @@ type IndexingTask struct { max taskhelp.Limiter } -func NewIndexingTask(db *harmonydb.DB, sc *ffi.SealCalls, indexStore *indexstore.IndexStore, pieceProvider *pieceprovider.PieceProvider, cfg *config.CurioConfig, max taskhelp.Limiter) *IndexingTask { +func NewIndexingTask(db *harmonydb.DB, sc *ffi.SealCalls, indexStore *indexstore.IndexStore, pieceProvider *pieceprovider.SectorReader, cfg *config.CurioConfig, max taskhelp.Limiter) *IndexingTask { return &IndexingTask{ db: db, diff --git a/tasks/indexing/task_ipni.go b/tasks/indexing/task_ipni.go index e1062faee..d64db0416 100644 --- a/tasks/indexing/task_ipni.go +++ b/tasks/indexing/task_ipni.go @@ -47,13 +47,13 @@ var ilog = logging.Logger("ipni") type IPNITask struct { db *harmonydb.DB indexStore *indexstore.IndexStore - pieceProvider *pieceprovider.PieceProvider + pieceProvider *pieceprovider.SectorReader sc *ffi.SealCalls cfg *config.CurioConfig max taskhelp.Limiter } -func NewIPNITask(db *harmonydb.DB, sc *ffi.SealCalls, indexStore *indexstore.IndexStore, pieceProvider *pieceprovider.PieceProvider, cfg *config.CurioConfig, max taskhelp.Limiter) *IPNITask { +func NewIPNITask(db *harmonydb.DB, sc *ffi.SealCalls, indexStore *indexstore.IndexStore, pieceProvider *pieceprovider.SectorReader, cfg *config.CurioConfig, max taskhelp.Limiter) *IPNITask { return &IPNITask{ db: db, indexStore: indexStore, diff --git a/tasks/message/sender_eth.go b/tasks/message/sender_eth.go new file mode 100644 index 000000000..0dbd582a7 --- /dev/null +++ b/tasks/message/sender_eth.go @@ -0,0 +1,404 @@ +package message + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "go.uber.org/multierr" + "golang.org/x/xerrors" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/harmony/harmonytask" + "github.com/filecoin-project/curio/harmony/resources" + "github.com/filecoin-project/curio/harmony/taskhelp" + "github.com/filecoin-project/curio/lib/promise" +) + +type SenderETH struct { + client *ethclient.Client + + sendTask *SendTaskETH + + db *harmonydb.DB +} + +type SendTaskETH struct { + sendTF promise.Promise[harmonytask.AddTaskFunc] + + client *ethclient.Client + + db *harmonydb.DB +} + +func (s *SendTaskETH) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.TODO() + + // Get transaction from the database + var dbTx struct { + FromAddress string `db:"from_address"` + ToAddress string `db:"to_address"` + UnsignedTx []byte `db:"unsigned_tx"` + UnsignedHash string `db:"unsigned_hash"` + Nonce *uint64 `db:"nonce"` + SignedTx []byte `db:"signed_tx"` + SendSuccess *bool `db:"send_success"` + SendError *string `db:"send_error"` + } + + err = s.db.QueryRow(ctx, + `SELECT from_address, to_address, unsigned_tx, unsigned_hash, nonce, signed_tx, send_success, send_error + FROM message_sends_eth + WHERE send_task_id = $1`, taskID).Scan( + &dbTx.FromAddress, &dbTx.ToAddress, &dbTx.UnsignedTx, &dbTx.UnsignedHash, &dbTx.Nonce, &dbTx.SignedTx, &dbTx.SendSuccess, &dbTx.SendError) + if err != nil { + return false, xerrors.Errorf("getting transaction from db: %w", err) + } + + // Deserialize the unsigned transaction + tx := new(types.Transaction) + err = tx.UnmarshalBinary(dbTx.UnsignedTx) + if err != nil { + return false, xerrors.Errorf("unmarshaling unsigned transaction: %w", err) + } + + fromAddress := common.HexToAddress(dbTx.FromAddress) + + // Acquire lock on from_address + for { + if !stillOwned() { + return false, xerrors.Errorf("lost ownership of task") + } + + // Try to acquire lock + cn, err := s.db.Exec(ctx, + `INSERT INTO message_send_eth_locks (from_address, task_id, claimed_at) + VALUES ($1, $2, CURRENT_TIMESTAMP) + ON CONFLICT (from_address) DO UPDATE + SET task_id = EXCLUDED.task_id, claimed_at = CURRENT_TIMESTAMP + WHERE message_send_eth_locks.task_id = $2`, dbTx.FromAddress, taskID) + if err != nil { + return false, xerrors.Errorf("acquiring send lock: %w", err) + } + + if cn == 1 { + // Acquired the lock + break + } + + // Wait and retry + log.Infow("waiting for send lock", "task_id", taskID, "from", dbTx.FromAddress) + time.Sleep(SendLockedWait) + } + + // Defer release of the lock + defer func() { + _, err2 := s.db.Exec(ctx, + `DELETE FROM message_send_eth_locks WHERE from_address = $1 AND task_id = $2`, dbTx.FromAddress, taskID) + if err2 != nil { + log.Errorw("releasing send lock", "task_id", taskID, "from", dbTx.FromAddress, "error", err2) + + // Ensure the task is retried + done = false + err = multierr.Append(err, xerrors.Errorf("releasing send lock: %w", err2)) + } + }() + + var signedTx *types.Transaction + + if dbTx.Nonce == nil { + // Get the latest nonce + pendingNonce, err := s.client.PendingNonceAt(ctx, fromAddress) + if err != nil { + return false, xerrors.Errorf("getting pending nonce: %w", err) + } + + // Get max nonce from successful transactions in DB + var dbNonce *uint64 + err = s.db.QueryRow(ctx, + `SELECT MAX(nonce) FROM message_sends_eth WHERE from_address = $1 AND send_success = TRUE`, dbTx.FromAddress).Scan(&dbNonce) + if err != nil { + return false, xerrors.Errorf("getting max nonce from db: %w", err) + } + + assignedNonce := pendingNonce + if dbNonce != nil && *dbNonce+1 > pendingNonce { + assignedNonce = *dbNonce + 1 + } + + // Update the transaction with the assigned nonce + tx = types.NewTransaction(assignedNonce, *tx.To(), tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data()) + + // Sign the transaction + signedTx, err = s.signTransaction(ctx, fromAddress, tx) + if err != nil { + return false, xerrors.Errorf("signing transaction: %w", err) + } + + // Serialize the signed transaction + signedTxData, err := signedTx.MarshalBinary() + if err != nil { + return false, xerrors.Errorf("serializing signed transaction: %w", err) + } + + // Update the database with nonce and signed transaction + n, err := s.db.Exec(ctx, + `UPDATE message_sends_eth + SET nonce = $1, signed_tx = $2, signed_hash = $3 + WHERE send_task_id = $4`, assignedNonce, signedTxData, signedTx.Hash().Hex(), taskID) + if err != nil { + return false, xerrors.Errorf("updating db record: %w", err) + } + if n != 1 { + return false, xerrors.Errorf("expected to update 1 row, updated %d", n) + } + } else { + // Transaction was previously signed but possibly failed to send + // Deserialize the signed transaction + signedTx = new(types.Transaction) + err = signedTx.UnmarshalBinary(dbTx.SignedTx) + if err != nil { + return false, xerrors.Errorf("unmarshaling signed transaction: %w", err) + } + } + + // Send the transaction + err = s.client.SendTransaction(ctx, signedTx) + + // Persist send result + var sendSuccess = err == nil + var sendError string + if err != nil { + sendError = err.Error() + } + + _, err = s.db.Exec(ctx, + `UPDATE message_sends_eth + SET send_success = $1, send_error = $2, send_time = CURRENT_TIMESTAMP + WHERE send_task_id = $3`, sendSuccess, sendError, taskID) + if err != nil { + return false, xerrors.Errorf("updating db record: %w", err) + } + + return true, nil +} + +func (s *SendTaskETH) signTransaction(ctx context.Context, fromAddress common.Address, tx *types.Transaction) (*types.Transaction, error) { + // Fetch the private key from the database + var privateKeyData []byte + err := s.db.QueryRow(ctx, + `SELECT private_key FROM eth_keys WHERE address = $1`, fromAddress.Hex()).Scan(&privateKeyData) + if err != nil { + return nil, xerrors.Errorf("fetching private key from db: %w", err) + } + + privateKey, err := crypto.ToECDSA(privateKeyData) + if err != nil { + return nil, xerrors.Errorf("converting private key: %w", err) + } + + // Get the chain ID + chainID, err := s.client.NetworkID(ctx) + if err != nil { + return nil, xerrors.Errorf("getting network ID: %w", err) + } + + // Sign the transaction + signer := types.LatestSignerForChainID(chainID) + signedTx, err := types.SignTx(tx, signer, privateKey) + if err != nil { + return nil, xerrors.Errorf("signing transaction: %w", err) + } + + return signedTx, nil +} + +func (s *SendTaskETH) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + if len(ids) == 0 { + // Should not happen + return nil, nil + } + + return &ids[0], nil +} + +func (s *SendTaskETH) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Max: taskhelp.Max(1024), + Name: "SendTransaction", + Cost: resources.Resources{ + Cpu: 0, + Gpu: 0, + Ram: 1 << 20, + }, + MaxFailures: 1000, + Follows: nil, + } +} + +func (s *SendTaskETH) Adder(taskFunc harmonytask.AddTaskFunc) { + s.sendTF.Set(taskFunc) +} + +var _ harmonytask.TaskInterface = &SendTaskETH{} +var _ = harmonytask.Reg(&SendTaskETH{}) + +// NewSenderETH creates a new SenderETH. +func NewSenderETH(client *ethclient.Client, db *harmonydb.DB) (*SenderETH, *SendTaskETH) { + st := &SendTaskETH{ + client: client, + db: db, + } + + return &SenderETH{ + client: client, + db: db, + sendTask: st, + }, st +} + +// Send sends an Ethereum transaction, coordinating nonce assignment, signing, and broadcasting. +func (s *SenderETH) Send(ctx context.Context, fromAddress common.Address, tx *types.Transaction, reason string) (common.Hash, error) { + // Ensure the transaction has zero nonce; it will be assigned during send task + if tx.Nonce() != 0 { + return common.Hash{}, xerrors.Errorf("Send expects transaction nonce to be 0, was %d", tx.Nonce()) + } + + if tx.Gas() == 0 { + // Estimate gas limit + msg := ethereum.CallMsg{ + From: fromAddress, + To: tx.To(), + Value: tx.Value(), + Data: tx.Data(), + } + + gasLimit, err := s.client.EstimateGas(ctx, msg) + if err != nil { + return common.Hash{}, fmt.Errorf("failed to estimate gas: %w", err) + } + if gasLimit == 0 { + return common.Hash{}, fmt.Errorf("estimated gas limit is zero") + } + + // Fetch current base fee + header, err := s.client.HeaderByNumber(ctx, nil) + if err != nil { + return common.Hash{}, fmt.Errorf("failed to get latest block header: %w", err) + } + + baseFee := header.BaseFee + if baseFee == nil { + return common.Hash{}, fmt.Errorf("base fee not available; network might not support EIP-1559") + } + + // Set GasTipCap (maxPriorityFeePerGas) + gasTipCap := big.NewInt(1e9) // 1 nanoFIL or 1 Gwei + // Calculate GasFeeCap (maxFeePerGas) + gasFeeCap := new(big.Int).Add(baseFee, gasTipCap) + + chainID, err := s.client.NetworkID(ctx) + if err != nil { + return common.Hash{}, xerrors.Errorf("getting network ID: %w", err) + } + + // Create a new transaction with estimated gas limit and fee caps + tx = types.NewTx(&types.DynamicFeeTx{ + ChainID: chainID, + Nonce: 0, // nonce will be set later + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + Gas: gasLimit, + To: tx.To(), + Value: tx.Value(), + Data: tx.Data(), + }) + } + + // Serialize the unsigned transaction + unsignedTxData, err := tx.MarshalBinary() + if err != nil { + return common.Hash{}, xerrors.Errorf("marshaling unsigned transaction: %w", err) + } + + unsignedHash := tx.Hash().Hex() + + // Push the task + taskAdder := s.sendTask.sendTF.Val(ctx) + + var sendTaskID *harmonytask.TaskID + taskAdder(func(id harmonytask.TaskID, txdb *harmonydb.Tx) (shouldCommit bool, seriousError error) { + _, err := txdb.Exec(`INSERT INTO message_sends_eth (from_address, to_address, send_reason, unsigned_tx, unsigned_hash, send_task_id) + VALUES ($1, $2, $3, $4, $5, $6)`, + fromAddress.Hex(), tx.To().Hex(), reason, unsignedTxData, unsignedHash, id) + if err != nil { + return false, xerrors.Errorf("inserting transaction into db: %w", err) + } + + sendTaskID = &id + + return true, nil + }) + + if sendTaskID == nil { + return common.Hash{}, xerrors.Errorf("failed to add task") + } + + // Wait for execution + var ( + pollInterval = 50 * time.Millisecond + pollIntervalMul = 2 + maxPollInterval = 5 * time.Second + pollLoops = 0 + + signedHash common.Hash + sendErr error + ) + + for { + var dbTx struct { + SignedHash *string `db:"signed_hash"` + SendSuccess *bool `db:"send_success"` + SendError *string `db:"send_error"` + } + + err := s.db.QueryRow(ctx, + `SELECT signed_hash, send_success, send_error FROM message_sends_eth WHERE send_task_id = $1`, sendTaskID).Scan( + &dbTx.SignedHash, &dbTx.SendSuccess, &dbTx.SendError) + if err != nil { + return common.Hash{}, xerrors.Errorf("getting send status for task: %w", err) + } + + if dbTx.SendSuccess == nil { + time.Sleep(pollInterval) + pollLoops++ + pollInterval *= time.Duration(pollIntervalMul) + if pollInterval > maxPollInterval { + pollInterval = maxPollInterval + } + continue + } + + if dbTx.SignedHash == nil || dbTx.SendError == nil { + return common.Hash{}, xerrors.Errorf("unexpected null values in send status") + } + + if !*dbTx.SendSuccess { + sendErr = xerrors.Errorf("send error: %s", *dbTx.SendError) + } else { + signedHash = common.HexToHash(*dbTx.SignedHash) + } + + break + } + + log.Infow("sent transaction", "hash", signedHash, "task_id", sendTaskID, "send_error", sendErr, "poll_loops", pollLoops) + + return signedHash, sendErr +} diff --git a/tasks/message/watch_eth.go b/tasks/message/watch_eth.go new file mode 100644 index 000000000..bf4acd8ce --- /dev/null +++ b/tasks/message/watch_eth.go @@ -0,0 +1,191 @@ +package message + +import ( + "context" + "encoding/json" + "errors" + "math/big" + "sync/atomic" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/harmony/harmonytask" + "github.com/filecoin-project/curio/lib/chainsched" + + types2 "github.com/filecoin-project/lotus/chain/types" +) + +type MessageWatcherEth struct { + db *harmonydb.DB + ht *harmonytask.TaskEngine + api *ethclient.Client + + stopping, stopped chan struct{} + + updateCh chan struct{} + bestBlockNumber atomic.Pointer[big.Int] +} + +func NewMessageWatcherEth(db *harmonydb.DB, ht *harmonytask.TaskEngine, pcs *chainsched.CurioChainSched, api *ethclient.Client) (*MessageWatcherEth, error) { + mw := &MessageWatcherEth{ + db: db, + ht: ht, + api: api, + stopping: make(chan struct{}), + stopped: make(chan struct{}), + updateCh: make(chan struct{}, 1), + } + go mw.run() + if err := pcs.AddHandler(mw.processHeadChange); err != nil { + return nil, err + } + return mw, nil +} + +func (mw *MessageWatcherEth) run() { + defer close(mw.stopped) + + for { + select { + case <-mw.stopping: + // TODO: cleanup assignments + return + case <-mw.updateCh: + mw.update() + } + } +} + +func (mw *MessageWatcherEth) update() { + ctx := context.Background() + + bestBlockNumber := mw.bestBlockNumber.Load() + + confirmedBlockNumber := new(big.Int).Sub(bestBlockNumber, big.NewInt(MinConfidence)) + if confirmedBlockNumber.Sign() < 0 { + // Not enough blocks yet + return + } + + machineID := mw.ht.ResourcesAvailable().MachineID + + // Assign pending transactions with null owner to ourselves + { + n, err := mw.db.Exec(ctx, `UPDATE message_waits_eth SET waiter_machine_id = $1 WHERE waiter_machine_id IS NULL AND tx_status = 'pending'`, machineID) + if err != nil { + log.Errorf("failed to assign pending transactions: %+v", err) + return + } + if n > 0 { + log.Debugw("assigned pending transactions to ourselves", "assigned", n) + } + } + + // Get transactions assigned to us + var txs []struct { + TxHash string `db:"signed_tx_hash"` + } + + err := mw.db.Select(ctx, &txs, `SELECT signed_tx_hash FROM message_waits_eth WHERE waiter_machine_id = $1 AND tx_status = 'pending' LIMIT 10000`, machineID) + if err != nil { + log.Errorf("failed to get assigned transactions: %+v", err) + return + } + + // Check if any of the transactions we have assigned are now confirmed + for _, tx := range txs { + txHash := common.HexToHash(tx.TxHash) + + receipt, err := mw.api.TransactionReceipt(ctx, txHash) + if err != nil { + if errors.Is(err, ethereum.NotFound) { + // Transaction is still pending + continue + } + log.Errorf("failed to get transaction receipt for hash %s: %+v", txHash.Hex(), err) + return + } + + // Transaction receipt found + + // Check if the transaction has enough confirmations + confirmations := new(big.Int).Sub(bestBlockNumber, receipt.BlockNumber) + if confirmations.Cmp(big.NewInt(MinConfidence)) < 0 { + // Not enough confirmations yet + continue + } + + // Get the transaction data + txData, _, err := mw.api.TransactionByHash(ctx, txHash) + if err != nil { + if errors.Is(err, ethereum.NotFound) { + log.Errorf("transaction data not found for txHash: %s", txHash.Hex()) + continue + } + log.Errorf("failed to get transaction by hash %s: %+v", txHash.Hex(), err) + return + } + + txDataJSON, err := json.Marshal(txData) + if err != nil { + log.Errorf("failed to marshal transaction data for hash %s: %+v", txHash.Hex(), err) + return + } + + receiptJSON, err := json.Marshal(receipt) + if err != nil { + log.Errorf("failed to marshal receipt data for hash %s: %+v", txHash.Hex(), err) + return + } + + txStatus := "confirmed" + txSuccess := receipt.Status == 1 + + // Update the database + _, err = mw.db.Exec(ctx, `UPDATE message_waits_eth SET + waiter_machine_id = NULL, + confirmed_block_number = $1, + confirmed_tx_hash = $2, + confirmed_tx_data = $3, + tx_status = $4, + tx_receipt = $5, + tx_success = $6 + WHERE signed_tx_hash = $7`, + receipt.BlockNumber.Int64(), + receipt.TxHash.Hex(), + txDataJSON, + txStatus, + receiptJSON, + txSuccess, + tx.TxHash, + ) + if err != nil { + log.Errorf("failed to update message wait for hash %s: %+v", txHash.Hex(), err) + return + } + } +} + +func (mw *MessageWatcherEth) Stop(ctx context.Context) error { + close(mw.stopping) + select { + case <-mw.stopped: + case <-ctx.Done(): + return ctx.Err() + } + return nil +} + +func (mw *MessageWatcherEth) processHeadChange(ctx context.Context, revert, apply *types2.TipSet) error { + if apply != nil { + mw.bestBlockNumber.Store(big.NewInt(int64(apply.Height()))) + select { + case mw.updateCh <- struct{}{}: + default: + } + } + return nil +} diff --git a/tasks/pdp/notify_task.go b/tasks/pdp/notify_task.go new file mode 100644 index 000000000..423e90864 --- /dev/null +++ b/tasks/pdp/notify_task.go @@ -0,0 +1,174 @@ +package pdp + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + logger "github.com/ipfs/go-log/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/harmony/harmonytask" + "github.com/filecoin-project/curio/harmony/resources" + "github.com/filecoin-project/curio/harmony/taskhelp" + "github.com/filecoin-project/curio/lib/passcall" +) + +var log = logger.Logger("pdp") + +type PDPNotifyTask struct { + db *harmonydb.DB +} + +func NewPDPNotifyTask(db *harmonydb.DB) *PDPNotifyTask { + return &PDPNotifyTask{db: db} +} + +func (t *PDPNotifyTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + // Fetch the pdp_piece_uploads entry associated with the taskID + var upload struct { + ID string `db:"id" json:"id"` + Service string `db:"service" json:"service"` + PieceCID *string `db:"piece_cid" json:"piece_cid"` + NotifyURL string `db:"notify_url" json:"notify_url"` + PieceRef int64 `db:"piece_ref" json:"piece_ref"` + CheckHashCodec string `db:"check_hash_codec" json:"check_hash_codec"` + CheckHash []byte `db:"check_hash" json:"check_hash"` + } + err = t.db.QueryRow(ctx, ` + SELECT id, service, piece_cid, notify_url, piece_ref, check_hash_codec, check_hash + FROM pdp_piece_uploads + WHERE notify_task_id = $1`, taskID).Scan( + &upload.ID, &upload.Service, &upload.PieceCID, &upload.NotifyURL, &upload.PieceRef, &upload.CheckHashCodec, &upload.CheckHash) + if err != nil { + return false, fmt.Errorf("failed to query pdp_piece_uploads for task %d: %w", taskID, err) + } + + // Perform HTTP Post request to the notify URL + upJson, err := json.Marshal(upload) + if err != nil { + return false, fmt.Errorf("failed to marshal upload to JSON: %w", err) + } + + log.Infow("PDP notify", "upload", upload, "task_id", taskID) + + if upload.NotifyURL != "" { + + resp, err := http.Post(upload.NotifyURL, "application/json", bytes.NewReader(upJson)) + if err != nil { + log.Errorw("HTTP POST request to notify_url failed", "notify_url", upload.NotifyURL, "upload_id", upload.ID, "error", err) + } else { + defer resp.Body.Close() + // Not reading the body as per requirement + log.Infow("HTTP GET request to notify_url succeeded", "notify_url", upload.NotifyURL, "upload_id", upload.ID) + } + } + + // Move the entry from pdp_piece_uploads to pdp_piecerefs + // Insert into pdp_piecerefs + _, err = t.db.Exec(ctx, ` + INSERT INTO pdp_piecerefs (service, piece_cid, piece_ref, created_at) + VALUES ($1, $2, $3, NOW())`, + upload.Service, upload.PieceCID, upload.PieceRef) + if err != nil { + return false, fmt.Errorf("failed to insert into pdp_piecerefs: %w", err) + } + + // Delete the entry from pdp_piece_uploads + _, err = t.db.Exec(ctx, `DELETE FROM pdp_piece_uploads WHERE id = $1`, upload.ID) + if err != nil { + return false, fmt.Errorf("failed to delete upload ID %s from pdp_piece_uploads: %w", upload.ID, err) + } + + log.Infof("Successfully processed PDP notify task %d for upload ID %s", taskID, upload.ID) + + return true, nil +} + +func (t *PDPNotifyTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + if len(ids) == 0 { + return nil, xerrors.Errorf("no task IDs provided") + } + id := ids[0] + return &id, nil +} + +func (t *PDPNotifyTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Name: "PDPNotify", + Cost: resources.Resources{ + Cpu: 1, + Ram: 128 << 20, // 128MB + }, + MaxFailures: 14, + RetryWait: taskhelp.RetryWaitExp(5*time.Second, 2), + IAmBored: passcall.Every(1*time.Minute, func(taskFunc harmonytask.AddTaskFunc) error { + return t.schedule(context.Background(), taskFunc) + }), + } +} + +func (t *PDPNotifyTask) schedule(ctx context.Context, taskFunc harmonytask.AddTaskFunc) error { + var stop bool + for !stop { + taskFunc(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + stop = true // Assume we're done unless we find more tasks to schedule + + // Query for pending notifications where: + // - piece_ref is not null + // - The piece_ref points to a parked_piece_refs entry + // - The parked_piece_refs entry points to a parked_pieces entry where complete = TRUE + // - notify_task_id is NULL + + var uploads []struct { + ID string `db:"id"` + } + + err := tx.Select(&uploads, ` + SELECT pu.id + FROM pdp_piece_uploads pu + JOIN parked_piece_refs pr ON pr.ref_id = pu.piece_ref + JOIN parked_pieces pp ON pp.id = pr.piece_id + WHERE + pu.piece_ref IS NOT NULL + AND pp.complete = TRUE + AND pu.notify_task_id IS NULL + LIMIT 1 + `) + if err != nil { + return false, xerrors.Errorf("getting uploads to notify: %w", err) + } + + if len(uploads) == 0 { + // No uploads to process + return false, nil + } + + // Update the pdp_piece_uploads entry to set notify_task_id + _, err = tx.Exec(` + UPDATE pdp_piece_uploads + SET notify_task_id = $1 + WHERE id = $2 AND notify_task_id IS NULL + `, id, uploads[0].ID) + if err != nil { + return false, xerrors.Errorf("updating notify_task_id: %w", err) + } + + stop = false // Continue scheduling as there might be more tasks + return true, nil // Commit the transaction + }) + } + return nil +} + +func (t *PDPNotifyTask) Adder(taskFunc harmonytask.AddTaskFunc) { +} + +var _ = harmonytask.Reg(&PDPNotifyTask{}) +var _ harmonytask.TaskInterface = &PDPNotifyTask{} diff --git a/tasks/pdp/proofset_addroot_watch.go b/tasks/pdp/proofset_addroot_watch.go new file mode 100644 index 000000000..c226f03b7 --- /dev/null +++ b/tasks/pdp/proofset_addroot_watch.go @@ -0,0 +1,208 @@ +package pdp + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "golang.org/x/xerrors" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/lib/chainsched" + "github.com/filecoin-project/curio/pdp/contract" + + chainTypes "github.com/filecoin-project/lotus/chain/types" +) + +// Structures to represent database records +type ProofSetRootAdd struct { + ProofSet uint64 `db:"proofset"` + AddMessageHash string `db:"add_message_hash"` +} + +// RootAddEntry represents entries from pdp_proofset_root_adds +type RootAddEntry struct { + ProofSet uint64 `db:"proofset"` + Root string `db:"root"` + AddMessageHash string `db:"add_message_hash"` + AddMessageIndex uint64 `db:"add_message_index"` + Subroot string `db:"subroot"` + SubrootOffset int64 `db:"subroot_offset"` + SubrootSize int64 `db:"subroot_size"` + PDPPieceRefID int64 `db:"pdp_pieceref"` + AddMessageOK *bool `db:"add_message_ok"` + PDPProofSetID uint64 `db:"proofset"` +} + +// NewWatcherRootAdd sets up the watcher for proof set root additions +func NewWatcherRootAdd(db *harmonydb.DB, ethClient *ethclient.Client, pcs *chainsched.CurioChainSched) { + if err := pcs.AddHandler(func(ctx context.Context, revert, apply *chainTypes.TipSet) error { + err := processPendingProofSetRootAdds(ctx, db, ethClient) + if err != nil { + log.Warnf("Failed to process pending proof set root adds: %v", err) + } + + return nil + }); err != nil { + panic(err) + } +} + +// processPendingProofSetRootAdds processes root additions that have been confirmed on-chain +func processPendingProofSetRootAdds(ctx context.Context, db *harmonydb.DB, ethClient *ethclient.Client) error { + // Query for pdp_proofset_root_adds entries where add_message_ok = TRUE + var rootAdds []ProofSetRootAdd + + err := db.Select(ctx, &rootAdds, ` + SELECT DISTINCT proofset, add_message_hash + FROM pdp_proofset_root_adds + WHERE add_message_ok = TRUE + `) + if err != nil { + return xerrors.Errorf("failed to select proof set root adds: %w", err) + } + + if len(rootAdds) == 0 { + // No pending root adds + return nil + } + + // Process each root addition + for _, rootAdd := range rootAdds { + err := processProofSetRootAdd(ctx, db, ethClient, rootAdd) + if err != nil { + log.Warnf("Failed to process root add for tx %s: %v", rootAdd.AddMessageHash, err) + continue + } + } + + return nil +} + +func processProofSetRootAdd(ctx context.Context, db *harmonydb.DB, ethClient *ethclient.Client, rootAdd ProofSetRootAdd) error { + // Retrieve the tx_receipt from message_waits_eth + var txReceiptJSON []byte + err := db.QueryRow(ctx, ` + SELECT tx_receipt + FROM message_waits_eth + WHERE signed_tx_hash = $1 + `, rootAdd.AddMessageHash).Scan(&txReceiptJSON) + if err != nil { + return xerrors.Errorf("failed to get tx_receipt for tx %s: %w", rootAdd.AddMessageHash, err) + } + + // Unmarshal the tx_receipt JSON into types.Receipt + var txReceipt types.Receipt + err = json.Unmarshal(txReceiptJSON, &txReceipt) + if err != nil { + return xerrors.Errorf("failed to unmarshal tx_receipt for tx %s: %w", rootAdd.AddMessageHash, err) + } + + // Parse the logs to extract root IDs and other data + err = extractAndInsertRootsFromReceipt(ctx, db, &txReceipt, rootAdd) + if err != nil { + return xerrors.Errorf("failed to extract roots from receipt for tx %s: %w", rootAdd.AddMessageHash, err) + } + + return nil +} + +func extractAndInsertRootsFromReceipt(ctx context.Context, db *harmonydb.DB, receipt *types.Receipt, rootAdd ProofSetRootAdd) error { + // Get the ABI from the contract metadata + pdpABI, err := contract.PDPVerifierMetaData.GetAbi() + if err != nil { + return fmt.Errorf("failed to get PDP ABI: %w", err) + } + + // Get the event definition + event, exists := pdpABI.Events["RootsAdded"] + if !exists { + return fmt.Errorf("RootsAdded event not found in ABI") + } + + var firstAdded *big.Int + eventFound := false + + // Iterate over the logs in the receipt + for _, vLog := range receipt.Logs { + // Check if the log corresponds to the RootsAdded event + if len(vLog.Topics) > 0 && vLog.Topics[0] == event.ID { + // Since 'firstAdded' is an indexed parameter, it's in Topics[1] + if len(vLog.Topics) < 2 { + return fmt.Errorf("log does not contain firstAdded topic") + } + + // Convert the topic to a big.Int + firstAdded = new(big.Int).SetBytes(vLog.Topics[1].Bytes()) + eventFound = true + // We found the event, so we can break the loop + break + } + } + + if !eventFound { + return fmt.Errorf("RootsAdded event not found in receipt") + } + + // Now we have the firstAdded rootId, proceed with database operations + + // Begin a database transaction + _, err = db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) { + // Fetch the entries from pdp_proofset_root_adds + var rootAddEntries []RootAddEntry + err := tx.Select(&rootAddEntries, ` + SELECT proofset, root, add_message_hash, add_message_index, subroot, subroot_offset, subroot_size, pdp_pieceref + FROM pdp_proofset_root_adds + WHERE proofset = $1 AND add_message_hash = $2 + ORDER BY add_message_index ASC, subroot_offset ASC + `, rootAdd.ProofSet, rootAdd.AddMessageHash) + if err != nil { + return false, fmt.Errorf("failed to select from pdp_proofset_root_adds: %w", err) + } + + // For each entry, calculate root_id and insert into pdp_proofset_roots + for _, entry := range rootAddEntries { + rootId := firstAdded.Uint64() + entry.AddMessageIndex + + // Insert into pdp_proofset_roots + _, err := tx.Exec(` + INSERT INTO pdp_proofset_roots ( + proofset, + root, + root_id, + subroot, + subroot_offset, + subroot_size, + pdp_pieceref, + add_message_hash, + add_message_index + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9 + ) + `, entry.ProofSet, entry.Root, rootId, entry.Subroot, entry.SubrootOffset, entry.SubrootSize, entry.PDPPieceRefID, entry.AddMessageHash, entry.AddMessageIndex) + if err != nil { + return false, fmt.Errorf("failed to insert into pdp_proofset_roots: %w", err) + } + } + + // Delete from pdp_proofset_root_adds + _, err = tx.Exec(` + DELETE FROM pdp_proofset_root_adds + WHERE proofset = $1 AND add_message_hash = $2 + `, rootAdd.ProofSet, rootAdd.AddMessageHash) + if err != nil { + return false, fmt.Errorf("failed to delete from pdp_proofset_root_adds: %w", err) + } + + return true, nil + }) + + if err != nil { + return fmt.Errorf("failed to process root additions in DB: %w", err) + } + + return nil +} diff --git a/tasks/pdp/proofset_create_watch.go b/tasks/pdp/proofset_create_watch.go new file mode 100644 index 000000000..8f54737ad --- /dev/null +++ b/tasks/pdp/proofset_create_watch.go @@ -0,0 +1,184 @@ +package pdp + +import ( + "context" + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "golang.org/x/xerrors" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/lib/chainsched" + "github.com/filecoin-project/curio/pdp/contract" + + chainTypes "github.com/filecoin-project/lotus/chain/types" +) + +type ProofSetCreate struct { + CreateMessageHash string `db:"create_message_hash"` + Service string `db:"service"` +} + +func NewWatcherCreate(db *harmonydb.DB, ethClient *ethclient.Client, pcs *chainsched.CurioChainSched) { + if err := pcs.AddHandler(func(ctx context.Context, revert, apply *chainTypes.TipSet) error { + err := processPendingProofSetCreates(ctx, db, ethClient) + if err != nil { + log.Warnf("Failed to process pending proof set creates: %v", err) + } + return nil + }); err != nil { + panic(err) + } +} + +func processPendingProofSetCreates(ctx context.Context, db *harmonydb.DB, ethClient *ethclient.Client) error { + // Query for pdp_proofset_creates entries where ok = TRUE and proofset_created = FALSE + var proofSetCreates []ProofSetCreate + + err := db.Select(ctx, &proofSetCreates, ` + SELECT create_message_hash, service + FROM pdp_proofset_creates + WHERE ok = TRUE AND proofset_created = FALSE + `) + if err != nil { + return xerrors.Errorf("failed to select proof set creates: %w", err) + } + + if len(proofSetCreates) == 0 { + // No pending proof set creates + return nil + } + + // Process each proof set create + for _, psc := range proofSetCreates { + err := processProofSetCreate(ctx, db, psc, ethClient) + if err != nil { + log.Warnf("Failed to process proof set create for tx %s: %v", psc.CreateMessageHash, err) + continue + } + } + + return nil +} + +func processProofSetCreate(ctx context.Context, db *harmonydb.DB, psc ProofSetCreate, ethClient *ethclient.Client) error { + // Retrieve the tx_receipt from message_waits_eth + var txReceiptJSON []byte + err := db.QueryRow(ctx, ` + SELECT tx_receipt + FROM message_waits_eth + WHERE signed_tx_hash = $1 + `, psc.CreateMessageHash).Scan(&txReceiptJSON) + if err != nil { + return xerrors.Errorf("failed to get tx_receipt for tx %s: %w", psc.CreateMessageHash, err) + } + + // Unmarshal the tx_receipt JSON into types.Receipt + var txReceipt types.Receipt + err = json.Unmarshal(txReceiptJSON, &txReceipt) + if err != nil { + return xerrors.Errorf("failed to unmarshal tx_receipt for tx %s: %w", psc.CreateMessageHash, err) + } + + // Parse the logs to extract the proofSetId + proofSetId, err := extractProofSetIdFromReceipt(&txReceipt) + if err != nil { + return xerrors.Errorf("failed to extract proofSetId from receipt for tx %s: %w", psc.CreateMessageHash, err) + } + + // Get the listener address for this proof set from the PDPVerifier contract + pdpVerifier, err := contract.NewPDPVerifier(contract.ContractAddresses().PDPVerifier, ethClient) + if err != nil { + return xerrors.Errorf("failed to instantiate PDPVerifier contract: %w", err) + } + + listenerAddr, err := pdpVerifier.GetProofSetListener(nil, big.NewInt(int64(proofSetId))) + if err != nil { + return xerrors.Errorf("failed to get listener address for proof set %d: %w", proofSetId, err) + } + + // Get the proving period from the listener + // Assumption: listener is a PDP Service with proving window informational methods + provingPeriod, challengeWindow, err := getProvingPeriodChallengeWindow(ctx, ethClient, listenerAddr) + if err != nil { + return xerrors.Errorf("failed to get max proving period: %w", err) + } + + // Insert a new entry into pdp_proof_sets + err = insertProofSet(ctx, db, psc.CreateMessageHash, proofSetId, psc.Service, provingPeriod, challengeWindow) + if err != nil { + return xerrors.Errorf("failed to insert proof set %d for tx %+v: %w", proofSetId, psc, err) + } + + // Update pdp_proofset_creates to set proofset_created = TRUE + _, err = db.Exec(ctx, ` + UPDATE pdp_proofset_creates + SET proofset_created = TRUE + WHERE create_message_hash = $1 + `, psc.CreateMessageHash) + if err != nil { + return xerrors.Errorf("failed to update proofset_creates for tx %s: %w", psc.CreateMessageHash, err) + } + + return nil +} + +func extractProofSetIdFromReceipt(receipt *types.Receipt) (uint64, error) { + pdpABI, err := contract.PDPVerifierMetaData.GetAbi() + if err != nil { + return 0, xerrors.Errorf("failed to get PDP ABI: %w", err) + } + + event, exists := pdpABI.Events["ProofSetCreated"] + if !exists { + return 0, xerrors.Errorf("ProofSetCreated event not found in ABI") + } + + for _, vLog := range receipt.Logs { + if len(vLog.Topics) > 0 && vLog.Topics[0] == event.ID { + if len(vLog.Topics) < 2 { + return 0, xerrors.Errorf("log does not contain setId topic") + } + + setIdBigInt := new(big.Int).SetBytes(vLog.Topics[1].Bytes()) + return setIdBigInt.Uint64(), nil + } + } + + return 0, xerrors.Errorf("ProofSetCreated event not found in receipt") +} + +func insertProofSet(ctx context.Context, db *harmonydb.DB, createMsg string, proofSetId uint64, service string, provingPeriod uint64, challengeWindow uint64) error { + // Implement the insertion into pdp_proof_sets table + // Adjust the SQL statement based on your table schema + _, err := db.Exec(ctx, ` + INSERT INTO pdp_proof_sets (id, create_message_hash, service, proving_period, challenge_window) + VALUES ($1, $2, $3, $4, $5) + `, proofSetId, createMsg, service, provingPeriod, challengeWindow) + return err +} + +func getProvingPeriodChallengeWindow(ctx context.Context, ethClient *ethclient.Client, listenerAddr common.Address) (uint64, uint64, error) { + // ProvingPeriod + schedule, err := contract.NewIPDPProvingSchedule(listenerAddr, ethClient) + if err != nil { + return 0, 0, xerrors.Errorf("failed to create proving schedule binding, check that listener has proving schedule methods: %w", err) + } + + period, err := schedule.GetMaxProvingPeriod(&bind.CallOpts{Context: ctx}) + if err != nil { + return 0, 0, xerrors.Errorf("failed to get proving period: %w", err) + } + + // ChallengeWindow + challengeWindow, err := schedule.ChallengeWindow(&bind.CallOpts{Context: ctx}) + if err != nil { + return 0, 0, xerrors.Errorf("failed to get challenge window: %w", err) + } + + return period, challengeWindow.Uint64(), nil +} diff --git a/tasks/pdp/task_init_pp.go b/tasks/pdp/task_init_pp.go new file mode 100644 index 000000000..1dcfbc4b7 --- /dev/null +++ b/tasks/pdp/task_init_pp.go @@ -0,0 +1,243 @@ +package pdp + +import ( + "context" + "database/sql" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "golang.org/x/xerrors" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/harmony/harmonytask" + "github.com/filecoin-project/curio/harmony/resources" + "github.com/filecoin-project/curio/lib/chainsched" + "github.com/filecoin-project/curio/lib/promise" + "github.com/filecoin-project/curio/pdp/contract" + "github.com/filecoin-project/curio/tasks/message" + + chainTypes "github.com/filecoin-project/lotus/chain/types" +) + +type InitProvingPeriodTask struct { + db *harmonydb.DB + ethClient *ethclient.Client + sender *message.SenderETH + + fil NextProvingPeriodTaskChainApi + + addFunc promise.Promise[harmonytask.AddTaskFunc] +} + +type InitProvingPeriodTaskChainApi interface { + ChainHead(context.Context) (*chainTypes.TipSet, error) +} + +func NewInitProvingPeriodTask(db *harmonydb.DB, ethClient *ethclient.Client, fil NextProvingPeriodTaskChainApi, chainSched *chainsched.CurioChainSched, sender *message.SenderETH) *InitProvingPeriodTask { + ipp := &InitProvingPeriodTask{ + db: db, + ethClient: ethClient, + sender: sender, + fil: fil, + } + + _ = chainSched.AddHandler(func(ctx context.Context, revert, apply *chainTypes.TipSet) error { + if apply == nil { + return nil + } + + // Now query the db for proof sets needing nextProvingPeriod inital call + var toCallInit []struct { + ProofSetID int64 `db:"id"` + } + + err := db.Select(ctx, &toCallInit, ` + SELECT id + FROM pdp_proof_sets + WHERE challenge_request_task_id IS NULL + AND init_ready AND prove_at_epoch IS NULL + `) + if err != nil && err != sql.ErrNoRows { + return xerrors.Errorf("failed to select proof sets needing nextProvingPeriod: %w", err) + } + + for _, ps := range toCallInit { + ipp.addFunc.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + // Update pdp_proof_sets to set challenge_request_task_id = id + affected, err := tx.Exec(` + UPDATE pdp_proof_sets + SET challenge_request_task_id = $1 + WHERE id = $2 AND challenge_request_task_id IS NULL + `, id, ps.ProofSetID) + if err != nil { + return false, xerrors.Errorf("failed to update pdp_proof_sets: %w", err) + } + if affected == 0 { + // Someone else might have already scheduled the task + return false, nil + } + + return true, nil + }) + } + + return nil + }) + + return ipp +} + +func (ipp *InitProvingPeriodTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + // Select the proof set where challenge_request_task_id = taskID + var proofSetID int64 + + err = ipp.db.QueryRow(ctx, ` + SELECT id + FROM pdp_proof_sets + WHERE challenge_request_task_id = $1 + `, taskID).Scan(&proofSetID) + if err == sql.ErrNoRows { + // No matching proof set, task is done (something weird happened, and e.g another task was spawned in place of this one) + return true, nil + } + if err != nil { + return false, xerrors.Errorf("failed to query pdp_proof_sets: %w", err) + } + + // Get the listener address for this proof set from the PDPVerifier contract + pdpVerifier, err := contract.NewPDPVerifier(contract.ContractAddresses().PDPVerifier, ipp.ethClient) + if err != nil { + return false, xerrors.Errorf("failed to instantiate PDPVerifier contract: %w", err) + } + + listenerAddr, err := pdpVerifier.GetProofSetListener(nil, big.NewInt(proofSetID)) + if err != nil { + return false, xerrors.Errorf("failed to get listener address for proof set %d: %w", proofSetID, err) + } + + // Determine the next challenge window start by consulting the listener + provingSchedule, err := contract.NewIPDPProvingSchedule(listenerAddr, ipp.ethClient) + if err != nil { + return false, xerrors.Errorf("failed to create proving schedule binding, check that listener has proving schedule methods: %w", err) + } + + // ChallengeWindow + challengeWindow, err := provingSchedule.ChallengeWindow(&bind.CallOpts{Context: ctx}) + if err != nil { + return false, xerrors.Errorf("failed to get challenge window: %w", err) + } + + init_prove_at, err := provingSchedule.InitChallengeWindowStart(&bind.CallOpts{Context: ctx}) + if err != nil { + return false, xerrors.Errorf("failed to get next challenge window start: %w", err) + } + init_prove_at = init_prove_at.Add(init_prove_at, challengeWindow.Div(challengeWindow, big.NewInt(2))) // Give a buffer of 1/2 challenge window epochs so that we are still within challenge window + // Instantiate the PDPVerifier contract + pdpContracts := contract.ContractAddresses() + pdpVeriferAddress := pdpContracts.PDPVerifier + + // Prepare the transaction data + abiData, err := contract.PDPVerifierMetaData.GetAbi() + if err != nil { + return false, xerrors.Errorf("failed to get PDPVerifier ABI: %w", err) + } + + data, err := abiData.Pack("nextProvingPeriod", big.NewInt(proofSetID), init_prove_at, []byte{}) + if err != nil { + return false, xerrors.Errorf("failed to pack data: %w", err) + } + + // Prepare the transaction + txEth := types.NewTransaction( + 0, // nonce (will be set by sender) + pdpVeriferAddress, // to + big.NewInt(0), // value + 0, // gasLimit (to be estimated) + nil, // gasPrice (to be set by sender) + data, // data + ) + + if !stillOwned() { + // Task was abandoned, don't send the transaction + return false, nil + } + + fromAddress, _, err := pdpVerifier.GetProofSetOwner(nil, big.NewInt(proofSetID)) + if err != nil { + return false, xerrors.Errorf("failed to get default sender address: %w", err) + } + + // Get the current tipset + ts, err := ipp.fil.ChainHead(ctx) + if err != nil { + return false, xerrors.Errorf("failed to get chain head: %w", err) + } + + // Send the transaction + reason := "pdp-proving-init" + txHash, err := ipp.sender.Send(ctx, fromAddress, txEth, reason) + if err != nil { + return false, xerrors.Errorf("failed to send transaction: %w", err) + } + + // Update the database in a transaction + _, err = ipp.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) { + // Update pdp_proof_sets + affected, err := tx.Exec(` + UPDATE pdp_proof_sets + SET challenge_request_msg_hash = $1, + prev_challenge_request_epoch = $2, + prove_at_epoch = $3 + WHERE id = $4 + `, txHash.Hex(), ts.Height(), init_prove_at.Uint64(), proofSetID) + if err != nil { + return false, xerrors.Errorf("failed to update pdp_proof_sets: %w", err) + } + if affected == 0 { + return false, xerrors.Errorf("pdp_proof_sets update affected 0 rows") + } + + // Insert into message_waits_eth + _, err = tx.Exec(` + INSERT INTO message_waits_eth (signed_tx_hash, tx_status) + VALUES ($1, 'pending') ON CONFLICT DO NOTHING + `, txHash.Hex()) + if err != nil { + return false, xerrors.Errorf("failed to insert into message_waits_eth: %w", err) + } + + return true, nil + }) + if err != nil { + return false, xerrors.Errorf("failed to perform database transaction: %w", err) + } + + // Task completed successfully + return true, nil +} + +func (ipp *InitProvingPeriodTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + id := ids[0] + return &id, nil +} + +func (ipp *InitProvingPeriodTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Name: "PDPInitPP", + Cost: resources.Resources{ + Cpu: 0, + Gpu: 0, + Ram: 1 << 20, + }, + } +} + +func (ipp *InitProvingPeriodTask) Adder(taskFunc harmonytask.AddTaskFunc) { + ipp.addFunc.Set(taskFunc) +} + +var _ = harmonytask.Reg(&InitProvingPeriodTask{}) diff --git a/tasks/pdp/task_next_pp.go b/tasks/pdp/task_next_pp.go new file mode 100644 index 000000000..676b579a2 --- /dev/null +++ b/tasks/pdp/task_next_pp.go @@ -0,0 +1,236 @@ +package pdp + +import ( + "context" + "database/sql" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "golang.org/x/xerrors" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/harmony/harmonytask" + "github.com/filecoin-project/curio/harmony/resources" + "github.com/filecoin-project/curio/lib/chainsched" + "github.com/filecoin-project/curio/lib/promise" + "github.com/filecoin-project/curio/pdp/contract" + "github.com/filecoin-project/curio/tasks/message" + + chainTypes "github.com/filecoin-project/lotus/chain/types" +) + +type NextProvingPeriodTask struct { + db *harmonydb.DB + ethClient *ethclient.Client + sender *message.SenderETH + + fil NextProvingPeriodTaskChainApi + + addFunc promise.Promise[harmonytask.AddTaskFunc] +} + +type NextProvingPeriodTaskChainApi interface { + ChainHead(context.Context) (*chainTypes.TipSet, error) +} + +func NewNextProvingPeriodTask(db *harmonydb.DB, ethClient *ethclient.Client, fil NextProvingPeriodTaskChainApi, chainSched *chainsched.CurioChainSched, sender *message.SenderETH) *NextProvingPeriodTask { + n := &NextProvingPeriodTask{ + db: db, + ethClient: ethClient, + sender: sender, + fil: fil, + } + + _ = chainSched.AddHandler(func(ctx context.Context, revert, apply *chainTypes.TipSet) error { + if apply == nil { + return nil + } + + // Now query the db for proof sets needing nextProvingPeriod + var toCallNext []struct { + ProofSetID int64 `db:"id"` + } + + err := db.Select(ctx, &toCallNext, ` + SELECT id + FROM pdp_proof_sets + WHERE challenge_request_task_id IS NULL + AND (prove_at_epoch + challenge_window) <= $1 + `, apply.Height()) + if err != nil && err != sql.ErrNoRows { + return xerrors.Errorf("failed to select proof sets needing nextProvingPeriod: %w", err) + } + + for _, ps := range toCallNext { + n.addFunc.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + // Update pdp_proof_sets to set challenge_request_task_id = id + affected, err := tx.Exec(` + UPDATE pdp_proof_sets + SET challenge_request_task_id = $1 + WHERE id = $2 AND challenge_request_task_id IS NULL + `, id, ps.ProofSetID) + if err != nil { + return false, xerrors.Errorf("failed to update pdp_proof_sets: %w", err) + } + if affected == 0 { + // Someone else might have already scheduled the task + return false, nil + } + + return true, nil + }) + } + + return nil + }) + + return n +} + +func (n *NextProvingPeriodTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + // Select the proof set where challenge_request_task_id = taskID + var proofSetID int64 + + err = n.db.QueryRow(ctx, ` + SELECT id + FROM pdp_proof_sets + WHERE challenge_request_task_id = $1 AND prove_at_epoch IS NOT NULL + `, taskID).Scan(&proofSetID) + if err == sql.ErrNoRows { + // No matching proof set, task is done (something weird happened, and e.g another task was spawned in place of this one) + return true, nil + } + if err != nil { + return false, xerrors.Errorf("failed to query pdp_proof_sets: %w", err) + } + + // Get the listener address for this proof set from the PDPVerifier contract + pdpVerifier, err := contract.NewPDPVerifier(contract.ContractAddresses().PDPVerifier, n.ethClient) + if err != nil { + return false, xerrors.Errorf("failed to instantiate PDPVerifier contract: %w", err) + } + + listenerAddr, err := pdpVerifier.GetProofSetListener(nil, big.NewInt(proofSetID)) + if err != nil { + return false, xerrors.Errorf("failed to get listener address for proof set %d: %w", proofSetID, err) + } + + // Determine the next challenge window start by consulting the listener + provingSchedule, err := contract.NewIPDPProvingSchedule(listenerAddr, n.ethClient) + if err != nil { + return false, xerrors.Errorf("failed to create proving schedule binding, check that listener has proving schedule methods: %w", err) + } + next_prove_at, err := provingSchedule.NextChallengeWindowStart(nil, big.NewInt(proofSetID)) + if err != nil { + return false, xerrors.Errorf("failed to get next challenge window start: %w", err) + } + + // Instantiate the PDPVerifier contract + pdpContracts := contract.ContractAddresses() + pdpVerifierAddress := pdpContracts.PDPVerifier + + // Prepare the transaction data + abiData, err := contract.PDPVerifierMetaData.GetAbi() + if err != nil { + return false, xerrors.Errorf("failed to get PDPVerifier ABI: %w", err) + } + + data, err := abiData.Pack("nextProvingPeriod", big.NewInt(proofSetID), next_prove_at, []byte{}) + if err != nil { + return false, xerrors.Errorf("failed to pack data: %w", err) + } + + // Prepare the transaction + txEth := types.NewTransaction( + 0, // nonce (will be set by sender) + pdpVerifierAddress, // to + big.NewInt(0), // value + 0, // gasLimit (to be estimated) + nil, // gasPrice (to be set by sender) + data, // data + ) + + if !stillOwned() { + // Task was abandoned, don't send the transaction + return false, nil + } + + fromAddress, _, err := pdpVerifier.GetProofSetOwner(nil, big.NewInt(proofSetID)) + if err != nil { + return false, xerrors.Errorf("failed to get default sender address: %w", err) + } + + // Get the current tipset + ts, err := n.fil.ChainHead(ctx) + if err != nil { + return false, xerrors.Errorf("failed to get chain head: %w", err) + } + + // Send the transaction + reason := "pdp-proving-period" + txHash, err := n.sender.Send(ctx, fromAddress, txEth, reason) + if err != nil { + return false, xerrors.Errorf("failed to send transaction: %w", err) + } + + // Update the database in a transaction + _, err = n.db.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) { + // Update pdp_proof_sets + affected, err := tx.Exec(` + UPDATE pdp_proof_sets + SET challenge_request_msg_hash = $1, + prev_challenge_request_epoch = $2, + prove_at_epoch = $3 + WHERE id = $4 + `, txHash.Hex(), ts.Height(), next_prove_at.Uint64(), proofSetID) + if err != nil { + return false, xerrors.Errorf("failed to update pdp_proof_sets: %w", err) + } + if affected == 0 { + return false, xerrors.Errorf("pdp_proof_sets update affected 0 rows") + } + + // Insert into message_waits_eth + _, err = tx.Exec(` + INSERT INTO message_waits_eth (signed_tx_hash, tx_status) + VALUES ($1, 'pending') ON CONFLICT DO NOTHING + `, txHash.Hex()) + if err != nil { + return false, xerrors.Errorf("failed to insert into message_waits_eth: %w", err) + } + + return true, nil + }) + if err != nil { + return false, xerrors.Errorf("failed to perform database transaction: %w", err) + } + + // Task completed successfully + log.Infow("Next challenge window scheduled", "epoch", next_prove_at) + + return true, nil +} + +func (n *NextProvingPeriodTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask.TaskEngine) (*harmonytask.TaskID, error) { + id := ids[0] + return &id, nil +} + +func (n *NextProvingPeriodTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Name: "PDPProvingPeriod", + Cost: resources.Resources{ + Cpu: 0, + Gpu: 0, + Ram: 1 << 20, + }, + } +} + +func (n *NextProvingPeriodTask) Adder(taskFunc harmonytask.AddTaskFunc) { + n.addFunc.Set(taskFunc) +} + +var _ = harmonytask.Reg(&NextProvingPeriodTask{}) diff --git a/tasks/pdp/task_prove.go b/tasks/pdp/task_prove.go new file mode 100644 index 000000000..382fe519c --- /dev/null +++ b/tasks/pdp/task_prove.go @@ -0,0 +1,764 @@ +package pdp + +import ( + "context" + "database/sql" + "encoding/binary" + "encoding/hex" + "errors" + "io" + "math/big" + "math/bits" + "sort" + "sync/atomic" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ipfs/go-cid" + pool "github.com/libp2p/go-buffer-pool" + "github.com/minio/sha256-simd" + "github.com/samber/lo" + "golang.org/x/crypto/sha3" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-commp-utils/zerocomm" + commcid "github.com/filecoin-project/go-fil-commcid" + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/curio/harmony/harmonydb" + "github.com/filecoin-project/curio/harmony/harmonytask" + "github.com/filecoin-project/curio/harmony/resources" + "github.com/filecoin-project/curio/lib/cachedreader" + "github.com/filecoin-project/curio/lib/chainsched" + "github.com/filecoin-project/curio/lib/promise" + "github.com/filecoin-project/curio/lib/proof" + "github.com/filecoin-project/curio/pdp/contract" + "github.com/filecoin-project/curio/tasks/message" + + chainTypes "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/storage/pipeline/lib/nullreader" +) + +const LeafSize = proof.NODE_SIZE + +type ProveTask struct { + db *harmonydb.DB + ethClient *ethclient.Client + sender *message.SenderETH + cpr *cachedreader.CachedPieceReader + fil ProveTaskChainApi + + head atomic.Pointer[chainTypes.TipSet] + + addFunc promise.Promise[harmonytask.AddTaskFunc] +} + +type ProveTaskChainApi interface { + StateGetRandomnessDigestFromBeacon(ctx context.Context, randEpoch abi.ChainEpoch, tsk chainTypes.TipSetKey) (abi.Randomness, error) //perm:read + ChainHead(context.Context) (*chainTypes.TipSet, error) //perm:read +} + +func NewProveTask(chainSched *chainsched.CurioChainSched, db *harmonydb.DB, ethClient *ethclient.Client, fil ProveTaskChainApi, sender *message.SenderETH, cpr *cachedreader.CachedPieceReader) *ProveTask { + pt := &ProveTask{ + db: db, + ethClient: ethClient, + sender: sender, + cpr: cpr, + fil: fil, + } + + // ProveTasks are created on pdp_proof_sets entries where + // challenge_request_msg_hash is not null (=not yet landed) + + err := chainSched.AddHandler(func(ctx context.Context, revert, apply *chainTypes.TipSet) error { + if apply == nil { + return nil + } + + pt.head.Store(apply) + + for { + more := false + + pt.addFunc.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, seriousError error) { + // Select proof sets ready for proving + var proofSets []struct { + ID int64 `db:"id"` + } + + err := tx.Select(&proofSets, ` + SELECT p.id + FROM pdp_proof_sets p + INNER JOIN message_waits_eth mw on mw.signed_tx_hash = p.challenge_request_msg_hash + WHERE p.challenge_request_msg_hash IS NOT NULL AND mw.tx_success = TRUE AND p.prove_at_epoch < $1 + LIMIT 2 + `, apply.Height()) + if err != nil { + return false, xerrors.Errorf("failed to select proof sets: %w", err) + } + + if len(proofSets) == 0 { + // No proof sets to process + return false, nil + } + + // Determine if there might be more proof sets to process + more = len(proofSets) == 2 + + // Process the first proof set + todo := proofSets[0] + + // Insert a new task into pdp_prove_tasks + affected, err := tx.Exec(` + INSERT INTO pdp_prove_tasks (proofset, task_id) + VALUES ($1, $2) ON CONFLICT DO NOTHING + `, todo.ID, id) + if err != nil { + return false, xerrors.Errorf("failed to insert into pdp_prove_tasks: %w", err) + } + if affected == 0 { + return false, nil + } + + // Update pdp_proof_sets to set next_challenge_possible = FALSE + affected, err = tx.Exec(` + UPDATE pdp_proof_sets + SET challenge_request_msg_hash = NULL + WHERE id = $1 AND challenge_request_msg_hash IS NOT NULL + `, todo.ID) + if err != nil { + return false, xerrors.Errorf("failed to update pdp_proof_sets: %w", err) + } + if affected == 0 { + more = false + return false, nil + } + + return true, nil + }) + + if !more { + break + } + } + + return nil + }) + if err != nil { + // Handler registration failed + panic(err) + } + + return pt +} + +func (p *ProveTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { + ctx := context.Background() + + // Retrieve proof set and challenge epoch for the task + var proofSetID int64 + + err = p.db.QueryRow(context.Background(), ` + SELECT proofset + FROM pdp_prove_tasks + WHERE task_id = $1 + `, taskID).Scan(&proofSetID) + if err != nil { + return false, xerrors.Errorf("failed to get task details: %w", err) + } + + pdpContracts := contract.ContractAddresses() + pdpVerifierAddress := pdpContracts.PDPVerifier + + pdpVerifier, err := contract.NewPDPVerifier(pdpVerifierAddress, p.ethClient) + if err != nil { + return false, xerrors.Errorf("failed to instantiate PDPVerifier contract at %s: %w", pdpVerifierAddress.Hex(), err) + } + + callOpts := &bind.CallOpts{ + Context: ctx, + } + + // Proof parameters + challengeEpoch, err := pdpVerifier.GetNextChallengeEpoch(callOpts, big.NewInt(proofSetID)) + if err != nil { + return false, xerrors.Errorf("failed to get next challenge epoch: %w", err) + } + + seed, err := p.fil.StateGetRandomnessDigestFromBeacon(ctx, abi.ChainEpoch(challengeEpoch.Int64()), chainTypes.EmptyTSK) + if err != nil { + return false, xerrors.Errorf("failed to get chain randomness from beacon for pdp prove: %w", err) + } + + proofs, err := p.GenerateProofs(ctx, pdpVerifier, proofSetID, seed, contract.NumChallenges) + if err != nil { + return false, xerrors.Errorf("failed to generate proofs: %w", err) + } + + abiData, err := contract.PDPVerifierMetaData.GetAbi() + if err != nil { + return false, xerrors.Errorf("failed to get PDPVerifier ABI: %w", err) + } + + data, err := abiData.Pack("provePossession", big.NewInt(proofSetID), proofs) + if err != nil { + return false, xerrors.Errorf("failed to pack data: %w", err) + } + + // [ ["0x559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e", ["0x559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e"]] ] + + /* { + // format proofs for logging + var proofStr string = "[ [\"0x" + proofStr += hex.EncodeToString(proofs[0].Leaf[:]) + proofStr += "\", [" + for i, proof := range proofs[0].Proof { + if i > 0 { + proofStr += ", " + } + proofStr += "\"0x" + proofStr += hex.EncodeToString(proof[:]) + proofStr += "\"" + } + + proofStr += "] ] ]" + + log.Infof("PDP Prove Task: proofSetID: %d, taskID: %d, proofs: %s", proofSetID, taskID, proofStr) + } */ + + + // If gas used is 0 fee is maximized + gasFee := big.NewInt(0) + proofFee, err := pdpVerifier.CalculateProofFee(callOpts, big.NewInt(proofSetID), gasFee) + if err != nil { + return false, xerrors.Errorf("failed to calculate proof fee: %w", err) + } + + // Add 2x buffer for certainty + proofFee = new(big.Int).Mul(proofFee, big.NewInt(3)) + + fromAddress, err := p.getSenderAddress(ctx) + if err != nil { + return false, xerrors.Errorf("failed to get sender address: %w", err) + } + + // Prepare the transaction (nonce will be set to 0, SenderETH will assign it) + txEth := types.NewTransaction( + 0, + pdpVerifierAddress, + proofFee, + 0, + nil, + data, + ) + + log.Infow("PDP Prove Task", + "proofSetID", proofSetID, + "taskID", taskID, + "proofs", proofs, + "data", hex.EncodeToString(data), + "gasFeeEstimate", gasFee, + "proofFee initial", proofFee.Div(proofFee, big.NewInt(3)), + "proofFee 3x", proofFee, + "txEth", txEth, + ) + + if !stillOwned() { + // Task was abandoned, don't send the proofs + return false, nil + } + + reason := "pdp-prove" + txHash, err := p.sender.Send(ctx, fromAddress, txEth, reason) + if err != nil { + 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 + return true, nil +} + +func (p *ProveTask) GenerateProofs(ctx context.Context, pdpService *contract.PDPVerifier, proofSetID int64, seed abi.Randomness, numChallenges int) ([]contract.PDPVerifierProof, error) { + proofs := make([]contract.PDPVerifierProof, numChallenges) + + callOpts := &bind.CallOpts{ + Context: ctx, + } + + totalLeafCount, err := pdpService.GetChallengeRange(callOpts, big.NewInt(proofSetID)) + if err != nil { + return nil, xerrors.Errorf("failed to get proof set leaf count: %w", err) + } + totalLeaves := totalLeafCount.Uint64() + + challenges := lo.Times(numChallenges, func(i int) int64 { + return generateChallengeIndex(seed, proofSetID, i, totalLeaves) + }) + + rootId, err := pdpService.FindRootIds(callOpts, big.NewInt(proofSetID), lo.Map(challenges, func(i int64, _ int) *big.Int { return big.NewInt(i) })) + if err != nil { + return nil, xerrors.Errorf("failed to find root IDs: %w", err) + } + + for i := 0; i < numChallenges; i++ { + root := rootId[i] + + proof, err := p.proveRoot(ctx, proofSetID, root.RootId.Int64(), root.Offset.Int64()) + if err != nil { + return nil, xerrors.Errorf("failed to prove root %d (%d, %d, %d): %w", i, proofSetID, root.RootId.Int64(), root.Offset.Int64(), err) + } + + proofs[i] = proof + } + + return proofs, nil +} + +func generateChallengeIndex(seed abi.Randomness, proofSetID int64, proofIndex int, totalLeaves uint64) int64 { + // Create a buffer to hold the concatenated data (96 bytes: 32 bytes * 3) + data := make([]byte, 0, 96) + + // Seed is a 32-byte big-endian representation + + data = append(data, seed...) + + // Convert proofSetID to 32-byte big-endian representation + proofSetIDBigInt := big.NewInt(proofSetID) + proofSetIDBytes := padTo32Bytes(proofSetIDBigInt.Bytes()) + data = append(data, proofSetIDBytes...) + + // Convert proofIndex to 8-byte big-endian representation + proofIndexBytes := make([]byte, 8) + binary.BigEndian.PutUint64(proofIndexBytes, uint64(proofIndex)) + data = append(data, proofIndexBytes...) + + // Compute the Keccak-256 hash + hash := sha3.NewLegacyKeccak256() + hash.Write(data) + hashBytes := hash.Sum(nil) + + // Convert hash to big.Int + hashInt := new(big.Int).SetBytes(hashBytes) + + // Compute challenge index + totalLeavesBigInt := new(big.Int).SetUint64(totalLeaves) + challengeIndex := new(big.Int).Mod(hashInt, totalLeavesBigInt) + + // Log for debugging + log.Debugw("generateChallengeIndex", + "seed", seed, + "proofSetID", proofSetID, + "proofIndex", proofIndex, + "totalLeaves", totalLeaves, + "data", hex.EncodeToString(data), + "hash", hex.EncodeToString(hashBytes), + "hashInt", hashInt, + "totalLeavesBigInt", totalLeavesBigInt, + "challengeIndex", challengeIndex, + ) + + return challengeIndex.Int64() +} + +// padTo32Bytes pads the input byte slice to 32 bytes with leading zeros +func padTo32Bytes(b []byte) []byte { + padded := make([]byte, 32) + copy(padded[32-len(b):], b) + return padded +} + +func (p *ProveTask) genSubrootMemtree(ctx context.Context, subrootCid string, subrootSize abi.PaddedPieceSize) ([]byte, error) { + subrootCidObj, err := cid.Parse(subrootCid) + if err != nil { + return nil, xerrors.Errorf("failed to parse subroot CID: %w", err) + } + + if subrootSize > proof.MaxMemtreeSize { + return nil, xerrors.Errorf("subroot size exceeds maximum: %d", subrootSize) + } + + subrootReader, unssize, err := p.cpr.GetSharedPieceReader(ctx, subrootCidObj) + if err != nil { + return nil, xerrors.Errorf("failed to get subroot reader: %w", err) + } + + var r io.Reader = subrootReader + + if unssize.Padded() > subrootSize { + return nil, xerrors.Errorf("subroot size mismatch: %d > %d", unssize.Padded(), subrootSize) + } else if unssize.Padded() < subrootSize { + // pad with zeros + r = io.MultiReader(r, nullreader.NewNullReader(abi.UnpaddedPieceSize(subrootSize-unssize.Padded()))) + } + + defer subrootReader.Close() + + return proof.BuildSha254Memtree(r, subrootSize.Unpadded()) +} + +func (p *ProveTask) proveRoot(ctx context.Context, proofSetID int64, rootId int64, challengedLeaf int64) (contract.PDPVerifierProof, error) { + const arity = 2 + + rootChallengeOffset := challengedLeaf * LeafSize + + // Retrieve the root and subroot + type subrootMeta struct { + Root string `db:"root"` + Subroot string `db:"subroot"` + SubrootOffset int64 `db:"subroot_offset"` // padded offset + SubrootSize int64 `db:"subroot_size"` // padded piece size + } + + var subroots []subrootMeta + + err := p.db.Select(context.Background(), &subroots, ` + SELECT root, subroot, subroot_offset, subroot_size + FROM pdp_proofset_roots + WHERE proofset = $1 AND root_id = $2 + ORDER BY subroot_offset ASC + `, proofSetID, rootId) + if err != nil { + return contract.PDPVerifierProof{}, xerrors.Errorf("failed to get root and subroot: %w", err) + } + + // find first subroot with subroot_offset >= rootChallengeOffset + challSubRoot, challSubrootIdx, ok := lo.FindLastIndexOf(subroots, func(subroot subrootMeta) bool { + return subroot.SubrootOffset < rootChallengeOffset + }) + if !ok { + return contract.PDPVerifierProof{}, xerrors.New("no subroot found") + } + + // build subroot memtree + memtree, err := p.genSubrootMemtree(ctx, challSubRoot.Subroot, abi.PaddedPieceSize(challSubRoot.SubrootSize)) + if err != nil { + return contract.PDPVerifierProof{}, xerrors.Errorf("failed to generate subroot memtree: %w", err) + } + + subrootChallengedLeaf := challengedLeaf - (challSubRoot.SubrootOffset / LeafSize) + log.Debugw("subrootChallengedLeaf", "subrootChallengedLeaf", subrootChallengedLeaf, "challengedLeaf", challengedLeaf, "subrootOffsetLs", challSubRoot.SubrootOffset/LeafSize) + + /* + type RawMerkleProof struct { + Leaf [32]byte + Proof [][32]byte + Root [32]byte + } + */ + subrootProof, err := proof.MemtreeProof(memtree, subrootChallengedLeaf) + pool.Put(memtree) + if err != nil { + return contract.PDPVerifierProof{}, xerrors.Errorf("failed to generate subroot proof: %w", err) + } + log.Debugw("subrootProof", "subrootProof", subrootProof) + + // build partial top-tree + type treeElem struct { + Level int // 1 == leaf, NODE_SIZE + Hash [LeafSize]byte + } + type elemIndex struct { + Level int + ElemOffset int64 // offset in terms of nodes at the current level + } + + partialTree := map[elemIndex]treeElem{} + var subrootsSize abi.PaddedPieceSize + + // 1. prefill the partial tree + for _, subroot := range subroots { + subrootsSize += abi.PaddedPieceSize(subroot.SubrootSize) + + unsCid, err := cid.Parse(subroot.Subroot) + if err != nil { + return contract.PDPVerifierProof{}, xerrors.Errorf("failed to parse subroot CID: %w", err) + } + + commp, err := commcid.CIDToPieceCommitmentV1(unsCid) + if err != nil { + return contract.PDPVerifierProof{}, xerrors.Errorf("failed to convert CID to piece commitment: %w", err) + } + + var comm [LeafSize]byte + copy(comm[:], commp) + + level := proof.NodeLevel(subroot.SubrootSize/LeafSize, arity) + offset := (subroot.SubrootOffset / LeafSize) >> uint(level-1) + partialTree[elemIndex{Level: level, ElemOffset: offset}] = treeElem{ + Level: level, + Hash: comm, + } + } + + rootSize := nextPowerOfTwo(subrootsSize) + rootLevel := proof.NodeLevel(int64(rootSize/LeafSize), arity) + + // 2. build the partial tree + // we do the build from the right side of the tree - elements are sorted by size, so only elements on the right side can have missing siblings + + isRight := func(offset int64) bool { + return offset&1 == 1 + } + + for i := len(subroots) - 1; i >= 0; i-- { + subroot := subroots[i] + level := proof.NodeLevel(subroot.SubrootSize/LeafSize, arity) + offset := (subroot.SubrootOffset / LeafSize) >> uint(level-1) + firstSubroot := i == 0 + + curElem := partialTree[elemIndex{Level: level, ElemOffset: offset}] + + log.Debugw("processing partialtree subroot", "curElem", curElem, "level", level, "offset", offset, "subroot", subroot.SubrootOffset, "subrootSz", subroot.SubrootSize) + + for !isRight(offset) { + // find the rightSibling + siblingIndex := elemIndex{Level: level, ElemOffset: offset + 1} + rightSibling, ok := partialTree[siblingIndex] + if !ok { + // if we're processing the first subroot branch, AND we've ran out of right siblings, we're done + if firstSubroot { + break + } + + // create a zero rightSibling + rightSibling = treeElem{ + Level: level, + Hash: zerocomm.PieceComms[level-zerocomm.Skip-1], + } + log.Debugw("rightSibling zero", "rightSibling", rightSibling, "siblingIndex", siblingIndex, "level", level, "offset", offset) + partialTree[siblingIndex] = rightSibling + } + + // compute the parent + parent := proof.ComputeBinShaParent(curElem.Hash, rightSibling.Hash) + parentLevel := level + 1 + parentOffset := offset / arity + + partialTree[elemIndex{Level: parentLevel, ElemOffset: parentOffset}] = treeElem{ + Level: parentLevel, + Hash: parent, + } + + // move to the parent + level = parentLevel + offset = parentOffset + } + } + + { + var partialTreeList []elemIndex + for k := range partialTree { + partialTreeList = append(partialTreeList, k) + } + sort.Slice(partialTreeList, func(i, j int) bool { + if partialTreeList[i].Level != partialTreeList[j].Level { + return partialTreeList[i].Level < partialTreeList[j].Level + } + return partialTreeList[i].ElemOffset < partialTreeList[j].ElemOffset + }) + log.Debugw("partialTree", "partialTree", partialTreeList) + } + + challLevel := proof.NodeLevel(challSubRoot.SubrootSize/LeafSize, arity) + challOffset := (challSubRoot.SubrootOffset / LeafSize) >> uint(challLevel-1) + + log.Debugw("challSubRoot", "challSubRoot", challSubrootIdx, "challLevel", challLevel, "challOffset", challOffset) + + challSubtreeLeaf := partialTree[elemIndex{Level: challLevel, ElemOffset: challOffset}] + if challSubtreeLeaf.Hash != subrootProof.Root { + return contract.PDPVerifierProof{}, xerrors.Errorf("subtree root doesn't match partial tree leaf, %x != %x", challSubtreeLeaf.Hash, subrootProof.Root) + } + + var out contract.PDPVerifierProof + copy(out.Leaf[:], subrootProof.Leaf[:]) + out.Proof = append(out.Proof, subrootProof.Proof...) + + currentLevel := challLevel + currentOffset := challOffset + + for currentLevel < rootLevel { + siblingOffset := currentOffset ^ 1 + + // Retrieve sibling hash from partialTree or use zero hash + siblingIndex := elemIndex{Level: currentLevel, ElemOffset: siblingOffset} + siblingElem, ok := partialTree[siblingIndex] + if !ok { + return contract.PDPVerifierProof{}, xerrors.Errorf("missing sibling at level %d, offset %d", currentLevel, siblingOffset) + } + + log.Debugw("siblingElem", "siblingElem", siblingElem, "siblingIndex", siblingIndex, "currentLevel", currentLevel, "currentOffset", currentOffset, "siblingOffset", siblingOffset) + + // Append the sibling's hash to the proof + out.Proof = append(out.Proof, siblingElem.Hash) + + // Move up to the parent node + currentOffset = currentOffset / arity + currentLevel++ + } + + log.Debugw("proof complete", "proof", out) + + rootCid, err := cid.Parse(subroots[0].Root) + if err != nil { + return contract.PDPVerifierProof{}, xerrors.Errorf("failed to parse root CID: %w", err) + } + commRoot, err := commcid.CIDToPieceCommitmentV1(rootCid) + if err != nil { + return contract.PDPVerifierProof{}, xerrors.Errorf("failed to convert CID to piece commitment: %w", err) + } + var cr [LeafSize]byte + copy(cr[:], commRoot) + + if !Verify(out, cr, uint64(challengedLeaf)) { + return contract.PDPVerifierProof{}, xerrors.Errorf("proof verification failed") + } + + // Return the completed proof + return out, nil +} + +func (p *ProveTask) getSenderAddress(ctx context.Context) (common.Address, error) { + // todo do based on proofset + + var addressStr string + err := p.db.QueryRow(ctx, `SELECT address FROM eth_keys WHERE role = 'pdp' LIMIT 1`).Scan(&addressStr) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return common.Address{}, errors.New("no sender address with role 'pdp' found") + } + return common.Address{}, err + } + address := common.HexToAddress(addressStr) + return address, nil +} + +func (p *ProveTask) cleanupDeletedRoots(ctx context.Context, proofSetID int64, pdpVerifier *contract.PDPVerifier) error { + + 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.Debugw("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 + } + id := ids[0] + return &id, nil +} + +func (p *ProveTask) TypeDetails() harmonytask.TaskTypeDetails { + return harmonytask.TaskTypeDetails{ + Name: "PDPProve", + Cost: resources.Resources{ + Cpu: 1, + Gpu: 0, + Ram: 256 << 20, // 256 MB + }, + MaxFailures: 5, + } +} + +func (p *ProveTask) Adder(taskFunc harmonytask.AddTaskFunc) { + p.addFunc.Set(taskFunc) +} + +func nextPowerOfTwo(n abi.PaddedPieceSize) abi.PaddedPieceSize { + lz := bits.LeadingZeros64(uint64(n - 1)) + return 1 << (64 - lz) +} + +func Verify(proof contract.PDPVerifierProof, root [32]byte, position uint64) bool { + computedHash := proof.Leaf + + for i := 0; i < len(proof.Proof); i++ { + sibling := proof.Proof[i] + + if position%2 == 0 { + log.Debugw("Verify", "position", position, "left-c", hex.EncodeToString(computedHash[:]), "right-s", hex.EncodeToString(sibling[:]), "ouh", hex.EncodeToString(shabytes(append(computedHash[:], sibling[:]...))[:])) + // If position is even, current node is on the left + computedHash = sha256.Sum256(append(computedHash[:], sibling[:]...)) + } else { + log.Debugw("Verify", "position", position, "left-s", hex.EncodeToString(sibling[:]), "right-c", hex.EncodeToString(computedHash[:]), "ouh", hex.EncodeToString(shabytes(append(sibling[:], computedHash[:]...))[:])) + // If position is odd, current node is on the right + computedHash = sha256.Sum256(append(sibling[:], computedHash[:]...)) + } + computedHash[31] &= 0x3F // set top bits to 00 + + // Move up to the parent node + position /= 2 + } + + // Compare the reconstructed root with the expected root + return computedHash == root +} + +func shabytes(in []byte) []byte { + out := sha256.Sum256(in) + return out[:] +} + +var _ = harmonytask.Reg(&ProveTask{}) +var _ harmonytask.TaskInterface = &ProveTask{} diff --git a/tasks/piece/task_park_piece.go b/tasks/piece/task_park_piece.go index 4b57bd856..43501d169 100644 --- a/tasks/piece/task_park_piece.go +++ b/tasks/piece/task_park_piece.go @@ -5,7 +5,6 @@ import ( "encoding/json" "math" "net/http" - "strconv" "time" "github.com/hashicorp/go-multierror" @@ -18,6 +17,7 @@ import ( "github.com/filecoin-project/curio/harmony/taskhelp" "github.com/filecoin-project/curio/lib/dealdata" ffi2 "github.com/filecoin-project/curio/lib/ffi" + "github.com/filecoin-project/curio/lib/paths" "github.com/filecoin-project/curio/lib/promise" "github.com/filecoin-project/curio/lib/storiface" ) @@ -30,46 +30,54 @@ const ParkMinFreeStoragePercent = 20 // ParkPieceTask gets a piece from some origin, and parks it in storage // Pieces are always f00, piece ID is mapped to pieceCID in the DB type ParkPieceTask struct { - db *harmonydb.DB - sc *ffi2.SealCalls + db *harmonydb.DB + sc *ffi2.SealCalls + remote *paths.Remote TF promise.Promise[harmonytask.AddTaskFunc] max int + + longTerm bool // Indicates if the task is for long-term pieces } func NewParkPieceTask(db *harmonydb.DB, sc *ffi2.SealCalls, max int) (*ParkPieceTask, error) { - pt := &ParkPieceTask{ - db: db, - sc: sc, + return newPieceTask(db, sc, nil, max, false) +} + +func NewStorePieceTask(db *harmonydb.DB, sc *ffi2.SealCalls, remote *paths.Remote, max int) (*ParkPieceTask, error) { + return newPieceTask(db, sc, remote, max, true) +} - max: max, +func newPieceTask(db *harmonydb.DB, sc *ffi2.SealCalls, remote *paths.Remote, max int, longTerm bool) (*ParkPieceTask, error) { + pt := &ParkPieceTask{ + db: db, + sc: sc, + remote: remote, + max: max, + longTerm: longTerm, } ctx := context.Background() - // We should delete all incomplete pieces before we start - // as we would have lost reader for these. The RPC caller will get an error - // when Curio shuts down before parking a piece. They can always retry. - // Leaving these pieces we utilise unnecessary resources in the form of ParkPieceTask - - _, err := db.Exec(ctx, `DELETE FROM parked_pieces WHERE complete = FALSE AND task_id IS NULL`) - if err != nil { - return nil, xerrors.Errorf("failed to delete incomplete parked pieces: %w", err) - } - go pt.pollPieceTasks(ctx) return pt, nil } func (p *ParkPieceTask) pollPieceTasks(ctx context.Context) { for { - // select parked pieces with no task_id + // Select parked pieces with no task_id and matching longTerm flag var pieceIDs []struct { ID storiface.PieceNumber `db:"id"` } - err := p.db.Select(ctx, &pieceIDs, `SELECT id FROM parked_pieces WHERE complete = FALSE AND task_id IS NULL`) + err := p.db.Select(ctx, &pieceIDs, ` + SELECT id + FROM parked_pieces + WHERE long_term = $1 + AND complete = FALSE + AND task_id IS NULL + `, p.longTerm) if err != nil { log.Errorf("failed to get parked pieces: %s", err) time.Sleep(PieceParkPollInterval) @@ -84,15 +92,17 @@ func (p *ParkPieceTask) pollPieceTasks(ctx context.Context) { for _, pieceID := range pieceIDs { pieceID := pieceID - // create a task for each piece + // Create a task for each piece p.TF.Val(ctx)(func(id harmonytask.TaskID, tx *harmonydb.Tx) (shouldCommit bool, err error) { - // update - n, err := tx.Exec(`UPDATE parked_pieces SET task_id = $1 WHERE id = $2 AND complete = FALSE AND task_id IS NULL`, id, pieceID.ID) + // Update + n, err := tx.Exec( + `UPDATE parked_pieces SET task_id = $1 WHERE id = $2 AND complete = FALSE AND task_id IS NULL AND long_term = $3`, + id, pieceID.ID, p.longTerm) if err != nil { return false, xerrors.Errorf("updating parked piece: %w", err) } - // commit only if we updated the piece + // Commit only if we updated the piece return n > 0, nil }) } @@ -102,22 +112,22 @@ func (p *ParkPieceTask) pollPieceTasks(ctx context.Context) { func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (done bool, err error) { ctx := context.Background() - // Define a struct to hold piece data. + // Fetch piece data var piecesData []struct { PieceID int64 `db:"id"` PieceCreatedAt time.Time `db:"created_at"` PieceCID string `db:"piece_cid"` Complete bool `db:"complete"` PiecePaddedSize int64 `db:"piece_padded_size"` - PieceRawSize string `db:"piece_raw_size"` + PieceRawSize int64 `db:"piece_raw_size"` } - // Select the piece data using the task ID. + // Select the piece data using the task ID and longTerm flag err = p.db.Select(ctx, &piecesData, ` SELECT id, created_at, piece_cid, complete, piece_padded_size, piece_raw_size FROM parked_pieces - WHERE task_id = $1 - `, taskID) + WHERE task_id = $1 AND long_term = $2 + `, taskID, p.longTerm) if err != nil { return false, xerrors.Errorf("fetching piece data: %w", err) } @@ -133,13 +143,12 @@ func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (d return true, nil } - // Define a struct for reference data. + // Fetch reference data var refData []struct { DataURL string `db:"data_url"` DataHeaders json.RawMessage `db:"data_headers"` } - // Now, select the first reference data that has a URL. err = p.db.Select(ctx, &refData, ` SELECT data_url, data_headers FROM parked_piece_refs @@ -152,12 +161,6 @@ func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (d return false, xerrors.Errorf("no refs found for piece_id: %d", pieceData.PieceID) } - // Convert piece_raw_size from string to int64. - pieceRawSize, err := strconv.ParseInt(pieceData.PieceRawSize, 10, 64) - if err != nil { - return false, xerrors.Errorf("parsing piece raw size: %w", err) - } - var merr error for i := range refData { @@ -167,7 +170,7 @@ func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (d if err != nil { return false, xerrors.Errorf("unmarshaling reference data headers: %w", err) } - upr := dealdata.NewUrlReader(refData[i].DataURL, hdrs, pieceRawSize) + upr := dealdata.NewUrlReader(p.remote, refData[i].DataURL, hdrs, pieceData.PieceRawSize) defer func() { _ = upr.Close() @@ -175,7 +178,12 @@ func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (d pnum := storiface.PieceNumber(pieceData.PieceID) - if err := p.sc.WritePiece(ctx, &taskID, pnum, pieceRawSize, upr); err != nil { + storageType := storiface.PathSealing + if p.longTerm { + storageType = storiface.PathStorage + } + + if err := p.sc.WritePiece(ctx, &taskID, pnum, pieceData.PieceRawSize, upr, storageType); err != nil { merr = multierror.Append(merr, xerrors.Errorf("write piece: %w", err)) continue } @@ -190,7 +198,7 @@ func (p *ParkPieceTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (d } } - // If no URL is found, this indicates an issue since at least one URL is expected. + // If no suitable data URL is found return false, xerrors.Errorf("no suitable data URL found for piece_id %d: %w", pieceData.PieceID, merr) } @@ -202,14 +210,24 @@ func (p *ParkPieceTask) CanAccept(ids []harmonytask.TaskID, engine *harmonytask. func (p *ParkPieceTask) TypeDetails() harmonytask.TaskTypeDetails { const maxSizePiece = 64 << 30 + taskName := "ParkPiece" + if p.longTerm { + taskName = "StorePiece" + } + + storageType := storiface.PathSealing + if p.longTerm { + storageType = storiface.PathStorage + } + return harmonytask.TaskTypeDetails{ Max: taskhelp.Max(p.max), - Name: "ParkPiece", + Name: taskName, Cost: resources.Resources{ Cpu: 1, Gpu: 0, Ram: 64 << 20, - Storage: p.sc.Storage(p.taskToRef, storiface.FTPiece, storiface.FTNone, maxSizePiece, storiface.PathSealing, ParkMinFreeStoragePercent), + Storage: p.sc.Storage(p.taskToRef, storiface.FTPiece, storiface.FTNone, maxSizePiece, storageType, ParkMinFreeStoragePercent), }, MaxFailures: 10, RetryWait: func(retries int) time.Duration { @@ -256,4 +274,5 @@ func (p *ParkPieceTask) Adder(taskFunc harmonytask.AddTaskFunc) { } var _ harmonytask.TaskInterface = &ParkPieceTask{} -var _ = harmonytask.Reg(&ParkPieceTask{}) +var _ = harmonytask.Reg(&ParkPieceTask{longTerm: false}) +var _ = harmonytask.Reg(&ParkPieceTask{longTerm: true}) diff --git a/web/api/webrpc/pdp.go b/web/api/webrpc/pdp.go new file mode 100644 index 000000000..c6c2f99a1 --- /dev/null +++ b/web/api/webrpc/pdp.go @@ -0,0 +1,228 @@ +package webrpc + +import ( + "context" + "crypto/ecdsa" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/yugabyte/pgx/v5" + xerrors "golang.org/x/xerrors" + + "github.com/filecoin-project/curio/harmony/harmonydb" +) + +// PDPService represents a PDP service +type PDPService struct { + ID int64 `db:"id" json:"id"` + Name string `db:"service_label" json:"name"` + PubKey []byte `db:"pubkey"` // Stored as bytes in DB, converted to PEM string in JSON + PubKeyStr string `json:"pubkey"` // PEM string for JSON response +} + +// PDPServices retrieves the list of PDP services from the database +func (a *WebRPC) PDPServices(ctx context.Context) ([]PDPService, error) { + services := []PDPService{} + + // Use w.deps.DB.Select to retrieve the services + err := a.deps.DB.Select(ctx, &services, `SELECT id, service_label, pubkey FROM pdp_services ORDER BY id ASC`) + if err != nil { + log.Errorf("PDPServices: failed to select services: %v", err) + return nil, fmt.Errorf("failed to retrieve services") + } + + // Convert pubkey bytes to PEM format string in the JSON response + for i, svc := range services { + pubKeyBytes := svc.PubKey + + // Encode the public key to PEM format + block := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: pubKeyBytes, + } + + pemBytes := pem.EncodeToMemory(block) + services[i].PubKeyStr = string(pemBytes) + } + + return services, nil +} + +// AddPDPService adds a new PDP service to the database +func (a *WebRPC) AddPDPService(ctx context.Context, name string, pubKey string) error { + name = strings.TrimSpace(name) + pubKey = strings.TrimSpace(pubKey) + + if name == "" { + return fmt.Errorf("service name cannot be empty") + } + if pubKey == "" { + return fmt.Errorf("public key cannot be empty") + } + + // Decode the public key from PEM format + block, _ := pem.Decode([]byte(pubKey)) + if block == nil || block.Type != "PUBLIC KEY" { + return fmt.Errorf("failed to parse public key PEM") + } + pubKeyBytes := block.Bytes + + // Validate the public key + _, err := x509.ParsePKIXPublicKey(pubKeyBytes) + if err != nil { + return fmt.Errorf("invalid public key: %v", err) + } + + // Check if a service with the same name already exists + var existingID int64 + err = a.deps.DB.QueryRow(ctx, `SELECT id FROM pdp_services WHERE service_label = $1`, name).Scan(&existingID) + if err == nil { + // Service with the same name exists + return fmt.Errorf("a service with the same name already exists") + } else if err != pgx.ErrNoRows { + // Some other error occurred + log.Errorf("AddPDPService: failed to check existing service: %v", err) + return fmt.Errorf("failed to add service") + } + + // Insert the new PDP service into the database + _, err = a.deps.DB.Exec(ctx, `INSERT INTO pdp_services (service_label, pubkey) VALUES ($1, $2)`, name, pubKeyBytes) + if err != nil { + log.Errorf("AddPDPService: failed to insert service: %v", err) + return fmt.Errorf("failed to add service") + } + + return nil +} + +// RemovePDPService removes a PDP service from the database +func (a *WebRPC) RemovePDPService(ctx context.Context, id int64) error { + // Optional: Authentication and Authorization checks + // For example, check if the user is an admin + + // Check if the service exists + var existingID int64 + err := a.deps.DB.QueryRow(ctx, `SELECT id FROM pdp_services WHERE id = $1`, id).Scan(&existingID) + if err != nil { + if err == pgx.ErrNoRows { + return fmt.Errorf("service with ID %d does not exist", id) + } + log.Errorf("RemovePDPService: failed to check existing service: %v", err) + return fmt.Errorf("failed to remove service") + } + + // Delete the service + _, err = a.deps.DB.Exec(ctx, `DELETE FROM pdp_services WHERE id = $1`, id) + if err != nil { + log.Errorf("RemovePDPService: failed to delete service: %v", err) + return fmt.Errorf("failed to remove service") + } + + return nil +} + +type PDPOwnerAddress struct { + Address string `db:"address" json:"address"` +} + +func (a *WebRPC) ImportPDPKey(ctx context.Context, hexPrivateKey string) (string, error) { + hexPrivateKey = strings.TrimSpace(hexPrivateKey) + if hexPrivateKey == "" { + return "", fmt.Errorf("private key cannot be empty") + } + + // Remove any leading '0x' from the hex string + hexPrivateKey = strings.TrimPrefix(hexPrivateKey, "0x") + hexPrivateKey = strings.TrimPrefix(hexPrivateKey, "0X") + + // Decode the hex private key + privateKeyBytes, err := hex.DecodeString(hexPrivateKey) + if err != nil { + return "", fmt.Errorf("failed to decode private key: %v", err) + } + + // Parse the private key + privateKey, err := crypto.ToECDSA(privateKeyBytes) + if err != nil { + return "", fmt.Errorf("invalid private key: %v", err) + } + + // Get the public key + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return "", xerrors.New("error casting public key to ECDSA") + } + + // Derive the address + address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + + // Insert into the database within a transaction + _, err = a.deps.DB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (bool, error) { + // Check if the owner_address already exists + var existingAddress string + err := tx.QueryRow(`SELECT address FROM eth_keys WHERE address = $1 AND role = 'pdp'`, address).Scan(&existingAddress) + if err == nil { + return false, fmt.Errorf("owner address %s already exists", address) + } else if err != pgx.ErrNoRows { + return false, fmt.Errorf("failed to check existing owner address: %v", err) + } + + // Insert the new owner address and private key + _, err = tx.Exec(`INSERT INTO eth_keys (address, private_key, role) VALUES ($1, $2, 'pdp')`, address, privateKeyBytes) + if err != nil { + return false, fmt.Errorf("failed to insert owner address: %v", err) + } + return true, nil + }) + if err != nil { + log.Errorf("ImportPDPKey: failed to import key: %v", err) + return "", fmt.Errorf("failed to import key") + } + + return address, nil +} + +func (a *WebRPC) ListPDPKeys(ctx context.Context) ([]string, error) { + addresses := []string{} + + // Use a.deps.DB.Select to retrieve the owner addresses + err := a.deps.DB.Select(ctx, &addresses, `SELECT address FROM eth_keys WHERE role = 'pdp' ORDER BY address ASC`) + if err != nil { + log.Errorf("ListPDPKeys: failed to select addresses: %v", err) + return nil, fmt.Errorf("failed to retrieve addresses") + } + + return addresses, nil +} + +func (a *WebRPC) RemovePDPKey(ctx context.Context, ownerAddress string) error { + ownerAddress = strings.TrimSpace(ownerAddress) + if ownerAddress == "" { + return fmt.Errorf("owner address cannot be empty") + } + + // Check if the owner address exists + var existingAddress string + err := a.deps.DB.QueryRow(ctx, `SELECT address FROM eth_keys WHERE address = $1 AND role = 'pdp'`, ownerAddress).Scan(&existingAddress) + if err != nil { + if err == pgx.ErrNoRows { + return fmt.Errorf("owner address %s does not exist", ownerAddress) + } + log.Errorf("RemovePDPKey: failed to check existing owner address: %v", err) + return fmt.Errorf("failed to remove key") + } + + // Delete the key + _, err = a.deps.DB.Exec(ctx, `DELETE FROM eth_keys WHERE address = $1 AND role = 'pdp'`, ownerAddress) + if err != nil { + log.Errorf("RemovePDPKey: failed to delete key: %v", err) + return fmt.Errorf("failed to remove key") + } + + return nil +} diff --git a/web/static/pages/pdp/index.html b/web/static/pages/pdp/index.html new file mode 100644 index 000000000..049237a94 --- /dev/null +++ b/web/static/pages/pdp/index.html @@ -0,0 +1,24 @@ + + + + Node Info + + + + + +
+
+
+

Proof of Data Possession

+
+
+
+
+ +
+
+
+
+ + diff --git a/web/static/pages/pdp/pdp.mjs b/web/static/pages/pdp/pdp.mjs new file mode 100644 index 000000000..74a05e853 --- /dev/null +++ b/web/static/pages/pdp/pdp.mjs @@ -0,0 +1,250 @@ +import { LitElement, html } from 'https://cdn.jsdelivr.net/gh/lit/dist@3/all/lit-all.min.js'; +import RPCCall from '/lib/jsonrpc.mjs'; + +customElements.define('pdp-info', class PDPElement extends LitElement { + static properties = { + services: { type: Array }, + keys: { type: Array }, + showAddServiceForm: { type: Boolean }, + showAddKeyForm: { type: Boolean }, + } + + constructor() { + super(); + this.services = []; + this.keys = []; + this.showAddServiceForm = false; + this.showAddKeyForm = false; + this.loadServices(); + this.loadKeys(); + } + + async loadServices() { + try { + this.services = await RPCCall('PDPServices', []); + } catch (error) { + console.error('Failed to load PDP services:', error); + } + } + + async loadKeys() { + try { + this.keys = await RPCCall('ListPDPKeys', []); + } catch (error) { + console.error('Failed to load PDP keys:', error); + } + } + + toggleAddServiceForm() { + this.showAddServiceForm = !this.showAddServiceForm; + } + + toggleAddKeyForm() { + this.showAddKeyForm = !this.showAddKeyForm; + } + + async addService(event) { + event.preventDefault(); + + const nameInput = this.shadowRoot.getElementById('service-name'); + const pubKeyInput = this.shadowRoot.getElementById('service-pubkey'); + + const name = nameInput.value.trim(); + const pubKey = pubKeyInput.value.trim(); + + if (!name || !pubKey) { + alert('Please provide both a name and a public key.'); + return; + } + + try { + // Call the RPC method to add the new PDP service + await RPCCall('AddPDPService', [name, pubKey]); + + // Reset the form + nameInput.value = ''; + pubKeyInput.value = ''; + + // Reload the services + await this.loadServices(); + + // Hide the form + this.showAddServiceForm = false; + } catch (error) { + console.error('Failed to add PDP service:', error); + alert('Failed to add PDP service: ' + (error.message || error)); + } + } + + async removeService(serviceId, serviceName) { + const confirmed = confirm(`Are you sure you want to remove the service "${serviceName}"?`); + if (!confirmed) { + return; + } + + try { + // Call the RPC method to remove the PDP service + await RPCCall('RemovePDPService', [serviceId]); + + // Reload the services + await this.loadServices(); + } catch (error) { + console.error('Failed to remove PDP service:', error); + alert('Failed to remove PDP service: ' + (error.message || error)); + } + } + + async addKey(event) { + event.preventDefault(); + + const privateKeyInput = this.shadowRoot.getElementById('private-key'); + + const privateKey = privateKeyInput.value.trim(); + + if (!privateKey) { + alert('Please provide a private key.'); + return; + } + + try { + // Call the RPC method to import the private key + const address = await RPCCall('ImportPDPKey', [privateKey]); + + // Reset the form + privateKeyInput.value = ''; + + // Reload the keys + await this.loadKeys(); + + // Hide the form + this.showAddKeyForm = false; + + alert(`Successfully imported key for address: ${address}`); + } catch (error) { + console.error('Failed to import key:', error); + alert('Failed to import key: ' + (error.message || error)); + } + } + + async removeKey(ownerAddress) { + const confirmed = confirm(`Are you sure you want to remove the key for address "${ownerAddress}"?`); + if (!confirmed) { + return; + } + + try { + // Call the RPC method to remove the key + await RPCCall('RemovePDPKey', [ownerAddress]); + + // Reload the keys + await this.loadKeys(); + } catch (error) { + console.error('Failed to remove key:', error); + alert('Failed to remove key: ' + (error.message || error)); + } + } + + render() { + return html` + + + + +
+

Services

+ ${this.services.length > 0 ? html` + + + + + + + + + + + ${this.services.map(service => html` + + + + + + + `)} + +
IDNamePublic KeyAction
${service.id}${service.name} + + + +
+ ` : html` +

No PDP services available.

+ `} + + + + ${this.showAddServiceForm ? html` +
+
+ + +
+
+ + +
+ +
+ ` : ''} + +
+ +

Owner Addresses

+ ${this.keys.length > 0 ? html` + + + + + + + + + ${this.keys.map(key => html` + + + + + `)} + +
Owner AddressAction
${key} + +
+ ` : html` +

No owner addresses available.

+ `} + + + + ${this.showAddKeyForm ? html` +
+
+ + +
+ +
+ ` : ''} +
+ `; + } +}); diff --git a/web/static/ux/curio-ux.mjs b/web/static/ux/curio-ux.mjs index 30dd910d6..8d7e4eea8 100644 --- a/web/static/ux/curio-ux.mjs +++ b/web/static/ux/curio-ux.mjs @@ -156,6 +156,10 @@ class CurioUX extends LitElement { + + + +