mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
initial auto-update
This commit is contained in:
parent
0966faee9f
commit
947f3f565f
142
client/cmd/link.go
Normal file
142
client/cmd/link.go
Normal file
@ -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")
|
||||
}
|
||||
46
client/cmd/node/info.go
Normal file
46
client/cmd/node/info.go
Normal file
@ -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)
|
||||
}
|
||||
115
client/cmd/node/install.go
Normal file
115
client/cmd/node/install.go
Normal file
@ -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
|
||||
}
|
||||
59
client/cmd/node/node.go
Normal file
59
client/cmd/node/node.go
Normal file
@ -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
|
||||
}
|
||||
284
client/cmd/node/service.go
Normal file
284
client/cmd/node/service.go
Normal file
@ -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")
|
||||
}
|
||||
411
client/cmd/node/shared.go
Normal file
411
client/cmd/node/shared.go
Normal file
@ -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 := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>{{.Label}}</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/quilibrium-node</string>
|
||||
<string>--config</string>
|
||||
<string>/opt/quilibrium/config/</string>
|
||||
</array>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>QUILIBRIUM_DATA_DIR</key>
|
||||
<string>{{.DataPath}}</string>
|
||||
<key>QUILIBRIUM_LOG_LEVEL</key>
|
||||
<string>info</string>
|
||||
<key>QUILIBRIUM_LISTEN_GRPC_MULTIADDR</key>
|
||||
<string>/ip4/127.0.0.1/tcp/8337</string>
|
||||
<key>QUILIBRIUM_LISTEN_REST_MULTIADDR</key>
|
||||
<string>/ip4/127.0.0.1/tcp/8338</string>
|
||||
<key>QUILIBRIUM_STATS_MULTIADDR</key>
|
||||
<string>/dns/stats.quilibrium.com/tcp/443</string>
|
||||
<key>QUILIBRIUM_NETWORK_ID</key>
|
||||
<string>0</string>
|
||||
<key>QUILIBRIUM_DEBUG</key>
|
||||
<string>false</string>
|
||||
<key>QUILIBRIUM_SIGNATURE_CHECK</key>
|
||||
<string>true</string>
|
||||
</dict>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>{{.LogPath}}/node.err</string>
|
||||
<key>StandardOutPath</key>
|
||||
<string>{{.LogPath}}/node.log</string>
|
||||
</dict>
|
||||
</plist>`
|
||||
|
||||
// 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)
|
||||
}
|
||||
96
client/cmd/node/update.go
Normal file
96
client/cmd/node/update.go
Normal file
@ -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)
|
||||
}
|
||||
1
client/cmd/node/utils.go
Normal file
1
client/cmd/node/utils.go
Normal file
@ -0,0 +1 @@
|
||||
package node
|
||||
@ -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)
|
||||
}
|
||||
|
||||
172
client/cmd/update.go
Normal file
172
client/cmd/update.go
Normal file
@ -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)
|
||||
}
|
||||
86
client/cmd/version.go
Normal file
86
client/cmd/version.go
Normal file
@ -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)
|
||||
}
|
||||
39
client/test/Dockerfile
Normal file
39
client/test/Dockerfile
Normal file
@ -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"]
|
||||
87
client/test/Dockerfile.qclient
Normal file
87
client/test/Dockerfile.qclient
Normal file
@ -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/
|
||||
|
||||
|
||||
|
||||
155
client/test/README.md
Normal file
155
client/test/README.md
Normal file
@ -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
|
||||
97
client/test/run_tests.sh
Executable file
97
client/test/run_tests.sh
Executable file
@ -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!"
|
||||
115
client/test/test_install.sh
Normal file
115
client/test/test_install.sh
Normal file
@ -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!"
|
||||
168
client/utils/download.go
Normal file
168
client/utils/download.go
Normal file
@ -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
|
||||
}
|
||||
238
client/utils/fileUtils.go
Normal file
238
client/utils/fileUtils.go
Normal file
@ -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)
|
||||
}
|
||||
28
client/utils/system.go
Normal file
28
client/utils/system.go
Normal file
@ -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
|
||||
}
|
||||
20
client/utils/types.go
Normal file
20
client/utils/types.go
Normal file
@ -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"
|
||||
)
|
||||
37
client/utils/userInputUtils.go
Normal file
37
client/utils/userInputUtils.go
Normal file
@ -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
|
||||
}
|
||||
8
verenc/generated/verenc/verenc.c
Normal file
8
verenc/generated/verenc/verenc.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include <verenc.h>
|
||||
|
||||
// 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);
|
||||
}
|
||||
1055
verenc/generated/verenc/verenc.go
Normal file
1055
verenc/generated/verenc/verenc.go
Normal file
File diff suppressed because it is too large
Load Diff
439
verenc/generated/verenc/verenc.h
Normal file
439
verenc/generated/verenc/verenc.h
Normal file
@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user