diff --git a/client/cmd/link.go b/client/cmd/link.go new file mode 100644 index 0000000..03ada27 --- /dev/null +++ b/client/cmd/link.go @@ -0,0 +1,142 @@ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +var symlinkPath string + +var linkCmd = &cobra.Command{ + Use: "link", + Short: "Create a symlink to qclient in PATH", + Long: fmt.Sprintf(`Create a symlink to the qclient binary in a suitable directory in your PATH. +This allows you to run qclient from anywhere without specifying the full path. + +By default it will create the symlink in the current directory /usr/local/bin. +You can also specify a custom directory using the --path flag. + +Example: qclient link --path /usr/local/bin`), + RunE: func(cmd *cobra.Command, args []string) error { + // Get the path to the current executable + execPath, err := os.Executable() + if err != nil { + return fmt.Errorf("failed to get executable path: %w", err) + } + + // Determine the target directory and path for the symlink + targetDir, targetPath, err := determineSymlinkLocation() + if err != nil { + return err + } + + // If operation was cancelled by the user + if targetDir == "" && targetPath == "" { + return nil + } + + // Create the symlink (handles existing symlinks) + if err := utils.CreateSymlink(execPath, targetPath); err != nil { + return err + } + + // Save the symlink path to ClientConfig + config := utils.ClientConfig{ + SymlinkPath: targetPath, + } + + // Use the UpdateClientConfig function to save the config + if err := utils.UpdateClientConfig(&config); err != nil { + fmt.Printf("Warning: Failed to save symlink path to config: %v\n", err) + } else { + fmt.Printf("Saved symlink path %s to client configuration\n", targetPath) + } + + fmt.Printf("Symlink created at %s\n", targetPath) + return nil + }, +} + +// determineSymlinkLocation finds the appropriate location for the symlink +// Returns the target directory, the full path for the symlink, and any error +func determineSymlinkLocation() (string, string, error) { + // If user provided a custom path + if symlinkPath != "" { + return validateUserProvidedPath(symlinkPath) + } + + // Otherwise, find a suitable directory in PATH + return utils.NodeDefaultSymlinkDir, utils.DefaultQClientSymlinkPath, nil +} + +// isDirectoryInPath checks if a directory is in the PATH environment variable +func isDirectoryInPath(dir string) bool { + pathEnv := os.Getenv("PATH") + pathDirs := strings.Split(pathEnv, string(os.PathListSeparator)) + + // Normalize paths for comparison + absDir, err := filepath.Abs(dir) + if err != nil { + return false + } + + for _, pathDir := range pathDirs { + absPathDir, err := filepath.Abs(pathDir) + if err != nil { + continue + } + + if absDir == absPathDir { + return true + } + } + + return false +} + +// validateUserProvidedPath checks if the provided path is a valid directory +func validateUserProvidedPath(path string) (string, string, error) { + // Check if the provided path is a directory + info, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return "", "", fmt.Errorf("directory does not exist: %s", path) + } + return "", "", fmt.Errorf("error checking directory: %w", err) + } + + if !info.IsDir() { + return "", "", fmt.Errorf("the specified path is not a directory: %s", path) + } + + // Check if the directory is in PATH + if !isDirectoryInPath(path) { + // Ask user for confirmation to proceed with a directory not in PATH + fmt.Printf("Warning: The directory '%s' is not in your PATH environment variable.\n", path) + fmt.Println("The symlink will be created, but you may not be able to run 'qclient' from anywhere.") + fmt.Print("Do you want to continue? [y/N]: ") + + var response string + fmt.Scanln(&response) + if strings.ToLower(response) != "y" { + fmt.Println("Operation cancelled.") + return "", "", nil + } + } + + // Use the provided directory + targetDir := path + targetPath := filepath.Join(targetDir, "qclient") + + return targetDir, targetPath, nil +} + +func init() { + rootCmd.AddCommand(linkCmd) + linkCmd.Flags().StringVar(&symlinkPath, "path", "", "Specify a custom directory for the symlink") +} diff --git a/client/cmd/node/info.go b/client/cmd/node/info.go new file mode 100644 index 0000000..c905c6e --- /dev/null +++ b/client/cmd/node/info.go @@ -0,0 +1,46 @@ +package node + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +// infoCmd represents the info command +var infoCmd = &cobra.Command{ + Use: "info", + Short: "Get information about the Quilibrium node", + Long: `Get information about the Quilibrium node. +Available subcommands: + latest-version Get the latest available version of Quilibrium node + +Examples: + # Get the latest version + qclient node info latest-version`, +} + +// latestVersionCmd represents the latest-version command +var latestVersionCmd = &cobra.Command{ + Use: "latest-version", + Short: "Get the latest available version of Quilibrium node", + Long: `Get the latest available version of Quilibrium node by querying the releases API. +This command fetches the version information from https://releases.quilibrium.com/release.`, + Run: func(cmd *cobra.Command, args []string) { + version, err := utils.GetLatestVersion(utils.ReleaseTypeNode) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + fmt.Fprintf(os.Stdout, "Latest available version: %s\n", version) + }, +} + +func init() { + // Add the latest-version subcommand to the info command + infoCmd.AddCommand(latestVersionCmd) + + // Add the info command to the node command + NodeCmd.AddCommand(infoCmd) +} diff --git a/client/cmd/node/install.go b/client/cmd/node/install.go new file mode 100644 index 0000000..a206a08 --- /dev/null +++ b/client/cmd/node/install.go @@ -0,0 +1,115 @@ +package node + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +// installCmd represents the command to install the Quilibrium node +var installCmd = &cobra.Command{ + Use: "install [version]", + Short: "Install Quilibrium node", + Long: `Install Quilibrium node binary. +If no version is specified, the latest version will be installed. + + +Examples: + # Install the latest version + qclient node install + + # Install a specific version + qclient node install 2.1.0 + + # Install without creating a dedicated user + qclient node install --no-create-user`, + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + // Get system information and validate + osType, arch, err := utils.GetSystemInfo() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + // Determine version to install + version := determineVersion(args) + + fmt.Fprintf(os.Stdout, "Installing Quilibrium node for %s-%s, version: %s\n", osType, arch, version) + + // Check if we need to create a dedicated user (opt-out) + + if err := createNodeUser(); err != nil { + fmt.Fprintf(os.Stderr, "Error creating user: %v\n", err) + fmt.Fprintf(os.Stdout, "Continuing with installation as current user...\n") + } + + // Install the node + installNode(version) + }, +} + +func init() { + // Add the install command to the node command + NodeCmd.AddCommand(installCmd) +} + +// installNode installs the Quilibrium node +func installNode(version string) { + // Create installation directory + if err := utils.ValidateAndCreateDir(installPath); err != nil { + fmt.Fprintf(os.Stderr, "Error creating installation directory: %v\n", err) + return + } + + // Create data directory + if err := utils.ValidateAndCreateDir(dataPath); err != nil { + fmt.Fprintf(os.Stderr, "Error creating data directory: %v\n", err) + return + } + + // Download and install the node + if version == "latest" { + latestVersion, err := utils.GetLatestVersion(utils.ReleaseTypeNode) + if err != nil { + fmt.Fprintf(os.Stderr, "Error getting latest version: %v\n", err) + return + } + + version = latestVersion + fmt.Fprintf(os.Stdout, "Installing latest version: %s\n", version) + } + + if err := installByVersion(version); err != nil { + fmt.Fprintf(os.Stderr, "Error installing specific version: %v\n", err) + return + } + + // Finish installation + nodeBinaryPath := filepath.Join(installPath, version, "node") + finishInstallation(nodeBinaryPath, version) +} + +// installByVersion installs a specific version of the Quilibrium node +func installByVersion(version string) error { + // Create version-specific directory + versionDir := filepath.Join(installPath, version) + if err := utils.ValidateAndCreateDir(versionDir); err != nil { + return fmt.Errorf("failed to create version directory: %w", err) + } + + // Download the release + if err := utils.DownloadRelease(utils.ReleaseTypeNode, version); err != nil { + return fmt.Errorf("failed to download release: %w", err) + } + + // Download signature files + if err := utils.DownloadReleaseSignatures(utils.ReleaseTypeNode, version); err != nil { + return fmt.Errorf("failed to download signature files: %w", err) + } + + return nil +} diff --git a/client/cmd/node/node.go b/client/cmd/node/node.go new file mode 100644 index 0000000..7b931d6 --- /dev/null +++ b/client/cmd/node/node.go @@ -0,0 +1,59 @@ +package node + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +var ( + // Base URL for Quilibrium releases + baseReleaseURL = "https://releases.quilibrium.com" + + // Default symlink path for the node binary + defaultSymlinkPath = "/usr/local/bin/quilibrium-node" + + // Default installation directory base paths in order of preference + installPath = "/opt/quilibrium" + + // Default data directory paths in order of preference + dataPath = "/var/lib/quilibrium" + + logPath = "/var/log/quilibrium" + + // Default user to run the node + nodeUser = "quilibrium" + + serviceName = "quilibrium-node" + + // Default config file name + defaultConfigFileName = "node.yaml" + + osType string + arch string +) + +// NodeCmd represents the node command +var NodeCmd = &cobra.Command{ + Use: "node", + Short: "Quilibrium node commands", + Long: `Run Quilibrium node commands.`, +} + +func init() { + // Add subcommands + NodeCmd.AddCommand(installCmd) + NodeCmd.AddCommand(updateNodeCmd) + NodeCmd.AddCommand(nodeServiceCmd) + + localOsType, localArch, err := utils.GetSystemInfo() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + osType = localOsType + arch = localArch +} diff --git a/client/cmd/node/service.go b/client/cmd/node/service.go new file mode 100644 index 0000000..d689d15 --- /dev/null +++ b/client/cmd/node/service.go @@ -0,0 +1,284 @@ +package node + +import ( + "fmt" + "os" + "os/exec" + + "github.com/spf13/cobra" + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +// nodeServiceCmd represents the command to manage the Quilibrium node service +var nodeServiceCmd = &cobra.Command{ + Use: "service [command]", + Short: "Manage the Quilibrium node service", + Long: `Manage the Quilibrium node service. +Available commands: + start Start the node service + stop Stop the node service + restart Restart the node service + status Check the status of the node service + enable Enable the node service to start on boot + disable Disable the node service from starting on boot + install Install the service for the current OS + +Examples: + # Start the node service + qclient node service start + + # Check service status + qclient node service status + + # Enable service to start on boot + qclient node service enable`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + command := args[0] + switch command { + case "start": + startService() + case "stop": + stopService() + case "restart": + restartService() + case "status": + checkServiceStatus() + case "enable": + enableService() + case "disable": + disableService() + case "reload": + reloadService() + case "install": + installService() + default: + fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command) + fmt.Fprintf(os.Stderr, "Available commands: start, stop, restart, status, enable, disable, reload, install\n") + os.Exit(1) + } + }, +} + +// installService installs the appropriate service configuration for the current OS +func installService() { + if err := utils.CheckAndRequestSudo("Installing service requires root privileges"); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + fmt.Fprintf(os.Stdout, "Installing Quilibrium node service for %s...\n", osType) + + if osType == "darwin" { + installMacOSService() + } else if osType == "linux" { + if err := createSystemdServiceFile(); err != nil { + fmt.Fprintf(os.Stderr, "Error creating systemd service file: %v\n", err) + return + } + } else { + fmt.Fprintf(os.Stderr, "Error: Unsupported operating system: %s\n", osType) + return + } + + fmt.Fprintf(os.Stdout, "Quilibrium node service installed successfully\n") +} + +// startService starts the Quilibrium node service +func startService() { + if err := utils.CheckAndRequestSudo("Starting service requires root privileges"); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + if osType == "darwin" { + // MacOS launchd command + cmd := exec.Command("sudo", "launchctl", "start", fmt.Sprintf("com.quilibrium.%s", serviceName)) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error starting service: %v\n", err) + return + } + } else { + // Linux systemd command + cmd := exec.Command("sudo", "systemctl", "start", serviceName) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error starting service: %v\n", err) + return + } + } + + fmt.Fprintf(os.Stdout, "Started Quilibrium node service\n") +} + +// stopService stops the Quilibrium node service +func stopService() { + if err := utils.CheckAndRequestSudo("Stopping service requires root privileges"); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + if osType == "darwin" { + // MacOS launchd command + cmd := exec.Command("sudo", "launchctl", "stop", fmt.Sprintf("com.quilibrium.%s", serviceName)) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error stopping service: %v\n", err) + return + } + } else { + // Linux systemd command + cmd := exec.Command("sudo", "systemctl", "stop", serviceName) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error stopping service: %v\n", err) + return + } + } + + fmt.Fprintf(os.Stdout, "Stopped Quilibrium node service\n") +} + +// restartService restarts the Quilibrium node service +func restartService() { + if err := utils.CheckAndRequestSudo("Restarting service requires root privileges"); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + if osType == "darwin" { + // MacOS launchd command - stop then start + stopCmd := exec.Command("sudo", "launchctl", "stop", fmt.Sprintf("com.quilibrium.%s", serviceName)) + if err := stopCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error stopping service: %v\n", err) + return + } + + startCmd := exec.Command("sudo", "launchctl", "start", fmt.Sprintf("com.quilibrium.%s", serviceName)) + if err := startCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error starting service: %v\n", err) + return + } + } else { + // Linux systemd command + cmd := exec.Command("sudo", "systemctl", "restart", serviceName) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error restarting service: %v\n", err) + return + } + } + + fmt.Fprintf(os.Stdout, "Restarted Quilibrium node service\n") +} + +// reloadService reloads the Quilibrium node service +func reloadService() { + if err := utils.CheckAndRequestSudo("Reloading service requires root privileges"); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + if osType == "darwin" { + // MacOS launchd command - unload then load + plistPath := fmt.Sprintf("/Library/LaunchDaemons/com.quilibrium.%s.plist", serviceName) + unloadCmd := exec.Command("sudo", "launchctl", "unload", plistPath) + if err := unloadCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error unloading service: %v\n", err) + return + } + + loadCmd := exec.Command("sudo", "launchctl", "load", plistPath) + if err := loadCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error loading service: %v\n", err) + return + } + + fmt.Fprintf(os.Stdout, "Reloaded launchd service\n") + } else { + // Linux systemd command + cmd := exec.Command("sudo", "systemctl", "daemon-reload") + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error reloading service: %v\n", err) + return + } + + fmt.Fprintf(os.Stdout, "Reloaded systemd service\n") + } +} + +// checkServiceStatus checks the status of the Quilibrium node service +func checkServiceStatus() { + if err := utils.CheckAndRequestSudo("Checking service status requires root privileges"); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + if osType == "darwin" { + // MacOS launchd command + cmd := exec.Command("sudo", "launchctl", "list", fmt.Sprintf("com.quilibrium.%s", serviceName)) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error checking service status: %v\n", err) + } + } else { + // Linux systemd command + cmd := exec.Command("sudo", "systemctl", "status", serviceName) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error checking service status: %v\n", err) + } + } +} + +// enableService enables the Quilibrium node service to start on boot +func enableService() { + if err := utils.CheckAndRequestSudo("Enabling service requires root privileges"); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + if osType == "darwin" { + // MacOS launchd command - load with -w flag to enable at boot + plistPath := fmt.Sprintf("/Library/LaunchDaemons/com.quilibrium.%s.plist", serviceName) + cmd := exec.Command("sudo", "launchctl", "load", "-w", plistPath) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error enabling service: %v\n", err) + return + } + } else { + // Linux systemd command + cmd := exec.Command("sudo", "systemctl", "enable", serviceName) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error enabling service: %v\n", err) + return + } + } + + fmt.Fprintf(os.Stdout, "Enabled Quilibrium node service to start on boot\n") +} + +// disableService disables the Quilibrium node service from starting on boot +func disableService() { + if err := utils.CheckAndRequestSudo("Disabling service requires root privileges"); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + if osType == "darwin" { + // MacOS launchd command - unload with -w flag to disable at boot + plistPath := fmt.Sprintf("/Library/LaunchDaemons/com.quilibrium.%s.plist", serviceName) + cmd := exec.Command("sudo", "launchctl", "unload", "-w", plistPath) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error disabling service: %v\n", err) + return + } + } else { + // Linux systemd command + cmd := exec.Command("sudo", "systemctl", "disable", serviceName) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error disabling service: %v\n", err) + return + } + } + + fmt.Fprintf(os.Stdout, "Disabled Quilibrium node service from starting on boot\n") +} diff --git a/client/cmd/node/shared.go b/client/cmd/node/shared.go new file mode 100644 index 0000000..568e032 --- /dev/null +++ b/client/cmd/node/shared.go @@ -0,0 +1,411 @@ +package node + +import ( + "bufio" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "text/template" + + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +// determineVersion gets the version to install from args or defaults to "latest" +func determineVersion(args []string) string { + if len(args) > 0 { + return args[0] + } + return "latest" +} + +// confirmPaths asks the user to confirm the installation and data paths +func confirmPaths(installPath, dataPath string) bool { + fmt.Print("Do you want to continue with these paths? [Y/n]: ") + reader := bufio.NewReader(os.Stdin) + response, _ := reader.ReadString('\n') + response = strings.TrimSpace(strings.ToLower(response)) + + return response == "" || response == "y" || response == "yes" +} + +// createNodeUser creates a dedicated user for running the node +func createNodeUser() error { + fmt.Fprintf(os.Stdout, "Creating dedicated user '%s' for running the node...\n", nodeUser) + + // Check for sudo privileges + if err := utils.CheckAndRequestSudo("Creating system user requires root privileges"); err != nil { + return fmt.Errorf("failed to get sudo privileges: %w", err) + } + + var cmd *exec.Cmd + + if osType == "linux" { + // Check if user already exists + checkCmd := exec.Command("id", nodeUser) + if checkCmd.Run() == nil { + fmt.Fprintf(os.Stdout, "User '%s' already exists\n", nodeUser) + return nil + } + + // Create user on Linux + cmd = exec.Command("useradd", "-r", "-s", "/bin/false", "-m", "-c", "Quilibrium Node User", nodeUser) + } else if osType == "darwin" { + // Check if user already exists on macOS + checkCmd := exec.Command("dscl", ".", "-read", "/Users/"+nodeUser) + if checkCmd.Run() == nil { + fmt.Fprintf(os.Stdout, "User '%s' already exists\n", nodeUser) + return nil + } + + // Create user on macOS + // Get next available user ID + uidCmd := exec.Command("dscl", ".", "-list", "/Users", "UniqueID") + uidOutput, err := uidCmd.Output() + if err != nil { + return fmt.Errorf("failed to get user IDs: %v", err) + } + + // Find the highest UID and add 1 + var maxUID int = 500 // Start with a reasonable system UID + for _, line := range strings.Split(string(uidOutput), "\n") { + fields := strings.Fields(line) + if len(fields) >= 2 { + var uid int + fmt.Sscanf(fields[len(fields)-1], "%d", &uid) + if uid > maxUID && uid < 65000 { // Avoid system UIDs + maxUID = uid + } + } + } + nextUID := maxUID + 1 + + // Create the user + cmd = exec.Command("dscl", ".", "-create", "/Users/"+nodeUser) + if err := cmd.Run(); err != nil { + return fmt.Errorf("Failed to create user: %v", err) + } + + // Set the user's properties + commands := [][]string{ + {"-create", "/Users/" + nodeUser, "UniqueID", fmt.Sprintf("%d", nextUID)}, + {"-create", "/Users/" + nodeUser, "PrimaryGroupID", "20"}, // staff group + {"-create", "/Users/" + nodeUser, "UserShell", "/bin/false"}, + {"-create", "/Users/" + nodeUser, "NFSHomeDirectory", "/var/empty"}, + {"-create", "/Users/" + nodeUser, "RealName", "Quilibrium Node User"}, + } + + for _, args := range commands { + cmd = exec.Command("dscl", append([]string{"."}, args...)...) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to set user property %s: %v", args[1], err) + } + } + + // Disable the user account + cmd = exec.Command("dscl", ".", "-create", "/Users/"+nodeUser, "Password", "*") + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to disable user account: %v", err) + } + + return nil + } else { + return fmt.Errorf("user creation not supported on %s", osType) + } + + // Run the command + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to create user: %w", err) + } + + fmt.Fprintf(os.Stdout, "User '%s' created successfully\n", nodeUser) + return nil +} + +// setOwnership sets the ownership of directories to the node user +func setOwnership() { + fmt.Fprintf(os.Stdout, "Setting ownership of %s and %s to %s...\n", installPath, dataPath, nodeUser) + + // Change ownership of installation directory + chownCmd := exec.Command("chown", "-R", nodeUser+":"+nodeUser, installPath) + if err := chownCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to change ownership of %s: %v\n", installPath, err) + } + + // Change ownership of data directory + chownCmd = exec.Command("chown", "-R", nodeUser+":"+nodeUser, dataPath) + if err := chownCmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to change ownership of %s: %v\n", dataPath, err) + } +} + +// setupLogRotation creates a logrotate configuration file for the Quilibrium node +func setupLogRotation() error { + // Check if we need sudo privileges for creating logrotate config + if err := utils.CheckAndRequestSudo("Creating logrotate configuration requires root privileges"); err != nil { + return fmt.Errorf("failed to get sudo privileges: %w", err) + } + + // Create logrotate configuration + configContent := fmt.Sprintf(`%s/*.log { + daily + rotate 7 + compress + delaycompress + missingok + notifempty + create 0640 %s %s + postrotate + systemctl reload quilibrium-node >/dev/null 2>&1 || true + endscript +}`, logPath, nodeUser, nodeUser) + + // Write the configuration file + configPath := "/etc/logrotate.d/quilibrium-node" + if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil { + return fmt.Errorf("failed to create logrotate configuration: %w", err) + } + + // Create log directory with proper permissions + if err := os.MkdirAll(logPath, 0750); err != nil { + return fmt.Errorf("failed to create log directory: %w", err) + } + + // Set ownership of log directory + chownCmd := exec.Command("chown", nodeUser+":"+nodeUser, logPath) + if err := chownCmd.Run(); err != nil { + return fmt.Errorf("failed to set log directory ownership: %w", err) + } + + fmt.Fprintf(os.Stdout, "Created log rotation configuration at %s\n", configPath) + return nil +} + +// finishInstallation completes the installation process by making the binary executable and creating a symlink +func finishInstallation(nodeBinaryPath string, version string) { + + setOwnership() + + // Make the binary executable + if err := os.Chmod(nodeBinaryPath, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error making binary executable: %v\n", err) + return + } + + // Check if we need sudo privileges for creating symlink in system directory + if strings.HasPrefix(defaultSymlinkPath, "/usr/") || strings.HasPrefix(defaultSymlinkPath, "/bin/") || strings.HasPrefix(defaultSymlinkPath, "/sbin/") { + if err := utils.CheckAndRequestSudo(fmt.Sprintf("Creating symlink at %s requires root privileges", defaultSymlinkPath)); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to get sudo privileges: %v\n", err) + return + } + } + + // Create symlink using the utils package + if err := utils.CreateSymlink(nodeBinaryPath, defaultSymlinkPath); err != nil { + fmt.Fprintf(os.Stderr, "Error creating symlink: %v\n", err) + } + + // Set up log rotation + if err := setupLogRotation(); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to set up log rotation: %v\n", err) + } + + // Create systemd service file + if osType == "linux" { + if err := createSystemdServiceFile(); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to create systemd service file: %v\n", err) + } + } else if osType == "darwin" { + installMacOSService() + } else { + fmt.Fprintf(os.Stderr, "Warning: Background service file creation not supported on %s\n", osType) + return + } + + // Print success message + printSuccessMessage(version) +} + +// printSuccessMessage prints a success message after installation +func printSuccessMessage(version string) { + fmt.Fprintf(os.Stdout, "\nSuccessfully installed Quilibrium node %s\n", version) + fmt.Fprintf(os.Stdout, "Installation directory: %s\n", installPath) + fmt.Fprintf(os.Stdout, "Data directory: %s\n", dataPath) + fmt.Fprintf(os.Stdout, "Binary symlinked to %s\n", defaultSymlinkPath) + fmt.Fprintf(os.Stdout, "Log directory: %s\n", logPath) + fmt.Fprintf(os.Stdout, "Environment file: /etc/default/quilibrium-node\n") + fmt.Fprintf(os.Stdout, "Service file: /etc/systemd/system/quilibrium-node.service\n") + + fmt.Fprintf(os.Stdout, "\nTo start the node, you can run:\n") + fmt.Fprintf(os.Stdout, " %s --config %s/config/config.yaml\n", + defaultSymlinkPath, dataPath) + fmt.Fprintf(os.Stdout, " # Or use systemd service:\n") + fmt.Fprintf(os.Stdout, " sudo systemctl start quilibrium-node\n") + + fmt.Fprintf(os.Stdout, "\nFor more options, run:\n") + fmt.Fprintf(os.Stdout, " quilibrium-node --help\n") +} + +// createSystemdServiceFile creates the systemd service file with environment file support +func createSystemdServiceFile() error { + // Check if we need sudo privileges + if err := utils.CheckAndRequestSudo("Creating systemd service file requires root privileges"); err != nil { + return fmt.Errorf("failed to get sudo privileges: %w", err) + } + + // Create environment file content + envContent := fmt.Sprintf(`# Quilibrium Node Environment`, dataPath) + + // Write environment file + envPath := filepath.Join(dataPath, "config", "quilibrium.env") + if err := os.WriteFile(envPath, []byte(envContent), 0640); err != nil { + return fmt.Errorf("failed to create environment file: %w", err) + } + + // Set ownership of environment file + chownCmd := exec.Command("chown", nodeUser+":"+nodeUser, envPath) + if err := chownCmd.Run(); err != nil { + return fmt.Errorf("failed to set environment file ownership: %w", err) + } + + // Create systemd service file content + serviceContent := fmt.Sprintf(`[Unit] +Description=Quilibrium Node Service +After=network.target + +[Service] +Type=simple +User=quilibrium +EnvironmentFile=/opt/quilibrium/config/quilibrium.env +ExecStart=/usr/local/bin/quilibrium-node --config /opt/quilibrium/config +Restart=on-failure +RestartSec=10 +LimitNOFILE=65535 + +[Install] +WantedBy=multi-user.target +`, nodeUser, defaultSymlinkPath, dataPath) + + // Write service file + servicePath := "/etc/systemd/system/quilibrium-node.service" + if err := os.WriteFile(servicePath, []byte(serviceContent), 0644); err != nil { + return fmt.Errorf("failed to create service file: %w", err) + } + + // Reload systemd daemon + reloadCmd := exec.Command("systemctl", "daemon-reload") + if err := reloadCmd.Run(); err != nil { + return fmt.Errorf("failed to reload systemd daemon: %w", err) + } + + fmt.Fprintf(os.Stdout, "Created systemd service file at %s\n", servicePath) + fmt.Fprintf(os.Stdout, "Created environment file at %s\n", envPath) + return nil +} + +// installMacOSService installs a launchd service on macOS +func installMacOSService() { + fmt.Println("Installing launchd service for Quilibrium node...") + + // Create plist file content + plistTemplate := ` + + + + Label + {{.Label}} + ProgramArguments + + /usr/local/bin/quilibrium-node + --config + /opt/quilibrium/config/ + + EnvironmentVariables + + QUILIBRIUM_DATA_DIR + {{.DataPath}} + QUILIBRIUM_LOG_LEVEL + info + QUILIBRIUM_LISTEN_GRPC_MULTIADDR + /ip4/127.0.0.1/tcp/8337 + QUILIBRIUM_LISTEN_REST_MULTIADDR + /ip4/127.0.0.1/tcp/8338 + QUILIBRIUM_STATS_MULTIADDR + /dns/stats.quilibrium.com/tcp/443 + QUILIBRIUM_NETWORK_ID + 0 + QUILIBRIUM_DEBUG + false + QUILIBRIUM_SIGNATURE_CHECK + true + + RunAtLoad + + KeepAlive + + StandardErrorPath + {{.LogPath}}/node.err + StandardOutPath + {{.LogPath}}/node.log + +` + + // Prepare template data + data := struct { + Label string + DataPath string + ServiceName string + LogPath string + }{ + Label: fmt.Sprintf("com.quilibrium.node"), + DataPath: dataPath, + ServiceName: "node", + LogPath: logPath, + } + + // Parse and execute template + tmpl, err := template.New("plist").Parse(plistTemplate) + if err != nil { + fmt.Printf("Error creating plist template: %v\n", err) + return + } + + // Determine plist file path + var plistPath = fmt.Sprintf("/Library/LaunchDaemons/%s.plist", data.Label) + + // Write plist file + file, err := os.Create(plistPath) + if err != nil { + fmt.Printf("Error creating plist file: %v\n", err) + return + } + defer file.Close() + + if err := tmpl.Execute(file, data); err != nil { + fmt.Printf("Error writing plist file: %v\n", err) + return + } + + // Set correct permissions + chownCmd := exec.Command("chown", "root:wheel", plistPath) + if err := chownCmd.Run(); err != nil { + fmt.Printf("Warning: Failed to change ownership of plist file: %v\n", err) + } + + // Load the service + var loadCmd = exec.Command("launchctl", "load", "-w", plistPath) + + if err := loadCmd.Run(); err != nil { + fmt.Printf("Error loading service: %v\n", err) + fmt.Println("You may need to load the service manually.") + } + + fmt.Printf("Launchd service installed successfully as %s\n", plistPath) + fmt.Println("\nTo start the service:") + fmt.Printf(" sudo launchctl start %s\n", data.Label) + fmt.Println("\nTo stop the service:") + fmt.Printf(" sudo launchctl stop %s\n", data.Label) + fmt.Println("\nTo view service logs:") + fmt.Printf(" cat %s/%s.log\n", data.LogPath, data.ServiceName) +} diff --git a/client/cmd/node/update.go b/client/cmd/node/update.go new file mode 100644 index 0000000..01e602c --- /dev/null +++ b/client/cmd/node/update.go @@ -0,0 +1,96 @@ +package node + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +// updateNodeCmd represents the command to update the Quilibrium node +var updateNodeCmd = &cobra.Command{ + Use: "update [version]", + Short: "Update Quilibrium node", + Long: `Update Quilibrium node to a specified version or the latest version. +If no version is specified, the latest version will be installed. + +Examples: + # Update to the latest version + qclient node update + + # Update to a specific version + qclient node update 2.1.0`, + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + // Get system information and validate + + // Determine version to install + version := determineVersion(args) + + fmt.Fprintf(os.Stdout, "Updating Quilibrium node for %s-%s, version: %s\n", osType, arch, version) + + // Update the node + updateNode(version) + }, +} + +func init() { + +} + +// updateNode handles the node update process +func updateNode(version string) { + // Check if we need sudo privileges + if err := utils.CheckAndRequestSudo(fmt.Sprintf("Updating node at %s requires root privileges", installPath)); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + // Create version-specific installation directory + versionDir := filepath.Join(installPath, "node", version) + if err := os.MkdirAll(versionDir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating installation directory: %v\n", err) + return + } + + // Create data directory + versionDataDir := filepath.Join(dataPath, "node", version) + if err := os.MkdirAll(versionDataDir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating data directory: %v\n", err) + return + } + + // Construct the expected filename for the specified version + // Remove 'v' prefix if present for filename construction + versionWithoutV := strings.TrimPrefix(version, "v") + fileName := fmt.Sprintf("node-%s-%s-%s", versionWithoutV, osType, arch) + + // Download the release directly + nodeBinaryPath := filepath.Join(dataPath, version, fileName) + err := utils.DownloadRelease(utils.ReleaseTypeNode, versionWithoutV) + if err != nil { + fmt.Fprintf(os.Stderr, "Error downloading version %s: %v\n", version, err) + fmt.Fprintf(os.Stderr, "The specified version %s does not exist for %s-%s\n", version, osType, arch) + // Clean up the created directories since installation failed + os.RemoveAll(versionDir) + os.RemoveAll(versionDataDir) + return + } + + // Download signature files + if err := utils.DownloadReleaseSignatures(utils.ReleaseTypeNode, versionWithoutV); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to download signature files: %v\n", err) + fmt.Fprintf(os.Stdout, "Continuing with installation...\n") + } + + // Ensure log rotation is set up + if err := setupLogRotation(); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to set up log rotation: %v\n", err) + } + + // Successfully downloaded the specific version + finishInstallation(nodeBinaryPath, version) +} diff --git a/client/cmd/node/utils.go b/client/cmd/node/utils.go new file mode 100644 index 0000000..2b4023a --- /dev/null +++ b/client/cmd/node/utils.go @@ -0,0 +1 @@ +package node diff --git a/client/cmd/root.go b/client/cmd/root.go index 292a816..e2a3cdc 100644 --- a/client/cmd/root.go +++ b/client/cmd/root.go @@ -1,6 +1,7 @@ package cmd import ( + "bufio" "bytes" "crypto/tls" "encoding/hex" @@ -17,6 +18,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" + "source.quilibrium.com/quilibrium/monorepo/client/cmd/node" + "source.quilibrium.com/quilibrium/monorepo/client/utils" "source.quilibrium.com/quilibrium/monorepo/node/config" ) @@ -27,10 +30,11 @@ var simulateFail bool var LightNode bool = false var DryRun bool = false var publicRPC bool = false - var rootCmd = &cobra.Command{ Use: "qclient", - Short: "Quilibrium RPC Client", + Short: "Quilibrium client", + Long: `Quilibrium client is a command-line tool for managing Quilibrium nodes. +It provides commands for installing, updating, and managing Quilibrium nodes.`, PersistentPreRun: func(cmd *cobra.Command, args []string) { if signatureCheck { ex, err := os.Executable() @@ -50,80 +54,97 @@ var rootCmd = &cobra.Command{ checksum := sha3.Sum256(b) digest, err := os.ReadFile(ex + ".dgst") if err != nil { - fmt.Println("Digest file not found") - os.Exit(1) - } + fmt.Println("The digest file was not found. Do you want to continue without signature verification? (y/n)") + fmt.Println("You can also use --signature-check=false in your command to skip this prompt") - parts := strings.Split(string(digest), " ") - if len(parts) != 2 { - fmt.Println("Invalid digest file format") - os.Exit(1) - } + reader := bufio.NewReader(os.Stdin) + response, _ := reader.ReadString('\n') + response = strings.TrimSpace(strings.ToLower(response)) - digestBytes, err := hex.DecodeString(parts[1][:64]) - if err != nil { - fmt.Println("Invalid digest file format") - os.Exit(1) - } - - if !bytes.Equal(checksum[:], digestBytes) { - fmt.Println("Invalid digest for node") - os.Exit(1) - } - - count := 0 - - for i := 1; i <= len(config.Signatories); i++ { - signatureFile := fmt.Sprintf(ex+".dgst.sig.%d", i) - sig, err := os.ReadFile(signatureFile) - if err != nil { - continue - } - - pubkey, _ := hex.DecodeString(config.Signatories[i-1]) - if !ed448.Verify(pubkey, digest, sig, "") { - fmt.Printf("Failed signature check for signatory #%d\n", i) + if response != "y" && response != "yes" { + fmt.Println("Exiting due to missing digest file") os.Exit(1) } - count++ - } - if count < ((len(config.Signatories)-4)/2)+((len(config.Signatories)-4)%2) { - fmt.Printf("Quorum on signatures not met") - os.Exit(1) - } + fmt.Println("Continuing without signature verification") + signatureCheck = false + } else { + parts := strings.Split(string(digest), " ") + if len(parts) != 2 { + fmt.Println("Invalid digest file format") + os.Exit(1) + } - fmt.Println("Signature check passed") + digestBytes, err := hex.DecodeString(parts[1][:64]) + if err != nil { + fmt.Println("Invalid digest file format") + os.Exit(1) + } + + if !bytes.Equal(checksum[:], digestBytes) { + fmt.Println("Invalid digest for node") + os.Exit(1) + } + + count := 0 + + for i := 1; i <= len(config.Signatories); i++ { + signatureFile := fmt.Sprintf(ex+".dgst.sig.%d", i) + sig, err := os.ReadFile(signatureFile) + if err != nil { + continue + } + + pubkey, _ := hex.DecodeString(config.Signatories[i-1]) + if !ed448.Verify(pubkey, digest, sig, "") { + fmt.Printf("Failed signature check for signatory #%d\n", i) + os.Exit(1) + } + count++ + } + + if count < ((len(config.Signatories)-4)/2)+((len(config.Signatories)-4)%2) { + fmt.Printf("Quorum on signatures not met") + os.Exit(1) + } + + fmt.Println("Signature check passed") + } } else { fmt.Println("Signature check bypassed, be sure you know what you're doing") } - _, err := os.Stat(configDirectory) - if os.IsNotExist(err) { - fmt.Printf("config directory doesn't exist: %s\n", configDirectory) - os.Exit(1) - } + // Skip config checks for node and link commands + if len(os.Args) > 1 && (os.Args[1] != "node" && os.Args[1] != "link") { + // These commands handle their own configuration + _, err := os.Stat(configDirectory) + if os.IsNotExist(err) { + fmt.Printf("config directory doesn't exist: %s\n", configDirectory) + os.Exit(1) + } - NodeConfig, err = config.LoadConfig(configDirectory, "", false) - if err != nil { - fmt.Printf("invalid config directory: %s\n", configDirectory) - os.Exit(1) - } + NodeConfig, err = config.LoadConfig(configDirectory, "", false) + if err != nil { + fmt.Printf("invalid config directory: %s\n", configDirectory) + os.Exit(1) + } - if publicRPC { - fmt.Println("Public RPC enabled, using light node") - LightNode = true - } - if !LightNode && NodeConfig.ListenGRPCMultiaddr == "" { - fmt.Println("No ListenGRPCMultiaddr found in config, using light node") - LightNode = true + if publicRPC { + fmt.Println("Public RPC enabled, using light node") + LightNode = true + } + + if !LightNode && NodeConfig.ListenGRPCMultiaddr == "" { + fmt.Println("No ListenGRPCMultiaddr found in config, using light node") + LightNode = true + } } }, } func Execute() { - err := rootCmd.Execute() - if err != nil { + if err := rootCmd.Execute(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } } @@ -193,6 +214,46 @@ func init() { &publicRPC, "public-rpc", false, - "uses the public RPC", + "uses the public RPCd", ) + + // Create config directory if it doesn't exist + rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + // Skip for help command + if cmd.Name() == "help" { + return nil + } + + // Create config directory if it doesn't exist + if err := os.MkdirAll(utils.ClientConfigDir, 0755); err != nil { + return fmt.Errorf("failed to create config directory: %v", err) + } + + // Check for signature files + ex, err := os.Executable() + if err != nil { + return fmt.Errorf("failed to get executable path: %v", err) + } + + digestPath := ex + ".dgst" + if signatureCheck && !utils.FileExists(digestPath) { + fmt.Println("Signature file not found. Would you like to download it? (y/n)") + reader := bufio.NewReader(os.Stdin) + response, _ := reader.ReadString('\n') + response = strings.TrimSpace(strings.ToLower(response)) + + if response == "y" || response == "yes" { + fmt.Println("Downloading signature file...") + // TODO: Implement signature download logic + fmt.Println("Signature download not implemented yet. Please download manually.") + } else { + fmt.Println("Continuing without signature verification") + signatureCheck = false + } + } + return nil + } + + // Add the node command + rootCmd.AddCommand(node.NodeCmd) } diff --git a/client/cmd/update.go b/client/cmd/update.go new file mode 100644 index 0000000..2db2b66 --- /dev/null +++ b/client/cmd/update.go @@ -0,0 +1,172 @@ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/spf13/cobra" + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +var ( + osType = runtime.GOOS + arch = runtime.GOARCH +) + +// updateCmd represents the command to update the Quilibrium client +var updateCmd = &cobra.Command{ + Use: "update [version]", + Short: "Update Quilibrium client", + Long: `Update Quilibrium client to a specified version or the latest version. +If no version is specified, the latest version will be installed. + +Examples: + # Update to the latest version + qclient update + + # Update to a specific version + qclient update 2.1.0`, + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + // Get system information and validate + localOsType, localArch, err := utils.GetSystemInfo() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + osType = localOsType + arch = localArch + + // Determine version to install + version := determineVersion(args) + + fmt.Fprintf(os.Stdout, "Updating Quilibrium client for %s-%s, version: %s\n", osType, arch, version) + + // Update the client + updateClient(version) + }, +} + +func init() { + rootCmd.AddCommand(updateCmd) +} + +// determineVersion gets the version to install from args or defaults to "latest" +func determineVersion(args []string) string { + if len(args) > 0 { + return args[0] + } + return "latest" +} + +// updateClient handles the client update process +func updateClient(version string) { + // Check if we need sudo privileges + if err := utils.CheckAndRequestSudo(fmt.Sprintf("Updating client at %s requires root privileges", utils.ClientInstallPath)); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return + } + + // Create version-specific installation directory + versionDir := filepath.Join(utils.ClientInstallPath, version) + if err := os.MkdirAll(versionDir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating installation directory: %v\n", err) + return + } + + // Create data directory + versionDataDir := filepath.Join(utils.ClientDataPath, version) + if err := os.MkdirAll(versionDataDir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating data directory: %v\n", err) + return + } + + // If version is "latest", get the latest version + if version == "latest" { + latestVersion, err := utils.GetLatestVersion(utils.ReleaseTypeQClient) + if err != nil { + fmt.Fprintf(os.Stderr, "Error getting latest version: %v\n", err) + return + } + version = latestVersion + fmt.Fprintf(os.Stdout, "Latest version: %s\n", version) + } + + // Construct the expected filename for the specified version + // Remove 'v' prefix if present for filename construction + versionWithoutV := strings.TrimPrefix(version, "v") + + // Download the release directly + err := utils.DownloadRelease(utils.ReleaseTypeQClient, versionWithoutV) + if err != nil { + fmt.Fprintf(os.Stderr, "Error downloading version %s: %v\n", version, err) + fmt.Fprintf(os.Stderr, "The specified version %s does not exist for %s-%s\n", version, osType, arch) + // Clean up the created directories since installation failed + os.RemoveAll(versionDir) + os.RemoveAll(versionDataDir) + return + } + + // Download signature files + if err := utils.DownloadReleaseSignatures(utils.ReleaseTypeQClient, versionWithoutV); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to download signature files: %v\n", err) + fmt.Fprintf(os.Stdout, "Continuing with installation...\n") + } + + // Successfully downloaded the specific version + finishInstallation(version) +} + +// finishInstallation completes the update process +func finishInstallation(version string) { + // Read current config + config, err := utils.ReadClientConfig() + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading config: %v\n", err) + return + } + + // Update config with new version + config.Version = version + if err := utils.UpdateClientConfig(config); err != nil { + fmt.Fprintf(os.Stderr, "Error updating config: %v\n", err) + return + } + + // Construct executable path + execPath := filepath.Join(utils.ClientInstallPath, version, "qclient") + + // Make the binary executable + if err := os.Chmod(execPath, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error making binary executable: %v\n", err) + return + } + + // Create symlink to the new version + symlinkPath := utils.DefaultQClientSymlinkPath + if config.SymlinkPath != "" { + symlinkPath = config.SymlinkPath + } + + // Check if we need sudo privileges for creating symlink in system directory + if strings.HasPrefix(symlinkPath, "/usr/") || strings.HasPrefix(symlinkPath, "/bin/") || strings.HasPrefix(symlinkPath, "/sbin/") { + if err := utils.CheckAndRequestSudo(fmt.Sprintf("Creating symlink at %s requires root privileges", symlinkPath)); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to get sudo privileges: %v\n", err) + return + } + } + + // Create symlink + if err := utils.CreateSymlink(execPath, symlinkPath); err != nil { + fmt.Fprintf(os.Stderr, "Error creating symlink: %v\n", err) + return + } + + fmt.Fprintf(os.Stdout, "Successfully updated qclient to version %s\n", version) + fmt.Fprintf(os.Stdout, "Executable: %s\n", execPath) + fmt.Fprintf(os.Stdout, "Symlink: %s\n", symlinkPath) +} diff --git a/client/cmd/version.go b/client/cmd/version.go new file mode 100644 index 0000000..e55a680 --- /dev/null +++ b/client/cmd/version.go @@ -0,0 +1,86 @@ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + + "github.com/spf13/cobra" + "source.quilibrium.com/quilibrium/monorepo/client/utils" +) + +// Version information - fallback if executable name doesn't contain version +const ( + DefaultVersion = "1.0.0" +) + +// VersionInfo holds version and hash information +type VersionInfo struct { + Version string + SHA256 string + MD5 string +} + +// GetVersionInfo extracts version from executable and optionally calculates hashes +func GetVersionInfo(calcChecksum bool) (VersionInfo, error) { + executable, err := os.Executable() + if err != nil { + return VersionInfo{Version: DefaultVersion}, fmt.Errorf("error getting executable path: %v", err) + } + + // Extract version from executable name (e.g. qclient-2.0.3-linux-amd) + baseName := filepath.Base(executable) + versionPattern := regexp.MustCompile(`qclient-([0-9]+\.[0-9]+\.[0-9]+)`) + matches := versionPattern.FindStringSubmatch(baseName) + + version := DefaultVersion + if len(matches) > 1 { + version = matches[1] + } + + // If version not found or checksum requested, calculate hash + if len(matches) <= 1 || calcChecksum { + sha256Hash, md5Hash, err := utils.CalculateFileHashes(executable) + if err != nil { + return VersionInfo{Version: version}, fmt.Errorf("error calculating file hashes: %v", err) + } + + return VersionInfo{ + Version: version, + SHA256: sha256Hash, + MD5: md5Hash, + }, nil + } + + return VersionInfo{ + Version: version, + }, nil +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Display the qclient version", + Long: `Display the qclient version and optionally calculate SHA256 and MD5 hashes of the executable.`, + Run: func(cmd *cobra.Command, args []string) { + showChecksum, _ := cmd.Flags().GetBool("checksum") + + info, err := GetVersionInfo(showChecksum) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + + fmt.Printf("qclient %s\n", info.Version) + + if info.SHA256 != "" && info.MD5 != "" { + fmt.Printf("SHA256: %s\n", info.SHA256) + fmt.Printf("MD5: %s\n", info.MD5) + } + }, +} + +func init() { + versionCmd.Flags().Bool("checksum", false, "Show SHA256 and MD5 checksums of the executable") + rootCmd.AddCommand(versionCmd) +} diff --git a/client/test/Dockerfile b/client/test/Dockerfile new file mode 100644 index 0000000..3463ef3 --- /dev/null +++ b/client/test/Dockerfile @@ -0,0 +1,39 @@ +# Use build argument to specify the base image +ARG DISTRO=ubuntu +ARG VERSION=24.04 + +# Use the specified distribution as the base image +FROM --platform=$BUILDPLATFORM ${DISTRO}:${VERSION} + +ARG TARGETARCH +ARG TARGETOS + +RUN echo "TARGETARCH: $TARGETARCH" +RUN echo "TARGETOS: $TARGETOS" + +# Install required packages +RUN apt-get update && apt-get install -y \ + curl \ + sudo \ + lsb-release \ + && rm -rf /var/lib/apt/lists/* + +# Create a non-root user for testing +RUN useradd -m -s /bin/bash testuser && \ + echo "testuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +# Set working directory +WORKDIR /app + +# Copy the client binary and test script +COPY build/qclient/${TARGETARCH}_${TARGETOS}/qclient /opt/quilibrium/bin/qclient +COPY test_install.sh /app/ + +# Set permissions +RUN chmod +x /opt/quilibrium/bin/qclient /app/test_install.sh + +# Switch to test user +USER testuser + +# Run the test script +CMD ["/app/test_install.sh"] \ No newline at end of file diff --git a/client/test/Dockerfile.qclient b/client/test/Dockerfile.qclient new file mode 100644 index 0000000..3749984 --- /dev/null +++ b/client/test/Dockerfile.qclient @@ -0,0 +1,87 @@ +FROM ubuntu:24.04 AS build-base + +ENV PATH="${PATH}:/root/.cargo/bin/" + +# Install GMP 6.2 (6.3 which MacOS is using only available on Debian unstable) +RUN apt-get update && apt-get install -y \ + build-essential \ + curl \ + git \ + cmake \ + libgmp-dev \ + libmpfr-dev \ + libmpfr6 \ + wget \ + m4 \ + pkg-config \ + gcc \ + g++ \ + make \ + autoconf \ + automake \ + libtool \ + && rm -rf /var/lib/apt/lists/* + +# Install Go 1.22.5 for amd64 +RUN wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz && \ + rm -rf /usr/local/go && \ + tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz && \ + rm go1.22.5.linux-amd64.tar.gz + +ENV PATH=$PATH:/usr/local/go/bin + +# Install FLINT library +RUN git clone https://github.com/flintlib/flint.git && \ + cd flint && \ + git checkout flint-3.0 && \ + ./bootstrap.sh && \ + ./configure \ + --prefix=/usr/local \ + --with-gmp=/usr/local \ + --with-mpfr=/usr/local \ + --enable-static \ + --disable-shared \ + CFLAGS="-O3" && \ + make && \ + make install && \ + cd .. && \ + rm -rf flint + +# Install Rust toolchain +COPY docker/rustup-init.sh /opt/rustup-init.sh +RUN /opt/rustup-init.sh -y --profile minimal + +# Install uniffi-bindgen-go +RUN cargo install uniffi-bindgen-go --git https://github.com/NordSecurity/uniffi-bindgen-go --tag v0.2.1+v0.25.0 + +FROM build-base AS build + +ENV GOEXPERIMENT=arenas +ENV QUILIBRIUM_SIGNATURE_CHECK=false + +WORKDIR /opt/ceremonyclient + +COPY . . + +## Generate Rust bindings for VDF +WORKDIR /opt/ceremonyclient/vdf +RUN ./generate.sh + +## Generate Rust bindings for BLS48581 +WORKDIR /opt/ceremonyclient/bls48581 +RUN ./generate.sh + +## Generate Rust bindings for VerEnc +WORKDIR /opt/ceremonyclient/verenc +RUN ./generate.sh + +# Build and install qclient +WORKDIR /opt/ceremonyclient/client +RUN ./build.sh -o qclient + +# Create final stage to copy the binary +FROM scratch AS export-stage +COPY --from=build /opt/ceremonyclient/client/qclient ./client/test/ + + + diff --git a/client/test/README.md b/client/test/README.md new file mode 100644 index 0000000..2ddd818 --- /dev/null +++ b/client/test/README.md @@ -0,0 +1,155 @@ +# Quil Test Runner Documentation + +This document describes the usage and functionality of the Quil test runner script (`run_tests.sh`), which is designed to run tests across different Linux distributions using Docker containers. + +## Overview + +The test runner allows you to: +- Run tests on multiple Linux distributions simultaneously +- Test on a specific distribution and version +- Customize container tags for test runs +- Build the client binary using a standardized Docker build process + +## Prerequisites + +- Docker installed and running on your system +- Bash shell +- Access to the Quil client source code +- Sufficient disk space for building the client (approximately 2GB recommended) + +## Build Environment + +The test runner uses a multi-stage build process based on `Dockerfile.qclient` which includes: + +### Base Build Environment +- Ubuntu 24.04 as the base image +- Essential build tools (gcc, g++, make, etc.) +- GMP 6.2 and MPFR libraries +- Go 1.22.0 (amd64) +- Rust toolchain (via rustup) +- FLINT library (version 3.0) +- uniffi-bindgen-go (v0.2.1+v0.25.0) + +### Build Process +1. Generates Rust bindings for: + - VDF (Verifiable Delay Function) + - BLS48581 (Boneh-Lynn-Shacham signature scheme) + - VerEnc (Verifiable Encryption) +2. Builds and installs: + - qclient binary + +## Usage + +### Basic Usage + +To run tests on all supported distributions (Ubuntu 22.04, Ubuntu 24.04, and Debian 12): + +```bash +./run_tests.sh +``` + +### Custom Test Configuration + +To run tests on a specific distribution and version: + +```bash +./run_tests.sh -d DISTRO -v VERSION [-t TAG] +``` + +#### Parameters: +- `-d, --distro`: The Linux distribution to test (e.g., ubuntu, debian) +- `-v, --version`: The version of the distribution (e.g., 22.04, 12) +- `-t, --tag`: (Optional) Custom tag for the test container. If not provided, a tag will be automatically generated + +#### Examples: + +```bash +# Test Ubuntu 22.04 with auto-generated tag +./run_tests.sh -d ubuntu -v 22.04 + +# Test Debian 12 with custom tag +./run_tests.sh -d debian -v 12 -t my-custom-test + +# Show help message +./run_tests.sh --help +``` + +## Supported Distributions + +By default, the script tests the following distributions: +- Ubuntu 22.04 +- Ubuntu 24.04 +- Debian 12 + +## How It Works + +1. The script first builds the client binary using `Dockerfile.qclient`: + - Creates a build container with all required dependencies + - Generates necessary Rust bindings + - Builds the qclient binary + - Extracts the binary to the test directory + - Cleans up the build container + +2. For each test run: + - Creates a Docker container using the specified distribution and version + - Copies the built client binary into the test container + - Builds the test environment + - Runs the tests + - Cleans up the container after completion + +## Error Handling + +- The script uses `set -e` to exit on any error +- If any test fails, the script will exit with status code 1 +- Docker containers are automatically removed after test completion using the `--rm` flag +- Build errors in `Dockerfile.qclient` will be clearly displayed in the output + +## Notes + +- When running all tests simultaneously, the script uses background processes to parallelize the test runs +- The script automatically generates container tags if not specified, using the format `distroversion` (e.g., `ubuntu2204`) +- Make sure you have sufficient system resources when running multiple tests simultaneously +- The build process requires significant disk space due to the multi-stage build and dependencies +- The client binary is built specifically for amd64 architecture + +## Troubleshooting + +If you encounter issues: + +1. Ensure Docker is running: + ```bash + systemctl status docker + ``` + +2. Check Docker logs for container-specific issues: + ```bash + docker logs quil-test-[tag] + ``` + +3. Verify you have sufficient disk space and memory for running multiple containers + +4. For build-related issues: + - Check if all required dependencies are available in the target distribution + - Verify the build environment has sufficient resources + - Check the build logs for specific error messages + - Ensure you're running on an amd64 system or using appropriate Docker platform settings + +## Direct Docker Build + +If you just want to build the qclient in a Docker container without running the tests (useful if you can't build for the target testing environment): + +```bash +# in the project root directory +## Will take awhile to build flint on initial build +docker build -f client/test/Dockerfile.qclient -t qclient . +``` + +This command builds the Docker image with the qclient binary according to the specifications in `Dockerfile.qclient`. The resulting image will be tagged as `qclient`. + +## Contributing + +When adding new distributions or versions: +1. Update the default test configurations in the script +2. Ensure the corresponding Dockerfile supports the new distribution/version +3. Test the changes thoroughly before committing +4. Verify that all dependencies in `Dockerfile.qclient` are available in the target distribution \ No newline at end of file diff --git a/client/test/run_tests.sh b/client/test/run_tests.sh new file mode 100755 index 0000000..a123f72 --- /dev/null +++ b/client/test/run_tests.sh @@ -0,0 +1,97 @@ +#!/bin/bash +set -e + +# Help function +show_help() { + echo "Usage: $0 [OPTIONS]" + echo "Run tests on specified Linux distributions" + echo "" + echo "Options:" + echo " -d, --distro DISTRO Specify the distribution (e.g., ubuntu, debian)" + echo " -v, --version VERSION Specify the version (e.g., 22.04, 12)" + echo " -t, --tag TAG Specify a custom tag for the test container" + echo " -h, --help Show this help message" + echo "" + echo "If no arguments are provided, runs tests on all supported distributions" + exit 0 +} + +# Parse command line arguments +DISTRO="" +VERSION="" +TAG="" +while [[ $# -gt 0 ]]; do + case $1 in + -d|--distro) + DISTRO="$2" + shift 2 + ;; + -v|--version) + VERSION="$2" + shift 2 + ;; + -t|--tag) + TAG="$2" + shift 2 + ;; + -h|--help) + show_help + ;; + *) + echo "Unknown option: $1" + show_help + ;; + esac +done + +# Build the client binary using Dockerfile.qclient +echo "Building client binary using Dockerfile.qclient..." +docker build -t quil-qclient-builder -f Dockerfile.qclient .. +docker create --name quil-qclient-temp quil-qclient-builder +docker cp quil-qclient-temp:/usr/local/bin/qclient ./qclient +docker rm quil-qclient-temp + +# Function to run tests for a specific distribution +run_distro_test() { + local distro=$1 + local version=$2 + local tag=$3 + echo "Testing on $distro $version..." + docker build \ + --build-arg DISTRO=$distro \ + --build-arg VERSION=$version \ + -t quil-test-$tag \ + -f Dockerfile . + docker run --rm quil-test-$tag +} + +# If custom distro/version/tag is provided, run single test +if [ ! -z "$DISTRO" ] && [ ! -z "$VERSION" ]; then + if [ -z "$TAG" ]; then + TAG="${DISTRO}${VERSION//./}" + fi + echo "Running custom test configuration..." + run_distro_test "$DISTRO" "$VERSION" "$TAG" +else + # Run tests on all distributions simultaneously + echo "Running tests on all distributions simultaneously..." + run_distro_test "ubuntu" "22.04" "ubuntu22" & + UBUNTU22_PID=$! + + run_distro_test "ubuntu" "24.04" "ubuntu24" & + UBUNTU24_PID=$! + + run_distro_test "debian" "12" "debian12" & + DEBIAN12_PID=$! + + # Wait for all tests to complete + wait $UBUNTU22_PID $UBUNTU24_PID $DEBIAN12_PID + + # Check exit status of each test + if [ $? -ne 0 ]; then + echo "One or more tests failed!" + exit 1 + fi +fi + +echo "All distribution tests completed!" diff --git a/client/test/test_install.sh b/client/test/test_install.sh new file mode 100644 index 0000000..e451677 --- /dev/null +++ b/client/test/test_install.sh @@ -0,0 +1,115 @@ +#!/bin/bash +set -e + +# Get distribution information +DISTRO=$(lsb_release -si 2>/dev/null || echo "Unknown") +VERSION=$(lsb_release -sr 2>/dev/null || echo "Unknown") + +echo "Starting Quilibrium node installation test on $DISTRO $VERSION..." + +# Test 1: Install latest version +echo "Test 1: Installing latest version..." +qclient node install + +get_latest_version() { + # Fetch the latest version from the releases API + local latest_version=$(curl -s https://releases.quilibrium.com/release | head -n 1 | cut -d'-' -f2) + + echo "$latest_version" +} + +LATEST_VERSION=$(get_latest_version) + +# Verify installation +echo "Verifying installation..." +if [ ! -f "/opt/quilibrium/$LATEST_VERSION/node-$LATEST_VERSION-linux-amd64" ]; then + echo "Error: Latest version binary not found" + exit 1 +fi + +# Verify latest version matches +echo "Verifying latest version matches..." +get_latest_version + +# Test 2: Install specific version +echo "Test 2: Installing specific version..." +qclient node install "2.0.6.2" + +# Verify specific version installation +echo "Verifying specific version installation..." +if [ ! -f "/opt/quilibrium/2.0.6.2/node-2.0.6.2-linux-amd64" ]; then + echo "Error: Specific version binary not found" + exit 1 +fi + +# Test 3: Verify service file creation +echo "Test 3: Verifying service file creation..." +if [ ! -f "/etc/systemd/system/quilibrium-node.service" ]; then + echo "Error: Service file not found" + exit 1 +fi + +# Verify service file content +echo "Verifying service file content..." +if ! grep -q "EnvironmentFile=/etc/default/quilibrium-node" /etc/systemd/system/quilibrium-node.service; then + echo "Error: Service file missing EnvironmentFile directive" + exit 1 +fi + +# Test 4: Verify environment file +echo "Test 4: Verifying environment file..." +if [ ! -f "/etc/default/quilibrium-node" ]; then + echo "Error: Environment file not found" + exit 1 +fi + +# Verify environment file permissions +echo "Verifying environment file permissions..." +if [ "$(stat -c %a /etc/default/quilibrium-node)" != "640" ]; then + echo "Error: Environment file has incorrect permissions" + exit 1 +fi + +# Test 5: Verify data directory +echo "Test 5: Verifying data directory..." +if [ ! -d "/var/lib/quilibrium" ]; then + echo "Error: Data directory not found" + exit 1 +fi + +# Verify data directory permissions +echo "Verifying data directory permissions..." +if [ "$(stat -c %a /var/lib/quilibrium)" != "755" ]; then + echo "Error: Data directory has incorrect permissions" + exit 1 +fi + +# Test 6: Verify config file +echo "Test 6: Verifying config file..." +if [ ! -f "/var/lib/quilibrium/config/node.yaml" ]; then + echo "Error: Config file not found" + exit 1 +fi + +# Verify config file permissions +echo "Verifying config file permissions..." +if [ "$(stat -c %a /var/lib/quilibrium/config/node.yaml)" != "644" ]; then + echo "Error: Config file has incorrect permissions" + exit 1 +fi + +# Test 7: Verify binary symlink +echo "Test 7: Verifying binary symlink..." +if [ ! -L "/usr/local/bin/quilibrium-node" ]; then + echo "Error: Binary symlink not found" + exit 1 +fi + +# Test 8: Verify binary execution +echo "Test 8: Verifying binary execution..." +if ! quilibrium-node --version > /dev/null 2>&1; then + echo "Error: Binary execution failed" + exit 1 +fi + +echo "All tests passed successfully on $DISTRO $VERSION!" \ No newline at end of file diff --git a/client/utils/download.go b/client/utils/download.go new file mode 100644 index 0000000..5c40a11 --- /dev/null +++ b/client/utils/download.go @@ -0,0 +1,168 @@ +package utils + +import ( + "bufio" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" +) + +var BaseReleaseURL = "https://releases.quilibrium.com" + +// DownloadRelease downloads a specific release file +func DownloadRelease(releaseType ReleaseType, version string) error { + fileName := fmt.Sprintf("%s-%s-%s-%s", releaseType, version, osType, arch) + return DownloadReleaseFile(releaseType, fileName, version, true) +} + +// GetLatestVersion fetches the latest version from the releases API +func GetLatestVersion(releaseType ReleaseType) (string, error) { + // Determine the appropriate URL based on the release type + releaseURL := fmt.Sprintf("%s/release", BaseReleaseURL) + if releaseType == ReleaseTypeQClient { + releaseURL = fmt.Sprintf("%s/qclient-release", BaseReleaseURL) + } + resp, err := http.Get(releaseURL) + if err != nil { + return "", fmt.Errorf("failed to fetch latest version: %w", err) + } + defer resp.Body.Close() + + scanner := bufio.NewScanner(resp.Body) + if !scanner.Scan() { + return "", fmt.Errorf("no response data found") + } + + // Get the first line which contains the filename + filename := scanner.Text() + + // Split the filename by "-" and get the version part + parts := strings.Split(filename, "-") + if len(parts) < 2 { + return "", fmt.Errorf("invalid filename format: %s", filename) + } + + // The version is the second part (index 1) + version := parts[1] + return version, nil +} + +// DownloadReleaseFile downloads a release file from the Quilibrium releases server +func DownloadReleaseFile(releaseType ReleaseType, fileName string, version string, showError bool) error { + url := fmt.Sprintf("%s/%s", BaseReleaseURL, fileName) + destPath := filepath.Join(DataPath, string(releaseType), version, fileName) + + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + if showError { + return fmt.Errorf("failed to download file: %s", resp.Status) + } else { + return nil + } + } + + out, err := os.Create(destPath) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + fmt.Fprintf(os.Stdout, "Downloaded %s to %s\n", fileName, destPath) + return nil +} + +// DownloadReleaseSignatures downloads signature files for a release +func DownloadReleaseSignatures(releaseType ReleaseType, version string) error { + var files []string + baseName := fmt.Sprintf("%s-%s-%s-%s", releaseType, version, osType, arch) + + // Add digest file URL + files = append(files, baseName+".dgst") + + // Add signature file URLs + signerNums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17} + for _, num := range signerNums { + files = append(files, fmt.Sprintf("%s.dgst.sig.%d", baseName, num)) + } + + for _, file := range files { + err := DownloadReleaseFile(releaseType, file, version, false) + if err != nil { + return err + } + } + return nil +} + +// GetLatestReleaseFiles fetches the list of available release files +func GetLatestReleaseFiles(releaseType ReleaseType) ([]string, error) { + releaseURL := fmt.Sprintf("%s/release", BaseReleaseURL) + if releaseType == ReleaseTypeQClient { + releaseURL = fmt.Sprintf("%s/qclient-release", BaseReleaseURL) + } + resp, err := http.Get(releaseURL) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to fetch releases: %s", resp.Status) + } + + // Read the response body and parse it + var releases []string + + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + releases = append(releases, scanner.Text()) + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading response: %w", err) + } + + return releases, nil +} + +// FilterReleasesByOSArch filters releases by OS and architecture +func FilterReleasesByOSArch(releases []string, osType, arch string) []string { + var filtered []string + for _, release := range releases { + if strings.Contains(release, osType) && strings.Contains(release, arch) { + filtered = append(filtered, release) + } + } + return filtered +} + +// ExtractVersionFromFileName extracts the version from a release filename +func ExtractVersionFromFileName(releaseType ReleaseType, fileName, osType, arch string) string { + version := strings.TrimPrefix(fileName, string(releaseType)+"-") + version = strings.TrimSuffix(version, "-"+osType+"-"+arch) + return version +} + +// DownloadAllReleaseFiles downloads all release files +func DownloadAllReleaseFiles(releaseType ReleaseType, fileNames []string, installDir string) bool { + for _, fileName := range fileNames { + filePath := filepath.Join(installDir, fileName) + if err := DownloadReleaseFile(releaseType, fileName, filePath, true); err != nil { + fmt.Fprintf(os.Stderr, "Error downloading release file %s: %v\n", fileName, err) + return false + } + } + return true +} diff --git a/client/utils/fileUtils.go b/client/utils/fileUtils.go new file mode 100644 index 0000000..ef6d91d --- /dev/null +++ b/client/utils/fileUtils.go @@ -0,0 +1,238 @@ +package utils + +import ( + "crypto/md5" + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + + "gopkg.in/yaml.v3" +) + +// DefaultNodeUser is the default user name for node operations +var DefaultNodeUser = "quilibrium" +var ClientConfigDir = filepath.Join("/etc/quilibrium/", "config") +var ClientConfigPath = filepath.Join(ClientConfigDir, "client.yaml") +var ClientInstallPath = filepath.Join("/opt/quilibrium/", "client") +var DataPath = filepath.Join("/var/quilibrium/", "data") +var ClientDataPath = filepath.Join(DataPath, "client") +var NodeDataPath = filepath.Join(DataPath, "node") +var NodeDefaultSymlinkDir = "/usr/local/bin" +var DefaultNodeSymlinkPath = filepath.Join(NodeDefaultSymlinkDir, "quilibrium-node") +var DefaultQClientSymlinkPath = filepath.Join(NodeDefaultSymlinkDir, "qclient") +var osType = runtime.GOOS +var arch = runtime.GOARCH + +// CalculateFileHashes calculates SHA256 and MD5 hashes for a file +func CalculateFileHashes(filePath string) (string, string, error) { + file, err := os.Open(filePath) + if err != nil { + return "", "", fmt.Errorf("error opening file: %w", err) + } + defer file.Close() + + // Calculate SHA256 + sha256Hash := sha256.New() + if _, err := io.Copy(sha256Hash, file); err != nil { + return "", "", fmt.Errorf("error calculating SHA256: %w", err) + } + + // Reset file position to beginning for MD5 calculation + if _, err := file.Seek(0, 0); err != nil { + return "", "", fmt.Errorf("error seeking file: %w", err) + } + + // Calculate MD5 + md5Hash := md5.New() + if _, err := io.Copy(md5Hash, file); err != nil { + return "", "", fmt.Errorf("error calculating MD5: %w", err) + } + + return hex.EncodeToString(sha256Hash.Sum(nil)), hex.EncodeToString(md5Hash.Sum(nil)), nil +} + +// CreateSymlink creates a symlink, handling the case where it already exists +func CreateSymlink(execPath, targetPath string) error { + // Check if the symlink already exists + if _, err := os.Lstat(targetPath); err == nil { + // Symlink exists, ask if user wants to overwrite + if !ConfirmSymlinkOverwrite(targetPath) { + fmt.Println("Operation cancelled.") + return nil + } + + // Remove existing symlink + if err := os.Remove(targetPath); err != nil { + return fmt.Errorf("failed to remove existing symlink: %w", err) + } + } + + // Create the symlink + if err := os.Symlink(execPath, targetPath); err != nil { + return fmt.Errorf("failed to create symlink: %w", err) + } + + return nil +} + +// ReadClientConfig reads the client configuration from the specified config directory +// If the config file doesn't exist, it returns an empty config +func ReadClientConfig() (*ClientConfig, error) { + // Check if config file exists + if !FileExists(ClientConfigPath) { + // Return empty config if file doesn't exist + return &ClientConfig{}, nil + } + + // Read the config file + data, err := os.ReadFile(ClientConfigPath) + if err != nil { + return nil, fmt.Errorf("error reading config file: %w", err) + } + + // Parse YAML + var config ClientConfig + err = yaml.Unmarshal(data, &config) + if err != nil { + return nil, fmt.Errorf("error parsing config file: %w", err) + } + + return &config, nil +} + +// UpdateClientConfig updates the client configuration in the specified config directory +// If the config file doesn't exist, it creates a new one +func UpdateClientConfig(config *ClientConfig) error { + configDir := ClientConfigDir + // Check if we need sudo privileges (if config directory is in a system directory) + if err := CheckAndRequestSudo(fmt.Sprintf("Updating config directory at %s requires root privileges", configDir)); err != nil { + return fmt.Errorf("failed to get sudo privileges: %w", err) + } + + // Create config directory if it doesn't exist + if err := os.MkdirAll(configDir, 0755); err != nil { + return fmt.Errorf("failed to create config directory: %w", err) + } + + configPath := GetConfigPath(configDir) + + // Marshal config to YAML + data, err := yaml.Marshal(config) + if err != nil { + return fmt.Errorf("error serializing config: %w", err) + } + + // Write config to file + if err := os.WriteFile(configPath, data, 0644); err != nil { + return fmt.Errorf("error writing config file: %w", err) + } + + // Set ownership if a dedicated user was created + if DefaultNodeUser != "" { + // Check for sudo privileges for changing ownership + if err := CheckAndRequestSudo(fmt.Sprintf("Changing ownership of %s requires root privileges", configPath)); err != nil { + return fmt.Errorf("failed to get sudo privileges: %w", err) + } + + chownCmd := exec.Command("chown", DefaultNodeUser+":"+DefaultNodeUser, configPath) + if err := chownCmd.Run(); err != nil { + return fmt.Errorf("failed to change ownership of config file: %w", err) + } + } + + return nil +} + +// CreateConfigFile creates a basic configuration file for the node +func CreateConfigFile(configDir, dataDir, version string) { + // Create a ClientConfig struct + config := ClientConfig{ + Version: version, + DataDir: ClientDataPath, + SymlinkPath: DefaultQClientSymlinkPath, + } + + // Use UpdateClientConfig to save the configuration + if err := UpdateClientConfig(&config); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Failed to create config file: %v\n", err) + return + } + + fmt.Fprintf(os.Stdout, "Created configuration file at %s/config.yaml\n", configDir) +} + +// ValidateAndCreateDir validates a directory path and creates it if it doesn't exist +func ValidateAndCreateDir(path string) error { + // Check if the directory exists + info, err := os.Stat(path) + if err == nil { + // Path exists, check if it's a directory + if !info.IsDir() { + return fmt.Errorf("%s exists but is not a directory", path) + } + return nil + } + + // Directory doesn't exist, try to create it + if os.IsNotExist(err) { + if err := os.MkdirAll(path, 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %v", path, err) + } + return nil + } + + // Some other error occurred + return fmt.Errorf("error checking directory %s: %v", path, err) +} + +// IsWritable checks if a directory is writable +func IsWritable(dir string) bool { + // Check if directory exists + info, err := os.Stat(dir) + if err != nil || !info.IsDir() { + return false + } + + // Check if directory is writable by creating a temporary file + tempFile := filepath.Join(dir, ".quilibrium_write_test") + file, err := os.Create(tempFile) + if err != nil { + return false + } + file.Close() + os.Remove(tempFile) + return true +} + +// CanCreateAndWrite checks if we can create and write to a directory +func CanCreateAndWrite(dir string) bool { + // Try to create the directory + if err := os.MkdirAll(dir, 0755); err != nil { + return false + } + + // Check if we can write to it + return IsWritable(dir) +} + +// FileExists checks if a file exists +func FileExists(path string) bool { + _, err := os.Stat(path) + return !os.IsNotExist(err) +} + +// GetConfigPath returns the path to the client configuration file +func GetConfigPath(configDir string) string { + return filepath.Join(configDir, "config.yaml") +} + +// IsClientConfigured checks if the client is configured +func IsClientConfigured(configDir string) bool { + configPath := GetConfigPath(configDir) + return FileExists(configPath) +} diff --git a/client/utils/system.go b/client/utils/system.go new file mode 100644 index 0000000..b76289d --- /dev/null +++ b/client/utils/system.go @@ -0,0 +1,28 @@ +package utils + +import ( + "fmt" + "runtime" +) + +// GetSystemInfo determines and validates the OS and architecture +func GetSystemInfo() (string, string, error) { + osType := runtime.GOOS + arch := runtime.GOARCH + + // Check if OS type is supported + if osType != "darwin" && osType != "linux" { + return "", "", fmt.Errorf("unsupported operating system: %s", osType) + } + + // Map Go architecture names to Quilibrium architecture names + if arch == "amd64" { + arch = "amd64" + } else if arch == "arm64" { + arch = "arm64" + } else { + return "", "", fmt.Errorf("unsupported architecture: %s", arch) + } + + return osType, arch, nil +} diff --git a/client/utils/types.go b/client/utils/types.go new file mode 100644 index 0000000..871af7d --- /dev/null +++ b/client/utils/types.go @@ -0,0 +1,20 @@ +package utils + +type ClientConfig struct { + Version string + DataDir string + SymlinkPath string +} + +type NodeConfig struct { + ClientConfig + DataDir string + User string +} + +type ReleaseType string + +const ( + ReleaseTypeQClient ReleaseType = "qclient" + ReleaseTypeNode ReleaseType = "node" +) diff --git a/client/utils/userInputUtils.go b/client/utils/userInputUtils.go new file mode 100644 index 0000000..8bb7ac3 --- /dev/null +++ b/client/utils/userInputUtils.go @@ -0,0 +1,37 @@ +package utils + +import ( + "fmt" + "os" + "os/exec" + "strings" +) + +// ConfirmSymlinkOverwrite asks the user to confirm overwriting an existing symlink +func ConfirmSymlinkOverwrite(path string) bool { + fmt.Printf("Symlink already exists at %s. Overwrite? [y/N]: ", path) + var response string + fmt.Scanln(&response) + return strings.ToLower(response) == "y" +} + +// CheckAndRequestSudo checks if we have sudo privileges and requests them if needed +func CheckAndRequestSudo(reason string) error { + // Check if we're already root + if os.Geteuid() == 0 { + return nil + } + + // Check if sudo is available + if _, err := exec.LookPath("sudo"); err != nil { + return fmt.Errorf("sudo is not available: %w", err) + } + + // Request sudo privileges + cmd := exec.Command("sudo", "-v") + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to get sudo privileges: %w", err) + } + + return nil +} diff --git a/verenc/generated/verenc/verenc.c b/verenc/generated/verenc/verenc.c new file mode 100644 index 0000000..b46e410 --- /dev/null +++ b/verenc/generated/verenc/verenc.c @@ -0,0 +1,8 @@ +#include + +// This file exists beacause of +// https://github.com/golang/go/issues/11263 + +void cgo_rust_task_callback_bridge_verenc(RustTaskCallback cb, const void * taskData, int8_t status) { + cb(taskData, status); +} \ No newline at end of file diff --git a/verenc/generated/verenc/verenc.go b/verenc/generated/verenc/verenc.go new file mode 100644 index 0000000..c36da21 --- /dev/null +++ b/verenc/generated/verenc/verenc.go @@ -0,0 +1,1055 @@ +package verenc + +// #include +import "C" + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + "unsafe" +) + +type RustBuffer = C.RustBuffer + +type RustBufferI interface { + AsReader() *bytes.Reader + Free() + ToGoBytes() []byte + Data() unsafe.Pointer + Len() int + Capacity() int +} + +func RustBufferFromExternal(b RustBufferI) RustBuffer { + return RustBuffer{ + capacity: C.int(b.Capacity()), + len: C.int(b.Len()), + data: (*C.uchar)(b.Data()), + } +} + +func (cb RustBuffer) Capacity() int { + return int(cb.capacity) +} + +func (cb RustBuffer) Len() int { + return int(cb.len) +} + +func (cb RustBuffer) Data() unsafe.Pointer { + return unsafe.Pointer(cb.data) +} + +func (cb RustBuffer) AsReader() *bytes.Reader { + b := unsafe.Slice((*byte)(cb.data), C.int(cb.len)) + return bytes.NewReader(b) +} + +func (cb RustBuffer) Free() { + rustCall(func(status *C.RustCallStatus) bool { + C.ffi_verenc_rustbuffer_free(cb, status) + return false + }) +} + +func (cb RustBuffer) ToGoBytes() []byte { + return C.GoBytes(unsafe.Pointer(cb.data), C.int(cb.len)) +} + +func stringToRustBuffer(str string) RustBuffer { + return bytesToRustBuffer([]byte(str)) +} + +func bytesToRustBuffer(b []byte) RustBuffer { + if len(b) == 0 { + return RustBuffer{} + } + // We can pass the pointer along here, as it is pinned + // for the duration of this call + foreign := C.ForeignBytes{ + len: C.int(len(b)), + data: (*C.uchar)(unsafe.Pointer(&b[0])), + } + + return rustCall(func(status *C.RustCallStatus) RustBuffer { + return C.ffi_verenc_rustbuffer_from_bytes(foreign, status) + }) +} + +type BufLifter[GoType any] interface { + Lift(value RustBufferI) GoType +} + +type BufLowerer[GoType any] interface { + Lower(value GoType) RustBuffer +} + +type FfiConverter[GoType any, FfiType any] interface { + Lift(value FfiType) GoType + Lower(value GoType) FfiType +} + +type BufReader[GoType any] interface { + Read(reader io.Reader) GoType +} + +type BufWriter[GoType any] interface { + Write(writer io.Writer, value GoType) +} + +type FfiRustBufConverter[GoType any, FfiType any] interface { + FfiConverter[GoType, FfiType] + BufReader[GoType] +} + +func LowerIntoRustBuffer[GoType any](bufWriter BufWriter[GoType], value GoType) RustBuffer { + // This might be not the most efficient way but it does not require knowing allocation size + // beforehand + var buffer bytes.Buffer + bufWriter.Write(&buffer, value) + + bytes, err := io.ReadAll(&buffer) + if err != nil { + panic(fmt.Errorf("reading written data: %w", err)) + } + return bytesToRustBuffer(bytes) +} + +func LiftFromRustBuffer[GoType any](bufReader BufReader[GoType], rbuf RustBufferI) GoType { + defer rbuf.Free() + reader := rbuf.AsReader() + item := bufReader.Read(reader) + if reader.Len() > 0 { + // TODO: Remove this + leftover, _ := io.ReadAll(reader) + panic(fmt.Errorf("Junk remaining in buffer after lifting: %s", string(leftover))) + } + return item +} + +func rustCallWithError[U any](converter BufLifter[error], callback func(*C.RustCallStatus) U) (U, error) { + var status C.RustCallStatus + returnValue := callback(&status) + err := checkCallStatus(converter, status) + + return returnValue, err +} + +func checkCallStatus(converter BufLifter[error], status C.RustCallStatus) error { + switch status.code { + case 0: + return nil + case 1: + return converter.Lift(status.errorBuf) + case 2: + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if status.errorBuf.len > 0 { + panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf))) + } else { + panic(fmt.Errorf("Rust panicked while handling Rust panic")) + } + default: + return fmt.Errorf("unknown status code: %d", status.code) + } +} + +func checkCallStatusUnknown(status C.RustCallStatus) error { + switch status.code { + case 0: + return nil + case 1: + panic(fmt.Errorf("function not returning an error returned an error")) + case 2: + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if status.errorBuf.len > 0 { + panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf))) + } else { + panic(fmt.Errorf("Rust panicked while handling Rust panic")) + } + default: + return fmt.Errorf("unknown status code: %d", status.code) + } +} + +func rustCall[U any](callback func(*C.RustCallStatus) U) U { + returnValue, err := rustCallWithError(nil, callback) + if err != nil { + panic(err) + } + return returnValue +} + +func writeInt8(writer io.Writer, value int8) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeUint8(writer io.Writer, value uint8) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeInt16(writer io.Writer, value int16) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeUint16(writer io.Writer, value uint16) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeInt32(writer io.Writer, value int32) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeUint32(writer io.Writer, value uint32) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeInt64(writer io.Writer, value int64) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeUint64(writer io.Writer, value uint64) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeFloat32(writer io.Writer, value float32) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeFloat64(writer io.Writer, value float64) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func readInt8(reader io.Reader) int8 { + var result int8 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readUint8(reader io.Reader) uint8 { + var result uint8 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readInt16(reader io.Reader) int16 { + var result int16 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readUint16(reader io.Reader) uint16 { + var result uint16 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readInt32(reader io.Reader) int32 { + var result int32 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readUint32(reader io.Reader) uint32 { + var result uint32 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readInt64(reader io.Reader) int64 { + var result int64 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readUint64(reader io.Reader) uint64 { + var result uint64 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readFloat32(reader io.Reader) float32 { + var result float32 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readFloat64(reader io.Reader) float64 { + var result float64 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func init() { + + uniffiCheckChecksums() +} + +func uniffiCheckChecksums() { + // Get the bindings contract version from our ComponentInterface + bindingsContractVersion := 24 + // Get the scaffolding contract version by calling the into the dylib + scaffoldingContractVersion := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint32_t { + return C.ffi_verenc_uniffi_contract_version(uniffiStatus) + }) + if bindingsContractVersion != int(scaffoldingContractVersion) { + // If this happens try cleaning and rebuilding your project + panic("verenc: UniFFI contract version mismatch") + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_verenc_checksum_func_chunk_data_for_verenc(uniffiStatus) + }) + if checksum != 16794 { + // If this happens try cleaning and rebuilding your project + panic("verenc: uniffi_verenc_checksum_func_chunk_data_for_verenc: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_verenc_checksum_func_combine_chunked_data(uniffiStatus) + }) + if checksum != 28541 { + // If this happens try cleaning and rebuilding your project + panic("verenc: uniffi_verenc_checksum_func_combine_chunked_data: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_verenc_checksum_func_new_verenc_proof(uniffiStatus) + }) + if checksum != 7394 { + // If this happens try cleaning and rebuilding your project + panic("verenc: uniffi_verenc_checksum_func_new_verenc_proof: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_verenc_checksum_func_new_verenc_proof_encrypt_only(uniffiStatus) + }) + if checksum != 17751 { + // If this happens try cleaning and rebuilding your project + panic("verenc: uniffi_verenc_checksum_func_new_verenc_proof_encrypt_only: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_verenc_checksum_func_verenc_compress(uniffiStatus) + }) + if checksum != 11234 { + // If this happens try cleaning and rebuilding your project + panic("verenc: uniffi_verenc_checksum_func_verenc_compress: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_verenc_checksum_func_verenc_recover(uniffiStatus) + }) + if checksum != 38626 { + // If this happens try cleaning and rebuilding your project + panic("verenc: uniffi_verenc_checksum_func_verenc_recover: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_verenc_checksum_func_verenc_verify(uniffiStatus) + }) + if checksum != 51440 { + // If this happens try cleaning and rebuilding your project + panic("verenc: uniffi_verenc_checksum_func_verenc_verify: UniFFI API checksum mismatch") + } + } +} + +type FfiConverterUint8 struct{} + +var FfiConverterUint8INSTANCE = FfiConverterUint8{} + +func (FfiConverterUint8) Lower(value uint8) C.uint8_t { + return C.uint8_t(value) +} + +func (FfiConverterUint8) Write(writer io.Writer, value uint8) { + writeUint8(writer, value) +} + +func (FfiConverterUint8) Lift(value C.uint8_t) uint8 { + return uint8(value) +} + +func (FfiConverterUint8) Read(reader io.Reader) uint8 { + return readUint8(reader) +} + +type FfiDestroyerUint8 struct{} + +func (FfiDestroyerUint8) Destroy(_ uint8) {} + +type FfiConverterUint64 struct{} + +var FfiConverterUint64INSTANCE = FfiConverterUint64{} + +func (FfiConverterUint64) Lower(value uint64) C.uint64_t { + return C.uint64_t(value) +} + +func (FfiConverterUint64) Write(writer io.Writer, value uint64) { + writeUint64(writer, value) +} + +func (FfiConverterUint64) Lift(value C.uint64_t) uint64 { + return uint64(value) +} + +func (FfiConverterUint64) Read(reader io.Reader) uint64 { + return readUint64(reader) +} + +type FfiDestroyerUint64 struct{} + +func (FfiDestroyerUint64) Destroy(_ uint64) {} + +type FfiConverterBool struct{} + +var FfiConverterBoolINSTANCE = FfiConverterBool{} + +func (FfiConverterBool) Lower(value bool) C.int8_t { + if value { + return C.int8_t(1) + } + return C.int8_t(0) +} + +func (FfiConverterBool) Write(writer io.Writer, value bool) { + if value { + writeInt8(writer, 1) + } else { + writeInt8(writer, 0) + } +} + +func (FfiConverterBool) Lift(value C.int8_t) bool { + return value != 0 +} + +func (FfiConverterBool) Read(reader io.Reader) bool { + return readInt8(reader) != 0 +} + +type FfiDestroyerBool struct{} + +func (FfiDestroyerBool) Destroy(_ bool) {} + +type FfiConverterString struct{} + +var FfiConverterStringINSTANCE = FfiConverterString{} + +func (FfiConverterString) Lift(rb RustBufferI) string { + defer rb.Free() + reader := rb.AsReader() + b, err := io.ReadAll(reader) + if err != nil { + panic(fmt.Errorf("reading reader: %w", err)) + } + return string(b) +} + +func (FfiConverterString) Read(reader io.Reader) string { + length := readInt32(reader) + buffer := make([]byte, length) + read_length, err := reader.Read(buffer) + if err != nil { + panic(err) + } + if read_length != int(length) { + panic(fmt.Errorf("bad read length when reading string, expected %d, read %d", length, read_length)) + } + return string(buffer) +} + +func (FfiConverterString) Lower(value string) RustBuffer { + return stringToRustBuffer(value) +} + +func (FfiConverterString) Write(writer io.Writer, value string) { + if len(value) > math.MaxInt32 { + panic("String is too large to fit into Int32") + } + + writeInt32(writer, int32(len(value))) + write_length, err := io.WriteString(writer, value) + if err != nil { + panic(err) + } + if write_length != len(value) { + panic(fmt.Errorf("bad write length when writing string, expected %d, written %d", len(value), write_length)) + } +} + +type FfiDestroyerString struct{} + +func (FfiDestroyerString) Destroy(_ string) {} + +type CompressedCiphertext struct { + Ctexts []VerencCiphertext + Aux [][]uint8 +} + +func (r *CompressedCiphertext) Destroy() { + FfiDestroyerSequenceTypeVerencCiphertext{}.Destroy(r.Ctexts) + FfiDestroyerSequenceSequenceUint8{}.Destroy(r.Aux) +} + +type FfiConverterTypeCompressedCiphertext struct{} + +var FfiConverterTypeCompressedCiphertextINSTANCE = FfiConverterTypeCompressedCiphertext{} + +func (c FfiConverterTypeCompressedCiphertext) Lift(rb RustBufferI) CompressedCiphertext { + return LiftFromRustBuffer[CompressedCiphertext](c, rb) +} + +func (c FfiConverterTypeCompressedCiphertext) Read(reader io.Reader) CompressedCiphertext { + return CompressedCiphertext{ + FfiConverterSequenceTypeVerencCiphertextINSTANCE.Read(reader), + FfiConverterSequenceSequenceUint8INSTANCE.Read(reader), + } +} + +func (c FfiConverterTypeCompressedCiphertext) Lower(value CompressedCiphertext) RustBuffer { + return LowerIntoRustBuffer[CompressedCiphertext](c, value) +} + +func (c FfiConverterTypeCompressedCiphertext) Write(writer io.Writer, value CompressedCiphertext) { + FfiConverterSequenceTypeVerencCiphertextINSTANCE.Write(writer, value.Ctexts) + FfiConverterSequenceSequenceUint8INSTANCE.Write(writer, value.Aux) +} + +type FfiDestroyerTypeCompressedCiphertext struct{} + +func (_ FfiDestroyerTypeCompressedCiphertext) Destroy(value CompressedCiphertext) { + value.Destroy() +} + +type VerencCiphertext struct { + C1 []uint8 + C2 []uint8 + I uint64 +} + +func (r *VerencCiphertext) Destroy() { + FfiDestroyerSequenceUint8{}.Destroy(r.C1) + FfiDestroyerSequenceUint8{}.Destroy(r.C2) + FfiDestroyerUint64{}.Destroy(r.I) +} + +type FfiConverterTypeVerencCiphertext struct{} + +var FfiConverterTypeVerencCiphertextINSTANCE = FfiConverterTypeVerencCiphertext{} + +func (c FfiConverterTypeVerencCiphertext) Lift(rb RustBufferI) VerencCiphertext { + return LiftFromRustBuffer[VerencCiphertext](c, rb) +} + +func (c FfiConverterTypeVerencCiphertext) Read(reader io.Reader) VerencCiphertext { + return VerencCiphertext{ + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterUint64INSTANCE.Read(reader), + } +} + +func (c FfiConverterTypeVerencCiphertext) Lower(value VerencCiphertext) RustBuffer { + return LowerIntoRustBuffer[VerencCiphertext](c, value) +} + +func (c FfiConverterTypeVerencCiphertext) Write(writer io.Writer, value VerencCiphertext) { + FfiConverterSequenceUint8INSTANCE.Write(writer, value.C1) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.C2) + FfiConverterUint64INSTANCE.Write(writer, value.I) +} + +type FfiDestroyerTypeVerencCiphertext struct{} + +func (_ FfiDestroyerTypeVerencCiphertext) Destroy(value VerencCiphertext) { + value.Destroy() +} + +type VerencDecrypt struct { + BlindingPubkey []uint8 + DecryptionKey []uint8 + Statement []uint8 + Ciphertexts CompressedCiphertext +} + +func (r *VerencDecrypt) Destroy() { + FfiDestroyerSequenceUint8{}.Destroy(r.BlindingPubkey) + FfiDestroyerSequenceUint8{}.Destroy(r.DecryptionKey) + FfiDestroyerSequenceUint8{}.Destroy(r.Statement) + FfiDestroyerTypeCompressedCiphertext{}.Destroy(r.Ciphertexts) +} + +type FfiConverterTypeVerencDecrypt struct{} + +var FfiConverterTypeVerencDecryptINSTANCE = FfiConverterTypeVerencDecrypt{} + +func (c FfiConverterTypeVerencDecrypt) Lift(rb RustBufferI) VerencDecrypt { + return LiftFromRustBuffer[VerencDecrypt](c, rb) +} + +func (c FfiConverterTypeVerencDecrypt) Read(reader io.Reader) VerencDecrypt { + return VerencDecrypt{ + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterTypeCompressedCiphertextINSTANCE.Read(reader), + } +} + +func (c FfiConverterTypeVerencDecrypt) Lower(value VerencDecrypt) RustBuffer { + return LowerIntoRustBuffer[VerencDecrypt](c, value) +} + +func (c FfiConverterTypeVerencDecrypt) Write(writer io.Writer, value VerencDecrypt) { + FfiConverterSequenceUint8INSTANCE.Write(writer, value.BlindingPubkey) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.DecryptionKey) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.Statement) + FfiConverterTypeCompressedCiphertextINSTANCE.Write(writer, value.Ciphertexts) +} + +type FfiDestroyerTypeVerencDecrypt struct{} + +func (_ FfiDestroyerTypeVerencDecrypt) Destroy(value VerencDecrypt) { + value.Destroy() +} + +type VerencProof struct { + BlindingPubkey []uint8 + EncryptionKey []uint8 + Statement []uint8 + Challenge []uint8 + Polycom [][]uint8 + Ctexts []VerencCiphertext + SharesRands []VerencShare +} + +func (r *VerencProof) Destroy() { + FfiDestroyerSequenceUint8{}.Destroy(r.BlindingPubkey) + FfiDestroyerSequenceUint8{}.Destroy(r.EncryptionKey) + FfiDestroyerSequenceUint8{}.Destroy(r.Statement) + FfiDestroyerSequenceUint8{}.Destroy(r.Challenge) + FfiDestroyerSequenceSequenceUint8{}.Destroy(r.Polycom) + FfiDestroyerSequenceTypeVerencCiphertext{}.Destroy(r.Ctexts) + FfiDestroyerSequenceTypeVerencShare{}.Destroy(r.SharesRands) +} + +type FfiConverterTypeVerencProof struct{} + +var FfiConverterTypeVerencProofINSTANCE = FfiConverterTypeVerencProof{} + +func (c FfiConverterTypeVerencProof) Lift(rb RustBufferI) VerencProof { + return LiftFromRustBuffer[VerencProof](c, rb) +} + +func (c FfiConverterTypeVerencProof) Read(reader io.Reader) VerencProof { + return VerencProof{ + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceTypeVerencCiphertextINSTANCE.Read(reader), + FfiConverterSequenceTypeVerencShareINSTANCE.Read(reader), + } +} + +func (c FfiConverterTypeVerencProof) Lower(value VerencProof) RustBuffer { + return LowerIntoRustBuffer[VerencProof](c, value) +} + +func (c FfiConverterTypeVerencProof) Write(writer io.Writer, value VerencProof) { + FfiConverterSequenceUint8INSTANCE.Write(writer, value.BlindingPubkey) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.EncryptionKey) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.Statement) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.Challenge) + FfiConverterSequenceSequenceUint8INSTANCE.Write(writer, value.Polycom) + FfiConverterSequenceTypeVerencCiphertextINSTANCE.Write(writer, value.Ctexts) + FfiConverterSequenceTypeVerencShareINSTANCE.Write(writer, value.SharesRands) +} + +type FfiDestroyerTypeVerencProof struct{} + +func (_ FfiDestroyerTypeVerencProof) Destroy(value VerencProof) { + value.Destroy() +} + +type VerencProofAndBlindingKey struct { + BlindingKey []uint8 + BlindingPubkey []uint8 + DecryptionKey []uint8 + EncryptionKey []uint8 + Statement []uint8 + Challenge []uint8 + Polycom [][]uint8 + Ctexts []VerencCiphertext + SharesRands []VerencShare +} + +func (r *VerencProofAndBlindingKey) Destroy() { + FfiDestroyerSequenceUint8{}.Destroy(r.BlindingKey) + FfiDestroyerSequenceUint8{}.Destroy(r.BlindingPubkey) + FfiDestroyerSequenceUint8{}.Destroy(r.DecryptionKey) + FfiDestroyerSequenceUint8{}.Destroy(r.EncryptionKey) + FfiDestroyerSequenceUint8{}.Destroy(r.Statement) + FfiDestroyerSequenceUint8{}.Destroy(r.Challenge) + FfiDestroyerSequenceSequenceUint8{}.Destroy(r.Polycom) + FfiDestroyerSequenceTypeVerencCiphertext{}.Destroy(r.Ctexts) + FfiDestroyerSequenceTypeVerencShare{}.Destroy(r.SharesRands) +} + +type FfiConverterTypeVerencProofAndBlindingKey struct{} + +var FfiConverterTypeVerencProofAndBlindingKeyINSTANCE = FfiConverterTypeVerencProofAndBlindingKey{} + +func (c FfiConverterTypeVerencProofAndBlindingKey) Lift(rb RustBufferI) VerencProofAndBlindingKey { + return LiftFromRustBuffer[VerencProofAndBlindingKey](c, rb) +} + +func (c FfiConverterTypeVerencProofAndBlindingKey) Read(reader io.Reader) VerencProofAndBlindingKey { + return VerencProofAndBlindingKey{ + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceTypeVerencCiphertextINSTANCE.Read(reader), + FfiConverterSequenceTypeVerencShareINSTANCE.Read(reader), + } +} + +func (c FfiConverterTypeVerencProofAndBlindingKey) Lower(value VerencProofAndBlindingKey) RustBuffer { + return LowerIntoRustBuffer[VerencProofAndBlindingKey](c, value) +} + +func (c FfiConverterTypeVerencProofAndBlindingKey) Write(writer io.Writer, value VerencProofAndBlindingKey) { + FfiConverterSequenceUint8INSTANCE.Write(writer, value.BlindingKey) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.BlindingPubkey) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.DecryptionKey) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.EncryptionKey) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.Statement) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.Challenge) + FfiConverterSequenceSequenceUint8INSTANCE.Write(writer, value.Polycom) + FfiConverterSequenceTypeVerencCiphertextINSTANCE.Write(writer, value.Ctexts) + FfiConverterSequenceTypeVerencShareINSTANCE.Write(writer, value.SharesRands) +} + +type FfiDestroyerTypeVerencProofAndBlindingKey struct{} + +func (_ FfiDestroyerTypeVerencProofAndBlindingKey) Destroy(value VerencProofAndBlindingKey) { + value.Destroy() +} + +type VerencShare struct { + S1 []uint8 + S2 []uint8 + I uint64 +} + +func (r *VerencShare) Destroy() { + FfiDestroyerSequenceUint8{}.Destroy(r.S1) + FfiDestroyerSequenceUint8{}.Destroy(r.S2) + FfiDestroyerUint64{}.Destroy(r.I) +} + +type FfiConverterTypeVerencShare struct{} + +var FfiConverterTypeVerencShareINSTANCE = FfiConverterTypeVerencShare{} + +func (c FfiConverterTypeVerencShare) Lift(rb RustBufferI) VerencShare { + return LiftFromRustBuffer[VerencShare](c, rb) +} + +func (c FfiConverterTypeVerencShare) Read(reader io.Reader) VerencShare { + return VerencShare{ + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterSequenceUint8INSTANCE.Read(reader), + FfiConverterUint64INSTANCE.Read(reader), + } +} + +func (c FfiConverterTypeVerencShare) Lower(value VerencShare) RustBuffer { + return LowerIntoRustBuffer[VerencShare](c, value) +} + +func (c FfiConverterTypeVerencShare) Write(writer io.Writer, value VerencShare) { + FfiConverterSequenceUint8INSTANCE.Write(writer, value.S1) + FfiConverterSequenceUint8INSTANCE.Write(writer, value.S2) + FfiConverterUint64INSTANCE.Write(writer, value.I) +} + +type FfiDestroyerTypeVerencShare struct{} + +func (_ FfiDestroyerTypeVerencShare) Destroy(value VerencShare) { + value.Destroy() +} + +type FfiConverterSequenceUint8 struct{} + +var FfiConverterSequenceUint8INSTANCE = FfiConverterSequenceUint8{} + +func (c FfiConverterSequenceUint8) Lift(rb RustBufferI) []uint8 { + return LiftFromRustBuffer[[]uint8](c, rb) +} + +func (c FfiConverterSequenceUint8) Read(reader io.Reader) []uint8 { + length := readInt32(reader) + if length == 0 { + return nil + } + result := make([]uint8, 0, length) + for i := int32(0); i < length; i++ { + result = append(result, FfiConverterUint8INSTANCE.Read(reader)) + } + return result +} + +func (c FfiConverterSequenceUint8) Lower(value []uint8) RustBuffer { + return LowerIntoRustBuffer[[]uint8](c, value) +} + +func (c FfiConverterSequenceUint8) Write(writer io.Writer, value []uint8) { + if len(value) > math.MaxInt32 { + panic("[]uint8 is too large to fit into Int32") + } + + writeInt32(writer, int32(len(value))) + for _, item := range value { + FfiConverterUint8INSTANCE.Write(writer, item) + } +} + +type FfiDestroyerSequenceUint8 struct{} + +func (FfiDestroyerSequenceUint8) Destroy(sequence []uint8) { + for _, value := range sequence { + FfiDestroyerUint8{}.Destroy(value) + } +} + +type FfiConverterSequenceTypeVerencCiphertext struct{} + +var FfiConverterSequenceTypeVerencCiphertextINSTANCE = FfiConverterSequenceTypeVerencCiphertext{} + +func (c FfiConverterSequenceTypeVerencCiphertext) Lift(rb RustBufferI) []VerencCiphertext { + return LiftFromRustBuffer[[]VerencCiphertext](c, rb) +} + +func (c FfiConverterSequenceTypeVerencCiphertext) Read(reader io.Reader) []VerencCiphertext { + length := readInt32(reader) + if length == 0 { + return nil + } + result := make([]VerencCiphertext, 0, length) + for i := int32(0); i < length; i++ { + result = append(result, FfiConverterTypeVerencCiphertextINSTANCE.Read(reader)) + } + return result +} + +func (c FfiConverterSequenceTypeVerencCiphertext) Lower(value []VerencCiphertext) RustBuffer { + return LowerIntoRustBuffer[[]VerencCiphertext](c, value) +} + +func (c FfiConverterSequenceTypeVerencCiphertext) Write(writer io.Writer, value []VerencCiphertext) { + if len(value) > math.MaxInt32 { + panic("[]VerencCiphertext is too large to fit into Int32") + } + + writeInt32(writer, int32(len(value))) + for _, item := range value { + FfiConverterTypeVerencCiphertextINSTANCE.Write(writer, item) + } +} + +type FfiDestroyerSequenceTypeVerencCiphertext struct{} + +func (FfiDestroyerSequenceTypeVerencCiphertext) Destroy(sequence []VerencCiphertext) { + for _, value := range sequence { + FfiDestroyerTypeVerencCiphertext{}.Destroy(value) + } +} + +type FfiConverterSequenceTypeVerencShare struct{} + +var FfiConverterSequenceTypeVerencShareINSTANCE = FfiConverterSequenceTypeVerencShare{} + +func (c FfiConverterSequenceTypeVerencShare) Lift(rb RustBufferI) []VerencShare { + return LiftFromRustBuffer[[]VerencShare](c, rb) +} + +func (c FfiConverterSequenceTypeVerencShare) Read(reader io.Reader) []VerencShare { + length := readInt32(reader) + if length == 0 { + return nil + } + result := make([]VerencShare, 0, length) + for i := int32(0); i < length; i++ { + result = append(result, FfiConverterTypeVerencShareINSTANCE.Read(reader)) + } + return result +} + +func (c FfiConverterSequenceTypeVerencShare) Lower(value []VerencShare) RustBuffer { + return LowerIntoRustBuffer[[]VerencShare](c, value) +} + +func (c FfiConverterSequenceTypeVerencShare) Write(writer io.Writer, value []VerencShare) { + if len(value) > math.MaxInt32 { + panic("[]VerencShare is too large to fit into Int32") + } + + writeInt32(writer, int32(len(value))) + for _, item := range value { + FfiConverterTypeVerencShareINSTANCE.Write(writer, item) + } +} + +type FfiDestroyerSequenceTypeVerencShare struct{} + +func (FfiDestroyerSequenceTypeVerencShare) Destroy(sequence []VerencShare) { + for _, value := range sequence { + FfiDestroyerTypeVerencShare{}.Destroy(value) + } +} + +type FfiConverterSequenceSequenceUint8 struct{} + +var FfiConverterSequenceSequenceUint8INSTANCE = FfiConverterSequenceSequenceUint8{} + +func (c FfiConverterSequenceSequenceUint8) Lift(rb RustBufferI) [][]uint8 { + return LiftFromRustBuffer[[][]uint8](c, rb) +} + +func (c FfiConverterSequenceSequenceUint8) Read(reader io.Reader) [][]uint8 { + length := readInt32(reader) + if length == 0 { + return nil + } + result := make([][]uint8, 0, length) + for i := int32(0); i < length; i++ { + result = append(result, FfiConverterSequenceUint8INSTANCE.Read(reader)) + } + return result +} + +func (c FfiConverterSequenceSequenceUint8) Lower(value [][]uint8) RustBuffer { + return LowerIntoRustBuffer[[][]uint8](c, value) +} + +func (c FfiConverterSequenceSequenceUint8) Write(writer io.Writer, value [][]uint8) { + if len(value) > math.MaxInt32 { + panic("[][]uint8 is too large to fit into Int32") + } + + writeInt32(writer, int32(len(value))) + for _, item := range value { + FfiConverterSequenceUint8INSTANCE.Write(writer, item) + } +} + +type FfiDestroyerSequenceSequenceUint8 struct{} + +func (FfiDestroyerSequenceSequenceUint8) Destroy(sequence [][]uint8) { + for _, value := range sequence { + FfiDestroyerSequenceUint8{}.Destroy(value) + } +} + +func ChunkDataForVerenc(data []uint8) [][]uint8 { + return FfiConverterSequenceSequenceUint8INSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_verenc_fn_func_chunk_data_for_verenc(FfiConverterSequenceUint8INSTANCE.Lower(data), _uniffiStatus) + })) +} + +func CombineChunkedData(chunks [][]uint8) []uint8 { + return FfiConverterSequenceUint8INSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_verenc_fn_func_combine_chunked_data(FfiConverterSequenceSequenceUint8INSTANCE.Lower(chunks), _uniffiStatus) + })) +} + +func NewVerencProof(data []uint8) VerencProofAndBlindingKey { + return FfiConverterTypeVerencProofAndBlindingKeyINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_verenc_fn_func_new_verenc_proof(FfiConverterSequenceUint8INSTANCE.Lower(data), _uniffiStatus) + })) +} + +func NewVerencProofEncryptOnly(data []uint8, encryptionKeyBytes []uint8) VerencProofAndBlindingKey { + return FfiConverterTypeVerencProofAndBlindingKeyINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_verenc_fn_func_new_verenc_proof_encrypt_only(FfiConverterSequenceUint8INSTANCE.Lower(data), FfiConverterSequenceUint8INSTANCE.Lower(encryptionKeyBytes), _uniffiStatus) + })) +} + +func VerencCompress(proof VerencProof) CompressedCiphertext { + return FfiConverterTypeCompressedCiphertextINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_verenc_fn_func_verenc_compress(FfiConverterTypeVerencProofINSTANCE.Lower(proof), _uniffiStatus) + })) +} + +func VerencRecover(recovery VerencDecrypt) []uint8 { + return FfiConverterSequenceUint8INSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_verenc_fn_func_verenc_recover(FfiConverterTypeVerencDecryptINSTANCE.Lower(recovery), _uniffiStatus) + })) +} + +func VerencVerify(proof VerencProof) bool { + return FfiConverterBoolINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) C.int8_t { + return C.uniffi_verenc_fn_func_verenc_verify(FfiConverterTypeVerencProofINSTANCE.Lower(proof), _uniffiStatus) + })) +} diff --git a/verenc/generated/verenc/verenc.h b/verenc/generated/verenc/verenc.h new file mode 100644 index 0000000..f240c59 --- /dev/null +++ b/verenc/generated/verenc/verenc.h @@ -0,0 +1,439 @@ + + +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + + + +#include +#include + +// The following structs are used to implement the lowest level +// of the FFI, and thus useful to multiple uniffied crates. +// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H. +#ifdef UNIFFI_SHARED_H + // We also try to prevent mixing versions of shared uniffi header structs. + // If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V6 + #ifndef UNIFFI_SHARED_HEADER_V6 + #error Combining helper code from multiple versions of uniffi is not supported + #endif // ndef UNIFFI_SHARED_HEADER_V6 +#else +#define UNIFFI_SHARED_H +#define UNIFFI_SHARED_HEADER_V6 +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️ + +typedef struct RustBuffer { + int32_t capacity; + int32_t len; + uint8_t *data; +} RustBuffer; + +typedef int32_t (*ForeignCallback)(uint64_t, int32_t, uint8_t *, int32_t, RustBuffer *); + +// Task defined in Rust that Go executes +typedef void (*RustTaskCallback)(const void *, int8_t); + +// Callback to execute Rust tasks using a Go routine +// +// Args: +// executor: ForeignExecutor lowered into a uint64_t value +// delay: Delay in MS +// task: RustTaskCallback to call +// task_data: data to pass the task callback +typedef int8_t (*ForeignExecutorCallback)(uint64_t, uint32_t, RustTaskCallback, void *); + +typedef struct ForeignBytes { + int32_t len; + const uint8_t *data; +} ForeignBytes; + +// Error definitions +typedef struct RustCallStatus { + int8_t code; + RustBuffer errorBuf; +} RustCallStatus; + +// Continuation callback for UniFFI Futures +typedef void (*RustFutureContinuation)(void * , int8_t); + +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️ +#endif // def UNIFFI_SHARED_H + +// Needed because we can't execute the callback directly from go. +void cgo_rust_task_callback_bridge_verenc(RustTaskCallback, const void *, int8_t); + +int8_t uniffiForeignExecutorCallbackverenc(uint64_t, uint32_t, RustTaskCallback, void*); + +void uniffiFutureContinuationCallbackverenc(void*, int8_t); + +RustBuffer uniffi_verenc_fn_func_chunk_data_for_verenc( + RustBuffer data, + RustCallStatus* out_status +); + +RustBuffer uniffi_verenc_fn_func_combine_chunked_data( + RustBuffer chunks, + RustCallStatus* out_status +); + +RustBuffer uniffi_verenc_fn_func_new_verenc_proof( + RustBuffer data, + RustCallStatus* out_status +); + +RustBuffer uniffi_verenc_fn_func_new_verenc_proof_encrypt_only( + RustBuffer data, + RustBuffer encryption_key_bytes, + RustCallStatus* out_status +); + +RustBuffer uniffi_verenc_fn_func_verenc_compress( + RustBuffer proof, + RustCallStatus* out_status +); + +RustBuffer uniffi_verenc_fn_func_verenc_recover( + RustBuffer recovery, + RustCallStatus* out_status +); + +int8_t uniffi_verenc_fn_func_verenc_verify( + RustBuffer proof, + RustCallStatus* out_status +); + +RustBuffer ffi_verenc_rustbuffer_alloc( + int32_t size, + RustCallStatus* out_status +); + +RustBuffer ffi_verenc_rustbuffer_from_bytes( + ForeignBytes bytes, + RustCallStatus* out_status +); + +void ffi_verenc_rustbuffer_free( + RustBuffer buf, + RustCallStatus* out_status +); + +RustBuffer ffi_verenc_rustbuffer_reserve( + RustBuffer buf, + int32_t additional, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_continuation_callback_set( + RustFutureContinuation callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_u8( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_u8( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_u8( + void* handle, + RustCallStatus* out_status +); + +uint8_t ffi_verenc_rust_future_complete_u8( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_i8( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_i8( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_i8( + void* handle, + RustCallStatus* out_status +); + +int8_t ffi_verenc_rust_future_complete_i8( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_u16( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_u16( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_u16( + void* handle, + RustCallStatus* out_status +); + +uint16_t ffi_verenc_rust_future_complete_u16( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_i16( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_i16( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_i16( + void* handle, + RustCallStatus* out_status +); + +int16_t ffi_verenc_rust_future_complete_i16( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_u32( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_u32( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_u32( + void* handle, + RustCallStatus* out_status +); + +uint32_t ffi_verenc_rust_future_complete_u32( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_i32( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_i32( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_i32( + void* handle, + RustCallStatus* out_status +); + +int32_t ffi_verenc_rust_future_complete_i32( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_u64( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_u64( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_u64( + void* handle, + RustCallStatus* out_status +); + +uint64_t ffi_verenc_rust_future_complete_u64( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_i64( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_i64( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_i64( + void* handle, + RustCallStatus* out_status +); + +int64_t ffi_verenc_rust_future_complete_i64( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_f32( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_f32( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_f32( + void* handle, + RustCallStatus* out_status +); + +float ffi_verenc_rust_future_complete_f32( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_f64( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_f64( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_f64( + void* handle, + RustCallStatus* out_status +); + +double ffi_verenc_rust_future_complete_f64( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_pointer( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_pointer( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_pointer( + void* handle, + RustCallStatus* out_status +); + +void* ffi_verenc_rust_future_complete_pointer( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_rust_buffer( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_rust_buffer( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_rust_buffer( + void* handle, + RustCallStatus* out_status +); + +RustBuffer ffi_verenc_rust_future_complete_rust_buffer( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_poll_void( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_cancel_void( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_free_void( + void* handle, + RustCallStatus* out_status +); + +void ffi_verenc_rust_future_complete_void( + void* handle, + RustCallStatus* out_status +); + +uint16_t uniffi_verenc_checksum_func_chunk_data_for_verenc( + RustCallStatus* out_status +); + +uint16_t uniffi_verenc_checksum_func_combine_chunked_data( + RustCallStatus* out_status +); + +uint16_t uniffi_verenc_checksum_func_new_verenc_proof( + RustCallStatus* out_status +); + +uint16_t uniffi_verenc_checksum_func_new_verenc_proof_encrypt_only( + RustCallStatus* out_status +); + +uint16_t uniffi_verenc_checksum_func_verenc_compress( + RustCallStatus* out_status +); + +uint16_t uniffi_verenc_checksum_func_verenc_recover( + RustCallStatus* out_status +); + +uint16_t uniffi_verenc_checksum_func_verenc_verify( + RustCallStatus* out_status +); + +uint32_t ffi_verenc_uniffi_contract_version( + RustCallStatus* out_status +); + + +