From 036f2a840e952695cc3c4662c98a48dc6549e435 Mon Sep 17 00:00:00 2001 From: huy Date: Thu, 2 Jan 2025 16:14:04 -0800 Subject: [PATCH 01/28] adapter --- .gitignore | 2 + adapter/config/claim-rewards.go | 72 +++++++++++++ adapter/config/commands.go | 131 ++++++++++++++++++++++++ adapter/config/exit.go | 121 ++++++++++++++++++++++ adapter/config/generate-deposit-data.go | 62 +++++++++++ adapter/config/generate-keys.go | 118 +++++++++++++++++++++ adapter/config/get-node-status.go | 105 +++++++++++++++++++ adapter/config/initialize.go | 44 ++++++++ adapter/config/upload-deposit-data.go | 30 ++++++ adapter/utils/flags.go | 81 +++++++++++++++ adapter/utils/validation.go | 20 ++++ 11 files changed, 786 insertions(+) create mode 100644 adapter/config/claim-rewards.go create mode 100644 adapter/config/commands.go create mode 100644 adapter/config/exit.go create mode 100644 adapter/config/generate-deposit-data.go create mode 100644 adapter/config/generate-keys.go create mode 100644 adapter/config/get-node-status.go create mode 100644 adapter/config/initialize.go create mode 100644 adapter/config/upload-deposit-data.go create mode 100644 adapter/utils/flags.go create mode 100644 adapter/utils/validation.go diff --git a/.gitignore b/.gitignore index 7e533e2..1b3bc21 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ build/ # Binaries hyperdrive-stakewise logs/ + +.DS_Store \ No newline at end of file diff --git a/adapter/config/claim-rewards.go b/adapter/config/claim-rewards.go new file mode 100644 index 0000000..091eaf7 --- /dev/null +++ b/adapter/config/claim-rewards.go @@ -0,0 +1,72 @@ +package config + +import ( + "github.com/urfave/cli/v2" +) + +func claimRewards(c *cli.Context) error { + return nil + // // Get the client + // hd, err := client.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := client.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + + // // Check if there's a node address ready + // _, ready, err := utils.CheckIfAddressReady(hd) + // if err != nil { + // return err + // } + // if !ready { + // return nil + // } + + // // Get the list of rewards available + // resp, err := sw.Api.Wallet.ClaimRewards() + // if err != nil { + // return err + // } + // fmt.Println("Your withdrawable rewards:") + // fmt.Printf("%.4f %s (%s)\n", eth.WeiToEth(resp.Data.WithdrawableToken), resp.Data.TokenSymbol, resp.Data.TokenName) + // fmt.Printf("%.4f ETH\n", eth.WeiToEth(resp.Data.WithdrawableNativeToken)) + // fmt.Println() + // fmt.Println("NOTE: this list only shows rewards that StakeWise has already returned to NodeSet. Your share may include more rewards, but StakeWise hasn't returned yet.") + // fmt.Println() + + // // Check if both balances are zero + // sum := big.NewInt(0) + // sum.Add(sum, resp.Data.WithdrawableNativeToken) + // sum.Add(sum, resp.Data.WithdrawableToken) + // if sum.Cmp(common.Big0) == 0 { + // fmt.Println("You don't have any rewards to claim.") + // return nil + // } + + // // Run the TX + // validated, err := tx.HandleTx(c, hd, resp.Data.TxInfo, + // "Are you sure you want to claim rewards?", + // "claiming rewards", + // "Claiming rewards...", + // ) + // if err != nil { + // return err + // } + // if !validated { + // return nil + // } + + // fmt.Println("Rewards successfully claimed.") + // return nil +} diff --git a/adapter/config/commands.go b/adapter/config/commands.go new file mode 100644 index 0000000..803e5af --- /dev/null +++ b/adapter/config/commands.go @@ -0,0 +1,131 @@ +package config + +import ( + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/urfave/cli/v2" +) + +// Handles `hd-module` commands +func RegisterCommands(app *cli.App) { + commands := []*cli.Command{ + { + Name: "nodeset", + Aliases: []string{"ns"}, + Usage: "Commands for interacting with the module's configuration", + Subcommands: []*cli.Command{ + { + Name: "upload-deposit-data", + Aliases: []string{"u"}, + Flags: []cli.Flag{ + utils.YesFlag, + }, + Usage: "Uploads the combined deposit data for all of your validator keys to NodeSet's Stakewise vault, so they can be assigned new deposits.", + Action: func(c *cli.Context) error { + utils.ValidateArgCount(c, 0) + return uploadDepositData(c) + }, + }, + { + Name: "generate-deposit-data", + Aliases: []string{"g"}, + Flags: []cli.Flag{ + utils.GeneratePubkeyFlag, + utils.GenerateIndentFlag, + }, + Usage: "Generates and prints the deposit data for your validators without uploading it to NodeSet. Useful for debugging.", + Action: func(c *cli.Context) error { + utils.ValidateArgCount(c, 0) + return generateDepositData(c) + }, + }, + }, + }, + { + Name: "status", + Aliases: []string{"s"}, + Usage: "Commands for interacting with the module's configuration", + Subcommands: []*cli.Command{ + { + Name: "status", + Aliases: []string{"s"}, + Usage: "Get active validators", + Action: func(c *cli.Context) error { + return getNodeStatus(c) + }, + }, + }, + }, + { + Name: "validator", + Aliases: []string{"v"}, + Usage: "Commands for interacting with the module's configuration", + Subcommands: []*cli.Command{ + { + Name: "exit", + Aliases: []string{"e"}, + Usage: "Exit a validator", + Flags: []cli.Flag{ + utils.PubkeysFlag, + utils.EpochFlag, + utils.NoBroadcastFlag, + }, + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return exit(c) + }, + }, + }, + }, + { + Name: "wallet", + Aliases: []string{"s"}, + Usage: "Commands for interacting with the module's configuration", + Subcommands: []*cli.Command{ + { + Name: "init", + Aliases: []string{"i"}, + Usage: "Clone the node wallet file into a wallet that the Stakewise operator service can use.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return initialize(c) + }, + }, + { + Name: "generate-keys", + Aliases: []string{"g"}, + Usage: "Generate new validator keys derived from your node wallet.", + Flags: []cli.Flag{ + utils.YesFlag, + utils.GenerateKeysCountFlag, + utils.GenerateKeysNoRestartFlag, + }, + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return generateKeys(c) + }, + }, + { + Name: "claim-rewards", + Aliases: []string{"cr"}, + Usage: "Claim rewards", + Flags: []cli.Flag{}, + Action: func(c *cli.Context) error { + // Run + return claimRewards(c) + }, + }, + }, + }, + } + + app.Commands = append(app.Commands, commands...) +} diff --git a/adapter/config/exit.go b/adapter/config/exit.go new file mode 100644 index 0000000..e08de81 --- /dev/null +++ b/adapter/config/exit.go @@ -0,0 +1,121 @@ +package config + +import ( + "github.com/urfave/cli/v2" +) + +func exit(c *cli.Context) error { + return nil + // // Get the client + // hd, err := client.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := client.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + + // // Check wallet status + // _, ready, err := utils.CheckIfWalletReady(hd) + // if err != nil { + // return err + // } + // if !ready { + // return nil + // } + + // // Get all active validators + // activeValidatorResponse, err := sw.Api.Status.GetValidatorStatuses() + // if err != nil { + // return fmt.Errorf("error while getting active validators: %w", err) + // } + // var activeValidators []beacon.ValidatorPubkey + // for _, state := range activeValidatorResponse.Data.States { + // if state.BeaconStatus == beacon.ValidatorState_ActiveOngoing { + // activeValidators = append(activeValidators, state.Pubkey) + // } + // } + // if len(activeValidators) == 0 { + // fmt.Println("None of your validators are active, so they cannot be exited.") + // return nil + // } + + // // Get selected validators + // options := make([]nutils.SelectionOption[beacon.ValidatorPubkey], len(activeValidators)) + // for i, pubkey := range activeValidators { + // option := &options[i] + // option.Element = &activeValidators[i] + // option.ID = activeValidators[i].HexWithPrefix() + // option.Display = fmt.Sprintf("%s (active since %s)", pubkey, time.Unix(0, 0)) // Placeholder, fill in with status details + // } + // selectedValidators, err := utils.GetMultiselectIndices(c, pubkeysFlag.Name, options, "Please select a validator to exit:") + // if err != nil { + // return fmt.Errorf("error determining validator selection: %w", err) + // } + + // // Get the epoch if set + // var epochPtr *uint64 + // if c.IsSet(epochFlag.Name) { + // epoch := c.Uint64(epochFlag.Name) + // epochPtr = &epoch + // } + + // // Get the no broadcast flag + // noBroadcastBool := false + // if c.IsSet(noBroadcastFlag.Name) { + // noBroadcastBool = c.Bool(noBroadcastFlag.Name) + // } + + // // Get the pubkeys + // pubkeys := make([]beacon.ValidatorPubkey, len(selectedValidators)) + // for i, validator := range selectedValidators { + // pubkeys[i] = *validator + // } + + // if !noBroadcastBool { + // // Show a warning message + // fmt.Printf("%sNOTE:\n", terminal.ColorYellow) + // fmt.Println("You are about to exit your validator(s). This will tell each one to stop all activities on the Beacon Chain.") + // fmt.Println("Please continue to run them until each one you've exited has been processed by the exit queue. It will no longer earn staking rewards after this point.") + // fmt.Printf("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (typically after a few days).%s\n", terminal.ColorReset) + + // // Prompt for confirmation + // if !(c.Bool(utils.YesFlag.Name) || utils.ConfirmWithIAgree(fmt.Sprintf("Are you sure you want to exit %d validator(s)? This action cannot be undone!", len(selectedValidators)))) { + // fmt.Println("Cancelled.") + // return nil + // } + // } + + // // Get signed exit messages + // response, err := sw.Api.Validator.Exit(pubkeys, epochPtr, noBroadcastBool) + // if err != nil { + // return fmt.Errorf("error while getting validator exit messages: %w", err) + // } + + // // Log successand return if not broadcasting + // if !noBroadcastBool { + // fmt.Println("Successfully exited the selected validator(s). It will take some time before their status is reflected on the Beacon Chain.") + // return nil + // } + + // // Print them all + // fmt.Printf("Exit epoch: %d\n", response.Data.Epoch) + // fmt.Println() + // for _, info := range response.Data.ExitInfos { + // fmt.Printf("Validator %d (%s):\n", info.Index, info.Pubkey.HexWithPrefix()) + // fmt.Printf("\tSignature: %s\n", info.Signature.HexWithPrefix()) + // fmt.Println() + // } + + // // Return + // return nil +} diff --git a/adapter/config/generate-deposit-data.go b/adapter/config/generate-deposit-data.go new file mode 100644 index 0000000..3c9fe99 --- /dev/null +++ b/adapter/config/generate-deposit-data.go @@ -0,0 +1,62 @@ +package config + +import ( + "github.com/urfave/cli/v2" +) + +func generateDepositData(c *cli.Context) error { + return nil + // // Get the client + // hd, err := client.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := client.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + + // // Parse the pubkeys + // pubkeyStrings := c.StringSlice(generatePubkeyFlag.Name) + // pubkeys := make([]beacon.ValidatorPubkey, len(pubkeyStrings)) + // for i, pubkeyString := range pubkeyStrings { + // pubkey, err := beacon.HexToValidatorPubkey(pubkeyString) + // if err != nil { + // return fmt.Errorf("error parsing pubkey [%s]: %w", pubkeyString, err) + // } + // pubkeys[i] = pubkey + // } + + // // Generate the deposit data + // fmt.Println("Generating deposit data...") + // response, err := sw.Api.Nodeset.GenerateDepositData(pubkeys) + // if err != nil { + // return err + // } + + // // Serialize the deposit data + // var bytes []byte + // shouldIndent := c.Bool(generateIndentFlag.Name) + // if shouldIndent { + // bytes, err = json.MarshalIndent(response.Data.Deposits, "", " ") + // } else { + // bytes, err = json.Marshal(response.Data.Deposits) + // } + // if err != nil { + // return fmt.Errorf("error serializing deposit data: %w", err) + // } + + // // Print the deposit data + // fmt.Println("Deposit data:") + // fmt.Println() + // fmt.Println(string(bytes)) + // return nil +} diff --git a/adapter/config/generate-keys.go b/adapter/config/generate-keys.go new file mode 100644 index 0000000..cb9aa07 --- /dev/null +++ b/adapter/config/generate-keys.go @@ -0,0 +1,118 @@ +package config + +import ( + "github.com/urfave/cli/v2" +) + +func generateKeys(c *cli.Context) error { + return nil + // // Get the client + // hd, err := client.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := client.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + // noRestart := c.Bool(generateKeysNoRestartFlag.Name) + + // // Check wallet status + // _, ready, err := utils.CheckIfWalletReady(hd) + // if err != nil { + // return err + // } + // if !ready { + // return nil + // } + + // // Get the count + // count := c.Uint64(generateKeysCountFlag.Name) + // if count == 0 { + // countString := cliutils.Prompt("How many keys would you like to generate?", "^\\d+$", "Invalid count, try again") + // count, err = input.ValidateUint("count", countString) + // if err != nil { + // return fmt.Errorf("invalid count [%s]: %w", countString, err) + // } + // } + + // fmt.Println("Note: key generation is an expensive process, this may take a long time! Progress will be printed as each key is generated.") + // fmt.Println() + + // // Generate the new keys + // startTime := time.Now() + // latestTime := startTime + // for i := uint64(0); i < count; i++ { + // response, err := sw.Api.Wallet.GenerateKeys(1, false) + // if err != nil { + // return fmt.Errorf("error generating keys: %w", err) + // } + // if len(response.Data.Pubkeys) == 0 { + // return fmt.Errorf("server did not return any pubkeys") + // } + + // elapsed := time.Since(latestTime) + // latestTime = time.Now() + // pubkey := response.Data.Pubkeys[0] + // fmt.Printf("Generated %s (%d/%d) in %s\n", pubkey.HexWithPrefix(), (i + 1), count, elapsed) + // } + // fmt.Printf("Completed in %s.\n", time.Since(startTime)) + // fmt.Println() + + // // Restart the Stakewise Operator + // if noRestart { + // fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Stakewise Operator container at your earliest convenience in order to deposit your new keys once it's your turn. Failure to do so will prevent your validators from ever being activated.%s\n", terminal.ColorYellow, terminal.ColorReset) + // } else { + // fmt.Print("Restarting Stakewise Operator... ") + // _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseOperator)) + // if err != nil { + // fmt.Println("error") + // fmt.Printf("%sWARNING: error restarting stakewise operator: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) + // fmt.Println("Please restart your Stakewise Operator container in order to be able to deposit for your new keys,") + // } else { + // fmt.Println("done!") + // } + // } + // fmt.Println() + + // // Restart the VC + // if noRestart { + // fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Validator Client at your earliest convenience in order to attest with your new keys. Failure to do so will result in any new validators being offline and *losing ETH* until you restart it.%s\n", terminal.ColorYellow, terminal.ColorReset) + // } else { + // fmt.Print("Restarting Validator Client... ") + // _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseValidator)) + // if err != nil { + // fmt.Println("error") + // fmt.Printf("%sWARNING: error restarting validator client: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) + // fmt.Println("Please restart your Validator Client in order to attest with your new keys!") + // } else { + // fmt.Println("done!") + // } + // } + // fmt.Println() + + // // Upload to the server + // newKeysUploaded, err := swcmdutils.UploadDepositData(c, hd, sw) + // if err != nil { + // return err + // } + + // if newKeysUploaded { + // if !noRestart { + // fmt.Println() + // fmt.Println("Your new keys are now ready for use. When one of them is selected for activation, your system will deposit it and begin attesting automatically.") + // } else { + // fmt.Println("Your new keys are uploaded, but you *must* restart your Validator Client at your earliest convenience to begin attesting once they are selected for depositing.") + // } + // } + + // return nil +} diff --git a/adapter/config/get-node-status.go b/adapter/config/get-node-status.go new file mode 100644 index 0000000..79e8621 --- /dev/null +++ b/adapter/config/get-node-status.go @@ -0,0 +1,105 @@ +package config + +import ( + "github.com/urfave/cli/v2" +) + +func getNodeStatus(c *cli.Context) error { + return nil + // // Get the client + // hd, err := client.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := client.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + + // // Check the registration status first + // shouldContinue, err := nodeset.CheckRegistrationStatus(c, hd) + // if err != nil { + // return fmt.Errorf("error checking nodeset registration status: %w", err) + // } + // if !shouldContinue { + // return nil + // } + + // // Get the validator statuses + // response, err := sw.Api.Status.GetValidatorStatuses() + // if err != nil { + // fmt.Printf("error fetching validator statuses: %v\n", err) + // return err + // } + + // if len(response.Data.States) == 0 { + // fmt.Println("You do not have any validators.") + // return nil + // } + + // for _, state := range response.Data.States { + // fmt.Printf("%s:\n", state.Pubkey.HexWithPrefix()) + + // // Print Beacon status + // if state.Index == "" { + // fmt.Println("\tBeacon State: Not seen by Beacon Chain yet") + // } else { + // fmt.Printf("\tBeacon Index: %s\n", state.Index) + // fmt.Printf("\tBeacon State: %s\n", getBeaconStatusLabel(state.BeaconStatus)) + // } + + // // Print NodeSet status + // fmt.Printf("\tNodeSet State: %s\n", getNodeSetStateLabel(state.NodesetStatus)) + // fmt.Println() + // } + + // return nil +} + +// func getBeaconStatusLabel(state beacon.ValidatorState) string { +// switch state { +// case beacon.ValidatorState_ActiveExiting: +// return "Active (Exiting in Progress)" +// case beacon.ValidatorState_ActiveOngoing: +// return "Active" +// case beacon.ValidatorState_ActiveSlashed: +// return "Slashed (Exit in Progress)" +// case beacon.ValidatorState_ExitedSlashed: +// return "Slashed (Exited)" +// case beacon.ValidatorState_ExitedUnslashed: +// return "Exited (Withdrawal Pending)" +// case beacon.ValidatorState_PendingInitialized: +// return "Seen on Beacon, Waiting for More Deposits" +// case beacon.ValidatorState_PendingQueued: +// return "In Beacon Activation Queue" +// case beacon.ValidatorState_WithdrawalDone: +// return "Exited and Withdrawn" +// case beacon.ValidatorState_WithdrawalPossible: +// return "Exited (Waiting for Wihdrawal)" +// default: +// return fmt.Sprintf("", state) +// } +// } + +// func getNodeSetStateLabel(state swtypes.NodesetStatus) string { +// switch state { +// case swtypes.NodesetStatus_Generated: +// return "Generated (Not Yet Uploaded)" +// case swtypes.NodesetStatus_RegisteredToStakewise: +// return "Registered with Stakewise" +// case swtypes.NodesetStatus_UploadedStakewise: +// return "Uploaded to Stakewise" +// case swtypes.NodesetStatus_UploadedToNodeset: +// return "Uploaded to NodeSet" +// default: +// return fmt.Sprintf("", state) +// } +// } diff --git a/adapter/config/initialize.go b/adapter/config/initialize.go new file mode 100644 index 0000000..c99535c --- /dev/null +++ b/adapter/config/initialize.go @@ -0,0 +1,44 @@ +package config + +import ( + "github.com/urfave/cli/v2" +) + +func initialize(c *cli.Context) error { + return nil + // // Get client + // hd, err := client.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := client.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + + // // Check wallet status + // _, ready, err := utils.CheckIfWalletReady(hd) + // if err != nil { + // return err + // } + // if !ready { + // return nil + // } + + // // Initialize the Stakewise wallet + // swResponse, err := sw.Api.Wallet.Initialize() + // if err != nil { + // return err + // } + + // fmt.Printf("Your node wallet has been successfully copied to the Stakewise module with address %s%s%s.", terminal.ColorBlue, swResponse.Data.AccountAddress.Hex(), terminal.ColorReset) + // return nil +} diff --git a/adapter/config/upload-deposit-data.go b/adapter/config/upload-deposit-data.go new file mode 100644 index 0000000..2042f09 --- /dev/null +++ b/adapter/config/upload-deposit-data.go @@ -0,0 +1,30 @@ +package config + +import ( + "github.com/urfave/cli/v2" +) + +func uploadDepositData(c *cli.Context) error { + return nil + // // Get the client + // hd, err := client.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := client.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + + // // Upload to the server + // _, err = swcmdutils.UploadDepositData(c, hd, sw) + // return err +} diff --git a/adapter/utils/flags.go b/adapter/utils/flags.go new file mode 100644 index 0000000..5990ec0 --- /dev/null +++ b/adapter/utils/flags.go @@ -0,0 +1,81 @@ +package utils + +import ( + "fmt" + + "github.com/urfave/cli/v2" +) + +var ( + YesFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "yes", + Aliases: []string{"y"}, + Usage: "Automatically confirm all interactive questions", + } + GeneratePubkeyFlag *cli.StringSliceFlag = &cli.StringSliceFlag{ + Name: "pubkey", + Aliases: []string{"p"}, + Usage: "The pubkey of the validator to generate deposit data for. Can be specified multiple times for more than one pubkey. If not specified, deposit data for all validator keys will be generated.", + } + + GenerateIndentFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "indent", + Aliases: []string{"i"}, + Usage: "Specify this to indent (pretty-print) the deposit data output.", + } + PubkeysFlag *cli.StringFlag = &cli.StringFlag{ + Name: "pubkeys", + Aliases: []string{"p"}, + Usage: "Comma-separated list of pubkeys (including 0x prefix) to get the exit message for", + } + EpochFlag *cli.Uint64Flag = &cli.Uint64Flag{ + Name: "epoch", + Aliases: []string{"e"}, + Usage: "(Optional) the epoch to use when creating the signed exit messages. If not specified, the current chain head will be used.", + } + NoBroadcastFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "no-broadcast", + Aliases: []string{"n"}, + Usage: "(Optional) pass this flag to skip broadcasting the exit message(s) and print them instead", + } + GenerateKeysCountFlag *cli.Uint64Flag = &cli.Uint64Flag{ + Name: "count", + Aliases: []string{"c"}, + Usage: "The number of keys to generate", + } + GenerateKeysNoRestartFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "no-restart", + Usage: fmt.Sprintf("Don't automatically restart the Stakewise Operator or Validator Client containers after generating keys. %sOnly use this if you know what you're doing and can restart them manually.%s", terminal.ColorRed, terminal.ColorReset), + } +) + +func InstantiateFlag[FlagType cli.Flag](prototype FlagType, description string) cli.Flag { + switch typedProto := any(prototype).(type) { + case *cli.BoolFlag: + return &cli.BoolFlag{ + Name: typedProto.Name, + Aliases: typedProto.Aliases, + Usage: description, + } + case *cli.Uint64Flag: + return &cli.Uint64Flag{ + Name: typedProto.Name, + Aliases: typedProto.Aliases, + Usage: description, + } + case *cli.StringFlag: + return &cli.StringFlag{ + Name: typedProto.Name, + Aliases: typedProto.Aliases, + Usage: description, + } + case *cli.Float64Flag: + return &cli.Float64Flag{ + Name: typedProto.Name, + Aliases: typedProto.Aliases, + Usage: description, + } + default: + panic("unsupported flag type") + } +} diff --git a/adapter/utils/validation.go b/adapter/utils/validation.go new file mode 100644 index 0000000..3627360 --- /dev/null +++ b/adapter/utils/validation.go @@ -0,0 +1,20 @@ +package utils + +import ( + "fmt" + "os" + + "github.com/urfave/cli/v2" +) + +// Validate command argument count +func ValidateArgCount(c *cli.Context, expectedCount int) { + argCount := c.Args().Len() + if argCount == expectedCount { + return + } + + // Handle invalid arg count + fmt.Fprintf(os.Stderr, "%sIncorrect argument count - expected %d but have %d%s\n\n", ColorRed, expectedCount, argCount, ColorReset) + cli.ShowSubcommandHelpAndExit(c, 1) +} From 5a139159b527830455e605cafe813e0f28154e9e Mon Sep 17 00:00:00 2001 From: huy Date: Fri, 3 Jan 2025 11:40:53 -0800 Subject: [PATCH 02/28] Moving stuff over IP --- adapter/config/hyperdrive-config.go | 50 ++++++++++++++++++ adapter/config/mev-boost-config.go | 72 ++++++++++++++++++++++++++ adapter/config/resources.go | 29 +++++++++++ adapter/config/settings.go | 3 ++ client/client.go | 79 +++++++++++++++++++++++++++++ client/global-config.go | 19 +++++++ client/utils/context.go | 66 ++++++++++++++++++++++++ 7 files changed, 318 insertions(+) create mode 100644 adapter/config/hyperdrive-config.go create mode 100644 adapter/config/mev-boost-config.go create mode 100644 adapter/config/resources.go create mode 100644 adapter/config/settings.go create mode 100644 client/global-config.go create mode 100644 client/utils/context.go diff --git a/adapter/config/hyperdrive-config.go b/adapter/config/hyperdrive-config.go new file mode 100644 index 0000000..0141284 --- /dev/null +++ b/adapter/config/hyperdrive-config.go @@ -0,0 +1,50 @@ +package config + +import "github.com/rocket-pool/node-manager-core/config" + +// The master configuration struct +type HyperdriveConfig struct { + // General settings + Network config.Parameter[config.Network] + ClientMode config.Parameter[config.ClientMode] + EnableIPv6 config.Parameter[bool] + ProjectName config.Parameter[string] + ApiPort config.Parameter[uint16] + UserDataPath config.Parameter[string] + AutoTxMaxFee config.Parameter[float64] + MaxPriorityFee config.Parameter[float64] + AutoTxGasThreshold config.Parameter[float64] + AdditionalDockerNetworks config.Parameter[string] + ClientTimeout config.Parameter[uint16] + + // The Docker Hub tag for the daemon container + ContainerTag config.Parameter[string] + + // Logging + Logging *config.LoggerConfig + + // Execution client settings + LocalExecutionClient *config.LocalExecutionConfig + ExternalExecutionClient *config.ExternalExecutionConfig + + // Beacon node settings + LocalBeaconClient *config.LocalBeaconConfig + ExternalBeaconClient *config.ExternalBeaconConfig + + // Fallback clients + Fallback *config.FallbackConfig + + // Metrics + Metrics *config.MetricsConfig + + // MEV-Boost + MevBoost *MevBoostConfig + + // Modules + Modules map[string]any + + // Internal fields + Version string + hyperdriveUserDirectory string + networkSettings []*HyperdriveSettings +} diff --git a/adapter/config/mev-boost-config.go b/adapter/config/mev-boost-config.go new file mode 100644 index 0000000..3000df0 --- /dev/null +++ b/adapter/config/mev-boost-config.go @@ -0,0 +1,72 @@ +package config + +import ( + "github.com/rocket-pool/node-manager-core/config" +) + +// Constants +const ( + mevBoostTag string = "flashbots/mev-boost:1.8.1" +) + +type MevSelectionMode string + +type MevRelayID string + +// A MEV relay +type MevRelay struct { + ID MevRelayID + Name string + Description string + Urls map[string]string +} + +// Configuration for MEV-Boost +type MevBoostConfig struct { + // Toggle to enable / disable + Enable config.Parameter[bool] + + // Ownership mode + Mode config.Parameter[config.ClientMode] + + // The mode for relay selection + SelectionMode config.Parameter[MevSelectionMode] + + // Flashbots relay + FlashbotsRelay config.Parameter[bool] + + // bloXroute max profit relay + BloxRouteMaxProfitRelay config.Parameter[bool] + + // bloXroute regulated relay + BloxRouteRegulatedRelay config.Parameter[bool] + + // Titan regional relay + TitanRegionalRelay config.Parameter[bool] + + // Custom relays provided by the user + CustomRelays config.Parameter[string] + + // The RPC port + Port config.Parameter[uint16] + + // Toggle for forwarding the HTTP port outside of Docker + OpenRpcPort config.Parameter[config.RpcPortMode] + + // The Docker Hub tag for MEV-Boost + ContainerTag config.Parameter[string] + + // Custom command line flags + AdditionalFlags config.Parameter[string] + + // The URL of an external MEV-Boost client + ExternalUrl config.Parameter[string] + + /////////////////////////// + // Non-editable settings // + /////////////////////////// + + parent *HyperdriveConfig + relays []MevRelay + relayMap map[MevRelayID]MevRelay +} diff --git a/adapter/config/resources.go b/adapter/config/resources.go new file mode 100644 index 0000000..eb0f898 --- /dev/null +++ b/adapter/config/resources.go @@ -0,0 +1,29 @@ +package config + +import "github.com/rocket-pool/node-manager-core/config" + +// Network settings with a field for Hyperdrive-specific settings +type HyperdriveSettings struct { + *config.NetworkSettings `yaml:",inline"` + + // Hyperdrive resources for the network + HyperdriveResources *HyperdriveResources `yaml:"hyperdriveResources" json:"hyperdriveResources"` +} + +// A collection of network-specific resources and getters for them +type HyperdriveResources struct { + // The URL for the NodeSet API server + NodeSetApiUrl string `yaml:"nodeSetApiUrl" json:"nodeSetApiUrl"` + + // The pubkey used to encrypt messages to nodeset.io + EncryptionPubkey string `yaml:"encryptionPubkey" json:"encryptionPubkey"` +} + +// An aggregated collection of resources for the selected network, including Hyperdrive resources +type MergedResources struct { + // Base network resources + *config.NetworkResources + + // Hyperdrive resources + *HyperdriveResources +} diff --git a/adapter/config/settings.go b/adapter/config/settings.go new file mode 100644 index 0000000..0e6acb2 --- /dev/null +++ b/adapter/config/settings.go @@ -0,0 +1,3 @@ +package config + +const HyperdriveDaemonRoute string = "hyperdrive" diff --git a/client/client.go b/client/client.go index 087264e..fc449be 100644 --- a/client/client.go +++ b/client/client.go @@ -1,12 +1,21 @@ package swclient import ( + "fmt" "log/slog" "net/http/httptrace" "net/url" + "path/filepath" + + docker "github.com/docker/docker/client" "github.com/nodeset-org/hyperdrive-daemon/shared/auth" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/client/utils" + "github.com/rocket-pool/node-manager-core/api/client" + "github.com/rocket-pool/node-manager-core/log" + "github.com/urfave/cli/v2" ) // Binder for the StakeWise API server @@ -19,6 +28,76 @@ type ApiClient struct { Status *StatusRequester } +// Hyperdrive client +type HyperdriveClient struct { + Api *ApiClient + Context *utils.HyperdriveContext + Logger *slog.Logger + docker *docker.Client + cfg *GlobalConfig + isNewCfg bool +} + +// Create new Hyperdrive client from CLI context +func NewHyperdriveClientFromCtx(c *cli.Context) (*HyperdriveClient, error) { + hdCtx := utils.GetHyperdriveContext(c) + return NewHyperdriveClientFromHyperdriveCtx(hdCtx) +} + +// Create new Hyperdrive client from a custom context +func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*HyperdriveClient, error) { + logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, config.HyperdriveDaemonRoute)) + + // Create the tracer if required + var tracer *httptrace.ClientTrace + if hdCtx.HttpTraceFile != nil { + var err error + tracer, err = createTracer(hdCtx.HttpTraceFile, logger) + if err != nil { + logger.Error("Error creating HTTP trace", log.Err(err)) + } + } + + // Load the network settings from disk + err := hdCtx.LoadNetworkSettings() + if err != nil { + return nil, fmt.Errorf("error loading network settings: %w", err) + } + + // Make the client + hdClient := &HyperdriveClient{ + Context: hdCtx, + Logger: logger, + } + + // Get the API URL + url := hdCtx.ApiUrl + if url == nil { + // Load the config to get the API port + cfg, _, err := hdClient.LoadConfig() + if err != nil { + return nil, fmt.Errorf("error loading config: %w", err) + } + + url, err = url.Parse(fmt.Sprintf("http://localhost:%d/%s", cfg.Hyperdrive.ApiPort.Value, hdconfig.HyperdriveApiClientRoute)) + if err != nil { + return nil, fmt.Errorf("error parsing Hyperdrive API URL: %w", err) + } + } + + // Create the auth manager + authPath := filepath.Join(hdCtx.UserDirPath, hdApiKeyRelPath) + err = auth.GenerateAuthKeyIfNotPresent(authPath, auth.DefaultKeyLength) + if err != nil { + return nil, fmt.Errorf("error generating Hyperdrive daemon API key: %w", err) + } + authMgr := auth.NewAuthorizationManager(authPath, cliIssuer, auth.DefaultRequestLifespan) + + // Create the API client + hdClient.Api = client.NewApiClient(url, logger, tracer, authMgr) + return hdClient, nil +} + // Creates a new API client instance func NewApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.ClientTrace, authMgr *auth.AuthorizationManager) *ApiClient { context := client.NewNetworkRequesterContext(apiUrl, logger, tracer, authMgr.AddAuthHeader) diff --git a/client/global-config.go b/client/global-config.go new file mode 100644 index 0000000..954df3f --- /dev/null +++ b/client/global-config.go @@ -0,0 +1,19 @@ +package swclient + +import ( + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" +) + +// Wrapper for global configuration +type GlobalConfig struct { + ExternalIP string + + // Hyperdrive + Hyperdrive *config.HyperdriveConfig + HyperdriveResources *config.MergedResources + + // StakeWise + StakeWise *swconfig.StakeWiseConfig + StakeWiseResources *swconfig.StakeWiseResources +} diff --git a/client/utils/context.go b/client/utils/context.go new file mode 100644 index 0000000..ef6c033 --- /dev/null +++ b/client/utils/context.go @@ -0,0 +1,66 @@ +package utils + +import ( + "math/big" + "net/url" + "os" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/urfave/cli/v2" +) + +const ( + contextMetadataName string = "hd-context" +) + +// Holds information about Hyperdrive's installation on the system +type InstallationInfo struct { + // The system path for Hyperdrive scripts used in the Docker containers + ScriptsDir string + + // The system path for Hyperdrive templates + TemplatesDir string + + // The system path for the source files to put in the user's override directory + OverrideSourceDir string + + // The system path for built-in network settings and resource definitions + NetworksDir string +} + +// Context for global settings +type HyperdriveContext struct { + *InstallationInfo + + // The path to the Hyperdrive user directory + UserDirPath string + + // The max fee for transactions + MaxFee float64 + + // The max priority fee for transactions + MaxPriorityFee float64 + + // The nonce for the first transaction, if set + Nonce *big.Int + + // True if debug mode is enabled + DebugEnabled bool + + // True if this is a secure session + SecureSession bool + + // The address and URL of the API server + ApiUrl *url.URL + + // The HTTP trace file if tracing is enabled + HttpTraceFile *os.File + + // The list of networks options and corresponding settings for Hyperdrive itself + HyperdriveNetworkSettings []*config.HyperdriveSettings +} + +// Get the Hyperdrive context from a CLI context +func GetHyperdriveContext(c *cli.Context) *HyperdriveContext { + return c.App.Metadata[contextMetadataName].(*HyperdriveContext) +} From 3ee1fd219f4a3878063be1018fc599593af105b3 Mon Sep 17 00:00:00 2001 From: huy Date: Fri, 3 Jan 2025 16:16:36 -0800 Subject: [PATCH 03/28] WIP --- adapter/client/config.go | 149 +++++++++++++++++++++++++++++++ adapter/client/tracer.go | 94 +++++++++++++++++++ adapter/config/settings.go | 8 +- adapter/utils/flags.go | 1 + adapter/utils/terminal/colors.go | 11 +++ adapter/utils/validation.go | 3 +- client/client.go | 8 +- go.mod | 1 + go.sum | 2 + 9 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 adapter/client/config.go create mode 100644 adapter/client/tracer.go create mode 100644 adapter/utils/terminal/colors.go diff --git a/adapter/client/config.go b/adapter/client/config.go new file mode 100644 index 0000000..460eb67 --- /dev/null +++ b/adapter/client/config.go @@ -0,0 +1,149 @@ +package client + +import ( + "fmt" + "html/template" + "os" + "path/filepath" + + "github.com/mitchellh/go-homedir" + + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + "github.com/nodeset-org/hyperdrive-stakewise/client/utils" + + hdconfig "github.com/nodeset-org/hyperdrive/shared/config" +) + +const ( + metricsDirMode os.FileMode = 0755 + prometheusConfigTemplate string = "prometheus-cfg.tmpl" + prometheusConfigTarget string = "prometheus.yml" + grafanaConfigTemplate string = "grafana-prometheus-datasource.tmpl" + grafanaConfigTarget string = "grafana-prometheus-datasource.yml" +) + +// Load the config +// Returns the global config and whether or not it was newly generated +func (c *swclient.HyperdriveClient) LoadConfig() (*swclient.GlobalConfig, bool, error) { + if c.cfg != nil { + return c.cfg, c.isNewCfg, nil + } + + settingsFilePath := filepath.Join(c.Context.UserDirPath, SettingsFile) + expandedPath, err := homedir.Expand(settingsFilePath) + if err != nil { + return nil, false, fmt.Errorf("error expanding settings file path: %w", err) + } + + cfg, err := LoadConfigFromFile(expandedPath, c.utils.HyperdriveNetworkSettings, c.Context.StakeWiseNetworkSettings, c.Context.ConstellationNetworkSettings) + if err != nil { + return nil, false, err + } + + if cfg != nil { + // A config was loaded, return it now + c.cfg = cfg + return cfg, false, nil + } + + // Config wasn't loaded, but there was no error - we should create one. + hdCfg, err := hdconfig.NewHyperdriveConfig(c.Context.UserDirPath, c.utils.HyperdriveNetworkSettings) + if err != nil { + return nil, false, fmt.Errorf("error creating Hyperdrive config: %w", err) + } + c.cfg, err = Newclient.GlobalConfig(hdCfg, c.utils.HyperdriveNetworkSettings, swCfg, c.Context.StakeWiseNetworkSettings, csCfg) + if err != nil { + return nil, false, fmt.Errorf("error creating global config: %w", err) + } + c.isNewCfg = true + return c.cfg, true, nil +} + +// Load the backup config +func (c *swclient.HyperdriveClient) LoadBackupConfig() (*swclient.GlobalConfig, error) { + settingsFilePath := filepath.Join(c.Context.UserDirPath, BackupSettingsFile) + expandedPath, err := homedir.Expand(settingsFilePath) + if err != nil { + return nil, fmt.Errorf("error expanding backup settings file path: %w", err) + } + + return LoadConfigFromFile(expandedPath, c.utils.HyperdriveNetworkSettings, c.Context.StakeWiseNetworkSettings) +} + +// Save the config +func (c *swclient.HyperdriveClient) SaveConfig(cfg *swclient.GlobalConfig) error { + settingsFileDirectoryPath, err := homedir.Expand(c.Context.UserDirPath) + if err != nil { + return err + } + err = SaveConfig(cfg, settingsFileDirectoryPath, SettingsFile) + if err != nil { + return fmt.Errorf("error saving config: %w", err) + } + + // Update the client's config cache + c.cfg = cfg + c.isNewCfg = false + return nil +} + +// Create the metrics and modules folders, and deploy the config templates for Prometheus and Grafana +func (c *HyperdriveClient) DeployMetricsConfigurations(config *swclient.GlobalConfig) error { + // Make sure the metrics path exists + metricsDirPath := filepath.Join(c.Context.UserDirPath, metricsDir) + modulesDirPath := filepath.Join(metricsDirPath, hdconfig.ModulesName) + err := os.MkdirAll(modulesDirPath, metricsDirMode) + if err != nil { + return fmt.Errorf("error creating metrics and modules directories [%s]: %w", modulesDirPath, err) + } + + err = updatePrometheusConfiguration(c.Context, config, metricsDirPath) + if err != nil { + return fmt.Errorf("error updating Prometheus configuration: %w", err) + } + err = updateGrafanaDatabaseConfiguration(c.Context, config, metricsDirPath) + if err != nil { + return fmt.Errorf("error updating Grafana configuration: %w", err) + } + return nil +} + +// Load the Prometheus config template, do a template variable substitution, and save it +func updatePrometheusConfiguration(ctx *utils.HyperdriveContext, config *swclient.GlobalConfig, metricsDirPath string) error { + prometheusConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, prometheusConfigTemplate)) + if err != nil { + return fmt.Errorf("error expanding Prometheus config template path: %w", err) + } + + prometheusConfigTargetPath, err := homedir.Expand(filepath.Join(metricsDirPath, prometheusConfigTarget)) + if err != nil { + return fmt.Errorf("error expanding Prometheus config target path: %w", err) + } + + t := template.Template{ + Src: prometheusConfigTemplatePath, + Dst: prometheusConfigTargetPath, + } + + return t.Write(config) +} + +// Load the Grafana config template, do a template variable substitution, and save it +func updateGrafanaDatabaseConfiguration(ctx *utils.HyperdriveContext, config *swclient.GlobalConfig, metricsDirPath string) error { + grafanaConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, grafanaConfigTemplate)) + if err != nil { + return fmt.Errorf("error expanding Grafana config template path: %w", err) + } + + grafanaConfigTargetPath, err := homedir.Expand(filepath.Join(metricsDirPath, grafanaConfigTarget)) + if err != nil { + return fmt.Errorf("error expanding Grafana config target path: %w", err) + } + + t := template.Template{ + Src: grafanaConfigTemplatePath, + Dst: grafanaConfigTargetPath, + } + + return t.Write(config) +} diff --git a/adapter/client/tracer.go b/adapter/client/tracer.go new file mode 100644 index 0000000..54f8598 --- /dev/null +++ b/adapter/client/tracer.go @@ -0,0 +1,94 @@ +package client + +import ( + "fmt" + "log/slog" + "net/http/httptrace" + "os" + + "github.com/rocket-pool/node-manager-core/log" +) + +func CreateTracer(file *os.File, logger *slog.Logger) (*httptrace.ClientTrace, error) { + tracer := &httptrace.ClientTrace{} + tracer.ConnectDone = func(network, addr string, err error) { + logger.Debug("HTTP Connect Done", + slog.String("network", network), + slog.String("addr", addr), + log.Err(err), + ) + err = writeToTraceFile(file, fmt.Sprintf("ConnectDone: network=%s, addr=%s, err=%v", network, addr, err)) + if err != nil { + logger.Debug("", log.Err(err)) + } + } + tracer.DNSDone = func(dnsInfo httptrace.DNSDoneInfo) { + logger.Debug("HTTP DNS Done", + slog.String("addrs", fmt.Sprint(dnsInfo.Addrs)), + slog.Bool("coalesced", dnsInfo.Coalesced), + log.Err(dnsInfo.Err), + ) + err := writeToTraceFile(file, fmt.Sprintf("DNSDone: addrs=%v, coalesced=%t, err=%v", dnsInfo.Addrs, dnsInfo.Coalesced, dnsInfo.Err)) + if err != nil { + logger.Debug("", log.Err(err)) + } + } + tracer.DNSStart = func(dnsInfo httptrace.DNSStartInfo) { + logger.Debug("HTTP DNS Start", + slog.String("host", dnsInfo.Host), + ) + err := writeToTraceFile(file, fmt.Sprintf("DNSStart: host=%s", dnsInfo.Host)) + if err != nil { + logger.Debug("", log.Err(err)) + } + } + tracer.GotConn = func(connInfo httptrace.GotConnInfo) { + logger.Debug("HTTP Got Connection", + slog.Bool("reused", connInfo.Reused), + slog.Bool("wasIdle", connInfo.WasIdle), + slog.Duration("idleTime", connInfo.IdleTime), + slog.String("localAddr", connInfo.Conn.LocalAddr().String()), + slog.String("remoteAddr", connInfo.Conn.RemoteAddr().String()), + ) + err := writeToTraceFile(file, fmt.Sprintf("GotConn: reused=%t, wasIdle=%t, idleTime=%s, localAddr=%s, remoteAddr=%s", connInfo.Reused, connInfo.WasIdle, connInfo.IdleTime, connInfo.Conn.LocalAddr().String(), connInfo.Conn.RemoteAddr().String())) + if err != nil { + logger.Debug("", log.Err(err)) + } + } + tracer.GotFirstResponseByte = func() { + logger.Debug("HTTP Got First Response Byte") + err := writeToTraceFile(file, "GotFirstResponseByte") + if err != nil { + logger.Debug("", log.Err(err)) + } + } + tracer.PutIdleConn = func(err error) { + logger.Debug("HTTP Put Idle Connection", + log.Err(err), + ) + err = writeToTraceFile(file, fmt.Sprintf("PutIdleConn: err=%v", err)) + if err != nil { + logger.Debug("", log.Err(err)) + } + } + tracer.WroteRequest = func(wroteInfo httptrace.WroteRequestInfo) { + logger.Debug("HTTP Wrote Request", + log.Err(wroteInfo.Err), + ) + err := writeToTraceFile(file, fmt.Sprintf("WroteRequest: err=%v", wroteInfo.Err)) + if err != nil { + logger.Debug("", log.Err(err)) + } + } + + return tracer, nil +} + +func writeToTraceFile(file *os.File, data string) error { + // Write the data + _, err := file.WriteString(data + "\n") + if err != nil { + return fmt.Errorf("error writing to HTTP trace file [%s]: %w", file.Name(), err) + } + return nil +} diff --git a/adapter/config/settings.go b/adapter/config/settings.go index 0e6acb2..5e31965 100644 --- a/adapter/config/settings.go +++ b/adapter/config/settings.go @@ -1,3 +1,9 @@ package config -const HyperdriveDaemonRoute string = "hyperdrive" +const ( + HyperdriveDaemonRoute string = "hyperdrive" + + // API Keys + SecretsDir string = "secrets" + DaemonKeyFilename string = "daemon.key" +) diff --git a/adapter/utils/flags.go b/adapter/utils/flags.go index 5990ec0..7ce9925 100644 --- a/adapter/utils/flags.go +++ b/adapter/utils/flags.go @@ -3,6 +3,7 @@ package utils import ( "fmt" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" "github.com/urfave/cli/v2" ) diff --git a/adapter/utils/terminal/colors.go b/adapter/utils/terminal/colors.go new file mode 100644 index 0000000..5eecb6b --- /dev/null +++ b/adapter/utils/terminal/colors.go @@ -0,0 +1,11 @@ +package terminal + +const ( + ColorReset string = "\033[0m" + ColorBold string = "\033[1m" + ColorRed string = "\033[31m" + ColorGreen string = "\033[32m" + ColorYellow string = "\033[33m" + ColorBlue string = "\033[36m" + ClearLine string = "\033[2K" +) diff --git a/adapter/utils/validation.go b/adapter/utils/validation.go index 3627360..7c47ebf 100644 --- a/adapter/utils/validation.go +++ b/adapter/utils/validation.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" "github.com/urfave/cli/v2" ) @@ -15,6 +16,6 @@ func ValidateArgCount(c *cli.Context, expectedCount int) { } // Handle invalid arg count - fmt.Fprintf(os.Stderr, "%sIncorrect argument count - expected %d but have %d%s\n\n", ColorRed, expectedCount, argCount, ColorReset) + fmt.Fprintf(os.Stderr, "%sIncorrect argument count - expected %d but have %d%s\n\n", terminal.ColorRed, expectedCount, argCount, terminal.ColorReset) cli.ShowSubcommandHelpAndExit(c, 1) } diff --git a/client/client.go b/client/client.go index fc449be..fb91a73 100644 --- a/client/client.go +++ b/client/client.go @@ -8,8 +8,10 @@ import ( "path/filepath" docker "github.com/docker/docker/client" + "github.com/fatih/color" "github.com/nodeset-org/hyperdrive-daemon/shared/auth" + hdclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/nodeset-org/hyperdrive-stakewise/client/utils" @@ -18,6 +20,10 @@ import ( "github.com/urfave/cli/v2" ) +const terminalLogColor color.Attribute = color.FgHiYellow + +var hdApiKeyRelPath string = filepath.Join(config.SecretsDir, config.DaemonKeyFilename) + // Binder for the StakeWise API server type ApiClient struct { context client.IRequesterContext @@ -52,7 +58,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype var tracer *httptrace.ClientTrace if hdCtx.HttpTraceFile != nil { var err error - tracer, err = createTracer(hdCtx.HttpTraceFile, logger) + tracer, err = hdclient.CreateTracer(hdCtx.HttpTraceFile, logger) if err != nil { logger.Error("Error creating HTTP trace", log.Err(err)) } diff --git a/go.mod b/go.mod index cbb9e31..c3fcc2e 100644 --- a/go.mod +++ b/go.mod @@ -92,6 +92,7 @@ require ( github.com/mattn/go-shellwords v1.0.12 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect diff --git a/go.sum b/go.sum index 88ebf8c..a9931eb 100644 --- a/go.sum +++ b/go.sum @@ -316,6 +316,8 @@ github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= From 0976ac164b4716bd20cda53d717d4542f643b317 Mon Sep 17 00:00:00 2001 From: huy Date: Mon, 6 Jan 2025 09:42:27 -0800 Subject: [PATCH 04/28] more fixing --- adapter/client/config.go | 149 --------------------------------- client/client.go | 156 +++++++++++++++++++++++++++++++++-- client/utils/context.go | 46 +++++++---- client/utils/install_info.go | 61 ++++++++++++++ 4 files changed, 239 insertions(+), 173 deletions(-) delete mode 100644 adapter/client/config.go create mode 100644 client/utils/install_info.go diff --git a/adapter/client/config.go b/adapter/client/config.go deleted file mode 100644 index 460eb67..0000000 --- a/adapter/client/config.go +++ /dev/null @@ -1,149 +0,0 @@ -package client - -import ( - "fmt" - "html/template" - "os" - "path/filepath" - - "github.com/mitchellh/go-homedir" - - swclient "github.com/nodeset-org/hyperdrive-stakewise/client" - "github.com/nodeset-org/hyperdrive-stakewise/client/utils" - - hdconfig "github.com/nodeset-org/hyperdrive/shared/config" -) - -const ( - metricsDirMode os.FileMode = 0755 - prometheusConfigTemplate string = "prometheus-cfg.tmpl" - prometheusConfigTarget string = "prometheus.yml" - grafanaConfigTemplate string = "grafana-prometheus-datasource.tmpl" - grafanaConfigTarget string = "grafana-prometheus-datasource.yml" -) - -// Load the config -// Returns the global config and whether or not it was newly generated -func (c *swclient.HyperdriveClient) LoadConfig() (*swclient.GlobalConfig, bool, error) { - if c.cfg != nil { - return c.cfg, c.isNewCfg, nil - } - - settingsFilePath := filepath.Join(c.Context.UserDirPath, SettingsFile) - expandedPath, err := homedir.Expand(settingsFilePath) - if err != nil { - return nil, false, fmt.Errorf("error expanding settings file path: %w", err) - } - - cfg, err := LoadConfigFromFile(expandedPath, c.utils.HyperdriveNetworkSettings, c.Context.StakeWiseNetworkSettings, c.Context.ConstellationNetworkSettings) - if err != nil { - return nil, false, err - } - - if cfg != nil { - // A config was loaded, return it now - c.cfg = cfg - return cfg, false, nil - } - - // Config wasn't loaded, but there was no error - we should create one. - hdCfg, err := hdconfig.NewHyperdriveConfig(c.Context.UserDirPath, c.utils.HyperdriveNetworkSettings) - if err != nil { - return nil, false, fmt.Errorf("error creating Hyperdrive config: %w", err) - } - c.cfg, err = Newclient.GlobalConfig(hdCfg, c.utils.HyperdriveNetworkSettings, swCfg, c.Context.StakeWiseNetworkSettings, csCfg) - if err != nil { - return nil, false, fmt.Errorf("error creating global config: %w", err) - } - c.isNewCfg = true - return c.cfg, true, nil -} - -// Load the backup config -func (c *swclient.HyperdriveClient) LoadBackupConfig() (*swclient.GlobalConfig, error) { - settingsFilePath := filepath.Join(c.Context.UserDirPath, BackupSettingsFile) - expandedPath, err := homedir.Expand(settingsFilePath) - if err != nil { - return nil, fmt.Errorf("error expanding backup settings file path: %w", err) - } - - return LoadConfigFromFile(expandedPath, c.utils.HyperdriveNetworkSettings, c.Context.StakeWiseNetworkSettings) -} - -// Save the config -func (c *swclient.HyperdriveClient) SaveConfig(cfg *swclient.GlobalConfig) error { - settingsFileDirectoryPath, err := homedir.Expand(c.Context.UserDirPath) - if err != nil { - return err - } - err = SaveConfig(cfg, settingsFileDirectoryPath, SettingsFile) - if err != nil { - return fmt.Errorf("error saving config: %w", err) - } - - // Update the client's config cache - c.cfg = cfg - c.isNewCfg = false - return nil -} - -// Create the metrics and modules folders, and deploy the config templates for Prometheus and Grafana -func (c *HyperdriveClient) DeployMetricsConfigurations(config *swclient.GlobalConfig) error { - // Make sure the metrics path exists - metricsDirPath := filepath.Join(c.Context.UserDirPath, metricsDir) - modulesDirPath := filepath.Join(metricsDirPath, hdconfig.ModulesName) - err := os.MkdirAll(modulesDirPath, metricsDirMode) - if err != nil { - return fmt.Errorf("error creating metrics and modules directories [%s]: %w", modulesDirPath, err) - } - - err = updatePrometheusConfiguration(c.Context, config, metricsDirPath) - if err != nil { - return fmt.Errorf("error updating Prometheus configuration: %w", err) - } - err = updateGrafanaDatabaseConfiguration(c.Context, config, metricsDirPath) - if err != nil { - return fmt.Errorf("error updating Grafana configuration: %w", err) - } - return nil -} - -// Load the Prometheus config template, do a template variable substitution, and save it -func updatePrometheusConfiguration(ctx *utils.HyperdriveContext, config *swclient.GlobalConfig, metricsDirPath string) error { - prometheusConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, prometheusConfigTemplate)) - if err != nil { - return fmt.Errorf("error expanding Prometheus config template path: %w", err) - } - - prometheusConfigTargetPath, err := homedir.Expand(filepath.Join(metricsDirPath, prometheusConfigTarget)) - if err != nil { - return fmt.Errorf("error expanding Prometheus config target path: %w", err) - } - - t := template.Template{ - Src: prometheusConfigTemplatePath, - Dst: prometheusConfigTargetPath, - } - - return t.Write(config) -} - -// Load the Grafana config template, do a template variable substitution, and save it -func updateGrafanaDatabaseConfiguration(ctx *utils.HyperdriveContext, config *swclient.GlobalConfig, metricsDirPath string) error { - grafanaConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, grafanaConfigTemplate)) - if err != nil { - return fmt.Errorf("error expanding Grafana config template path: %w", err) - } - - grafanaConfigTargetPath, err := homedir.Expand(filepath.Join(metricsDirPath, grafanaConfigTarget)) - if err != nil { - return fmt.Errorf("error expanding Grafana config target path: %w", err) - } - - t := template.Template{ - Src: grafanaConfigTemplatePath, - Dst: grafanaConfigTargetPath, - } - - return t.Write(config) -} diff --git a/client/client.go b/client/client.go index fb91a73..f0275c1 100644 --- a/client/client.go +++ b/client/client.go @@ -5,13 +5,19 @@ import ( "log/slog" "net/http/httptrace" "net/url" + "os" "path/filepath" + hdtemplate "github.com/nodeset-org/hyperdrive/hyperdrive-cli/client/template" + docker "github.com/docker/docker/client" "github.com/fatih/color" + "github.com/mitchellh/go-homedir" "github.com/nodeset-org/hyperdrive-daemon/shared/auth" - hdclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" + hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" + swclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/nodeset-org/hyperdrive-stakewise/client/utils" @@ -20,7 +26,19 @@ import ( "github.com/urfave/cli/v2" ) -const terminalLogColor color.Attribute = color.FgHiYellow +const ( + metricsDirMode os.FileMode = 0755 + prometheusConfigTemplate string = "prometheus-cfg.tmpl" + prometheusConfigTarget string = "prometheus.yml" + grafanaConfigTemplate string = "grafana-prometheus-datasource.tmpl" + grafanaConfigTarget string = "grafana-prometheus-datasource.yml" + + SettingsFile string = "user-settings.yml" + BackupSettingsFile string = "user-settings-backup.yml" + metricsDir string = "metrics" + + terminalLogColor color.Attribute = color.FgHiYellow +) var hdApiKeyRelPath string = filepath.Join(config.SecretsDir, config.DaemonKeyFilename) @@ -58,7 +76,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype var tracer *httptrace.ClientTrace if hdCtx.HttpTraceFile != nil { var err error - tracer, err = hdclient.CreateTracer(hdCtx.HttpTraceFile, logger) + tracer, err = swclient.CreateTracer(hdCtx.HttpTraceFile, logger) if err != nil { logger.Error("Error creating HTTP trace", log.Err(err)) } @@ -71,7 +89,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype } // Make the client - hdClient := &HyperdriveClient{ + swClient := &HyperdriveClient{ Context: hdCtx, Logger: logger, } @@ -80,7 +98,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype url := hdCtx.ApiUrl if url == nil { // Load the config to get the API port - cfg, _, err := hdClient.LoadConfig() + cfg, _, err := swClient.LoadConfig() if err != nil { return nil, fmt.Errorf("error loading config: %w", err) } @@ -100,8 +118,8 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype authMgr := auth.NewAuthorizationManager(authPath, cliIssuer, auth.DefaultRequestLifespan) // Create the API client - hdClient.Api = client.NewApiClient(url, logger, tracer, authMgr) - return hdClient, nil + swClient.Api = client.NewApiClient(url, logger, tracer, authMgr) + return swClient, nil } // Creates a new API client instance @@ -118,3 +136,127 @@ func NewApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.Client } return client } + +func (c *HyperdriveClient) LoadConfig() (*GlobalConfig, bool, error) { + if c.cfg != nil { + return c.cfg, c.isNewCfg, nil + } + + settingsFilePath := filepath.Join(c.Context.UserDirPath, SettingsFile) + expandedPath, err := homedir.Expand(settingsFilePath) + if err != nil { + return nil, false, fmt.Errorf("error expanding settings file path: %w", err) + } + + cfg, err := LoadConfigFromFile(expandedPath, c.Context.HyperdriveNetworkSettings, c.Context.StakeWiseNetworkSettings, c.Context.ConstellationNetworkSettings) + if err != nil { + return nil, false, err + } + + if cfg != nil { + // A config was loaded, return it now + c.cfg = cfg + return cfg, false, nil + } + + // Config wasn't loaded, but there was no error - we should create one. + hdCfg, err := hdconfig.NewHyperdriveConfig(c.Context.UserDirPath, c.Context.HyperdriveNetworkSettings) + if err != nil { + return nil, false, fmt.Errorf("error creating Hyperdrive config: %w", err) + } + c.cfg, err = GlobalConfig(hdCfg, c.Context.HyperdriveNetworkSettings, swCfg, c.Context.StakeWiseNetworkSettings, csCfg) + if err != nil { + return nil, false, fmt.Errorf("error creating global config: %w", err) + } + c.isNewCfg = true + return c.cfg, true, nil +} + +// Load the backup config +func (c *HyperdriveClient) LoadBackupConfig() (*GlobalConfig, error) { + settingsFilePath := filepath.Join(c.Context.UserDirPath, BackupSettingsFile) + expandedPath, err := homedir.Expand(settingsFilePath) + if err != nil { + return nil, fmt.Errorf("error expanding backup settings file path: %w", err) + } + + return LoadConfigFromFile(expandedPath, c.Context.HyperdriveNetworkSettings, c.Context.StakeWiseNetworkSettings) +} + +// Save the config +func (c *HyperdriveClient) SaveConfig(cfg *GlobalConfig) error { + settingsFileDirectoryPath, err := homedir.Expand(c.Context.UserDirPath) + if err != nil { + return err + } + err = SaveConfig(cfg, settingsFileDirectoryPath, SettingsFile) + if err != nil { + return fmt.Errorf("error saving config: %w", err) + } + + // Update the client's config cache + c.cfg = cfg + c.isNewCfg = false + return nil +} + +// Create the metrics and modules folders, and deploy the config templates for Prometheus and Grafana +func (c *HyperdriveClient) DeployMetricsConfigurations(config *GlobalConfig) error { + // Make sure the metrics path exists + metricsDirPath := filepath.Join(c.Context.UserDirPath, metricsDir) + modulesDirPath := filepath.Join(metricsDirPath, hdconfig.ModulesName) + err := os.MkdirAll(modulesDirPath, metricsDirMode) + if err != nil { + return fmt.Errorf("error creating metrics and modules directories [%s]: %w", modulesDirPath, err) + } + + err = updatePrometheusConfiguration(c.Context, config, metricsDirPath) + if err != nil { + return fmt.Errorf("error updating Prometheus configuration: %w", err) + } + err = updateGrafanaDatabaseConfiguration(c.Context, config, metricsDirPath) + if err != nil { + return fmt.Errorf("error updating Grafana configuration: %w", err) + } + return nil +} + +// Load the Prometheus config template, do a template variable substitution, and save it +func updatePrometheusConfiguration(ctx *utils.HyperdriveContext, config *GlobalConfig, metricsDirPath string) error { + prometheusConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, prometheusConfigTemplate)) + if err != nil { + return fmt.Errorf("error expanding Prometheus config template path: %w", err) + } + + prometheusConfigTargetPath, err := homedir.Expand(filepath.Join(metricsDirPath, prometheusConfigTarget)) + if err != nil { + return fmt.Errorf("error expanding Prometheus config target path: %w", err) + } + + t := hdtemplate.Template{ + Src: prometheusConfigTemplatePath, + Dst: prometheusConfigTargetPath, + } + + return t.Write(config) +} + +// Load the Grafana config template, do a template variable substitution, and save it +func updateGrafanaDatabaseConfiguration(ctx *utils.HyperdriveContext, config *GlobalConfig, metricsDirPath string) error { + grafanaConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, grafanaConfigTemplate)) + if err != nil { + return fmt.Errorf("error expanding Grafana config template path: %w", err) + } + + grafanaConfigTargetPath, err := homedir.Expand(filepath.Join(metricsDirPath, grafanaConfigTarget)) + if err != nil { + return fmt.Errorf("error expanding Grafana config target path: %w", err) + } + + t := hdtemplate.Template{ + Src: grafanaConfigTemplatePath, + Dst: grafanaConfigTargetPath, + } + + return t.Write(config) +} diff --git a/client/utils/context.go b/client/utils/context.go index ef6c033..0aec909 100644 --- a/client/utils/context.go +++ b/client/utils/context.go @@ -1,33 +1,22 @@ package utils import ( + "fmt" "math/big" "net/url" "os" + "path/filepath" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/urfave/cli/v2" + + hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" + sharedconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" ) const ( contextMetadataName string = "hd-context" ) -// Holds information about Hyperdrive's installation on the system -type InstallationInfo struct { - // The system path for Hyperdrive scripts used in the Docker containers - ScriptsDir string - - // The system path for Hyperdrive templates - TemplatesDir string - - // The system path for the source files to put in the user's override directory - OverrideSourceDir string - - // The system path for built-in network settings and resource definitions - NetworksDir string -} - // Context for global settings type HyperdriveContext struct { *InstallationInfo @@ -57,7 +46,30 @@ type HyperdriveContext struct { HttpTraceFile *os.File // The list of networks options and corresponding settings for Hyperdrive itself - HyperdriveNetworkSettings []*config.HyperdriveSettings + HyperdriveNetworkSettings []*hdconfig.HyperdriveSettings + + // The list of networks options and corresponding settings for the StakeWise module + StakeWiseNetworkSettings []*sharedconfig.StakeWiseSettings +} + +// Load the network settings +func (c *HyperdriveContext) LoadNetworkSettings() error { + var err error + installInfo := NewInstallationInfo() + c.InstallationInfo = installInfo + + c.HyperdriveNetworkSettings, err = hdconfig.LoadSettingsFiles(installInfo.NetworksDir) + if err != nil { + return fmt.Errorf("error loading hyperdrive network settings from path [%s]: %s", installInfo.NetworksDir, err.Error()) + } + + swNetSettingsDir := filepath.Join(installInfo.NetworksDir, hdconfig.ModulesName, sharedconfig.ModuleName) + c.StakeWiseNetworkSettings, err = sharedconfig.LoadSettingsFiles(swNetSettingsDir) + if err != nil { + return fmt.Errorf("error loading stakewise network settings from path [%s]: %s", swNetSettingsDir, err.Error()) + } + + return nil } // Get the Hyperdrive context from a CLI context diff --git a/client/utils/install_info.go b/client/utils/install_info.go new file mode 100644 index 0000000..52dda5c --- /dev/null +++ b/client/utils/install_info.go @@ -0,0 +1,61 @@ +package utils + +import ( + "os" + "path/filepath" + "runtime" +) + +const ( + // Environment variable to set the system path for unit tests + TestSystemDirEnvVar string = "HYPERDRIVE_TEST_SYSTEM_DIR" + + // System dir path for Linux + LinuxSystemDir string = "/usr/share/hyperdrive" + + // Subfolders under the system dir + ScriptsDir string = "scripts" + TemplatesDir string = "templates" + OverrideSourceDir string = "override" + NetworksDir string = "networks" +) + +// Holds information about Hyperdrive's installation on the system +type InstallationInfo struct { + // The system path for Hyperdrive scripts used in the Docker containers + ScriptsDir string + + // The system path for Hyperdrive templates + TemplatesDir string + + // The system path for the source files to put in the user's override directory + OverrideSourceDir string + + // The system path for built-in network settings and resource definitions + NetworksDir string +} + +// Creates a new installation info instance +func NewInstallationInfo() *InstallationInfo { + systemDir := os.Getenv(TestSystemDirEnvVar) + if systemDir == "" { + switch runtime.GOOS { + // This is where to add different paths for different OS's like macOS + default: + // By default just use the Linux path + systemDir = LinuxSystemDir + } + } + + return NewInstallationInfoForSystemDir(systemDir) +} + +// Creates a new installation info instance with the given system directory +func NewInstallationInfoForSystemDir(systemDir string) *InstallationInfo { + return &InstallationInfo{ + ScriptsDir: filepath.Join(systemDir, ScriptsDir), + TemplatesDir: filepath.Join(systemDir, TemplatesDir), + OverrideSourceDir: filepath.Join(systemDir, OverrideSourceDir), + NetworksDir: filepath.Join(systemDir, NetworksDir), + } +} From b5dc1b914be33edaccdd4b1cad756f450c4c040d Mon Sep 17 00:00:00 2001 From: huy Date: Mon, 6 Jan 2025 10:56:10 -0800 Subject: [PATCH 05/28] fix all import issues --- adapter/client/template/compose-file.go | 49 ++++++++ adapter/client/template/template.go | 47 ++++++++ client/client.go | 53 +++++++-- client/global-config.go | 149 +++++++++++++++++++++++- 4 files changed, 288 insertions(+), 10 deletions(-) create mode 100644 adapter/client/template/compose-file.go create mode 100644 adapter/client/template/template.go diff --git a/adapter/client/template/compose-file.go b/adapter/client/template/compose-file.go new file mode 100644 index 0000000..bfdd176 --- /dev/null +++ b/adapter/client/template/compose-file.go @@ -0,0 +1,49 @@ +package template + +import ( + "fmt" + "path/filepath" +) + +const ( + TemplateSuffix string = ".tmpl" + ComposeFileSuffix string = ".yml" +) + +type ComposePaths struct { + RuntimePath string + TemplatePath string + OverridePath string +} + +type ComposeFile struct { + name string + paths *ComposePaths +} + +func (c *ComposePaths) File(name string) *ComposeFile { + return &ComposeFile{ + name: name, + paths: c, + } +} + +// Given a ComposeFile returned by ComposePaths.File, find and parse the .tmpl +// from the TemplatePath, populate and save to the RuntimePath, and return a +// slice of compose definitions pertaining to the container (including the override). +func (c *ComposeFile) Write(data interface{}) ([]string, error) { + composePath := filepath.Join(c.paths.RuntimePath, c.name+ComposeFileSuffix) + tmpl := Template{ + Src: filepath.Join(c.paths.TemplatePath, c.name+TemplateSuffix), + Dst: composePath, + } + err := tmpl.Write(data) + if err != nil { + return nil, fmt.Errorf("error writing %s compose definition: %w", c.name, err) + } + + return []string{ + composePath, + filepath.Join(c.paths.OverridePath, c.name+ComposeFileSuffix), + }, nil +} diff --git a/adapter/client/template/template.go b/adapter/client/template/template.go new file mode 100644 index 0000000..d6549ac --- /dev/null +++ b/adapter/client/template/template.go @@ -0,0 +1,47 @@ +package template + +import ( + "fmt" + "os" + "text/template" + + "github.com/alessio/shellescape" +) + +// Template is a wrapper around text/template with filesystem operations baked in. +type Template struct { + // Src is the path on disk to the .tmpl file + Src string + + // Dst is the path on disk to the output file + Dst string +} + +func (t Template) Write(data interface{}) error { + // Open the output file, creating it if it doesn't exist + runtimeFile, err := os.OpenFile(t.Dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0664) + if err != nil { + return fmt.Errorf("Could not open templated file %s for writing: %w", shellescape.Quote(t.Dst), err) + } + defer runtimeFile.Close() + + // Parse the template + tmpl, err := template.ParseFiles(t.Src) + if err != nil { + return fmt.Errorf("Error reading template file %s: %w", shellescape.Quote(t.Src), err) + } + + // Replace template variables and write the result + err = tmpl.Execute(runtimeFile, data) + if err != nil { + return fmt.Errorf("Error writing and substituting template: %w", err) + } + + // If the file was newly created, 0664 may have been altered by umask, so chmod back to 0664. + err = os.Chmod(t.Dst, 0664) + if err != nil { + return fmt.Errorf("Could not set templated file (%s) permissions: %w", shellescape.Quote(t.Dst), err) + } + + return nil +} diff --git a/client/client.go b/client/client.go index f0275c1..7eec8c9 100644 --- a/client/client.go +++ b/client/client.go @@ -8,7 +8,7 @@ import ( "os" "path/filepath" - hdtemplate "github.com/nodeset-org/hyperdrive/hyperdrive-cli/client/template" + clitemplate "github.com/nodeset-org/hyperdrive-stakewise/adapter/client/template" docker "github.com/docker/docker/client" "github.com/fatih/color" @@ -17,9 +17,9 @@ import ( "github.com/nodeset-org/hyperdrive-daemon/shared/auth" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" swclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/nodeset-org/hyperdrive-stakewise/client/utils" + swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" "github.com/rocket-pool/node-manager-core/api/client" "github.com/rocket-pool/node-manager-core/log" @@ -38,6 +38,8 @@ const ( metricsDir string = "metrics" terminalLogColor color.Attribute = color.FgHiYellow + + cliIssuer string = "hd-cli" ) var hdApiKeyRelPath string = filepath.Join(config.SecretsDir, config.DaemonKeyFilename) @@ -118,7 +120,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype authMgr := auth.NewAuthorizationManager(authPath, cliIssuer, auth.DefaultRequestLifespan) // Create the API client - swClient.Api = client.NewApiClient(url, logger, tracer, authMgr) + swClient.Api = NewApiClient(url, logger, tracer, authMgr) return swClient, nil } @@ -148,7 +150,7 @@ func (c *HyperdriveClient) LoadConfig() (*GlobalConfig, bool, error) { return nil, false, fmt.Errorf("error expanding settings file path: %w", err) } - cfg, err := LoadConfigFromFile(expandedPath, c.Context.HyperdriveNetworkSettings, c.Context.StakeWiseNetworkSettings, c.Context.ConstellationNetworkSettings) + cfg, err := LoadConfigFromFile(expandedPath, c.Context.HyperdriveNetworkSettings, c.Context.StakeWiseNetworkSettings) if err != nil { return nil, false, err } @@ -164,7 +166,11 @@ func (c *HyperdriveClient) LoadConfig() (*GlobalConfig, bool, error) { if err != nil { return nil, false, fmt.Errorf("error creating Hyperdrive config: %w", err) } - c.cfg, err = GlobalConfig(hdCfg, c.Context.HyperdriveNetworkSettings, swCfg, c.Context.StakeWiseNetworkSettings, csCfg) + swCfg, err := swconfig.NewStakeWiseConfig(hdCfg, c.Context.StakeWiseNetworkSettings) + if err != nil { + return nil, false, fmt.Errorf("error creating StakeWise config: %w", err) + } + c.cfg, err = NewGlobalConfig(hdCfg, c.Context.HyperdriveNetworkSettings, swCfg, c.Context.StakeWiseNetworkSettings) if err != nil { return nil, false, fmt.Errorf("error creating global config: %w", err) } @@ -233,7 +239,7 @@ func updatePrometheusConfiguration(ctx *utils.HyperdriveContext, config *GlobalC return fmt.Errorf("error expanding Prometheus config target path: %w", err) } - t := hdtemplate.Template{ + t := clitemplate.Template{ Src: prometheusConfigTemplatePath, Dst: prometheusConfigTargetPath, } @@ -253,10 +259,43 @@ func updateGrafanaDatabaseConfiguration(ctx *utils.HyperdriveContext, config *Gl return fmt.Errorf("error expanding Grafana config target path: %w", err) } - t := hdtemplate.Template{ + t := clitemplate.Template{ Src: grafanaConfigTemplatePath, Dst: grafanaConfigTargetPath, } return t.Write(config) } + +// Loads a config without updating it if it exists +func LoadConfigFromFile(configPath string, hdSettings []*hdconfig.HyperdriveSettings, swSettings []*swconfig.StakeWiseSettings) (*GlobalConfig, error) { + // Make sure the config file exists + _, err := os.Stat(configPath) + if os.IsNotExist(err) { + return nil, nil + } + + // Get the Hyperdrive config + hdCfg, err := hdconfig.LoadFromFile(configPath, hdSettings) + if err != nil { + return nil, err + } + + // Get the StakeWise config + swCfg, err := swconfig.NewStakeWiseConfig(hdCfg, swSettings) + if err != nil { + return nil, err + } + + // Load the module configs + cfg, err := NewGlobalConfig(hdCfg, hdSettings, swCfg, swSettings) + if err != nil { + return nil, fmt.Errorf("error creating global configuration: %w", err) + } + err = cfg.DeserializeModules() + if err != nil { + return nil, fmt.Errorf("error loading module configs from [%s]: %w", configPath, err) + } + + return cfg, nil +} diff --git a/client/global-config.go b/client/global-config.go index 954df3f..768f878 100644 --- a/client/global-config.go +++ b/client/global-config.go @@ -1,8 +1,17 @@ package swclient import ( - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + "reflect" + + "github.com/alessio/shellescape" + hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" + "gopkg.in/yaml.v2" ) // Wrapper for global configuration @@ -10,10 +19,144 @@ type GlobalConfig struct { ExternalIP string // Hyperdrive - Hyperdrive *config.HyperdriveConfig - HyperdriveResources *config.MergedResources + Hyperdrive *hdconfig.HyperdriveConfig + HyperdriveResources *hdconfig.MergedResources // StakeWise StakeWise *swconfig.StakeWiseConfig StakeWiseResources *swconfig.StakeWiseResources } + +// Serialize the config and all modules +func (c *GlobalConfig) Serialize() map[string]any { + return c.Hyperdrive.Serialize(c.GetAllModuleConfigs(), false) +} + +// Get the configs for all of the modules in the system +func (c *GlobalConfig) GetAllModuleConfigs() []hdconfig.IModuleConfig { + return []hdconfig.IModuleConfig{ + c.StakeWise, + } +} + +// Saves a config +func SaveConfig(cfg *GlobalConfig, directory string, filename string) error { + path := filepath.Join(directory, filename) + + settings := cfg.Serialize() + configBytes, err := yaml.Marshal(settings) + if err != nil { + return fmt.Errorf("could not serialize settings file: %w", err) + } + + // Make a tmp file + // The empty string directs CreateTemp to use the OS's $TMPDIR (or GetTempPath) on windows + // The * in the second string is replaced with random characters by CreateTemp + f, err := os.CreateTemp(directory, ".tmp-"+filename+"-*") + if err != nil { + if errors.Is(err, fs.ErrExist) { + return fmt.Errorf("could not create file to save config to disk... do you need to clean your tmpdir (%s)?: %w", os.TempDir(), err) + } + + return fmt.Errorf("could not create file to save config to disk: %w", err) + } + // Clean up the temporary files + // This prevents us from filling up `directory` with partially written files on failure + // If the file is successfully written, it fails with an error since it will be renamed + // before it is deleted, which we explicitly ignore / don't care about. + defer func() { + // Clean up tmp files, if any found + oldFiles, err := filepath.Glob(filepath.Join(directory, ".tmp-"+filename+"-*")) + if err != nil { + // Only possible error is ErrBadPattern, which we should catch + // during development, since the pattern is a comptime constant. + panic(err.Error()) + } + + for _, match := range oldFiles { + os.RemoveAll(match) + } + }() + + // Save the serialized settings to the temporary file + if _, err := f.Write(configBytes); err != nil { + return fmt.Errorf("could not write Hyperdrive config to %s: %w", shellescape.Quote(path), err) + } + + // Close the file for writing + if err := f.Close(); err != nil { + return fmt.Errorf("error saving Hyperdrive config to %s: %w", shellescape.Quote(path), err) + } + + // Rename the temp file to overwrite the actual file. + // On Unix systems this operation is atomic and won't fail if the disk is now full + if err := os.Rename(f.Name(), path); err != nil { + return fmt.Errorf("error replacing old Hyperdrive config with %s: %w", f.Name(), err) + } + + // Just in case the rename didn't overwrite (and preserve the perms of) the original file, set them now. + if err := os.Chmod(path, 0664); err != nil { + return fmt.Errorf("error updating permissions of %s: %w", path, err) + } + + return nil +} + +// Make a new global config +func NewGlobalConfig(hdCfg *hdconfig.HyperdriveConfig, hdSettings []*hdconfig.HyperdriveSettings, swCfg *swconfig.StakeWiseConfig, swSettings []*swconfig.StakeWiseSettings) (*GlobalConfig, error) { + // Make the config + cfg := &GlobalConfig{ + Hyperdrive: hdCfg, + StakeWise: swCfg, + } + + // Get the HD resources + network := hdCfg.Network.Value + for _, setting := range hdSettings { + if setting.Key == network { + cfg.HyperdriveResources = &hdconfig.MergedResources{ + NetworkResources: setting.NetworkResources, + HyperdriveResources: setting.HyperdriveResources, + } + break + } + } + if cfg.HyperdriveResources == nil { + return nil, fmt.Errorf("could not find hyperdrive resources for network [%s]", network) + } + + // Get the StakeWise resources + for _, setting := range swSettings { + if setting.Key == network { + cfg.StakeWiseResources = setting.StakeWiseResources + break + } + } + if cfg.StakeWiseResources == nil { + return nil, fmt.Errorf("could not find stakewise resources for network [%s]", network) + } + /* + for _, module := range cfg.GetAllModuleConfigs() { + config.ApplyDefaults(module, hdCfg.Network.Value) + } + */ + return cfg, nil +} + +// Deserialize the config's modules (assumes the Hyperdrive config itself has already been deserialized) +func (c *GlobalConfig) DeserializeModules() error { + // Load Stakewise + stakewiseName := c.StakeWise.GetModuleName() + section, exists := c.Hyperdrive.Modules[stakewiseName] + if exists { + configMap, ok := section.(map[string]any) + if !ok { + return fmt.Errorf("config module section [%s] is not a map, it's a %s", stakewiseName, reflect.TypeOf(section)) + } + err := c.StakeWise.Deserialize(configMap, c.Hyperdrive.Network.Value) + if err != nil { + return fmt.Errorf("error deserializing stakewise configuration: %w", err) + } + } + return nil +} From 8f4fea113859047954040d9217e6e31da6439625 Mon Sep 17 00:00:00 2001 From: huy Date: Mon, 6 Jan 2025 18:14:25 -0800 Subject: [PATCH 06/28] wip --- adapter/config/generate-keys.go | 38 ++++++++------- client/auth.go | 9 ++++ client/client.go | 86 +++++++++++++++++++++++++++++---- testing/node.go | 4 +- 4 files changed, 108 insertions(+), 29 deletions(-) create mode 100644 client/auth.go diff --git a/adapter/config/generate-keys.go b/adapter/config/generate-keys.go index cb9aa07..01b7a24 100644 --- a/adapter/config/generate-keys.go +++ b/adapter/config/generate-keys.go @@ -1,28 +1,30 @@ package config import ( + "fmt" + + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/urfave/cli/v2" ) func generateKeys(c *cli.Context) error { - return nil - // // Get the client - // hd, err := client.NewHyperdriveClientFromCtx(c) - // if err != nil { - // return err - // } - // sw, err := client.NewStakewiseClientFromCtx(c, hd) - // if err != nil { - // return err - // } - // cfg, _, err := hd.LoadConfig() - // if err != nil { - // return fmt.Errorf("error loading Hyperdrive config: %w", err) - // } - // if !cfg.StakeWise.Enabled.Value { - // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - // return nil - // } + // Get the client + hd, err := swclient.NewHyperdriveClientFromCtx(c) + if err != nil { + return err + } + sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + if err != nil { + return err + } + cfg, _, err := hd.LoadConfig() + if err != nil { + return fmt.Errorf("error loading Hyperdrive config: %w", err) + } + if !cfg.StakeWise.Enabled.Value { + fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + return nil + } // noRestart := c.Bool(generateKeysNoRestartFlag.Name) // // Check wallet status diff --git a/client/auth.go b/client/auth.go new file mode 100644 index 0000000..de8b73f --- /dev/null +++ b/client/auth.go @@ -0,0 +1,9 @@ +package swclient + +import ( + "path/filepath" + + hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" +) + +var moduleApiKeyRelPath string = filepath.Join(hdconfig.SecretsDir, hdconfig.ModulesName) diff --git a/client/client.go b/client/client.go index 7eec8c9..c88123d 100644 --- a/client/client.go +++ b/client/client.go @@ -9,11 +9,13 @@ import ( "path/filepath" clitemplate "github.com/nodeset-org/hyperdrive-stakewise/adapter/client/template" + "github.com/nodeset-org/hyperdrive/hyperdrive-cli/utils/context" docker "github.com/docker/docker/client" "github.com/fatih/color" "github.com/mitchellh/go-homedir" + hdclient "github.com/nodeset-org/hyperdrive-daemon/client" "github.com/nodeset-org/hyperdrive-daemon/shared/auth" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" swclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" @@ -43,9 +45,10 @@ const ( ) var hdApiKeyRelPath string = filepath.Join(config.SecretsDir, config.DaemonKeyFilename) +var swApiKeyRelPath string = filepath.Join(moduleApiKeyRelPath, swconfig.ModuleName, hdconfig.DaemonKeyFilename) // Binder for the StakeWise API server -type ApiClient struct { +type SwApiClient struct { context client.IRequesterContext Nodeset *NodesetRequester Validator *ValidatorRequester @@ -56,7 +59,7 @@ type ApiClient struct { // Hyperdrive client type HyperdriveClient struct { - Api *ApiClient + Api *hdclient.ApiClient Context *utils.HyperdriveContext Logger *slog.Logger docker *docker.Client @@ -64,6 +67,13 @@ type HyperdriveClient struct { isNewCfg bool } +// Stakewise client +type StakewiseClient struct { + Api *SwApiClient + Context *context.HyperdriveContext + Logger *slog.Logger +} + // Create new Hyperdrive client from CLI context func NewHyperdriveClientFromCtx(c *cli.Context) (*HyperdriveClient, error) { hdCtx := utils.GetHyperdriveContext(c) @@ -72,7 +82,7 @@ func NewHyperdriveClientFromCtx(c *cli.Context) (*HyperdriveClient, error) { // Create new Hyperdrive client from a custom context func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*HyperdriveClient, error) { - logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, config.HyperdriveDaemonRoute)) + logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, hdconfig.HyperdriveDaemonRoute)) // Create the tracer if required var tracer *httptrace.ClientTrace @@ -91,7 +101,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype } // Make the client - swClient := &HyperdriveClient{ + hdClient := &HyperdriveClient{ Context: hdCtx, Logger: logger, } @@ -100,7 +110,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype url := hdCtx.ApiUrl if url == nil { // Load the config to get the API port - cfg, _, err := swClient.LoadConfig() + cfg, _, err := hdClient.LoadConfig() if err != nil { return nil, fmt.Errorf("error loading config: %w", err) } @@ -120,15 +130,15 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype authMgr := auth.NewAuthorizationManager(authPath, cliIssuer, auth.DefaultRequestLifespan) // Create the API client - swClient.Api = NewApiClient(url, logger, tracer, authMgr) - return swClient, nil + hdClient.Api = hdclient.NewApiClient(url, logger, tracer, authMgr) + return hdClient, nil } // Creates a new API client instance -func NewApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.ClientTrace, authMgr *auth.AuthorizationManager) *ApiClient { +func NewSwApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.ClientTrace, authMgr *auth.AuthorizationManager) *SwApiClient { context := client.NewNetworkRequesterContext(apiUrl, logger, tracer, authMgr.AddAuthHeader) - client := &ApiClient{ + client := &SwApiClient{ context: context, Nodeset: NewNodesetRequester(context), Validator: NewValidatorRequester(context), @@ -299,3 +309,61 @@ func LoadConfigFromFile(configPath string, hdSettings []*hdconfig.HyperdriveSett return cfg, nil } + +// Create new Stakewise client from CLI context +// Only use this function from commands that may work if the Daemon service doesn't exist +func NewStakewiseClientFromCtx(c *cli.Context, hdClient *HyperdriveClient) (*StakewiseClient, error) { + hdCtx := context.GetHyperdriveContext(c) + return NewStakewiseClientFromHyperdriveCtx(hdCtx, hdClient) +} + +// Create new Stakewise client from a custom context +// Only use this function from commands that may work if the Daemon service doesn't exist +func NewStakewiseClientFromHyperdriveCtx(hdCtx *context.HyperdriveContext, hdClient *HyperdriveClient) (*StakewiseClient, error) { + logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, swconfig.ModuleName)) + + // Create the tracer if required + var tracer *httptrace.ClientTrace + if hdCtx.HttpTraceFile != nil { + var err error + tracer, err = swclient.CreateTracer(hdCtx.HttpTraceFile, logger) + if err != nil { + logger.Error("Error creating HTTP trace", log.Err(err)) + } + } + + // Make the client + swClient := &StakewiseClient{ + Context: hdCtx, + Logger: logger, + } + + // Get the API URL + url := hdCtx.ApiUrl + if url == nil { + var err error + url, err = url.Parse(fmt.Sprintf("http://localhost:%d/%s", hdClient.cfg.StakeWise.ApiPort.Value, swconfig.ApiClientRoute)) + if err != nil { + return nil, fmt.Errorf("error parsing StakeWise API URL: %w", err) + } + } else { + host := fmt.Sprintf("%s://%s:%d/%s", url.Scheme, url.Hostname(), hdClient.cfg.StakeWise.ApiPort.Value, swconfig.ApiClientRoute) + var err error + url, err = url.Parse(host) + if err != nil { + return nil, fmt.Errorf("error parsing StakeWise API URL: %w", err) + } + } + + // Create the auth manager + authPath := filepath.Join(hdCtx.UserDirPath, swApiKeyRelPath) + err := auth.GenerateAuthKeyIfNotPresent(authPath, auth.DefaultKeyLength) + if err != nil { + return nil, fmt.Errorf("error generating StakeWise module API key: %w", err) + } + authMgr := auth.NewAuthorizationManager(authPath, cliIssuer, auth.DefaultRequestLifespan) + + // Create the API client + swClient.Api = NewSwApiClient(url, logger, tracer, authMgr) + return swClient, nil +} diff --git a/testing/node.go b/testing/node.go index bd7b5a4..3cd96b8 100644 --- a/testing/node.go +++ b/testing/node.go @@ -31,7 +31,7 @@ type StakeWiseNode struct { serverMgr *swserver.ServerManager // An HTTP API client for the daemon - client *swclient.ApiClient + client *swclient.SwApiClient // The client logger logger *slog.Logger @@ -97,7 +97,7 @@ func (n *StakeWiseNode) GetServerManager() *swserver.ServerManager { } // Get the HTTP API client for interacting with the node's daemon server -func (n *StakeWiseNode) GetApiClient() *swclient.ApiClient { +func (n *StakeWiseNode) GetApiClient() *swclient.SwApiClient { return n.client } From ef4b34b83e5ba8daf9189898e0b89be96eaea989 Mon Sep 17 00:00:00 2001 From: huy Date: Tue, 7 Jan 2025 08:14:44 -0800 Subject: [PATCH 07/28] more fixing imports --- adapter/config/commands.go | 25 +++ adapter/config/generate-deposit-data.go | 104 +++++----- adapter/config/get-node-status.go | 182 +++++++++--------- adapter/config/initialize.go | 71 +++---- adapter/utils/address.go | 25 +++ .../{ => utils}/config/hyperdrive-config.go | 0 .../{ => utils}/config/mev-boost-config.go | 0 adapter/{ => utils}/config/resources.go | 0 adapter/{ => utils}/config/settings.go | 0 client/client.go | 18 +- testing/node.go | 2 +- 11 files changed, 245 insertions(+), 182 deletions(-) create mode 100644 adapter/utils/address.go rename adapter/{ => utils}/config/hyperdrive-config.go (100%) rename adapter/{ => utils}/config/mev-boost-config.go (100%) rename adapter/{ => utils}/config/resources.go (100%) rename adapter/{ => utils}/config/settings.go (100%) diff --git a/adapter/config/commands.go b/adapter/config/commands.go index 803e5af..62c14f9 100644 --- a/adapter/config/commands.go +++ b/adapter/config/commands.go @@ -1,10 +1,35 @@ package config import ( + "fmt" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" "github.com/urfave/cli/v2" ) +var ( + generateKeysCountFlag *cli.Uint64Flag = &cli.Uint64Flag{ + Name: "count", + Aliases: []string{"c"}, + Usage: "The number of keys to generate", + } + generateKeysNoRestartFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "no-restart", + Usage: fmt.Sprintf("Don't automatically restart the Stakewise Operator or Validator Client containers after generating keys. %sOnly use this if you know what you're doing and can restart them manually.%s", terminal.ColorRed, terminal.ColorReset), + } + generatePubkeyFlag *cli.StringSliceFlag = &cli.StringSliceFlag{ + Name: "pubkey", + Aliases: []string{"p"}, + Usage: "The pubkey of the validator to generate deposit data for. Can be specified multiple times for more than one pubkey. If not specified, deposit data for all validator keys will be generated.", + } + generateIndentFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "indent", + Aliases: []string{"i"}, + Usage: "Specify this to indent (pretty-print) the deposit data output.", + } +) + // Handles `hd-module` commands func RegisterCommands(app *cli.App) { commands := []*cli.Command{ diff --git a/adapter/config/generate-deposit-data.go b/adapter/config/generate-deposit-data.go index 3c9fe99..78fdded 100644 --- a/adapter/config/generate-deposit-data.go +++ b/adapter/config/generate-deposit-data.go @@ -1,62 +1,66 @@ package config import ( + "encoding/json" + "fmt" + + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + "github.com/rocket-pool/node-manager-core/beacon" "github.com/urfave/cli/v2" ) func generateDepositData(c *cli.Context) error { - return nil - // // Get the client - // hd, err := client.NewHyperdriveClientFromCtx(c) - // if err != nil { - // return err - // } - // sw, err := client.NewStakewiseClientFromCtx(c, hd) - // if err != nil { - // return err - // } - // cfg, _, err := hd.LoadConfig() - // if err != nil { - // return fmt.Errorf("error loading Hyperdrive config: %w", err) - // } - // if !cfg.StakeWise.Enabled.Value { - // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - // return nil - // } + // Get the client + hd, err := swclient.NewHyperdriveClientFromCtx(c) + if err != nil { + return err + } + sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + if err != nil { + return err + } + cfg, _, err := hd.LoadConfig() + if err != nil { + return fmt.Errorf("error loading Hyperdrive config: %w", err) + } + if !cfg.StakeWise.Enabled.Value { + fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + return nil + } - // // Parse the pubkeys - // pubkeyStrings := c.StringSlice(generatePubkeyFlag.Name) - // pubkeys := make([]beacon.ValidatorPubkey, len(pubkeyStrings)) - // for i, pubkeyString := range pubkeyStrings { - // pubkey, err := beacon.HexToValidatorPubkey(pubkeyString) - // if err != nil { - // return fmt.Errorf("error parsing pubkey [%s]: %w", pubkeyString, err) - // } - // pubkeys[i] = pubkey - // } + // Parse the pubkeys + pubkeyStrings := c.StringSlice(generatePubkeyFlag.Name) + pubkeys := make([]beacon.ValidatorPubkey, len(pubkeyStrings)) + for i, pubkeyString := range pubkeyStrings { + pubkey, err := beacon.HexToValidatorPubkey(pubkeyString) + if err != nil { + return fmt.Errorf("error parsing pubkey [%s]: %w", pubkeyString, err) + } + pubkeys[i] = pubkey + } - // // Generate the deposit data - // fmt.Println("Generating deposit data...") - // response, err := sw.Api.Nodeset.GenerateDepositData(pubkeys) - // if err != nil { - // return err - // } + // Generate the deposit data + fmt.Println("Generating deposit data...") + response, err := sw.Api.Nodeset.GenerateDepositData(pubkeys) + if err != nil { + return err + } - // // Serialize the deposit data - // var bytes []byte - // shouldIndent := c.Bool(generateIndentFlag.Name) - // if shouldIndent { - // bytes, err = json.MarshalIndent(response.Data.Deposits, "", " ") - // } else { - // bytes, err = json.Marshal(response.Data.Deposits) - // } - // if err != nil { - // return fmt.Errorf("error serializing deposit data: %w", err) - // } + // Serialize the deposit data + var bytes []byte + shouldIndent := c.Bool(generateIndentFlag.Name) + if shouldIndent { + bytes, err = json.MarshalIndent(response.Data.Deposits, "", " ") + } else { + bytes, err = json.Marshal(response.Data.Deposits) + } + if err != nil { + return fmt.Errorf("error serializing deposit data: %w", err) + } - // // Print the deposit data - // fmt.Println("Deposit data:") - // fmt.Println() - // fmt.Println(string(bytes)) - // return nil + // Print the deposit data + fmt.Println("Deposit data:") + fmt.Println() + fmt.Println(string(bytes)) + return nil } diff --git a/adapter/config/get-node-status.go b/adapter/config/get-node-status.go index 79e8621..8a80eaf 100644 --- a/adapter/config/get-node-status.go +++ b/adapter/config/get-node-status.go @@ -1,105 +1,111 @@ package config import ( + "fmt" + + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + swtypes "github.com/nodeset-org/hyperdrive-stakewise/shared/types" + + "github.com/nodeset-org/hyperdrive/hyperdrive-cli/commands/nodeset" + "github.com/rocket-pool/node-manager-core/beacon" "github.com/urfave/cli/v2" ) func getNodeStatus(c *cli.Context) error { - return nil - // // Get the client - // hd, err := client.NewHyperdriveClientFromCtx(c) - // if err != nil { - // return err - // } - // sw, err := client.NewStakewiseClientFromCtx(c, hd) - // if err != nil { - // return err - // } - // cfg, _, err := hd.LoadConfig() - // if err != nil { - // return fmt.Errorf("error loading Hyperdrive config: %w", err) - // } - // if !cfg.StakeWise.Enabled.Value { - // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - // return nil - // } + // Get the client + hd, err := swclient.NewHyperdriveClientFromCtx(c) + if err != nil { + return err + } + sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + if err != nil { + return err + } + cfg, _, err := hd.LoadConfig() + if err != nil { + return fmt.Errorf("error loading Hyperdrive config: %w", err) + } + if !cfg.StakeWise.Enabled.Value { + fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + return nil + } - // // Check the registration status first - // shouldContinue, err := nodeset.CheckRegistrationStatus(c, hd) - // if err != nil { - // return fmt.Errorf("error checking nodeset registration status: %w", err) - // } - // if !shouldContinue { - // return nil - // } + // Check the registration status first + shouldContinue, err := nodeset.CheckRegistrationStatus(c, hd) + if err != nil { + return fmt.Errorf("error checking nodeset registration status: %w", err) + } + if !shouldContinue { + return nil + } - // // Get the validator statuses - // response, err := sw.Api.Status.GetValidatorStatuses() - // if err != nil { - // fmt.Printf("error fetching validator statuses: %v\n", err) - // return err - // } + // Get the validator statuses + response, err := sw.Api.Status.GetValidatorStatuses() + if err != nil { + fmt.Printf("error fetching validator statuses: %v\n", err) + return err + } - // if len(response.Data.States) == 0 { - // fmt.Println("You do not have any validators.") - // return nil - // } + if len(response.Data.States) == 0 { + fmt.Println("You do not have any validators.") + return nil + } - // for _, state := range response.Data.States { - // fmt.Printf("%s:\n", state.Pubkey.HexWithPrefix()) + for _, state := range response.Data.States { + fmt.Printf("%s:\n", state.Pubkey.HexWithPrefix()) - // // Print Beacon status - // if state.Index == "" { - // fmt.Println("\tBeacon State: Not seen by Beacon Chain yet") - // } else { - // fmt.Printf("\tBeacon Index: %s\n", state.Index) - // fmt.Printf("\tBeacon State: %s\n", getBeaconStatusLabel(state.BeaconStatus)) - // } + // Print Beacon status + if state.Index == "" { + fmt.Println("\tBeacon State: Not seen by Beacon Chain yet") + } else { + fmt.Printf("\tBeacon Index: %s\n", state.Index) + fmt.Printf("\tBeacon State: %s\n", getBeaconStatusLabel(state.BeaconStatus)) + } - // // Print NodeSet status - // fmt.Printf("\tNodeSet State: %s\n", getNodeSetStateLabel(state.NodesetStatus)) - // fmt.Println() - // } + // Print NodeSet status + fmt.Printf("\tNodeSet State: %s\n", getNodeSetStateLabel(state.NodesetStatus)) + fmt.Println() + } - // return nil + return nil } -// func getBeaconStatusLabel(state beacon.ValidatorState) string { -// switch state { -// case beacon.ValidatorState_ActiveExiting: -// return "Active (Exiting in Progress)" -// case beacon.ValidatorState_ActiveOngoing: -// return "Active" -// case beacon.ValidatorState_ActiveSlashed: -// return "Slashed (Exit in Progress)" -// case beacon.ValidatorState_ExitedSlashed: -// return "Slashed (Exited)" -// case beacon.ValidatorState_ExitedUnslashed: -// return "Exited (Withdrawal Pending)" -// case beacon.ValidatorState_PendingInitialized: -// return "Seen on Beacon, Waiting for More Deposits" -// case beacon.ValidatorState_PendingQueued: -// return "In Beacon Activation Queue" -// case beacon.ValidatorState_WithdrawalDone: -// return "Exited and Withdrawn" -// case beacon.ValidatorState_WithdrawalPossible: -// return "Exited (Waiting for Wihdrawal)" -// default: -// return fmt.Sprintf("", state) -// } -// } +func getBeaconStatusLabel(state beacon.ValidatorState) string { + switch state { + case beacon.ValidatorState_ActiveExiting: + return "Active (Exiting in Progress)" + case beacon.ValidatorState_ActiveOngoing: + return "Active" + case beacon.ValidatorState_ActiveSlashed: + return "Slashed (Exit in Progress)" + case beacon.ValidatorState_ExitedSlashed: + return "Slashed (Exited)" + case beacon.ValidatorState_ExitedUnslashed: + return "Exited (Withdrawal Pending)" + case beacon.ValidatorState_PendingInitialized: + return "Seen on Beacon, Waiting for More Deposits" + case beacon.ValidatorState_PendingQueued: + return "In Beacon Activation Queue" + case beacon.ValidatorState_WithdrawalDone: + return "Exited and Withdrawn" + case beacon.ValidatorState_WithdrawalPossible: + return "Exited (Waiting for Wihdrawal)" + default: + return fmt.Sprintf("", state) + } +} -// func getNodeSetStateLabel(state swtypes.NodesetStatus) string { -// switch state { -// case swtypes.NodesetStatus_Generated: -// return "Generated (Not Yet Uploaded)" -// case swtypes.NodesetStatus_RegisteredToStakewise: -// return "Registered with Stakewise" -// case swtypes.NodesetStatus_UploadedStakewise: -// return "Uploaded to Stakewise" -// case swtypes.NodesetStatus_UploadedToNodeset: -// return "Uploaded to NodeSet" -// default: -// return fmt.Sprintf("", state) -// } -// } +func getNodeSetStateLabel(state swtypes.NodesetStatus) string { + switch state { + case swtypes.NodesetStatus_Generated: + return "Generated (Not Yet Uploaded)" + case swtypes.NodesetStatus_RegisteredToStakewise: + return "Registered with Stakewise" + case swtypes.NodesetStatus_UploadedStakewise: + return "Uploaded to Stakewise" + case swtypes.NodesetStatus_UploadedToNodeset: + return "Uploaded to NodeSet" + default: + return fmt.Sprintf("", state) + } +} diff --git a/adapter/config/initialize.go b/adapter/config/initialize.go index c99535c..1de79c4 100644 --- a/adapter/config/initialize.go +++ b/adapter/config/initialize.go @@ -1,44 +1,49 @@ package config import ( + "fmt" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/urfave/cli/v2" ) func initialize(c *cli.Context) error { - return nil - // // Get client - // hd, err := client.NewHyperdriveClientFromCtx(c) - // if err != nil { - // return err - // } - // sw, err := client.NewStakewiseClientFromCtx(c, hd) - // if err != nil { - // return err - // } - // cfg, _, err := hd.LoadConfig() - // if err != nil { - // return fmt.Errorf("error loading Hyperdrive config: %w", err) - // } - // if !cfg.StakeWise.Enabled.Value { - // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - // return nil - // } + // Get client - // // Check wallet status - // _, ready, err := utils.CheckIfWalletReady(hd) - // if err != nil { - // return err - // } - // if !ready { - // return nil - // } + hd, err := swclient.NewHyperdriveClientFromCtx(c) + if err != nil { + return err + } + sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + if err != nil { + return err + } + cfg, _, err := hd.LoadConfig() + if err != nil { + return fmt.Errorf("error loading Hyperdrive config: %w", err) + } + if !cfg.StakeWise.Enabled.Value { + fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + return nil + } - // // Initialize the Stakewise wallet - // swResponse, err := sw.Api.Wallet.Initialize() - // if err != nil { - // return err - // } + // Check wallet status + _, ready, err := utils.CheckIfWalletReady(hd) + if err != nil { + return err + } + if !ready { + return nil + } - // fmt.Printf("Your node wallet has been successfully copied to the Stakewise module with address %s%s%s.", terminal.ColorBlue, swResponse.Data.AccountAddress.Hex(), terminal.ColorReset) - // return nil + // Initialize the Stakewise wallet + swResponse, err := sw.Api.Wallet.Initialize() + if err != nil { + return err + } + + fmt.Printf("Your node wallet has been successfully copied to the Stakewise module with address %s%s%s.", terminal.ColorBlue, swResponse.Data.AccountAddress.Hex(), terminal.ColorReset) + return nil } diff --git a/adapter/utils/address.go b/adapter/utils/address.go new file mode 100644 index 0000000..6cd6cae --- /dev/null +++ b/adapter/utils/address.go @@ -0,0 +1,25 @@ +package utils + +import ( + "fmt" + + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + + "github.com/rocket-pool/node-manager-core/wallet" +) + +func CheckIfWalletReady(hd *swclient.HyperdriveClient) (wallet.WalletStatus, bool, error) { + // Get & check wallet status + statusResponse, err := hd.Api.Wallet.Status() + if err != nil { + return wallet.WalletStatus{}, false, err + } + status := statusResponse.Data.WalletStatus + + // Check if it's already set properly and the wallet has been loaded + if !wallet.IsWalletReady(status) { + fmt.Println("The node wallet is not loaded or your node is in read-only mode. Please run `hyperdrive wallet status` for more details.") + return status, false, nil + } + return status, true, nil +} diff --git a/adapter/config/hyperdrive-config.go b/adapter/utils/config/hyperdrive-config.go similarity index 100% rename from adapter/config/hyperdrive-config.go rename to adapter/utils/config/hyperdrive-config.go diff --git a/adapter/config/mev-boost-config.go b/adapter/utils/config/mev-boost-config.go similarity index 100% rename from adapter/config/mev-boost-config.go rename to adapter/utils/config/mev-boost-config.go diff --git a/adapter/config/resources.go b/adapter/utils/config/resources.go similarity index 100% rename from adapter/config/resources.go rename to adapter/utils/config/resources.go diff --git a/adapter/config/settings.go b/adapter/utils/config/settings.go similarity index 100% rename from adapter/config/settings.go rename to adapter/utils/config/settings.go diff --git a/client/client.go b/client/client.go index c88123d..149c96a 100644 --- a/client/client.go +++ b/client/client.go @@ -9,9 +9,8 @@ import ( "path/filepath" clitemplate "github.com/nodeset-org/hyperdrive-stakewise/adapter/client/template" - "github.com/nodeset-org/hyperdrive/hyperdrive-cli/utils/context" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/config" - docker "github.com/docker/docker/client" "github.com/fatih/color" "github.com/mitchellh/go-homedir" @@ -19,7 +18,6 @@ import ( "github.com/nodeset-org/hyperdrive-daemon/shared/auth" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" swclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/nodeset-org/hyperdrive-stakewise/client/utils" swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" @@ -59,10 +57,10 @@ type SwApiClient struct { // Hyperdrive client type HyperdriveClient struct { - Api *hdclient.ApiClient - Context *utils.HyperdriveContext - Logger *slog.Logger - docker *docker.Client + Api *hdclient.ApiClient + Context *utils.HyperdriveContext + Logger *slog.Logger + // docker *docker.Client cfg *GlobalConfig isNewCfg bool } @@ -70,7 +68,7 @@ type HyperdriveClient struct { // Stakewise client type StakewiseClient struct { Api *SwApiClient - Context *context.HyperdriveContext + Context *utils.HyperdriveContext Logger *slog.Logger } @@ -313,13 +311,13 @@ func LoadConfigFromFile(configPath string, hdSettings []*hdconfig.HyperdriveSett // Create new Stakewise client from CLI context // Only use this function from commands that may work if the Daemon service doesn't exist func NewStakewiseClientFromCtx(c *cli.Context, hdClient *HyperdriveClient) (*StakewiseClient, error) { - hdCtx := context.GetHyperdriveContext(c) + hdCtx := utils.GetHyperdriveContext(c) return NewStakewiseClientFromHyperdriveCtx(hdCtx, hdClient) } // Create new Stakewise client from a custom context // Only use this function from commands that may work if the Daemon service doesn't exist -func NewStakewiseClientFromHyperdriveCtx(hdCtx *context.HyperdriveContext, hdClient *HyperdriveClient) (*StakewiseClient, error) { +func NewStakewiseClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext, hdClient *HyperdriveClient) (*StakewiseClient, error) { logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, swconfig.ModuleName)) // Create the tracer if required diff --git a/testing/node.go b/testing/node.go index 3cd96b8..4014f24 100644 --- a/testing/node.go +++ b/testing/node.go @@ -63,7 +63,7 @@ func newStakeWiseNode(sp swcommon.IStakeWiseServiceProvider, address string, cli } clientAuthMgr := auth.NewAuthorizationManager("", "sw-client", auth.DefaultRequestLifespan) clientAuthMgr.SetKey([]byte(apiAuthKey)) - apiClient := swclient.NewApiClient(url, clientLogger, nil, clientAuthMgr) + apiClient := swclient.NewSwApiClient(url, clientLogger, nil, clientAuthMgr) return &StakeWiseNode{ sp: sp, From e50f7e73a1f0b2d329411f499f8f9e8a74a0a3c9 Mon Sep 17 00:00:00 2001 From: huy Date: Tue, 7 Jan 2025 11:43:34 -0800 Subject: [PATCH 08/28] more cleanup --- adapter/config/generate-keys.go | 169 ++++++++++++++------------- adapter/config/get-node-status.go | 4 +- adapter/utils/flags.go | 5 + adapter/utils/nodeset.go | 98 ++++++++++++++++ adapter/utils/prompt.go | 114 ++++++++++++++++++ adapter/utils/upload-deposit-data.go | 100 ++++++++++++++++ go.mod | 22 ++-- go.sum | 41 ++++--- 8 files changed, 442 insertions(+), 111 deletions(-) create mode 100644 adapter/utils/nodeset.go create mode 100644 adapter/utils/prompt.go create mode 100644 adapter/utils/upload-deposit-data.go diff --git a/adapter/config/generate-keys.go b/adapter/config/generate-keys.go index 01b7a24..d101422 100644 --- a/adapter/config/generate-keys.go +++ b/adapter/config/generate-keys.go @@ -2,8 +2,13 @@ package config import ( "fmt" + "time" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" + "github.com/rocket-pool/node-manager-core/utils/input" "github.com/urfave/cli/v2" ) @@ -25,96 +30,96 @@ func generateKeys(c *cli.Context) error { fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") return nil } - // noRestart := c.Bool(generateKeysNoRestartFlag.Name) + noRestart := c.Bool(generateKeysNoRestartFlag.Name) - // // Check wallet status - // _, ready, err := utils.CheckIfWalletReady(hd) - // if err != nil { - // return err - // } - // if !ready { - // return nil - // } + // Check wallet status + _, ready, err := utils.CheckIfWalletReady(hd) + if err != nil { + return err + } + if !ready { + return nil + } - // // Get the count - // count := c.Uint64(generateKeysCountFlag.Name) - // if count == 0 { - // countString := cliutils.Prompt("How many keys would you like to generate?", "^\\d+$", "Invalid count, try again") - // count, err = input.ValidateUint("count", countString) - // if err != nil { - // return fmt.Errorf("invalid count [%s]: %w", countString, err) - // } - // } + // Get the count + count := c.Uint64(generateKeysCountFlag.Name) + if count == 0 { + countString := utils.Prompt("How many keys would you like to generate?", "^\\d+$", "Invalid count, try again") + count, err = input.ValidateUint("count", countString) + if err != nil { + return fmt.Errorf("invalid count [%s]: %w", countString, err) + } + } - // fmt.Println("Note: key generation is an expensive process, this may take a long time! Progress will be printed as each key is generated.") - // fmt.Println() + fmt.Println("Note: key generation is an expensive process, this may take a long time! Progress will be printed as each key is generated.") + fmt.Println() - // // Generate the new keys - // startTime := time.Now() - // latestTime := startTime - // for i := uint64(0); i < count; i++ { - // response, err := sw.Api.Wallet.GenerateKeys(1, false) - // if err != nil { - // return fmt.Errorf("error generating keys: %w", err) - // } - // if len(response.Data.Pubkeys) == 0 { - // return fmt.Errorf("server did not return any pubkeys") - // } + // Generate the new keys + startTime := time.Now() + latestTime := startTime + for i := uint64(0); i < count; i++ { + response, err := sw.Api.Wallet.GenerateKeys(1, false) + if err != nil { + return fmt.Errorf("error generating keys: %w", err) + } + if len(response.Data.Pubkeys) == 0 { + return fmt.Errorf("server did not return any pubkeys") + } - // elapsed := time.Since(latestTime) - // latestTime = time.Now() - // pubkey := response.Data.Pubkeys[0] - // fmt.Printf("Generated %s (%d/%d) in %s\n", pubkey.HexWithPrefix(), (i + 1), count, elapsed) - // } - // fmt.Printf("Completed in %s.\n", time.Since(startTime)) - // fmt.Println() + elapsed := time.Since(latestTime) + latestTime = time.Now() + pubkey := response.Data.Pubkeys[0] + fmt.Printf("Generated %s (%d/%d) in %s\n", pubkey.HexWithPrefix(), (i + 1), count, elapsed) + } + fmt.Printf("Completed in %s.\n", time.Since(startTime)) + fmt.Println() - // // Restart the Stakewise Operator - // if noRestart { - // fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Stakewise Operator container at your earliest convenience in order to deposit your new keys once it's your turn. Failure to do so will prevent your validators from ever being activated.%s\n", terminal.ColorYellow, terminal.ColorReset) - // } else { - // fmt.Print("Restarting Stakewise Operator... ") - // _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseOperator)) - // if err != nil { - // fmt.Println("error") - // fmt.Printf("%sWARNING: error restarting stakewise operator: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) - // fmt.Println("Please restart your Stakewise Operator container in order to be able to deposit for your new keys,") - // } else { - // fmt.Println("done!") - // } - // } - // fmt.Println() + // Restart the Stakewise Operator + if noRestart { + fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Stakewise Operator container at your earliest convenience in order to deposit your new keys once it's your turn. Failure to do so will prevent your validators from ever being activated.%s\n", terminal.ColorYellow, terminal.ColorReset) + } else { + fmt.Print("Restarting Stakewise Operator... ") + _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseOperator)) + if err != nil { + fmt.Println("error") + fmt.Printf("%sWARNING: error restarting stakewise operator: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) + fmt.Println("Please restart your Stakewise Operator container in order to be able to deposit for your new keys,") + } else { + fmt.Println("done!") + } + } + fmt.Println() - // // Restart the VC - // if noRestart { - // fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Validator Client at your earliest convenience in order to attest with your new keys. Failure to do so will result in any new validators being offline and *losing ETH* until you restart it.%s\n", terminal.ColorYellow, terminal.ColorReset) - // } else { - // fmt.Print("Restarting Validator Client... ") - // _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseValidator)) - // if err != nil { - // fmt.Println("error") - // fmt.Printf("%sWARNING: error restarting validator client: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) - // fmt.Println("Please restart your Validator Client in order to attest with your new keys!") - // } else { - // fmt.Println("done!") - // } - // } - // fmt.Println() + // Restart the VC + if noRestart { + fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Validator Client at your earliest convenience in order to attest with your new keys. Failure to do so will result in any new validators being offline and *losing ETH* until you restart it.%s\n", terminal.ColorYellow, terminal.ColorReset) + } else { + fmt.Print("Restarting Validator Client... ") + _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseValidator)) + if err != nil { + fmt.Println("error") + fmt.Printf("%sWARNING: error restarting validator client: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) + fmt.Println("Please restart your Validator Client in order to attest with your new keys!") + } else { + fmt.Println("done!") + } + } + fmt.Println() - // // Upload to the server - // newKeysUploaded, err := swcmdutils.UploadDepositData(c, hd, sw) - // if err != nil { - // return err - // } + // Upload to the server + newKeysUploaded, err := utils.UploadDepositData(c, hd, sw) + if err != nil { + return err + } - // if newKeysUploaded { - // if !noRestart { - // fmt.Println() - // fmt.Println("Your new keys are now ready for use. When one of them is selected for activation, your system will deposit it and begin attesting automatically.") - // } else { - // fmt.Println("Your new keys are uploaded, but you *must* restart your Validator Client at your earliest convenience to begin attesting once they are selected for depositing.") - // } - // } + if newKeysUploaded { + if !noRestart { + fmt.Println() + fmt.Println("Your new keys are now ready for use. When one of them is selected for activation, your system will deposit it and begin attesting automatically.") + } else { + fmt.Println("Your new keys are uploaded, but you *must* restart your Validator Client at your earliest convenience to begin attesting once they are selected for depositing.") + } + } - // return nil + return nil } diff --git a/adapter/config/get-node-status.go b/adapter/config/get-node-status.go index 8a80eaf..aaf8bd6 100644 --- a/adapter/config/get-node-status.go +++ b/adapter/config/get-node-status.go @@ -3,10 +3,10 @@ package config import ( "fmt" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" swtypes "github.com/nodeset-org/hyperdrive-stakewise/shared/types" - "github.com/nodeset-org/hyperdrive/hyperdrive-cli/commands/nodeset" "github.com/rocket-pool/node-manager-core/beacon" "github.com/urfave/cli/v2" ) @@ -31,7 +31,7 @@ func getNodeStatus(c *cli.Context) error { } // Check the registration status first - shouldContinue, err := nodeset.CheckRegistrationStatus(c, hd) + shouldContinue, err := utils.CheckRegistrationStatus(c, hd) if err != nil { return fmt.Errorf("error checking nodeset registration status: %w", err) } diff --git a/adapter/utils/flags.go b/adapter/utils/flags.go index 7ce9925..df9b953 100644 --- a/adapter/utils/flags.go +++ b/adapter/utils/flags.go @@ -48,6 +48,11 @@ var ( Name: "no-restart", Usage: fmt.Sprintf("Don't automatically restart the Stakewise Operator or Validator Client containers after generating keys. %sOnly use this if you know what you're doing and can restart them manually.%s", terminal.ColorRed, terminal.ColorReset), } + RegisterEmailFlag *cli.StringFlag = &cli.StringFlag{ + Name: "email", + Aliases: []string{"e"}, + Usage: "Email address to register with NodeSet.", + } ) func InstantiateFlag[FlagType cli.Flag](prototype FlagType, description string) cli.Flag { diff --git a/adapter/utils/nodeset.go b/adapter/utils/nodeset.go new file mode 100644 index 0000000..ccbd598 --- /dev/null +++ b/adapter/utils/nodeset.go @@ -0,0 +1,98 @@ +package utils + +import ( + "fmt" + "net/mail" + + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + + "github.com/urfave/cli/v2" +) + +// CheckRegistrationStatus checks the registration status of the node with NodeSet and prompts the user to register if not already done +// Returns whether or not the caller should continue with its operation after this check completes, or if it should exit +func CheckRegistrationStatus(c *cli.Context, hd *swclient.HyperdriveClient) (bool, error) { + // Check if the node is already registered + hasWallet, shouldRegister, err := checkRegistrationStatusImpl(hd) + if err != nil { + return false, err + } + if !shouldRegister { + return hasWallet, nil + } + + // Prompt for registration + if c.Bool(YesFlag.Name) { + return false, nil + } + if !Confirm("Would you like to register your node now?") { + fmt.Println("Cancelled.") + return false, nil + } + + return hasWallet, registerNodeImpl(c, hd) +} + +// Returns true if the node should register because it hasn't yet and is able to +func checkRegistrationStatusImpl(hd *swclient.HyperdriveClient) (bool, bool, error) { + // Check wallet status + _, ready, err := CheckIfWalletReady(hd) + if err != nil { + return false, false, err + } + if !ready { + return false, false, nil + } + + // Get the registration status + resp, err := hd.Api.NodeSet.GetRegistrationStatus() + if err != nil { + return false, false, err + } + switch resp.Data.Status { + case api.NodeSetRegistrationStatus_Unknown: + fmt.Println("Hyperdrive couldn't check your node's registration status:") + fmt.Println(resp.Data.ErrorMessage) + fmt.Println("Please try again later.") + case api.NodeSetRegistrationStatus_NoWallet: + fmt.Println("Your node can't be registered until you have a node wallet initialized. Please run `hyperdrive wallet init` or `hyperdrive wallet recover` first.") + case api.NodeSetRegistrationStatus_Unregistered: + fmt.Println("Your node is not currently registered with NodeSet.") + return true, true, nil + case api.NodeSetRegistrationStatus_Registered: + fmt.Println("Your node is registered with NodeSet.") + } + return true, false, nil +} + +// Registers the node with NodeSet +func registerNodeImpl(c *cli.Context, hd *swclient.HyperdriveClient) error { + // Get the email + email := c.String(RegisterEmailFlag.Name) + if email == "" { + for { + email = Prompt("Enter the email address you'd like to register with NodeSet:", "^.*$", "Invalid email address, try again") + _, err := mail.ParseAddress(email) + if err == nil { + break + } + fmt.Println("Invalid email address, try again") + } + } + + // Register the node + response, err := hd.Api.NodeSet.RegisterNode(email) + if err != nil { + return fmt.Errorf("error registering node: %w", err) + } + + // Validation + if response.Data.NotWhitelisted { + fmt.Printf("Your node has not been whitelisted in the NodeSet account for email address [%s]. Please go to the NodeSet website and add your node to your account's whitelist.\n", email) + return nil + } + + fmt.Println("Node successfully registered.") + return err +} diff --git a/adapter/utils/prompt.go b/adapter/utils/prompt.go new file mode 100644 index 0000000..4eaf58a --- /dev/null +++ b/adapter/utils/prompt.go @@ -0,0 +1,114 @@ +package utils + +import ( + "bufio" + "fmt" + "os" + "regexp" + "strconv" + "strings" + "syscall" + + "github.com/nodeset-org/hyperdrive/hyperdrive-cli/utils/terminal" + "golang.org/x/term" +) + +// Prompt for user input +func Prompt(initialPrompt string, expectedFormat string, incorrectFormatPrompt string) string { + + // Print initial prompt + fmt.Println(initialPrompt) + + // Get valid user input + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan(); !regexp.MustCompile(expectedFormat).MatchString(scanner.Text()); scanner.Scan() { + fmt.Println("") + fmt.Println(incorrectFormatPrompt) + } + fmt.Println("") + + // Return user input + return scanner.Text() + +} + +// Prompt for confirmation +func Confirm(initialPrompt string) bool { + response := Prompt(fmt.Sprintf("%s [y/n]", initialPrompt), "(?i)^(y|yes|n|no)$", "Please answer 'y' or 'n'") + return (strings.ToLower(response[:1]) == "y") +} + +// Prompt for 'I agree' confirmation (used on important questions to avoid a quick 'y' response from the user) +func ConfirmWithIAgree(initialPrompt string) bool { + response := Prompt(fmt.Sprintf("%s [Type 'I agree' or 'n']", initialPrompt), "(?i)^(i agree|n|no)$", "Please answer 'I agree' or 'n'") + return (len(response) == 7 && strings.ToLower(response[:7]) == "i agree") +} + +// Prompt for user selection +func Select(initialPrompt string, options []string) (int, string) { + + // Get prompt + prompt := initialPrompt + for i, option := range options { + prompt += fmt.Sprintf("\n%d: %s", (i + 1), option) + } + + // Get expected response format + optionNumbers := []string{} + for i := range options { + optionNumbers = append(optionNumbers, strconv.Itoa(i+1)) + } + expectedFormat := fmt.Sprintf("^(%s)$", strings.Join(optionNumbers, "|")) + + // Prompt user + response := Prompt(prompt, expectedFormat, "Please enter a number corresponding to an option") + + // Get selected option + index, _ := strconv.Atoi(response) + selectedIndex := index - 1 + selectedOption := options[selectedIndex] + + // Return + return selectedIndex, selectedOption + +} + +// Prompts the user to verify that there is nobody looking over their shoulder before printing sensitive information. +func ConfirmSecureSession(warning string) bool { + if !Confirm(fmt.Sprintf("%s%s%s\nAre you sure you want to continue?", terminal.ColorYellow, warning, terminal.ColorReset)) { + fmt.Println("Cancelled.") + return false + } + + return true +} + +// Prompt for password input +func PromptPassword(initialPrompt string, expectedFormat string, incorrectFormatPrompt string) string { + // Print initial prompt + fmt.Println(initialPrompt) + + // Get valid user input + var input string + var init bool + for !init || !regexp.MustCompile(expectedFormat).MatchString(input) { + + // Incorrect format + if init { + fmt.Println("") + fmt.Println(incorrectFormatPrompt) + } else { + init = true + } + + // Read password + if bytes, err := term.ReadPassword(syscall.Stdin); err != nil { + fmt.Println(fmt.Errorf("error reading password: %w", err)) + } else { + input = string(bytes) + } + + } + fmt.Println("") + return input +} diff --git a/adapter/utils/upload-deposit-data.go b/adapter/utils/upload-deposit-data.go new file mode 100644 index 0000000..05fa1e7 --- /dev/null +++ b/adapter/utils/upload-deposit-data.go @@ -0,0 +1,100 @@ +package utils + +import ( + "fmt" + + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + "github.com/urfave/cli/v2" +) + +func printUploadError(err error) { + fmt.Println("Error") + fmt.Printf("%sWARNING: Error uploading deposit data to NodeSet: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) + fmt.Println("Please upload the deposit data for all of your keys with `hyperdrive stakewise nodeset upload-deposit-data` when you're ready. Without it, NodeSet won't be able to assign new deposits to your validators.") + fmt.Println() +} + +// Upload deposit data to the server +func UploadDepositData(c *cli.Context, hd *swclient.HyperdriveClient, sw *swclient.StakewiseClient) (bool, error) { + // Check wallet status + _, ready, err := CheckIfWalletReady(hd) + if err != nil { + return false, err + } + if !ready { + return false, nil + } + + // Warn user prior to uploading deposit data + fmt.Println("NOTE: There is currently no way to remove a validator's deposit data from the NodeSet service once you've uploaded it. The key will be eligible for activation at any time, so this node must remain online at all times to handle activation and validation duties.") + fmt.Printf("%sIf you turn the node off, you may be removed from NodeSet for negligence of duty!%s\n", terminal.ColorYellow, terminal.ColorReset) + fmt.Println() + + if !(c.Bool(YesFlag.Name) || Confirm("Do you want to continue uploading your deposit data?")) { + fmt.Println("Cancelled.") + return false, nil + } + + // Initial attempt to upload all deposit data + fmt.Println("Uploading deposit data to the NodeSet server...") + response, err := sw.Api.Nodeset.UploadDepositData() + if err != nil { + printUploadError(err) + return false, nil + } + if response.Data.UnregisteredNode { + fmt.Println("Your node is not registered with NodeSet yet. Please register your node first.") + return false, nil + } + + data := response.Data + + newKeyCount := len(data.NewPubkeys) + remainingKeyCount := len(data.RemainingPubkeys) + + if data.InvalidWithdrawalCredentials { + fmt.Printf("%sWARNING: Your deposit data contained withdrawal credentials that do not correspond to a valid StakeWise vault. Please contact the Hyperdrive developers and report this issue.%s\n", terminal.ColorYellow, terminal.ColorReset) + return false, nil + } + if data.NotAuthorizedForMainnet { + fmt.Printf("%sWARNING: Your deposit data was rejected because you are not currently authorized to access the Mainnet vault. You will need to run on the Holesky testnet first before being given access to Mainnet.%s\n", terminal.ColorYellow, terminal.ColorReset) + return false, nil + } + + if data.SufficientBalance { + if newKeyCount == 0 { + fmt.Printf("All of your validator keys are already registered (%s%d%s in total).\n", terminal.ColorGreen, data.TotalCount, terminal.ColorReset) + fmt.Printf("%s%d%s are pending activation.\n", terminal.ColorGreen, data.PendingCount, terminal.ColorReset) + fmt.Printf("%s%d%s have been activated already.\n", terminal.ColorGreen, data.ActiveCount, terminal.ColorReset) + return false, nil + } + fmt.Printf("Registered %s%d%s new validator keys:\n", terminal.ColorGreen, newKeyCount, terminal.ColorReset) + for _, key := range data.NewPubkeys { + fmt.Println(key.HexWithPrefix()) + } + } else { + fmt.Printf("%sWarning: not all keys were uploaded due to insufficient balance.%s\n", terminal.ColorYellow, terminal.ColorReset) + fmt.Printf("Current wallet balance: %s%f%s\n", terminal.ColorGreen, data.Balance, terminal.ColorReset) + fmt.Printf("Remaining unregistered keys: %s%d%s\n", terminal.ColorGreen, remainingKeyCount, terminal.ColorReset) + fmt.Printf("You need %s%f%s more ETH to register your remaining keys.\n", terminal.ColorGreen, data.RemainingEthRequired, terminal.ColorReset) + + totalUnregisteredKeyCount := len(data.NewPubkeys) + len(data.RemainingPubkeys) + if newKeyCount == 0 { + fmt.Printf("\nUploaded 0 out of %d new keys.\n", totalUnregisteredKeyCount) + } else { + fmt.Printf("\nUploaded %d out of %d new keys:\n", newKeyCount, totalUnregisteredKeyCount) + for _, key := range data.NewPubkeys { + fmt.Println(key.HexWithPrefix()) + } + } + } + data.PendingCount += uint64(newKeyCount) + fmt.Println() + fmt.Printf("Total keys: %s%d%s\n", terminal.ColorGreen, data.TotalCount, terminal.ColorReset) + fmt.Printf("%s%d%s are registered and pending activation.\n", terminal.ColorGreen, data.PendingCount, terminal.ColorReset) + fmt.Printf("%s%d%s are registered and have been activated already.\n", terminal.ColorGreen, data.ActiveCount, terminal.ColorReset) + + return newKeyCount != 0, nil +} diff --git a/go.mod b/go.mod index c3fcc2e..4f22b42 100644 --- a/go.mod +++ b/go.mod @@ -5,20 +5,24 @@ go 1.22 toolchain go1.22.7 require ( - github.com/ethereum/go-ethereum v1.14.8 + github.com/alessio/shellescape v1.4.2 + github.com/ethereum/go-ethereum v1.14.11 github.com/fatih/color v1.17.0 github.com/goccy/go-json v0.10.3 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 - github.com/nodeset-org/hyperdrive-daemon v1.1.0 - github.com/nodeset-org/nodeset-client-go v1.2.1 + github.com/mitchellh/go-homedir v1.1.0 + github.com/nodeset-org/hyperdrive v1.1.1 + github.com/nodeset-org/hyperdrive-daemon v1.1.1 + github.com/nodeset-org/nodeset-client-go v1.2.2 github.com/nodeset-org/osha v0.3.1 github.com/rocket-pool/batch-query v1.0.0 - github.com/rocket-pool/node-manager-core v0.5.2-0.20241011014615-2cf2a45afa0a + github.com/rocket-pool/node-manager-core v0.5.2-0.20241029172412-6cb22253be3f github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.27.2 github.com/wealdtech/go-eth2-types/v2 v2.8.2 github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1 + golang.org/x/term v0.22.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -29,7 +33,6 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.5 // indirect - github.com/alessio/shellescape v1.4.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/btcsuite/btcd v0.24.2 // indirect @@ -51,6 +54,7 @@ require ( github.com/containerd/ttrpc v1.2.5 // indirect github.com/containerd/typeurl/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect @@ -63,6 +67,7 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/ethereum/c-kzg-4844 v1.0.2 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/ferranbt/fastssz v0.1.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -80,6 +85,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-memdb v1.3.4 // indirect + github.com/hashicorp/go-uuid v1.0.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/herumi/bls-eth-go-binary v1.36.1 // indirect @@ -92,7 +98,6 @@ require ( github.com/mattn/go-shellwords v1.0.12 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect @@ -131,13 +136,13 @@ require ( github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e // indirect github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b // indirect github.com/prysmaticlabs/prysm/v5 v5.1.0 // indirect - github.com/rivo/uniseg v0.4.7 // indirect + github.com/rs/cors v1.10.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sethvargo/go-password v0.3.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/supranational/blst v0.3.12 // indirect + github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect @@ -166,6 +171,7 @@ require ( golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.171.0 // indirect google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect diff --git a/go.sum b/go.sum index a9931eb..a797e22 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/e github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= -github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= @@ -154,10 +154,10 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/c-kzg-4844 v1.0.2 h1:8tV84BCEiPeOkiVgW9mpYBeBUir2bkCNVqxPwwVeO+s= github.com/ethereum/c-kzg-4844 v1.0.2/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.14.8 h1:NgOWvXS+lauK+zFukEvi85UmmsS/OkV0N23UZ1VTIig= -github.com/ethereum/go-ethereum v1.14.8/go.mod h1:TJhyuDq0JDppAkFXgqjwpdlQApywnu/m10kFPxh8vvs= -github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= -github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= +github.com/ethereum/go-ethereum v1.14.11 h1:8nFDCUUE67rPc6AKxFj7JKaOa2W/W1Rse3oS6LvvxEY= +github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -253,8 +253,9 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= -github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -376,10 +377,12 @@ github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOEL github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nodeset-org/hyperdrive-daemon v1.1.0 h1:q3JSFSw6qIdeU+JmbjGB4gn1Q9e8Zff+1q2E0Fjeoag= -github.com/nodeset-org/hyperdrive-daemon v1.1.0/go.mod h1:42hfvtW4Lvfz0uVtoujQ2b1nUatpH8s8fMNuzoTFoRc= -github.com/nodeset-org/nodeset-client-go v1.2.1 h1:2Nybz1IONZBCD+j4+MgRBIvA3dNW4+ek1+kFtB/D6MY= -github.com/nodeset-org/nodeset-client-go v1.2.1/go.mod h1:TATOnCsIvDjC7C+h1izgw0+t35N40Hr/CxhgaLK78e4= +github.com/nodeset-org/hyperdrive v1.1.1 h1:PImbcczTqGpx0UPJugQIZkxzlWMJn9O5+138mOrbeKA= +github.com/nodeset-org/hyperdrive v1.1.1/go.mod h1:BId3aaponZ2gPeNReLIdB+AAK0sDOKUxV3xAQQrs4fg= +github.com/nodeset-org/hyperdrive-daemon v1.1.1 h1:aUQzEKmyuYhVTzn3AQEIdUm335xBYmsCTJfPdGNA5r0= +github.com/nodeset-org/hyperdrive-daemon v1.1.1/go.mod h1:tzqdqfRR4Xcy4GtdSiEnfjOFj3+g4TvuZJpYljCQEBM= +github.com/nodeset-org/nodeset-client-go v1.2.2 h1:mjJnH5uw/CF5TWdRxdU5+Cw/ERRLz4hl9VsRFVO0Wdo= +github.com/nodeset-org/nodeset-client-go v1.2.2/go.mod h1:TATOnCsIvDjC7C+h1izgw0+t35N40Hr/CxhgaLK78e4= github.com/nodeset-org/osha v0.3.1 h1:xHDjCswxGDazY/UsZ0QOpcu7gTVnEuUwXcKGVAz72lI= github.com/nodeset-org/osha v0.3.1/go.mod h1:47D6kYMuxYDTbul3w/YtE1LKA0hfzbXzCEC3M9oQlOU= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -449,12 +452,12 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rocket-pool/batch-query v1.0.0 h1:5HejmT1n1fIdLIqUhTNwbkG2PGOPl3IVjCpFQcQZ4I4= github.com/rocket-pool/batch-query v1.0.0/go.mod h1:d1CmxShzk0fioJ4yX0eFGhz2an1odnW/LZ2cp3eDGIQ= -github.com/rocket-pool/node-manager-core v0.5.2-0.20241011014615-2cf2a45afa0a h1:oVdhSAFXzNFZ0ypQuh7zwyi0mKrKccnzlutgWxzUBrk= -github.com/rocket-pool/node-manager-core v0.5.2-0.20241011014615-2cf2a45afa0a/go.mod h1:/H1wq3skacZi4zjgnKTtnm0wBLJH7H5r0CvLtWFs19Y= +github.com/rocket-pool/node-manager-core v0.5.2-0.20241029172412-6cb22253be3f h1:yGcY5EBYh/8f5zqyoorI3XHS/oD9uyGdbIpSOHSKWI8= +github.com/rocket-pool/node-manager-core v0.5.2-0.20241029172412-6cb22253be3f/go.mod h1:/FFOK81WO/FZO+Y/9LeBRkVE17VkyyerAb9oBh9fpag= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= @@ -482,8 +485,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.12 h1:Vfas2U2CFHhniv2QkUm2OVa1+pGTdqtpqm9NnhUUbZ8= -github.com/supranational/blst v0.3.12/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -660,8 +663,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU= +google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= From 60aa8100da63d57760ec47eb141c09824a490e72 Mon Sep 17 00:00:00 2001 From: huy Date: Tue, 7 Jan 2025 13:07:00 -0800 Subject: [PATCH 09/28] more fixes --- adapter/config/claim-rewards.go | 125 +++++++------- adapter/utils/address.go | 108 ++++++++++++ adapter/utils/flags.go | 20 +++ adapter/utils/gas.go | 281 ++++++++++++++++++++++++++++++++ adapter/utils/tx/tx.go | 253 ++++++++++++++++++++++++++++ 5 files changed, 728 insertions(+), 59 deletions(-) create mode 100644 adapter/utils/gas.go create mode 100644 adapter/utils/tx/tx.go diff --git a/adapter/config/claim-rewards.go b/adapter/config/claim-rewards.go index 091eaf7..84b075b 100644 --- a/adapter/config/claim-rewards.go +++ b/adapter/config/claim-rewards.go @@ -1,72 +1,79 @@ package config import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/tx" + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + "github.com/rocket-pool/node-manager-core/eth" "github.com/urfave/cli/v2" ) func claimRewards(c *cli.Context) error { - return nil - // // Get the client - // hd, err := client.NewHyperdriveClientFromCtx(c) - // if err != nil { - // return err - // } - // sw, err := client.NewStakewiseClientFromCtx(c, hd) - // if err != nil { - // return err - // } - // cfg, _, err := hd.LoadConfig() - // if err != nil { - // return fmt.Errorf("error loading Hyperdrive config: %w", err) - // } - // if !cfg.StakeWise.Enabled.Value { - // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - // return nil - // } + // Get the client + hd, err := swclient.NewHyperdriveClientFromCtx(c) + if err != nil { + return err + } + sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + if err != nil { + return err + } + cfg, _, err := hd.LoadConfig() + if err != nil { + return fmt.Errorf("error loading Hyperdrive config: %w", err) + } + if !cfg.StakeWise.Enabled.Value { + fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + return nil + } - // // Check if there's a node address ready - // _, ready, err := utils.CheckIfAddressReady(hd) - // if err != nil { - // return err - // } - // if !ready { - // return nil - // } + // Check if there's a node address ready + _, ready, err := utils.CheckIfAddressReady(hd) + if err != nil { + return err + } + if !ready { + return nil + } - // // Get the list of rewards available - // resp, err := sw.Api.Wallet.ClaimRewards() - // if err != nil { - // return err - // } - // fmt.Println("Your withdrawable rewards:") - // fmt.Printf("%.4f %s (%s)\n", eth.WeiToEth(resp.Data.WithdrawableToken), resp.Data.TokenSymbol, resp.Data.TokenName) - // fmt.Printf("%.4f ETH\n", eth.WeiToEth(resp.Data.WithdrawableNativeToken)) - // fmt.Println() - // fmt.Println("NOTE: this list only shows rewards that StakeWise has already returned to NodeSet. Your share may include more rewards, but StakeWise hasn't returned yet.") - // fmt.Println() + // Get the list of rewards available + resp, err := sw.Api.Wallet.ClaimRewards() + if err != nil { + return err + } + fmt.Println("Your withdrawable rewards:") + fmt.Printf("%.4f %s (%s)\n", eth.WeiToEth(resp.Data.WithdrawableToken), resp.Data.TokenSymbol, resp.Data.TokenName) + fmt.Printf("%.4f ETH\n", eth.WeiToEth(resp.Data.WithdrawableNativeToken)) + fmt.Println() + fmt.Println("NOTE: this list only shows rewards that StakeWise has already returned to NodeSet. Your share may include more rewards, but StakeWise hasn't returned yet.") + fmt.Println() - // // Check if both balances are zero - // sum := big.NewInt(0) - // sum.Add(sum, resp.Data.WithdrawableNativeToken) - // sum.Add(sum, resp.Data.WithdrawableToken) - // if sum.Cmp(common.Big0) == 0 { - // fmt.Println("You don't have any rewards to claim.") - // return nil - // } + // Check if both balances are zero + sum := big.NewInt(0) + sum.Add(sum, resp.Data.WithdrawableNativeToken) + sum.Add(sum, resp.Data.WithdrawableToken) + if sum.Cmp(common.Big0) == 0 { + fmt.Println("You don't have any rewards to claim.") + return nil + } - // // Run the TX - // validated, err := tx.HandleTx(c, hd, resp.Data.TxInfo, - // "Are you sure you want to claim rewards?", - // "claiming rewards", - // "Claiming rewards...", - // ) - // if err != nil { - // return err - // } - // if !validated { - // return nil - // } + // Run the TX + validated, err := tx.HandleTx(c, hd, resp.Data.TxInfo, + "Are you sure you want to claim rewards?", + "claiming rewards", + "Claiming rewards...", + ) + if err != nil { + return err + } + if !validated { + return nil + } - // fmt.Println("Rewards successfully claimed.") - // return nil + fmt.Println("Rewards successfully claimed.") + return nil } diff --git a/adapter/utils/address.go b/adapter/utils/address.go index 6cd6cae..5f0c11e 100644 --- a/adapter/utils/address.go +++ b/adapter/utils/address.go @@ -3,6 +3,8 @@ package utils import ( "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/rocket-pool/node-manager-core/wallet" @@ -23,3 +25,109 @@ func CheckIfWalletReady(hd *swclient.HyperdriveClient) (wallet.WalletStatus, boo } return status, true, nil } + +// Verifies the daemon has a node address ready and loaded (allows for masquerade mode support). +func CheckIfAddressReady(hd *swclient.HyperdriveClient) (wallet.WalletStatus, bool, error) { + // Get & check wallet status + statusResponse, err := hd.Api.Wallet.Status() + if err != nil { + return wallet.WalletStatus{}, false, err + } + status := statusResponse.Data.WalletStatus + + // There's an address ready + if status.Address.HasAddress { + if status.Address.NodeAddress != status.Wallet.WalletAddress { + fmt.Printf("%sReminder: You are currently masquerading as %s%s%s.\nYou can create transactions but cannot sign or submit them.%s\n", terminal.ColorGreen, terminal.ColorBlue, status.Address.NodeAddress, terminal.ColorGreen, terminal.ColorReset) + fmt.Println() + } + return status, true, nil + } + + // If the address isn't ready, check if the wallet's ready + if !status.Wallet.IsLoaded { + if !status.Wallet.IsOnDisk { + fmt.Println("The node wallet has not been initialized yet. Please run `hyperdrive wallet init` or `hyperdrive wallet recover` first, then run this again.") + return status, false, nil + } + fmt.Println("The daemon requires your node wallet's password to unlock it. Please run `hyperdrive wallet set-password` first, then run this again.") + return status, false, nil + } + + // The address isn't ready but the wallet is so have the user run restore-address to fix it + fmt.Printf("The node wallet is %s%s%s but the node address is not set. Please restore it with `hyperdrive wallet restore-address` or `hyperdrive wallet masquerade` first, then run this again.", terminal.ColorBlue, status.Wallet.WalletAddress, terminal.ColorReset) + return status, false, nil +} + +// Print a TX's details to the console. +func PrintTransactionHash(hd *swclient.HyperdriveClient, hash common.Hash) { + finalMessage := "Waiting for the transaction to be included in a block... you may wait here for it, or press CTRL+C to exit and return to the terminal.\n\n" + printTransactionHashImpl(hd, hash, finalMessage) +} + +// Print a TX's details to the console, but inform the user NOT to cancel it. +func PrintTransactionHashNoCancel(hd *swclient.HyperdriveClient, hash common.Hash) { + finalMessage := "Waiting for the transaction to be included in a block... **DO NOT EXIT!** This transaction is one of several that must be completed.\n\n" + printTransactionHashImpl(hd, hash, finalMessage) +} + +// Print a batch of transaction hashes to the console. +func PrintTransactionBatchHashes(hd *swclient.HyperdriveClient, hashes []common.Hash) { + finalMessage := "Waiting for the transactions to be included in one or more blocks... you may wait here for them, or press CTRL+C to exit and return to the terminal.\n\n" + + // Print the hashes + fmt.Println("Transactions have been submitted with the following hashes:") + hashStrings := make([]string, len(hashes)) + for i, hash := range hashes { + hashString := hash.String() + hashStrings[i] = hashString + fmt.Println(hashString) + } + fmt.Println() + + txWatchUrl := getTxWatchUrl(hd) + if txWatchUrl != "" { + fmt.Println("You may follow their progress by visiting the following URLs in sequence:") + for _, hash := range hashStrings { + fmt.Printf("%s/%s\n", txWatchUrl, hash) + } + } + fmt.Println() + + fmt.Print(finalMessage) +} + +// Print a warning to the console if the user set a custom nonce, but this operation involves multiple transactions +func PrintMultiTransactionNonceWarning() { + fmt.Printf("%sNOTE: You have specified the `nonce` flag to indicate a custom nonce for this transaction.\n"+ + "However, this operation requires multiple transactions.\n"+ + "Hyperdrive will use your custom value as a basis, and increment it for each additional transaction.\n"+ + "If you have multiple pending transactions, this MAY OVERRIDE more than the one that you specified.%s\n\n", terminal.ColorYellow, terminal.ColorReset) +} + +// Implementation of PrintTransactionHash and PrintTransactionHashNoCancel +func printTransactionHashImpl(hd *swclient.HyperdriveClient, hash common.Hash, finalMessage string) { + txWatchUrl := getTxWatchUrl(hd) + hashString := hash.String() + fmt.Printf("Transaction has been submitted with hash %s.\n", hashString) + if txWatchUrl != "" { + fmt.Printf("You may follow its progress by visiting:\n") + fmt.Printf("%s/%s\n\n", txWatchUrl, hashString) + } + fmt.Print(finalMessage) +} + +// Get the URL for watching the transaction in a block explorer +func getTxWatchUrl(hd *swclient.HyperdriveClient) string { + cfg, isNew, err := hd.LoadConfig() + if err != nil { + fmt.Printf("Warning: couldn't read config file so the transaction URL will be unavailable (%s).\n", err) + return "" + } + + if isNew { + fmt.Print("Settings file not found. Please run `hyperdrive service config` to set up Hyperdrive.") + return "" + } + return cfg.HyperdriveResources.TxWatchUrl +} diff --git a/adapter/utils/flags.go b/adapter/utils/flags.go index df9b953..a804479 100644 --- a/adapter/utils/flags.go +++ b/adapter/utils/flags.go @@ -53,6 +53,26 @@ var ( Aliases: []string{"e"}, Usage: "Email address to register with NodeSet.", } + ForceGasLimitFlag *cli.Uint64Flag = &cli.Uint64Flag{ + Name: "force-gas-limit", + Aliases: []string{"fgl"}, + Usage: fmt.Sprintf("Force a specific gas limit for all transactions produced by the command being run, overriding the gas limit estimated by transaction simulation. %sOnly use this if you know what you're doing.%s", terminal.ColorRed, terminal.ColorReset), + } + PrintTxDataFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "print-tx-data", + Aliases: []string{"pt"}, + Usage: "Print any TX data for transactions without signing or submitting them. Useful for masquerade mode or offline wallet operations.", + } + IgnoreTxSimFailureFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "ignore-tx-sim-failure", + Aliases: []string{"itsf"}, + Usage: fmt.Sprintf("Ignore any transaction simulation failures and sign / submit transactions even if they will revert. %sThis can result in failed transactions and loss of funds. Only use this if you absolutely know what you're doing.%s", terminal.ColorRed, terminal.ColorReset), + } + SignTxOnlyFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "sign-tx-only", + Aliases: []string{"st"}, + Usage: "Sign any TXs and print the results, but don't submit it to the network. Useful if you want to save a TX for later or bundle it up with a service like Flashbots.", + } ) func InstantiateFlag[FlagType cli.Flag](prototype FlagType, description string) cli.Flag { diff --git a/adapter/utils/gas.go b/adapter/utils/gas.go new file mode 100644 index 0000000..87c5218 --- /dev/null +++ b/adapter/utils/gas.go @@ -0,0 +1,281 @@ +package utils + +import ( + "fmt" + "math" + "math/big" + "strconv" + + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + "github.com/nodeset-org/hyperdrive/hyperdrive-cli/utils/terminal" + "github.com/rocket-pool/node-manager-core/eth" + "github.com/rocket-pool/node-manager-core/gas" + "github.com/urfave/cli/v2" +) + +func GetMaxFees(c *cli.Context, hd *swclient.HyperdriveClient, simResult eth.SimulationResult) (*big.Int, *big.Int, error) { + cfg, isNew, err := hd.LoadConfig() + if err != nil { + return nil, nil, fmt.Errorf("error getting Hyperdrive configuration: %w", err) + } + if isNew { + return nil, nil, fmt.Errorf("Settings file not found. Please run `hyperdrive service config` to set up Hyperdrive.") + } + + // Get the max fee - prioritize the CLI arguments, default to the config file setting + maxFeeGwei := hd.Context.MaxFee + + // Get the priority fee - prioritize the CLI arguments, default to the config file setting + maxPriorityFeeGwei := hd.Context.MaxPriorityFee + if maxPriorityFeeGwei == 0 { + maxPriorityFee := eth.GweiToWei(cfg.Hyperdrive.MaxPriorityFee.Value) + if maxPriorityFee == nil || maxPriorityFee.Uint64() == 0 { + defaultFee := cfg.Hyperdrive.MaxPriorityFee.Default[cfg.Hyperdrive.Network.Value] + fmt.Printf("%sNOTE: max priority fee not set or set to 0, defaulting to %.4f gwei%s\n", terminal.ColorYellow, defaultFee, terminal.ColorReset) + maxPriorityFeeGwei = defaultFee + } else { + maxPriorityFeeGwei = eth.WeiToGwei(maxPriorityFee) + } + } + + // Use the requested max fee and priority fee if provided + if maxFeeGwei != 0 { + if maxPriorityFeeGwei > maxFeeGwei { + fmt.Printf("NOTE: Priority fee cannot be greater than max fee. Lowering priority fee to %.2f gwei.\n", maxFeeGwei) + maxPriorityFeeGwei = maxFeeGwei + } + fmt.Printf("%sUsing the requested max fee of %.2f gwei (including a max priority fee of %.2f gwei).\n", terminal.ColorYellow, maxFeeGwei, maxPriorityFeeGwei) + lowLimit := maxFeeGwei / eth.WeiPerGwei * float64(simResult.EstimatedGasLimit) + highLimit := maxFeeGwei / eth.WeiPerGwei * float64(simResult.SafeGasLimit) + fmt.Printf("Total cost: %.4f to %.4f ETH%s\n", lowLimit, highLimit, terminal.ColorReset) + } else { + if c.Bool(YesFlag.Name) { + maxFeeWei, err := GetHeadlessMaxFeeWei() + if err != nil { + return nil, nil, err + } + maxFeeGwei = eth.WeiToGwei(maxFeeWei) + } else { + // Try to get the latest gas prices from Etherchain + etherchainData, err := gas.GetEtherchainGasPrices() + if err == nil { + // Print the Etherchain data and ask for an amount + maxFeeGwei = handleEtherchainGasPrices(etherchainData, simResult, maxPriorityFeeGwei, 0) + + } else { + // Fallback to Etherscan + fmt.Printf("%sWarning: couldn't get gas estimates from Etherchain - %s\nFalling back to Etherscan%s\n", terminal.ColorYellow, err.Error(), terminal.ColorReset) + etherscanData, err := gas.GetEtherscanGasPrices() + if err == nil { + // Print the Etherscan data and ask for an amount + maxFeeGwei = handleEtherscanGasPrices(etherscanData, simResult, maxPriorityFeeGwei, 0) + } else { + return nil, nil, fmt.Errorf("Error getting gas price suggestions: %w", err) + } + } + } + if maxPriorityFeeGwei > maxFeeGwei { + fmt.Printf("NOTE: Priority fee cannot be greater than max fee. Lowering priority fee to %.2f gwei.\n", maxFeeGwei) + maxPriorityFeeGwei = maxFeeGwei + } + fmt.Printf("%sUsing a max fee of %.2f gwei and a priority fee of %.2f gwei.\n%s", terminal.ColorBlue, maxFeeGwei, maxPriorityFeeGwei, terminal.ColorReset) + } + + // Verify the node has enough ETH to use this max fee + maxFee := eth.GweiToWei(maxFeeGwei) + ethRequired := big.NewInt(0).Mul(maxFee, big.NewInt(int64(simResult.SafeGasLimit))) + response, err := hd.Api.Wallet.Balance() + if err != nil { + fmt.Printf("%sWARNING: couldn't check the ETH balance of the node (%s)\nPlease ensure your node wallet has enough ETH to pay for this transaction.%s\n\n", terminal.ColorYellow, err.Error(), terminal.ColorReset) + } else if response.Data.Balance.Cmp(ethRequired) < 0 { + return nil, nil, fmt.Errorf("Your node has %.6f ETH in its wallet, which is not enough to pay for this transaction with a max fee of %.4f gwei; you require at least %.6f more ETH.", eth.WeiToEth(response.Data.Balance), maxFeeGwei, eth.WeiToEth(big.NewInt(0).Sub(ethRequired, response.Data.Balance))) + } + maxPriorityFee := eth.GweiToWei(maxPriorityFeeGwei) + + return maxFee, maxPriorityFee, nil +} + +// Get the suggested max fee for service operations +func GetHeadlessMaxFeeWei() (*big.Int, error) { + etherchainData, err := gas.GetEtherchainGasPrices() + if err == nil { + return etherchainData.RapidWei, nil + } + + fmt.Printf("%sWARNING: couldn't get gas estimates from Etherchain - %s\nFalling back to Etherscan%s\n", terminal.ColorYellow, err.Error(), terminal.ColorReset) + etherscanData, err := gas.GetEtherscanGasPrices() + if err == nil { + return eth.GweiToWei(etherscanData.FastGwei), nil + } + + return nil, fmt.Errorf("error getting gas price suggestions: %w", err) +} + +func handleEtherchainGasPrices(gasSuggestion gas.EtherchainGasFeeSuggestion, simResult eth.SimulationResult, priorityFee float64, gasLimit uint64) float64 { + rapidGwei := math.Ceil(eth.WeiToGwei(gasSuggestion.RapidWei) + priorityFee) + rapidEth := eth.WeiToEth(gasSuggestion.RapidWei) + + var rapidLowLimit float64 + var rapidHighLimit float64 + if gasLimit == 0 { + rapidLowLimit = rapidEth * float64(simResult.EstimatedGasLimit) + rapidHighLimit = rapidEth * float64(simResult.SafeGasLimit) + } else { + rapidLowLimit = rapidEth * float64(gasLimit) + rapidHighLimit = rapidLowLimit + } + + fastGwei := math.Ceil(eth.WeiToGwei(gasSuggestion.FastWei) + priorityFee) + fastEth := eth.WeiToEth(gasSuggestion.FastWei) + + var fastLowLimit float64 + var fastHighLimit float64 + if gasLimit == 0 { + fastLowLimit = fastEth * float64(simResult.EstimatedGasLimit) + fastHighLimit = fastEth * float64(simResult.SafeGasLimit) + } else { + fastLowLimit = fastEth * float64(gasLimit) + fastHighLimit = fastLowLimit + } + + standardGwei := math.Ceil(eth.WeiToGwei(gasSuggestion.StandardWei) + priorityFee) + standardEth := eth.WeiToEth(gasSuggestion.StandardWei) + + var standardLowLimit float64 + var standardHighLimit float64 + if gasLimit == 0 { + standardLowLimit = standardEth * float64(simResult.EstimatedGasLimit) + standardHighLimit = standardEth * float64(simResult.SafeGasLimit) + } else { + standardLowLimit = standardEth * float64(gasLimit) + standardHighLimit = standardLowLimit + } + + slowGwei := math.Ceil(eth.WeiToGwei(gasSuggestion.SlowWei) + priorityFee) + slowEth := eth.WeiToEth(gasSuggestion.SlowWei) + + var slowLowLimit float64 + var slowHighLimit float64 + if gasLimit == 0 { + slowLowLimit = slowEth * float64(simResult.EstimatedGasLimit) + slowHighLimit = slowEth * float64(simResult.SafeGasLimit) + } else { + slowLowLimit = slowEth * float64(gasLimit) + slowHighLimit = slowLowLimit + } + + fmt.Printf("%s+============== Suggested Gas Prices ==============+\n", terminal.ColorBlue) + fmt.Println("| Avg Wait Time | Max Fee | Total Gas Cost |") + fmt.Printf("| %-13s | %-9s | %.4f to %.4f ETH |\n", + gasSuggestion.RapidTime, fmt.Sprintf("%d gwei", int(rapidGwei)), rapidLowLimit, rapidHighLimit) + fmt.Printf("| %-13s | %-9s | %.4f to %.4f ETH |\n", + gasSuggestion.FastTime, fmt.Sprintf("%d gwei", int(fastGwei)), fastLowLimit, fastHighLimit) + fmt.Printf("| %-13s | %-9s | %.4f to %.4f ETH |\n", + gasSuggestion.StandardTime, fmt.Sprintf("%d gwei", int(standardGwei)), standardLowLimit, standardHighLimit) + fmt.Printf("| %-13s | %-9s | %.4f to %.4f ETH |\n", + gasSuggestion.SlowTime, fmt.Sprintf("%d gwei", int(slowGwei)), slowLowLimit, slowHighLimit) + fmt.Printf("+==================================================+\n\n%s", terminal.ColorReset) + + fmt.Printf("These prices include a maximum priority fee of %.2f gwei.\n", priorityFee) + + for { + desiredPrice := Prompt( + fmt.Sprintf("Please enter your max fee (including the priority fee) or leave blank for the default of %d gwei:", int(fastGwei)), + "^(?:[1-9]\\d*|0)?(?:\\.\\d+)?$", + "Not a valid gas price, try again:") + + if desiredPrice == "" { + return fastGwei + } + + desiredPriceFloat, err := strconv.ParseFloat(desiredPrice, 64) + if err != nil { + fmt.Printf("Not a valid gas price (%s), try again.", err.Error()) + fmt.Println() + continue + } + if desiredPriceFloat <= 0 { + fmt.Println("Max fee must be greater than zero.") + continue + } + + return desiredPriceFloat + } +} + +func handleEtherscanGasPrices(gasSuggestion gas.EtherscanGasFeeSuggestion, simResult eth.SimulationResult, priorityFee float64, gasLimit uint64) float64 { + fastGwei := math.Ceil(gasSuggestion.FastGwei + priorityFee) + fastEth := gasSuggestion.FastGwei / eth.WeiPerGwei + + var fastLowLimit float64 + var fastHighLimit float64 + if gasLimit == 0 { + fastLowLimit = fastEth * float64(simResult.EstimatedGasLimit) + fastHighLimit = fastEth * float64(simResult.SafeGasLimit) + } else { + fastLowLimit = fastEth * float64(gasLimit) + fastHighLimit = fastLowLimit + } + + standardGwei := math.Ceil(gasSuggestion.StandardGwei + priorityFee) + standardEth := gasSuggestion.StandardGwei / eth.WeiPerGwei + + var standardLowLimit float64 + var standardHighLimit float64 + if gasLimit == 0 { + standardLowLimit = standardEth * float64(simResult.EstimatedGasLimit) + standardHighLimit = standardEth * float64(simResult.SafeGasLimit) + } else { + standardLowLimit = standardEth * float64(gasLimit) + standardHighLimit = standardLowLimit + } + + slowGwei := math.Ceil(gasSuggestion.SlowGwei + priorityFee) + slowEth := gasSuggestion.SlowGwei / eth.WeiPerGwei + + var slowLowLimit float64 + var slowHighLimit float64 + if gasLimit == 0 { + slowLowLimit = slowEth * float64(simResult.EstimatedGasLimit) + slowHighLimit = slowEth * float64(simResult.SafeGasLimit) + } else { + slowLowLimit = slowEth * float64(gasLimit) + slowHighLimit = slowLowLimit + } + + fmt.Printf("%s+============ Suggested Gas Prices ============+\n", terminal.ColorBlue) + fmt.Println("| Speed | Max Fee | Total Gas Cost |") + fmt.Printf("| Fast | %-9s | %.4f to %.4f ETH |\n", + fmt.Sprintf("%d gwei", int(fastGwei)), fastLowLimit, fastHighLimit) + fmt.Printf("| Standard | %-9s | %.4f to %.4f ETH |\n", + fmt.Sprintf("%d gwei", int(standardGwei)), standardLowLimit, standardHighLimit) + fmt.Printf("| Slow | %-9s | %.4f to %.4f ETH |\n", + fmt.Sprintf("%d gwei", int(slowGwei)), slowLowLimit, slowHighLimit) + fmt.Printf("+==============================================+\n\n%s", terminal.ColorReset) + + fmt.Printf("These prices include a maximum priority fee of %.2f gwei.\n", priorityFee) + + for { + desiredPrice := Prompt( + fmt.Sprintf("Please enter your max fee (including the priority fee) or leave blank for the default of %d gwei:", int(fastGwei)), + "^(?:[1-9]\\d*|0)?(?:\\.\\d+)?$", + "Not a valid gas price, try again:") + + if desiredPrice == "" { + return fastGwei + } + + desiredPriceFloat, err := strconv.ParseFloat(desiredPrice, 64) + if err != nil { + fmt.Printf("Not a valid gas price (%s), try again.", err.Error()) + fmt.Println() + continue + } + if desiredPriceFloat <= 0 { + fmt.Println("Max fee must be greater than zero.") + continue + } + + return desiredPriceFloat + } +} diff --git a/adapter/utils/tx/tx.go b/adapter/utils/tx/tx.go new file mode 100644 index 0000000..fb89401 --- /dev/null +++ b/adapter/utils/tx/tx.go @@ -0,0 +1,253 @@ +package tx + +import ( + "fmt" + "math/big" + "sync" + + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + "github.com/rocket-pool/node-manager-core/eth" + "github.com/urfave/cli/v2" + "golang.org/x/sync/errgroup" +) + +// Handle a transaction, either printing its details, signing it, or submitting it and waiting for it to be included +func HandleTx(c *cli.Context, hd *swclient.HyperdriveClient, txInfo *eth.TransactionInfo, confirmMessage string, identifier string, submissionMessage string) (bool, error) { + return handleTxImpl(c, hd, txInfo, confirmMessage, identifier, submissionMessage, false) +} + +// Handle a transaction, either printing its details, signing it, or submitting it and waiting for it to be included, explicitly requiring the user to enter "I Agree" in the confirmation prompt +func HandleTxWithIAgree(c *cli.Context, hd *swclient.HyperdriveClient, txInfo *eth.TransactionInfo, confirmMessage string, identifier string, submissionMessage string) (bool, error) { + return handleTxImpl(c, hd, txInfo, confirmMessage, identifier, submissionMessage, true) +} + +// Implementation of the transaction handling logic +func handleTxImpl(c *cli.Context, hd *swclient.HyperdriveClient, txInfo *eth.TransactionInfo, confirmMessage string, identifier string, submissionMessage string, useIAgree bool) (bool, error) { + // Overwrite the gas limit if requested + if c.IsSet(utils.ForceGasLimitFlag.Name) { + manualLimit := c.Uint64(utils.ForceGasLimitFlag.Name) + txInfo.SimulationResult.EstimatedGasLimit = manualLimit + txInfo.SimulationResult.SafeGasLimit = manualLimit + } + + // Print the TX data if requested + if c.Bool(utils.PrintTxDataFlag.Name) { + fmt.Printf("TX Data for %s:\n", identifier) + fmt.Printf("\tTo: %s\n", txInfo.To.Hex()) + fmt.Printf("\tData: %s\n", hexutil.Encode(txInfo.Data)) + fmt.Printf("\tValue: %s\n", txInfo.Value.String()) + fmt.Printf("\tEst. Gas: %d\n", txInfo.SimulationResult.EstimatedGasLimit) + fmt.Printf("\tSafe Gas: %d\n", txInfo.SimulationResult.SafeGasLimit) + + // Warn if the TX failed simulation + if txInfo.SimulationResult.SimulationError != "" { + fmt.Printf("%sWARNING: '%s' failed simulation: %s\nThis transaction will likely revert if you submit it.%s\n", terminal.ColorYellow, identifier, txInfo.SimulationResult.SimulationError, terminal.ColorReset) + } + return false, nil + } + + // Make sure the TX was successful + if txInfo.SimulationResult.SimulationError != "" { + if c.Bool(utils.IgnoreTxSimFailureFlag.Name) { + fmt.Printf("%sWARNING: '%s' failed simulation: %s\nThis transaction will likely revert if you submit it.%s\n", terminal.ColorYellow, identifier, txInfo.SimulationResult.SimulationError, terminal.ColorReset) + } else { + return false, fmt.Errorf("simulating %s failed: %s", identifier, txInfo.SimulationResult.SimulationError) + } + } + + // Assign max fees + maxFee, maxPrioFee, err := utils.GetMaxFees(c, hd, txInfo.SimulationResult) + if err != nil { + return false, fmt.Errorf("error getting fee information: %w", err) + } + + // Check the nonce flag + var nonce *big.Int + if hd.Context.Nonce.Cmp(common.Big0) > 0 { + nonce = hd.Context.Nonce + } + + // Create the submission from the TX info + submission, _ := eth.CreateTxSubmissionFromInfo(txInfo, nil) + + // Sign only (no submission) if requested + if c.Bool(utils.SignTxOnlyFlag.Name) { + response, err := hd.Api.Tx.SignTx(submission, nonce, maxFee, maxPrioFee) + if err != nil { + return false, fmt.Errorf("error signing transaction: %w", err) + } + fmt.Printf("Signed transaction (%s):\n", identifier) + fmt.Println(response.Data.SignedTx) + fmt.Println() + updateCustomNonce(hd) + return false, nil + } + + // Confirm submission + var confirmFunc func(string) bool + if useIAgree { + confirmFunc = utils.ConfirmWithIAgree + } else { + confirmFunc = utils.Confirm + } + if !(c.Bool(utils.YesFlag.Name) || confirmFunc(confirmMessage)) { + fmt.Println("Cancelled.") + return false, nil + } + + // Submit it + fmt.Println(submissionMessage) + response, err := hd.Api.Tx.SubmitTx(submission, nonce, maxFee, maxPrioFee) + if err != nil { + return false, fmt.Errorf("error submitting transaction: %w", err) + } + + // Wait for it + utils.PrintTransactionHash(hd, response.Data.TxHash) + if _, err = hd.Api.Tx.WaitForTransaction(response.Data.TxHash); err != nil { + return false, fmt.Errorf("error waiting for transaction: %w", err) + } + + updateCustomNonce(hd) + return true, nil +} + +// Handle a batch of transactions, either printing their details, signing them, or submitting them and waiting for them to be included +func HandleTxBatch(c *cli.Context, hd *swclient.HyperdriveClient, txInfos []*eth.TransactionInfo, confirmMessage string, identifierFunc func(int) string, submissionMessage string) (bool, error) { + // Overwrite the gas limit if requested + if c.IsSet(utils.ForceGasLimitFlag.Name) { + manualLimit := c.Uint64(utils.ForceGasLimitFlag.Name) + for _, txInfo := range txInfos { + txInfo.SimulationResult.EstimatedGasLimit = manualLimit + txInfo.SimulationResult.SafeGasLimit = manualLimit + } + } + + // Print the TX data if requested + if c.Bool(utils.PrintTxDataFlag.Name) { + for i, info := range txInfos { + id := identifierFunc(i) + fmt.Printf("Data for TX %d (%s):\n", i, identifierFunc(i)) + fmt.Printf("\tTo: %s\n", info.To.Hex()) + fmt.Printf("\tData: %s\n", hexutil.Encode(info.Data)) + fmt.Printf("\tValue: %s\n", info.Value.String()) + fmt.Printf("\tEst. Gas: %d\n", info.SimulationResult.EstimatedGasLimit) + fmt.Printf("\tSafe Gas: %d\n", info.SimulationResult.SafeGasLimit) + fmt.Println() + + // Warn if the TX failed simulation + if info.SimulationResult.SimulationError != "" { + fmt.Printf("%sWARNING: '%s' failed simulation: %s\nThis transaction will likely revert if you submit it.%s\n", terminal.ColorYellow, id, info.SimulationResult.SimulationError, terminal.ColorReset) + fmt.Println() + } + } + return false, nil + } + + // Make sure the TXs were successful + for i, txInfo := range txInfos { + if txInfo.SimulationResult.SimulationError != "" { + if c.Bool(utils.IgnoreTxSimFailureFlag.Name) { + fmt.Printf("%sWARNING: '%s' failed simulation: %s\nThis transaction will likely revert if you submit it.%s\n", terminal.ColorYellow, identifierFunc(i), txInfo.SimulationResult.SimulationError, terminal.ColorReset) + } else { + return false, fmt.Errorf("simulating %s failed: %s", identifierFunc(i), txInfo.SimulationResult.SimulationError) + } + } + } + + // Assign max fees + var simResult eth.SimulationResult + for _, info := range txInfos { + simResult.EstimatedGasLimit += info.SimulationResult.EstimatedGasLimit + simResult.SafeGasLimit += info.SimulationResult.SafeGasLimit + } + maxFee, maxPrioFee, err := utils.GetMaxFees(c, hd, simResult) + if err != nil { + return false, fmt.Errorf("error getting fee information: %w", err) + } + + // Check the nonce flag + var nonce *big.Int + if hd.Context.Nonce.Cmp(common.Big0) > 0 { + nonce = hd.Context.Nonce + } + + // Create the submissions from the TX infos + submissions := make([]*eth.TransactionSubmission, len(txInfos)) + for i, info := range txInfos { + submission, _ := eth.CreateTxSubmissionFromInfo(info, nil) + submissions[i] = submission + } + + // Sign only (no submission) if requested + if c.Bool(utils.SignTxOnlyFlag.Name) { + response, err := hd.Api.Tx.SignTxBatch(submissions, nonce, maxFee, maxPrioFee) + if err != nil { + return false, fmt.Errorf("error signing transactions: %w", err) + } + + for i, tx := range response.Data.SignedTxs { + fmt.Printf("Signed transaction (%s):\n", identifierFunc(i)) + fmt.Println(tx) + fmt.Println() + } + return false, nil + } + + // Confirm submission + if !(c.Bool(utils.YesFlag.Name) || utils.Confirm(confirmMessage)) { + fmt.Println("Cancelled.") + return false, nil + } + + // Submit them + fmt.Println(submissionMessage) + response, err := hd.Api.Tx.SubmitTxBatch(submissions, nonce, maxFee, maxPrioFee) + if err != nil { + return false, fmt.Errorf("error submitting transactions: %w", err) + } + + // Wait for them + utils.PrintTransactionBatchHashes(hd, response.Data.TxHashes) + return true, waitForTransactions(hd, response.Data.TxHashes, identifierFunc) +} + +// Wait for a batch of transactions to get included in blocks +func waitForTransactions(hd *swclient.HyperdriveClient, hashes []common.Hash, identifierFunc func(int) string) error { + var wg errgroup.Group + var lock sync.Mutex + total := len(hashes) + successCount := 0 + + // Create waiters for each TX + for i, hash := range hashes { + i := i + hash := hash + wg.Go(func() error { + if _, err := hd.Api.Tx.WaitForTransaction(hash); err != nil { + return fmt.Errorf("error waiting for transaction %s: %w", hash.Hex(), err) + } + lock.Lock() + successCount++ + fmt.Printf("TX %s (%s) complete (%d/%d)\n", hash.Hex(), identifierFunc(i), successCount, total) + lock.Unlock() + return nil + }) + } + if err := wg.Wait(); err != nil { + return fmt.Errorf("error waiting for transactions: %w", err) + } + return nil +} + +// If a custom nonce is set, increment it for the next transaction +func updateCustomNonce(hd *swclient.HyperdriveClient) { + if hd.Context.Nonce.Cmp(common.Big0) > 0 { + hd.Context.Nonce.Add(hd.Context.Nonce, common.Big1) + } +} From 62b8b0dd55ec0e4137e3b4379d85a61913b3b62d Mon Sep 17 00:00:00 2001 From: huy Date: Tue, 7 Jan 2025 16:43:40 -0800 Subject: [PATCH 10/28] rest of functions --- adapter/config/exit.go | 250 ++++++++++++++------------ adapter/config/upload-deposit-data.go | 45 ++--- adapter/utils/selection.go | 44 +++++ 3 files changed, 206 insertions(+), 133 deletions(-) create mode 100644 adapter/utils/selection.go diff --git a/adapter/config/exit.go b/adapter/config/exit.go index e08de81..d021f17 100644 --- a/adapter/config/exit.go +++ b/adapter/config/exit.go @@ -1,121 +1,147 @@ package config import ( + "fmt" + "time" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" + nutils "github.com/rocket-pool/node-manager-core/cli/utils" + + "github.com/rocket-pool/node-manager-core/beacon" "github.com/urfave/cli/v2" ) +var ( + pubkeysFlag *cli.StringFlag = &cli.StringFlag{ + Name: "pubkeys", + Aliases: []string{"p"}, + Usage: "Comma-separated list of pubkeys (including 0x prefix) to get the exit message for", + } + epochFlag *cli.Uint64Flag = &cli.Uint64Flag{ + Name: "epoch", + Aliases: []string{"e"}, + Usage: "(Optional) the epoch to use when creating the signed exit messages. If not specified, the current chain head will be used.", + } + noBroadcastFlag *cli.BoolFlag = &cli.BoolFlag{ + Name: "no-broadcast", + Aliases: []string{"n"}, + Usage: "(Optional) pass this flag to skip broadcasting the exit message(s) and print them instead", + } +) + func exit(c *cli.Context) error { + // Get the client + hd, err := swclient.NewHyperdriveClientFromCtx(c) + if err != nil { + return err + } + sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + if err != nil { + return err + } + cfg, _, err := hd.LoadConfig() + if err != nil { + return fmt.Errorf("error loading Hyperdrive config: %w", err) + } + if !cfg.StakeWise.Enabled.Value { + fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + return nil + } + + // Check wallet status + _, ready, err := utils.CheckIfWalletReady(hd) + if err != nil { + return err + } + if !ready { + return nil + } + + // Get all active validators + activeValidatorResponse, err := sw.Api.Status.GetValidatorStatuses() + if err != nil { + return fmt.Errorf("error while getting active validators: %w", err) + } + var activeValidators []beacon.ValidatorPubkey + for _, state := range activeValidatorResponse.Data.States { + if state.BeaconStatus == beacon.ValidatorState_ActiveOngoing { + activeValidators = append(activeValidators, state.Pubkey) + } + } + if len(activeValidators) == 0 { + fmt.Println("None of your validators are active, so they cannot be exited.") + return nil + } + + // Get selected validators + options := make([]nutils.SelectionOption[beacon.ValidatorPubkey], len(activeValidators)) + for i, pubkey := range activeValidators { + option := &options[i] + option.Element = &activeValidators[i] + option.ID = activeValidators[i].HexWithPrefix() + option.Display = fmt.Sprintf("%s (active since %s)", pubkey, time.Unix(0, 0)) // Placeholder, fill in with status details + } + selectedValidators, err := utils.GetMultiselectIndices(c, pubkeysFlag.Name, options, "Please select a validator to exit:") + if err != nil { + return fmt.Errorf("error determining validator selection: %w", err) + } + + // Get the epoch if set + var epochPtr *uint64 + if c.IsSet(epochFlag.Name) { + epoch := c.Uint64(epochFlag.Name) + epochPtr = &epoch + } + + // Get the no broadcast flag + noBroadcastBool := false + if c.IsSet(noBroadcastFlag.Name) { + noBroadcastBool = c.Bool(noBroadcastFlag.Name) + } + + // Get the pubkeys + pubkeys := make([]beacon.ValidatorPubkey, len(selectedValidators)) + for i, validator := range selectedValidators { + pubkeys[i] = *validator + } + + if !noBroadcastBool { + // Show a warning message + fmt.Printf("%sNOTE:\n", terminal.ColorYellow) + fmt.Println("You are about to exit your validator(s). This will tell each one to stop all activities on the Beacon Chain.") + fmt.Println("Please continue to run them until each one you've exited has been processed by the exit queue. It will no longer earn staking rewards after this point.") + fmt.Printf("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (typically after a few days).%s\n", terminal.ColorReset) + + // Prompt for confirmation + if !(c.Bool(utils.YesFlag.Name) || utils.ConfirmWithIAgree(fmt.Sprintf("Are you sure you want to exit %d validator(s)? This action cannot be undone!", len(selectedValidators)))) { + fmt.Println("Cancelled.") + return nil + } + } + + // Get signed exit messages + response, err := sw.Api.Validator.Exit(pubkeys, epochPtr, noBroadcastBool) + if err != nil { + return fmt.Errorf("error while getting validator exit messages: %w", err) + } + + // Log successand return if not broadcasting + if !noBroadcastBool { + fmt.Println("Successfully exited the selected validator(s). It will take some time before their status is reflected on the Beacon Chain.") + return nil + } + + // Print them all + fmt.Printf("Exit epoch: %d\n", response.Data.Epoch) + fmt.Println() + for _, info := range response.Data.ExitInfos { + fmt.Printf("Validator %d (%s):\n", info.Index, info.Pubkey.HexWithPrefix()) + fmt.Printf("\tSignature: %s\n", info.Signature.HexWithPrefix()) + fmt.Println() + } + + // Return return nil - // // Get the client - // hd, err := client.NewHyperdriveClientFromCtx(c) - // if err != nil { - // return err - // } - // sw, err := client.NewStakewiseClientFromCtx(c, hd) - // if err != nil { - // return err - // } - // cfg, _, err := hd.LoadConfig() - // if err != nil { - // return fmt.Errorf("error loading Hyperdrive config: %w", err) - // } - // if !cfg.StakeWise.Enabled.Value { - // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - // return nil - // } - - // // Check wallet status - // _, ready, err := utils.CheckIfWalletReady(hd) - // if err != nil { - // return err - // } - // if !ready { - // return nil - // } - - // // Get all active validators - // activeValidatorResponse, err := sw.Api.Status.GetValidatorStatuses() - // if err != nil { - // return fmt.Errorf("error while getting active validators: %w", err) - // } - // var activeValidators []beacon.ValidatorPubkey - // for _, state := range activeValidatorResponse.Data.States { - // if state.BeaconStatus == beacon.ValidatorState_ActiveOngoing { - // activeValidators = append(activeValidators, state.Pubkey) - // } - // } - // if len(activeValidators) == 0 { - // fmt.Println("None of your validators are active, so they cannot be exited.") - // return nil - // } - - // // Get selected validators - // options := make([]nutils.SelectionOption[beacon.ValidatorPubkey], len(activeValidators)) - // for i, pubkey := range activeValidators { - // option := &options[i] - // option.Element = &activeValidators[i] - // option.ID = activeValidators[i].HexWithPrefix() - // option.Display = fmt.Sprintf("%s (active since %s)", pubkey, time.Unix(0, 0)) // Placeholder, fill in with status details - // } - // selectedValidators, err := utils.GetMultiselectIndices(c, pubkeysFlag.Name, options, "Please select a validator to exit:") - // if err != nil { - // return fmt.Errorf("error determining validator selection: %w", err) - // } - - // // Get the epoch if set - // var epochPtr *uint64 - // if c.IsSet(epochFlag.Name) { - // epoch := c.Uint64(epochFlag.Name) - // epochPtr = &epoch - // } - - // // Get the no broadcast flag - // noBroadcastBool := false - // if c.IsSet(noBroadcastFlag.Name) { - // noBroadcastBool = c.Bool(noBroadcastFlag.Name) - // } - - // // Get the pubkeys - // pubkeys := make([]beacon.ValidatorPubkey, len(selectedValidators)) - // for i, validator := range selectedValidators { - // pubkeys[i] = *validator - // } - - // if !noBroadcastBool { - // // Show a warning message - // fmt.Printf("%sNOTE:\n", terminal.ColorYellow) - // fmt.Println("You are about to exit your validator(s). This will tell each one to stop all activities on the Beacon Chain.") - // fmt.Println("Please continue to run them until each one you've exited has been processed by the exit queue. It will no longer earn staking rewards after this point.") - // fmt.Printf("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (typically after a few days).%s\n", terminal.ColorReset) - - // // Prompt for confirmation - // if !(c.Bool(utils.YesFlag.Name) || utils.ConfirmWithIAgree(fmt.Sprintf("Are you sure you want to exit %d validator(s)? This action cannot be undone!", len(selectedValidators)))) { - // fmt.Println("Cancelled.") - // return nil - // } - // } - - // // Get signed exit messages - // response, err := sw.Api.Validator.Exit(pubkeys, epochPtr, noBroadcastBool) - // if err != nil { - // return fmt.Errorf("error while getting validator exit messages: %w", err) - // } - - // // Log successand return if not broadcasting - // if !noBroadcastBool { - // fmt.Println("Successfully exited the selected validator(s). It will take some time before their status is reflected on the Beacon Chain.") - // return nil - // } - - // // Print them all - // fmt.Printf("Exit epoch: %d\n", response.Data.Epoch) - // fmt.Println() - // for _, info := range response.Data.ExitInfos { - // fmt.Printf("Validator %d (%s):\n", info.Index, info.Pubkey.HexWithPrefix()) - // fmt.Printf("\tSignature: %s\n", info.Signature.HexWithPrefix()) - // fmt.Println() - // } - - // // Return - // return nil } diff --git a/adapter/config/upload-deposit-data.go b/adapter/config/upload-deposit-data.go index 2042f09..3baf692 100644 --- a/adapter/config/upload-deposit-data.go +++ b/adapter/config/upload-deposit-data.go @@ -1,30 +1,33 @@ package config import ( + "fmt" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/urfave/cli/v2" ) func uploadDepositData(c *cli.Context) error { - return nil - // // Get the client - // hd, err := client.NewHyperdriveClientFromCtx(c) - // if err != nil { - // return err - // } - // sw, err := client.NewStakewiseClientFromCtx(c, hd) - // if err != nil { - // return err - // } - // cfg, _, err := hd.LoadConfig() - // if err != nil { - // return fmt.Errorf("error loading Hyperdrive config: %w", err) - // } - // if !cfg.StakeWise.Enabled.Value { - // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - // return nil - // } + // Get the client + hd, err := swclient.NewHyperdriveClientFromCtx(c) + if err != nil { + return err + } + sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + if err != nil { + return err + } + cfg, _, err := hd.LoadConfig() + if err != nil { + return fmt.Errorf("error loading Hyperdrive config: %w", err) + } + if !cfg.StakeWise.Enabled.Value { + fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + return nil + } - // // Upload to the server - // _, err = swcmdutils.UploadDepositData(c, hd, sw) - // return err + // Upload to the server + _, err = utils.UploadDepositData(c, hd, sw) + return err } diff --git a/adapter/utils/selection.go b/adapter/utils/selection.go new file mode 100644 index 0000000..937db15 --- /dev/null +++ b/adapter/utils/selection.go @@ -0,0 +1,44 @@ +package utils + +import ( + "fmt" + "strings" + + "github.com/rocket-pool/node-manager-core/cli/utils" + "github.com/urfave/cli/v2" +) + +// Get the list of elements the user wants to use for a multi-select operation +func GetMultiselectIndices[DataType any](c *cli.Context, flagName string, options []utils.SelectionOption[DataType], prompt string) ([]*DataType, error) { + flag := "" + if c.IsSet(flagName) { + flag = strings.TrimSpace(fmt.Sprintf("%v", c.Value(flagName))) + } + + // Handle all + if flag == "all" { + selectedElements := make([]*DataType, len(options)) + for i, option := range options { + selectedElements[i] = option.Element + } + return selectedElements, nil + } + + // Handle one or more + if flag != "" { + return utils.ParseOptionIDs(flag, options) + } + + // No headless flag, so prompt interactively + if c.Bool(YesFlag.Name) { + return nil, fmt.Errorf("the '%s' flag (non-interactive mode) is specified but the '%s' flag (selection) is missing", YesFlag.Name, flagName) + } + fmt.Println(prompt) + fmt.Println() + for i, option := range options { + fmt.Printf("%d: %s\n", i+1, option.Display) + } + fmt.Println() + indexSelection := Prompt("Use a comma separated list (such as '1,2,3' or '1-4,6-8,10') or leave it blank to select all options.", ".*", "Invalid index selection") + return utils.ParseIndexSelection(indexSelection, options) +} From d31b1233e1a1a6435ffda5e279502516a678cf9d Mon Sep 17 00:00:00 2001 From: huy Date: Wed, 8 Jan 2025 09:39:37 -0800 Subject: [PATCH 11/28] nit changes --- client/auth.go | 9 --------- client/client.go | 26 ++++++++++++++------------ testing/node.go | 2 +- 3 files changed, 15 insertions(+), 22 deletions(-) delete mode 100644 client/auth.go diff --git a/client/auth.go b/client/auth.go deleted file mode 100644 index de8b73f..0000000 --- a/client/auth.go +++ /dev/null @@ -1,9 +0,0 @@ -package swclient - -import ( - "path/filepath" - - hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" -) - -var moduleApiKeyRelPath string = filepath.Join(hdconfig.SecretsDir, hdconfig.ModulesName) diff --git a/client/client.go b/client/client.go index 149c96a..a9b8c9d 100644 --- a/client/client.go +++ b/client/client.go @@ -43,18 +43,10 @@ const ( ) var hdApiKeyRelPath string = filepath.Join(config.SecretsDir, config.DaemonKeyFilename) +var moduleApiKeyRelPath string = filepath.Join(hdconfig.SecretsDir, hdconfig.ModulesName) var swApiKeyRelPath string = filepath.Join(moduleApiKeyRelPath, swconfig.ModuleName, hdconfig.DaemonKeyFilename) -// Binder for the StakeWise API server -type SwApiClient struct { - context client.IRequesterContext - Nodeset *NodesetRequester - Validator *ValidatorRequester - Wallet *WalletRequester - Service *ServiceRequester - Status *StatusRequester -} - +// TODO: Remove and reference from hyperdrive repo // Hyperdrive client type HyperdriveClient struct { Api *hdclient.ApiClient @@ -65,6 +57,16 @@ type HyperdriveClient struct { isNewCfg bool } +// Binder for the StakeWise API server +type SwApiClient struct { + context client.IRequesterContext + Nodeset *NodesetRequester + Validator *ValidatorRequester + Wallet *WalletRequester + Service *ServiceRequester + Status *StatusRequester +} + // Stakewise client type StakewiseClient struct { Api *SwApiClient @@ -133,7 +135,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype } // Creates a new API client instance -func NewSwApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.ClientTrace, authMgr *auth.AuthorizationManager) *SwApiClient { +func NewApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.ClientTrace, authMgr *auth.AuthorizationManager) *SwApiClient { context := client.NewNetworkRequesterContext(apiUrl, logger, tracer, authMgr.AddAuthHeader) client := &SwApiClient{ @@ -362,6 +364,6 @@ func NewStakewiseClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext, hdClien authMgr := auth.NewAuthorizationManager(authPath, cliIssuer, auth.DefaultRequestLifespan) // Create the API client - swClient.Api = NewSwApiClient(url, logger, tracer, authMgr) + swClient.Api = NewApiClient(url, logger, tracer, authMgr) return swClient, nil } diff --git a/testing/node.go b/testing/node.go index 4014f24..3cd96b8 100644 --- a/testing/node.go +++ b/testing/node.go @@ -63,7 +63,7 @@ func newStakeWiseNode(sp swcommon.IStakeWiseServiceProvider, address string, cli } clientAuthMgr := auth.NewAuthorizationManager("", "sw-client", auth.DefaultRequestLifespan) clientAuthMgr.SetKey([]byte(apiAuthKey)) - apiClient := swclient.NewSwApiClient(url, clientLogger, nil, clientAuthMgr) + apiClient := swclient.NewApiClient(url, clientLogger, nil, clientAuthMgr) return &StakeWiseNode{ sp: sp, From 714c6d85142323eb0ce927ca5c283a37ee88b23d Mon Sep 17 00:00:00 2001 From: huy Date: Wed, 8 Jan 2025 09:42:33 -0800 Subject: [PATCH 12/28] refactor --- adapter/{config => command}/claim-rewards.go | 2 +- adapter/{config => command}/commands.go | 2 +- adapter/{config => command}/exit.go | 2 +- adapter/{config => command}/generate-deposit-data.go | 2 +- adapter/{config => command}/generate-keys.go | 2 +- adapter/{config => command}/get-node-status.go | 2 +- adapter/{config => command}/initialize.go | 2 +- adapter/{config => command}/upload-deposit-data.go | 2 +- {client => adapter}/utils/context.go | 0 {client => adapter}/utils/install_info.go | 0 client/client.go | 8 ++++---- testing/node.go | 4 ++-- 12 files changed, 14 insertions(+), 14 deletions(-) rename adapter/{config => command}/claim-rewards.go (99%) rename adapter/{config => command}/commands.go (99%) rename adapter/{config => command}/exit.go (99%) rename adapter/{config => command}/generate-deposit-data.go (99%) rename adapter/{config => command}/generate-keys.go (99%) rename adapter/{config => command}/get-node-status.go (99%) rename adapter/{config => command}/initialize.go (98%) rename adapter/{config => command}/upload-deposit-data.go (97%) rename {client => adapter}/utils/context.go (100%) rename {client => adapter}/utils/install_info.go (100%) diff --git a/adapter/config/claim-rewards.go b/adapter/command/claim-rewards.go similarity index 99% rename from adapter/config/claim-rewards.go rename to adapter/command/claim-rewards.go index 84b075b..45c2552 100644 --- a/adapter/config/claim-rewards.go +++ b/adapter/command/claim-rewards.go @@ -1,4 +1,4 @@ -package config +package command import ( "fmt" diff --git a/adapter/config/commands.go b/adapter/command/commands.go similarity index 99% rename from adapter/config/commands.go rename to adapter/command/commands.go index 62c14f9..acff32b 100644 --- a/adapter/config/commands.go +++ b/adapter/command/commands.go @@ -1,4 +1,4 @@ -package config +package command import ( "fmt" diff --git a/adapter/config/exit.go b/adapter/command/exit.go similarity index 99% rename from adapter/config/exit.go rename to adapter/command/exit.go index d021f17..9ae31c6 100644 --- a/adapter/config/exit.go +++ b/adapter/command/exit.go @@ -1,4 +1,4 @@ -package config +package command import ( "fmt" diff --git a/adapter/config/generate-deposit-data.go b/adapter/command/generate-deposit-data.go similarity index 99% rename from adapter/config/generate-deposit-data.go rename to adapter/command/generate-deposit-data.go index 78fdded..045b410 100644 --- a/adapter/config/generate-deposit-data.go +++ b/adapter/command/generate-deposit-data.go @@ -1,4 +1,4 @@ -package config +package command import ( "encoding/json" diff --git a/adapter/config/generate-keys.go b/adapter/command/generate-keys.go similarity index 99% rename from adapter/config/generate-keys.go rename to adapter/command/generate-keys.go index d101422..09c5a75 100644 --- a/adapter/config/generate-keys.go +++ b/adapter/command/generate-keys.go @@ -1,4 +1,4 @@ -package config +package command import ( "fmt" diff --git a/adapter/config/get-node-status.go b/adapter/command/get-node-status.go similarity index 99% rename from adapter/config/get-node-status.go rename to adapter/command/get-node-status.go index aaf8bd6..d9ffea7 100644 --- a/adapter/config/get-node-status.go +++ b/adapter/command/get-node-status.go @@ -1,4 +1,4 @@ -package config +package command import ( "fmt" diff --git a/adapter/config/initialize.go b/adapter/command/initialize.go similarity index 98% rename from adapter/config/initialize.go rename to adapter/command/initialize.go index 1de79c4..ab703ee 100644 --- a/adapter/config/initialize.go +++ b/adapter/command/initialize.go @@ -1,4 +1,4 @@ -package config +package command import ( "fmt" diff --git a/adapter/config/upload-deposit-data.go b/adapter/command/upload-deposit-data.go similarity index 97% rename from adapter/config/upload-deposit-data.go rename to adapter/command/upload-deposit-data.go index 3baf692..848475d 100644 --- a/adapter/config/upload-deposit-data.go +++ b/adapter/command/upload-deposit-data.go @@ -1,4 +1,4 @@ -package config +package command import ( "fmt" diff --git a/client/utils/context.go b/adapter/utils/context.go similarity index 100% rename from client/utils/context.go rename to adapter/utils/context.go diff --git a/client/utils/install_info.go b/adapter/utils/install_info.go similarity index 100% rename from client/utils/install_info.go rename to adapter/utils/install_info.go diff --git a/client/client.go b/client/client.go index a9b8c9d..673a0b1 100644 --- a/client/client.go +++ b/client/client.go @@ -58,7 +58,7 @@ type HyperdriveClient struct { } // Binder for the StakeWise API server -type SwApiClient struct { +type ApiClient struct { context client.IRequesterContext Nodeset *NodesetRequester Validator *ValidatorRequester @@ -69,7 +69,7 @@ type SwApiClient struct { // Stakewise client type StakewiseClient struct { - Api *SwApiClient + Api *ApiClient Context *utils.HyperdriveContext Logger *slog.Logger } @@ -135,10 +135,10 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*Hype } // Creates a new API client instance -func NewApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.ClientTrace, authMgr *auth.AuthorizationManager) *SwApiClient { +func NewApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.ClientTrace, authMgr *auth.AuthorizationManager) *ApiClient { context := client.NewNetworkRequesterContext(apiUrl, logger, tracer, authMgr.AddAuthHeader) - client := &SwApiClient{ + client := &ApiClient{ context: context, Nodeset: NewNodesetRequester(context), Validator: NewValidatorRequester(context), diff --git a/testing/node.go b/testing/node.go index 3cd96b8..bd7b5a4 100644 --- a/testing/node.go +++ b/testing/node.go @@ -31,7 +31,7 @@ type StakeWiseNode struct { serverMgr *swserver.ServerManager // An HTTP API client for the daemon - client *swclient.SwApiClient + client *swclient.ApiClient // The client logger logger *slog.Logger @@ -97,7 +97,7 @@ func (n *StakeWiseNode) GetServerManager() *swserver.ServerManager { } // Get the HTTP API client for interacting with the node's daemon server -func (n *StakeWiseNode) GetApiClient() *swclient.SwApiClient { +func (n *StakeWiseNode) GetApiClient() *swclient.ApiClient { return n.client } From e7a492080701930467c31a7ddd159bb7b25bf71b Mon Sep 17 00:00:00 2001 From: huy Date: Wed, 8 Jan 2025 09:56:21 -0800 Subject: [PATCH 13/28] wip --- client/client.go | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/client.go b/client/client.go index 673a0b1..c145653 100644 --- a/client/client.go +++ b/client/client.go @@ -9,6 +9,7 @@ import ( "path/filepath" clitemplate "github.com/nodeset-org/hyperdrive-stakewise/adapter/client/template" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/config" "github.com/fatih/color" @@ -18,7 +19,6 @@ import ( "github.com/nodeset-org/hyperdrive-daemon/shared/auth" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" swclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" - "github.com/nodeset-org/hyperdrive-stakewise/client/utils" swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" "github.com/rocket-pool/node-manager-core/api/client" diff --git a/go.mod b/go.mod index 4f22b42..a6ae126 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/urfave/cli/v2 v2.27.2 github.com/wealdtech/go-eth2-types/v2 v2.8.2 github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1 + golang.org/x/sync v0.7.0 golang.org/x/term v0.22.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -167,7 +168,6 @@ require ( golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20240716160929-1d5bc16f04a8 // indirect golang.org/x/net v0.27.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect From 679d777efe8d395a467e8e158c68f8626368c35b Mon Sep 17 00:00:00 2001 From: huy Date: Wed, 8 Jan 2025 10:08:01 -0800 Subject: [PATCH 14/28] fix circular deps --- adapter/command/claim-rewards.go | 4 ++-- adapter/command/exit.go | 3 ++- adapter/command/generate-keys.go | 3 ++- adapter/command/initialize.go | 4 ++-- adapter/utils/{ => context}/context.go | 2 +- adapter/utils/{ => context}/install_info.go | 2 +- adapter/utils/nodeset.go | 3 ++- adapter/utils/tx/tx.go | 5 +++-- adapter/utils/upload-deposit-data.go | 3 ++- adapter/utils/{ => wallet}/address.go | 2 +- client/client.go | 18 +++++++++--------- 11 files changed, 27 insertions(+), 22 deletions(-) rename adapter/utils/{ => context}/context.go (99%) rename adapter/utils/{ => context}/install_info.go (99%) rename adapter/utils/{ => wallet}/address.go (99%) diff --git a/adapter/command/claim-rewards.go b/adapter/command/claim-rewards.go index 45c2552..e76a9f9 100644 --- a/adapter/command/claim-rewards.go +++ b/adapter/command/claim-rewards.go @@ -5,8 +5,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/tx" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/rocket-pool/node-manager-core/eth" "github.com/urfave/cli/v2" @@ -32,7 +32,7 @@ func claimRewards(c *cli.Context) error { } // Check if there's a node address ready - _, ready, err := utils.CheckIfAddressReady(hd) + _, ready, err := wallet.CheckIfAddressReady(hd) if err != nil { return err } diff --git a/adapter/command/exit.go b/adapter/command/exit.go index 9ae31c6..a067b3c 100644 --- a/adapter/command/exit.go +++ b/adapter/command/exit.go @@ -6,6 +6,7 @@ import ( "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" nutils "github.com/rocket-pool/node-manager-core/cli/utils" @@ -51,7 +52,7 @@ func exit(c *cli.Context) error { } // Check wallet status - _, ready, err := utils.CheckIfWalletReady(hd) + _, ready, err := wallet.CheckIfWalletReady(hd) if err != nil { return err } diff --git a/adapter/command/generate-keys.go b/adapter/command/generate-keys.go index 09c5a75..9aa1265 100644 --- a/adapter/command/generate-keys.go +++ b/adapter/command/generate-keys.go @@ -6,6 +6,7 @@ import ( "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" "github.com/rocket-pool/node-manager-core/utils/input" @@ -33,7 +34,7 @@ func generateKeys(c *cli.Context) error { noRestart := c.Bool(generateKeysNoRestartFlag.Name) // Check wallet status - _, ready, err := utils.CheckIfWalletReady(hd) + _, ready, err := wallet.CheckIfWalletReady(hd) if err != nil { return err } diff --git a/adapter/command/initialize.go b/adapter/command/initialize.go index ab703ee..8500f6f 100644 --- a/adapter/command/initialize.go +++ b/adapter/command/initialize.go @@ -3,8 +3,8 @@ package command import ( "fmt" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/urfave/cli/v2" ) @@ -30,7 +30,7 @@ func initialize(c *cli.Context) error { } // Check wallet status - _, ready, err := utils.CheckIfWalletReady(hd) + _, ready, err := wallet.CheckIfWalletReady(hd) if err != nil { return err } diff --git a/adapter/utils/context.go b/adapter/utils/context/context.go similarity index 99% rename from adapter/utils/context.go rename to adapter/utils/context/context.go index 0aec909..59429c4 100644 --- a/adapter/utils/context.go +++ b/adapter/utils/context/context.go @@ -1,4 +1,4 @@ -package utils +package context import ( "fmt" diff --git a/adapter/utils/install_info.go b/adapter/utils/context/install_info.go similarity index 99% rename from adapter/utils/install_info.go rename to adapter/utils/context/install_info.go index 52dda5c..951cb8f 100644 --- a/adapter/utils/install_info.go +++ b/adapter/utils/context/install_info.go @@ -1,4 +1,4 @@ -package utils +package context import ( "os" diff --git a/adapter/utils/nodeset.go b/adapter/utils/nodeset.go index ccbd598..dce6277 100644 --- a/adapter/utils/nodeset.go +++ b/adapter/utils/nodeset.go @@ -5,6 +5,7 @@ import ( "net/mail" "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/urfave/cli/v2" @@ -37,7 +38,7 @@ func CheckRegistrationStatus(c *cli.Context, hd *swclient.HyperdriveClient) (boo // Returns true if the node should register because it hasn't yet and is able to func checkRegistrationStatusImpl(hd *swclient.HyperdriveClient) (bool, bool, error) { // Check wallet status - _, ready, err := CheckIfWalletReady(hd) + _, ready, err := wallet.CheckIfWalletReady(hd) if err != nil { return false, false, err } diff --git a/adapter/utils/tx/tx.go b/adapter/utils/tx/tx.go index fb89401..fddaea7 100644 --- a/adapter/utils/tx/tx.go +++ b/adapter/utils/tx/tx.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" "github.com/rocket-pool/node-manager-core/eth" "github.com/urfave/cli/v2" "golang.org/x/sync/errgroup" @@ -108,7 +109,7 @@ func handleTxImpl(c *cli.Context, hd *swclient.HyperdriveClient, txInfo *eth.Tra } // Wait for it - utils.PrintTransactionHash(hd, response.Data.TxHash) + wallet.PrintTransactionHash(hd, response.Data.TxHash) if _, err = hd.Api.Tx.WaitForTransaction(response.Data.TxHash); err != nil { return false, fmt.Errorf("error waiting for transaction: %w", err) } @@ -213,7 +214,7 @@ func HandleTxBatch(c *cli.Context, hd *swclient.HyperdriveClient, txInfos []*eth } // Wait for them - utils.PrintTransactionBatchHashes(hd, response.Data.TxHashes) + wallet.PrintTransactionBatchHashes(hd, response.Data.TxHashes) return true, waitForTransactions(hd, response.Data.TxHashes, identifierFunc) } diff --git a/adapter/utils/upload-deposit-data.go b/adapter/utils/upload-deposit-data.go index 05fa1e7..0a3fa25 100644 --- a/adapter/utils/upload-deposit-data.go +++ b/adapter/utils/upload-deposit-data.go @@ -6,6 +6,7 @@ import ( swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" "github.com/urfave/cli/v2" ) @@ -19,7 +20,7 @@ func printUploadError(err error) { // Upload deposit data to the server func UploadDepositData(c *cli.Context, hd *swclient.HyperdriveClient, sw *swclient.StakewiseClient) (bool, error) { // Check wallet status - _, ready, err := CheckIfWalletReady(hd) + _, ready, err := wallet.CheckIfWalletReady(hd) if err != nil { return false, err } diff --git a/adapter/utils/address.go b/adapter/utils/wallet/address.go similarity index 99% rename from adapter/utils/address.go rename to adapter/utils/wallet/address.go index 5f0c11e..d407b31 100644 --- a/adapter/utils/address.go +++ b/adapter/utils/wallet/address.go @@ -1,4 +1,4 @@ -package utils +package wallet import ( "fmt" diff --git a/client/client.go b/client/client.go index c145653..615b3b2 100644 --- a/client/client.go +++ b/client/client.go @@ -9,8 +9,8 @@ import ( "path/filepath" clitemplate "github.com/nodeset-org/hyperdrive-stakewise/adapter/client/template" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/context" "github.com/fatih/color" "github.com/mitchellh/go-homedir" @@ -50,7 +50,7 @@ var swApiKeyRelPath string = filepath.Join(moduleApiKeyRelPath, swconfig.ModuleN // Hyperdrive client type HyperdriveClient struct { Api *hdclient.ApiClient - Context *utils.HyperdriveContext + Context *context.HyperdriveContext Logger *slog.Logger // docker *docker.Client cfg *GlobalConfig @@ -70,18 +70,18 @@ type ApiClient struct { // Stakewise client type StakewiseClient struct { Api *ApiClient - Context *utils.HyperdriveContext + Context *context.HyperdriveContext Logger *slog.Logger } // Create new Hyperdrive client from CLI context func NewHyperdriveClientFromCtx(c *cli.Context) (*HyperdriveClient, error) { - hdCtx := utils.GetHyperdriveContext(c) + hdCtx := context.GetHyperdriveContext(c) return NewHyperdriveClientFromHyperdriveCtx(hdCtx) } // Create new Hyperdrive client from a custom context -func NewHyperdriveClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext) (*HyperdriveClient, error) { +func NewHyperdriveClientFromHyperdriveCtx(hdCtx *context.HyperdriveContext) (*HyperdriveClient, error) { logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, hdconfig.HyperdriveDaemonRoute)) // Create the tracer if required @@ -238,7 +238,7 @@ func (c *HyperdriveClient) DeployMetricsConfigurations(config *GlobalConfig) err } // Load the Prometheus config template, do a template variable substitution, and save it -func updatePrometheusConfiguration(ctx *utils.HyperdriveContext, config *GlobalConfig, metricsDirPath string) error { +func updatePrometheusConfiguration(ctx *context.HyperdriveContext, config *GlobalConfig, metricsDirPath string) error { prometheusConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, prometheusConfigTemplate)) if err != nil { return fmt.Errorf("error expanding Prometheus config template path: %w", err) @@ -258,7 +258,7 @@ func updatePrometheusConfiguration(ctx *utils.HyperdriveContext, config *GlobalC } // Load the Grafana config template, do a template variable substitution, and save it -func updateGrafanaDatabaseConfiguration(ctx *utils.HyperdriveContext, config *GlobalConfig, metricsDirPath string) error { +func updateGrafanaDatabaseConfiguration(ctx *context.HyperdriveContext, config *GlobalConfig, metricsDirPath string) error { grafanaConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, grafanaConfigTemplate)) if err != nil { return fmt.Errorf("error expanding Grafana config template path: %w", err) @@ -313,13 +313,13 @@ func LoadConfigFromFile(configPath string, hdSettings []*hdconfig.HyperdriveSett // Create new Stakewise client from CLI context // Only use this function from commands that may work if the Daemon service doesn't exist func NewStakewiseClientFromCtx(c *cli.Context, hdClient *HyperdriveClient) (*StakewiseClient, error) { - hdCtx := utils.GetHyperdriveContext(c) + hdCtx := context.GetHyperdriveContext(c) return NewStakewiseClientFromHyperdriveCtx(hdCtx, hdClient) } // Create new Stakewise client from a custom context // Only use this function from commands that may work if the Daemon service doesn't exist -func NewStakewiseClientFromHyperdriveCtx(hdCtx *utils.HyperdriveContext, hdClient *HyperdriveClient) (*StakewiseClient, error) { +func NewStakewiseClientFromHyperdriveCtx(hdCtx *context.HyperdriveContext, hdClient *HyperdriveClient) (*StakewiseClient, error) { logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, swconfig.ModuleName)) // Create the tracer if required From ef51fbe290b0e385542e24b5c81449e9e6aebe83 Mon Sep 17 00:00:00 2001 From: huy Date: Wed, 8 Jan 2025 14:15:02 -0800 Subject: [PATCH 15/28] lint --- adapter/utils/config/hyperdrive-config.go | 6 +++--- adapter/utils/config/mev-boost-config.go | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/adapter/utils/config/hyperdrive-config.go b/adapter/utils/config/hyperdrive-config.go index 0141284..44cc331 100644 --- a/adapter/utils/config/hyperdrive-config.go +++ b/adapter/utils/config/hyperdrive-config.go @@ -44,7 +44,7 @@ type HyperdriveConfig struct { Modules map[string]any // Internal fields - Version string - hyperdriveUserDirectory string - networkSettings []*HyperdriveSettings + Version string + // hyperdriveUserDirectory string + // networkSettings []*HyperdriveSettings } diff --git a/adapter/utils/config/mev-boost-config.go b/adapter/utils/config/mev-boost-config.go index 3000df0..ec48b8a 100644 --- a/adapter/utils/config/mev-boost-config.go +++ b/adapter/utils/config/mev-boost-config.go @@ -6,7 +6,7 @@ import ( // Constants const ( - mevBoostTag string = "flashbots/mev-boost:1.8.1" +// mevBoostTag string = "flashbots/mev-boost:1.8.1" ) type MevSelectionMode string @@ -66,7 +66,7 @@ type MevBoostConfig struct { // Non-editable settings // /////////////////////////// - parent *HyperdriveConfig - relays []MevRelay - relayMap map[MevRelayID]MevRelay + // parent *HyperdriveConfig + // relays []MevRelay + // relayMap map[MevRelayID]MevRelay } From 80be3ef678b1e245259ae8aabd96670e8e28e8bb Mon Sep 17 00:00:00 2001 From: huy Date: Wed, 8 Jan 2025 14:18:37 -0800 Subject: [PATCH 16/28] remove all templating stuff --- adapter/client/template/compose-file.go | 49 ---------------- adapter/client/template/template.go | 47 ---------------- client/client.go | 75 +------------------------ 3 files changed, 3 insertions(+), 168 deletions(-) delete mode 100644 adapter/client/template/compose-file.go delete mode 100644 adapter/client/template/template.go diff --git a/adapter/client/template/compose-file.go b/adapter/client/template/compose-file.go deleted file mode 100644 index bfdd176..0000000 --- a/adapter/client/template/compose-file.go +++ /dev/null @@ -1,49 +0,0 @@ -package template - -import ( - "fmt" - "path/filepath" -) - -const ( - TemplateSuffix string = ".tmpl" - ComposeFileSuffix string = ".yml" -) - -type ComposePaths struct { - RuntimePath string - TemplatePath string - OverridePath string -} - -type ComposeFile struct { - name string - paths *ComposePaths -} - -func (c *ComposePaths) File(name string) *ComposeFile { - return &ComposeFile{ - name: name, - paths: c, - } -} - -// Given a ComposeFile returned by ComposePaths.File, find and parse the .tmpl -// from the TemplatePath, populate and save to the RuntimePath, and return a -// slice of compose definitions pertaining to the container (including the override). -func (c *ComposeFile) Write(data interface{}) ([]string, error) { - composePath := filepath.Join(c.paths.RuntimePath, c.name+ComposeFileSuffix) - tmpl := Template{ - Src: filepath.Join(c.paths.TemplatePath, c.name+TemplateSuffix), - Dst: composePath, - } - err := tmpl.Write(data) - if err != nil { - return nil, fmt.Errorf("error writing %s compose definition: %w", c.name, err) - } - - return []string{ - composePath, - filepath.Join(c.paths.OverridePath, c.name+ComposeFileSuffix), - }, nil -} diff --git a/adapter/client/template/template.go b/adapter/client/template/template.go deleted file mode 100644 index d6549ac..0000000 --- a/adapter/client/template/template.go +++ /dev/null @@ -1,47 +0,0 @@ -package template - -import ( - "fmt" - "os" - "text/template" - - "github.com/alessio/shellescape" -) - -// Template is a wrapper around text/template with filesystem operations baked in. -type Template struct { - // Src is the path on disk to the .tmpl file - Src string - - // Dst is the path on disk to the output file - Dst string -} - -func (t Template) Write(data interface{}) error { - // Open the output file, creating it if it doesn't exist - runtimeFile, err := os.OpenFile(t.Dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0664) - if err != nil { - return fmt.Errorf("Could not open templated file %s for writing: %w", shellescape.Quote(t.Dst), err) - } - defer runtimeFile.Close() - - // Parse the template - tmpl, err := template.ParseFiles(t.Src) - if err != nil { - return fmt.Errorf("Error reading template file %s: %w", shellescape.Quote(t.Src), err) - } - - // Replace template variables and write the result - err = tmpl.Execute(runtimeFile, data) - if err != nil { - return fmt.Errorf("Error writing and substituting template: %w", err) - } - - // If the file was newly created, 0664 may have been altered by umask, so chmod back to 0664. - err = os.Chmod(t.Dst, 0664) - if err != nil { - return fmt.Errorf("Could not set templated file (%s) permissions: %w", shellescape.Quote(t.Dst), err) - } - - return nil -} diff --git a/client/client.go b/client/client.go index 615b3b2..c437d80 100644 --- a/client/client.go +++ b/client/client.go @@ -8,7 +8,6 @@ import ( "os" "path/filepath" - clitemplate "github.com/nodeset-org/hyperdrive-stakewise/adapter/client/template" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/config" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/context" @@ -27,12 +26,6 @@ import ( ) const ( - metricsDirMode os.FileMode = 0755 - prometheusConfigTemplate string = "prometheus-cfg.tmpl" - prometheusConfigTarget string = "prometheus.yml" - grafanaConfigTemplate string = "grafana-prometheus-datasource.tmpl" - grafanaConfigTarget string = "grafana-prometheus-datasource.yml" - SettingsFile string = "user-settings.yml" BackupSettingsFile string = "user-settings-backup.yml" metricsDir string = "metrics" @@ -49,10 +42,9 @@ var swApiKeyRelPath string = filepath.Join(moduleApiKeyRelPath, swconfig.ModuleN // TODO: Remove and reference from hyperdrive repo // Hyperdrive client type HyperdriveClient struct { - Api *hdclient.ApiClient - Context *context.HyperdriveContext - Logger *slog.Logger - // docker *docker.Client + Api *hdclient.ApiClient + Context *context.HyperdriveContext + Logger *slog.Logger cfg *GlobalConfig isNewCfg bool } @@ -216,67 +208,6 @@ func (c *HyperdriveClient) SaveConfig(cfg *GlobalConfig) error { return nil } -// Create the metrics and modules folders, and deploy the config templates for Prometheus and Grafana -func (c *HyperdriveClient) DeployMetricsConfigurations(config *GlobalConfig) error { - // Make sure the metrics path exists - metricsDirPath := filepath.Join(c.Context.UserDirPath, metricsDir) - modulesDirPath := filepath.Join(metricsDirPath, hdconfig.ModulesName) - err := os.MkdirAll(modulesDirPath, metricsDirMode) - if err != nil { - return fmt.Errorf("error creating metrics and modules directories [%s]: %w", modulesDirPath, err) - } - - err = updatePrometheusConfiguration(c.Context, config, metricsDirPath) - if err != nil { - return fmt.Errorf("error updating Prometheus configuration: %w", err) - } - err = updateGrafanaDatabaseConfiguration(c.Context, config, metricsDirPath) - if err != nil { - return fmt.Errorf("error updating Grafana configuration: %w", err) - } - return nil -} - -// Load the Prometheus config template, do a template variable substitution, and save it -func updatePrometheusConfiguration(ctx *context.HyperdriveContext, config *GlobalConfig, metricsDirPath string) error { - prometheusConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, prometheusConfigTemplate)) - if err != nil { - return fmt.Errorf("error expanding Prometheus config template path: %w", err) - } - - prometheusConfigTargetPath, err := homedir.Expand(filepath.Join(metricsDirPath, prometheusConfigTarget)) - if err != nil { - return fmt.Errorf("error expanding Prometheus config target path: %w", err) - } - - t := clitemplate.Template{ - Src: prometheusConfigTemplatePath, - Dst: prometheusConfigTargetPath, - } - - return t.Write(config) -} - -// Load the Grafana config template, do a template variable substitution, and save it -func updateGrafanaDatabaseConfiguration(ctx *context.HyperdriveContext, config *GlobalConfig, metricsDirPath string) error { - grafanaConfigTemplatePath, err := homedir.Expand(filepath.Join(ctx.TemplatesDir, grafanaConfigTemplate)) - if err != nil { - return fmt.Errorf("error expanding Grafana config template path: %w", err) - } - - grafanaConfigTargetPath, err := homedir.Expand(filepath.Join(metricsDirPath, grafanaConfigTarget)) - if err != nil { - return fmt.Errorf("error expanding Grafana config target path: %w", err) - } - - t := clitemplate.Template{ - Src: grafanaConfigTemplatePath, - Dst: grafanaConfigTargetPath, - } - - return t.Write(config) -} - // Loads a config without updating it if it exists func LoadConfigFromFile(configPath string, hdSettings []*hdconfig.HyperdriveSettings, swSettings []*swconfig.StakeWiseSettings) (*GlobalConfig, error) { // Make sure the config file exists From cebe9776cb769668e5d21017be9fb8aeec29488f Mon Sep 17 00:00:00 2001 From: huy Date: Wed, 8 Jan 2025 16:09:56 -0800 Subject: [PATCH 17/28] WIP --- adapter/utils/config/hyperdrive-config.go | 50 ---------------- adapter/utils/config/mev-boost-config.go | 72 ----------------------- adapter/utils/config/settings.go | 9 --- client/client.go | 11 ++-- 4 files changed, 7 insertions(+), 135 deletions(-) delete mode 100644 adapter/utils/config/hyperdrive-config.go delete mode 100644 adapter/utils/config/mev-boost-config.go delete mode 100644 adapter/utils/config/settings.go diff --git a/adapter/utils/config/hyperdrive-config.go b/adapter/utils/config/hyperdrive-config.go deleted file mode 100644 index 44cc331..0000000 --- a/adapter/utils/config/hyperdrive-config.go +++ /dev/null @@ -1,50 +0,0 @@ -package config - -import "github.com/rocket-pool/node-manager-core/config" - -// The master configuration struct -type HyperdriveConfig struct { - // General settings - Network config.Parameter[config.Network] - ClientMode config.Parameter[config.ClientMode] - EnableIPv6 config.Parameter[bool] - ProjectName config.Parameter[string] - ApiPort config.Parameter[uint16] - UserDataPath config.Parameter[string] - AutoTxMaxFee config.Parameter[float64] - MaxPriorityFee config.Parameter[float64] - AutoTxGasThreshold config.Parameter[float64] - AdditionalDockerNetworks config.Parameter[string] - ClientTimeout config.Parameter[uint16] - - // The Docker Hub tag for the daemon container - ContainerTag config.Parameter[string] - - // Logging - Logging *config.LoggerConfig - - // Execution client settings - LocalExecutionClient *config.LocalExecutionConfig - ExternalExecutionClient *config.ExternalExecutionConfig - - // Beacon node settings - LocalBeaconClient *config.LocalBeaconConfig - ExternalBeaconClient *config.ExternalBeaconConfig - - // Fallback clients - Fallback *config.FallbackConfig - - // Metrics - Metrics *config.MetricsConfig - - // MEV-Boost - MevBoost *MevBoostConfig - - // Modules - Modules map[string]any - - // Internal fields - Version string - // hyperdriveUserDirectory string - // networkSettings []*HyperdriveSettings -} diff --git a/adapter/utils/config/mev-boost-config.go b/adapter/utils/config/mev-boost-config.go deleted file mode 100644 index ec48b8a..0000000 --- a/adapter/utils/config/mev-boost-config.go +++ /dev/null @@ -1,72 +0,0 @@ -package config - -import ( - "github.com/rocket-pool/node-manager-core/config" -) - -// Constants -const ( -// mevBoostTag string = "flashbots/mev-boost:1.8.1" -) - -type MevSelectionMode string - -type MevRelayID string - -// A MEV relay -type MevRelay struct { - ID MevRelayID - Name string - Description string - Urls map[string]string -} - -// Configuration for MEV-Boost -type MevBoostConfig struct { - // Toggle to enable / disable - Enable config.Parameter[bool] - - // Ownership mode - Mode config.Parameter[config.ClientMode] - - // The mode for relay selection - SelectionMode config.Parameter[MevSelectionMode] - - // Flashbots relay - FlashbotsRelay config.Parameter[bool] - - // bloXroute max profit relay - BloxRouteMaxProfitRelay config.Parameter[bool] - - // bloXroute regulated relay - BloxRouteRegulatedRelay config.Parameter[bool] - - // Titan regional relay - TitanRegionalRelay config.Parameter[bool] - - // Custom relays provided by the user - CustomRelays config.Parameter[string] - - // The RPC port - Port config.Parameter[uint16] - - // Toggle for forwarding the HTTP port outside of Docker - OpenRpcPort config.Parameter[config.RpcPortMode] - - // The Docker Hub tag for MEV-Boost - ContainerTag config.Parameter[string] - - // Custom command line flags - AdditionalFlags config.Parameter[string] - - // The URL of an external MEV-Boost client - ExternalUrl config.Parameter[string] - - /////////////////////////// - // Non-editable settings // - /////////////////////////// - - // parent *HyperdriveConfig - // relays []MevRelay - // relayMap map[MevRelayID]MevRelay -} diff --git a/adapter/utils/config/settings.go b/adapter/utils/config/settings.go deleted file mode 100644 index 5e31965..0000000 --- a/adapter/utils/config/settings.go +++ /dev/null @@ -1,9 +0,0 @@ -package config - -const ( - HyperdriveDaemonRoute string = "hyperdrive" - - // API Keys - SecretsDir string = "secrets" - DaemonKeyFilename string = "daemon.key" -) diff --git a/client/client.go b/client/client.go index c437d80..a06f343 100644 --- a/client/client.go +++ b/client/client.go @@ -8,7 +8,6 @@ import ( "os" "path/filepath" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/config" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/context" "github.com/fatih/color" @@ -26,16 +25,20 @@ import ( ) const ( + SecretsDir string = "secrets" + DaemonKeyFilename string = "daemon.key" + SettingsFile string = "user-settings.yml" BackupSettingsFile string = "user-settings-backup.yml" metricsDir string = "metrics" - terminalLogColor color.Attribute = color.FgHiYellow + terminalLogColor color.Attribute = color.FgHiYellow + HyperdriveDaemonRoute string = "hyperdrive" cliIssuer string = "hd-cli" ) -var hdApiKeyRelPath string = filepath.Join(config.SecretsDir, config.DaemonKeyFilename) +var hdApiKeyRelPath string = filepath.Join(SecretsDir, DaemonKeyFilename) var moduleApiKeyRelPath string = filepath.Join(hdconfig.SecretsDir, hdconfig.ModulesName) var swApiKeyRelPath string = filepath.Join(moduleApiKeyRelPath, swconfig.ModuleName, hdconfig.DaemonKeyFilename) @@ -74,7 +77,7 @@ func NewHyperdriveClientFromCtx(c *cli.Context) (*HyperdriveClient, error) { // Create new Hyperdrive client from a custom context func NewHyperdriveClientFromHyperdriveCtx(hdCtx *context.HyperdriveContext) (*HyperdriveClient, error) { - logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, hdconfig.HyperdriveDaemonRoute)) + logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, HyperdriveDaemonRoute)) // Create the tracer if required var tracer *httptrace.ClientTrace From 7a1e9943a9770eef47d4463b6714d986bd416221 Mon Sep 17 00:00:00 2001 From: huy Date: Wed, 8 Jan 2025 16:22:35 -0800 Subject: [PATCH 18/28] build adapter script --- build.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/build.sh b/build.sh index 8c62b36..327080b 100755 --- a/build.sh +++ b/build.sh @@ -16,6 +16,24 @@ fail() { } +# Builds the adapter image and pushes it to Docker Hub +# NOTE: You must install qemu first; e.g. sudo apt-get install -y qemu qemu-user-static +build_adapter() { + echo "Building adapter image..." + # If uploading, make and push a manifest + if [ "$UPLOAD" = true ]; then + docker buildx build --rm --platform=linux/amd64,linux/arm64 --build-arg BINARIES_PATH=build/$VERSION -t nodeset/hyperdrive-stakewise-adapter:$VERSION -f docker/adapter.dockerfile --push . || fail "Error building adapter image." + elif [ "$LOCAL_UPLOAD" = true ]; then + if [ -z "$LOCAL_DOCKER_REGISTRY" ]; then + fail "LOCAL_DOCKER_REGISTRY must be set to upload to a local registry." + fi + docker buildx build --rm --platform=linux/amd64,linux/arm64 --build-arg BINARIES_PATH=build/$VERSION -t $LOCAL_DOCKER_REGISTRY/nodeset/hyperdrive-stakewise-adapter:$VERSION -f docker/adapter.dockerfile --push . || fail "Error building adapter image." + else + docker buildx build --rm --load --build-arg BINARIES_PATH=build/$VERSION -t nodeset/hyperdrive-stakewise-adapter:$VERSION -f docker/adapter.dockerfile . || fail "Error building adapter image." + fi + echo "done!" +} + # Builds the Stakewise daemon image and pushes it to Docker Hub # NOTE: You must install qemu first; e.g. sudo apt-get install -y qemu qemu-user-static build_daemon() { From ed6caf9e58c8c9060e56766035c5a84f78ad4851 Mon Sep 17 00:00:00 2001 From: huy Date: Fri, 10 Jan 2025 10:23:08 -0800 Subject: [PATCH 19/28] Get config instance --- adapter/{command => config}/claim-rewards.go | 2 +- adapter/{command => config}/commands.go | 2 +- adapter/{command => config}/exit.go | 2 +- .../generate-deposit-data.go | 2 +- adapter/{command => config}/generate-keys.go | 2 +- .../{command => config}/get-node-status.go | 2 +- adapter/config/he-config.go | 336 ++++++++++++++++++ adapter/config/ids/ids.go | 16 + adapter/{command => config}/initialize.go | 2 +- .../upload-deposit-data.go | 2 +- adapter/hd-module/get-config-instance.go | 45 +++ adapter/utils/auth.go | 99 ++++++ adapter/utils/flags.go | 6 + adapter/utils/gas.go | 2 +- adapter/utils/prompt.go | 2 +- go.mod | 2 +- go.sum | 2 + shared/config/ids/config.go | 106 ++++++ 18 files changed, 621 insertions(+), 11 deletions(-) rename adapter/{command => config}/claim-rewards.go (99%) rename adapter/{command => config}/commands.go (99%) rename adapter/{command => config}/exit.go (99%) rename adapter/{command => config}/generate-deposit-data.go (99%) rename adapter/{command => config}/generate-keys.go (99%) rename adapter/{command => config}/get-node-status.go (99%) create mode 100644 adapter/config/he-config.go create mode 100644 adapter/config/ids/ids.go rename adapter/{command => config}/initialize.go (98%) rename adapter/{command => config}/upload-deposit-data.go (97%) create mode 100644 adapter/hd-module/get-config-instance.go create mode 100644 adapter/utils/auth.go create mode 100644 shared/config/ids/config.go diff --git a/adapter/command/claim-rewards.go b/adapter/config/claim-rewards.go similarity index 99% rename from adapter/command/claim-rewards.go rename to adapter/config/claim-rewards.go index e76a9f9..a28367c 100644 --- a/adapter/command/claim-rewards.go +++ b/adapter/config/claim-rewards.go @@ -1,4 +1,4 @@ -package command +package config import ( "fmt" diff --git a/adapter/command/commands.go b/adapter/config/commands.go similarity index 99% rename from adapter/command/commands.go rename to adapter/config/commands.go index acff32b..62c14f9 100644 --- a/adapter/command/commands.go +++ b/adapter/config/commands.go @@ -1,4 +1,4 @@ -package command +package config import ( "fmt" diff --git a/adapter/command/exit.go b/adapter/config/exit.go similarity index 99% rename from adapter/command/exit.go rename to adapter/config/exit.go index a067b3c..2006897 100644 --- a/adapter/command/exit.go +++ b/adapter/config/exit.go @@ -1,4 +1,4 @@ -package command +package config import ( "fmt" diff --git a/adapter/command/generate-deposit-data.go b/adapter/config/generate-deposit-data.go similarity index 99% rename from adapter/command/generate-deposit-data.go rename to adapter/config/generate-deposit-data.go index 045b410..78fdded 100644 --- a/adapter/command/generate-deposit-data.go +++ b/adapter/config/generate-deposit-data.go @@ -1,4 +1,4 @@ -package command +package config import ( "encoding/json" diff --git a/adapter/command/generate-keys.go b/adapter/config/generate-keys.go similarity index 99% rename from adapter/command/generate-keys.go rename to adapter/config/generate-keys.go index 9aa1265..3aaa956 100644 --- a/adapter/command/generate-keys.go +++ b/adapter/config/generate-keys.go @@ -1,4 +1,4 @@ -package command +package config import ( "fmt" diff --git a/adapter/command/get-node-status.go b/adapter/config/get-node-status.go similarity index 99% rename from adapter/command/get-node-status.go rename to adapter/config/get-node-status.go index d9ffea7..aaf8bd6 100644 --- a/adapter/command/get-node-status.go +++ b/adapter/config/get-node-status.go @@ -1,4 +1,4 @@ -package command +package config import ( "fmt" diff --git a/adapter/config/he-config.go b/adapter/config/he-config.go new file mode 100644 index 0000000..98037fe --- /dev/null +++ b/adapter/config/he-config.go @@ -0,0 +1,336 @@ +package config + +import ( + "fmt" + "os" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config/ids" + nativecfg "github.com/nodeset-org/hyperdrive-stakewise/shared/config/ids" + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v3" +) + +const ( + FloatThreshold float64 = 75.0 +) + +type PortMode string + +const ( + PortMode_Closed PortMode = "closed" + PortMode_Localhost PortMode = "localhost" + PortMode_External PortMode = "external" +) + +type ExampleConfig struct { + // ExampleBool hdconfig.BoolParameterMetadata + + // ExampleInt hdconfig.IntParameterMetadata + + // ExampleUint hdconfig.UintParameterMetadata + + // ExampleFloat hdconfig.FloatParameterMetadata + + // ExampleString hdconfig.StringParameterMetadata + + // ExampleChoice hdconfig.ChoiceParameterMetadata[nativecfg.ExampleOption] + + SubConfig *SubConfig + + ServerConfig *ServerConfig +} + +func NewExampleConfig() *ExampleConfig { + cfg := &ExampleConfig{} + + // // ExampleBool + // cfg.ExampleBool.ID = hdconfig.Identifier(ids.ExampleBoolID) + // cfg.ExampleBool.Name = "Example Boolean" + // cfg.ExampleBool.Description.Default = "This is an example of a boolean parameter. It doesn't directly affect the service, but it does control the behavior of some other config parameters." + // cfg.ExampleBool.AffectedContainers = []string{shared.ServiceContainerName} + // cfg.ExampleBool.Value = cfg.ExampleBool.Default + + // // ExampleInt + // cfg.ExampleInt.ID = hdconfig.Identifier(ids.ExampleIntID) + // cfg.ExampleInt.Name = "Example Integer" + // cfg.ExampleInt.Description.Default = "This is an example of an integer parameter." + // cfg.ExampleInt.AffectedContainers = []string{shared.ServiceContainerName} + // cfg.ExampleInt.Value = cfg.ExampleInt.Default + + // // ExampleUint + // cfg.ExampleUint.ID = hdconfig.Identifier(ids.ExampleUintID) + // cfg.ExampleUint.Name = "Example Unsigned Integer" + // cfg.ExampleUint.Description.Default = "This is an example of an unsigned integer parameter." + // cfg.ExampleUint.AffectedContainers = []string{shared.ServiceContainerName} + // cfg.ExampleUint.Value = cfg.ExampleUint.Default + + // // ExampleFloat + // cfg.ExampleFloat.ID = hdconfig.Identifier(ids.ExampleFloatID) + // cfg.ExampleFloat.Name = "Example Float" + // cfg.ExampleFloat.Description.Default = "This is an example of a float parameter with a minimum and maximum set." + // cfg.ExampleFloat.Default = 50 + // cfg.ExampleFloat.MinValue = 0.0 + // cfg.ExampleFloat.MaxValue = 100.0 + // cfg.ExampleFloat.Value = cfg.ExampleFloat.Default + // cfg.ExampleFloat.AffectedContainers = []string{shared.ServiceContainerName} + + // // ExampleString + // cfg.ExampleString.ID = hdconfig.Identifier(ids.ExampleStringID) + // cfg.ExampleString.Name = "Example String" + // cfg.ExampleString.Description.Default = "This is an example of a string parameter. It has a max length and regex pattern set." + // cfg.ExampleString.MaxLength = 10 + // cfg.ExampleString.Regex = "^[a-zA-Z]*$" + // cfg.ExampleString.Value = cfg.ExampleString.Default + // cfg.ExampleString.AffectedContainers = []string{shared.ServiceContainerName} + + // // Options for ExampleChoice + // options := make([]hdconfig.ParameterMetadataOption[nativecfg.ExampleOption], 3) + // options[0].Name = "One" + // options[0].Description.Default = "This is the first option." + // options[0].Value = nativecfg.ExampleOption_One + + // thresholdString := strconv.FormatFloat(FloatThreshold, 'f', -1, 64) + // options[1].Name = "Two" + // options[1].Description.Default = "This is the second option. It is hidden when ExampleFloat is less than " + thresholdString + "." + // options[1].Description.Template = fmt.Sprintf("{{if lt .GetValue %s %s}}This option is hidden because the float is less than %s.{{else}}This option is visible because the float is greater than or equal to %s.{{end}}", ids.ExampleFloatID, thresholdString, thresholdString, thresholdString) + // options[1].Value = nativecfg.ExampleOption_Two + // options[1].Disabled.Default = true + // options[1].Disabled.Template = "{{if eq .GetValue " + ids.ExampleBoolID + " true}}false{{else}}{{.UseDefault}}{{end}}" + + // options[2].Name = "Three" + // options[2].Description.Default = "This is the third option." + // options[2].Value = nativecfg.ExampleOption_Three + + // // ExampleChoice + // cfg.ExampleChoice.ID = hdconfig.Identifier(ids.ExampleChoiceID) + // cfg.ExampleChoice.Name = "Example Choice" + // cfg.ExampleChoice.Description.Default = "This is an example of a choice parameter between multiple options." + // cfg.ExampleChoice.Options = options + // cfg.ExampleChoice.Default = options[0].Value + // cfg.ExampleChoice.Value = cfg.ExampleChoice.Default + // cfg.ExampleChoice.AffectedContainers = []string{} + + // Subconfigs + cfg.SubConfig = NewSubConfig() + cfg.ServerConfig = NewServerConfig() + + return cfg +} + +type SubConfig struct { + hdconfig.SectionMetadataHeader + + SubExampleBool hdconfig.BoolParameterMetadata + + SubExampleChoice hdconfig.ChoiceParameterMetadata[nativecfg.ExampleOption] +} + +func NewSubConfig() *SubConfig { + cfg := &SubConfig{} + // cfg.ID = hdconfig.Identifier(ids.SubConfigID) + // cfg.Name = "Sub Config" + // cfg.Description.Default = "This is a sub-section of the main configuration." + // cfg.Hidden.Default = true + // cfg.Hidden.Template = "{{if eq .GetValue " + ids.ExampleBoolID + " true}}false{{else}}true{{end}}" + + // // SubExampleBool + // cfg.SubExampleBool.ID = hdconfig.Identifier(ids.SubExampleBoolID) + // cfg.SubExampleBool.Name = "Sub Example Boolean" + // cfg.SubExampleBool.Description.Default = "This is an example of a boolean parameter in a sub-section." + // cfg.SubExampleBool.Value = cfg.SubExampleBool.Default + + // // Options for SubExampleChoice + // options := make([]hdconfig.ParameterMetadataOption[nativecfg.ExampleOption], 2) + // options[0].Name = "One" + // options[0].Description.Default = "This is the first option." + // options[0].Value = nativecfg.ExampleOption_One + + // options[1].Name = "Two" + // options[1].Description.Default = "This is the second option." + // options[1].Value = nativecfg.ExampleOption_Two + + // // SubExampleChoice + // cfg.SubExampleChoice.ID = hdconfig.Identifier(ids.SubExampleChoiceID) + // cfg.SubExampleChoice.Name = "Sub Example Choice" + // cfg.SubExampleChoice.Description.Default = "This is an example of a choice parameter between multiple options in a sub-section." + // cfg.SubExampleChoice.Options = options + // cfg.SubExampleChoice.Default = options[1].Value + // cfg.SubExampleChoice.Value = cfg.SubExampleChoice.Default + + return cfg +} + +type ServerConfig struct { + hdconfig.SectionMetadataHeader + + Port hdconfig.UintParameterMetadata + + PortMode hdconfig.ChoiceParameterMetadata[PortMode] +} + +func NewServerConfig() *ServerConfig { + cfg := &ServerConfig{} + // cfg.ID = hdconfig.Identifier(ids.ServerConfigID) + // cfg.Name = "Service Config" + // cfg.Description.Default = "This is the configuration for the module's service. This isn't used by the service directly, but it is used by Hyperdrive itself in the service's Docker Compose file template to configure the service during its starting process." + + // // Port + // cfg.Port.ID = hdconfig.Identifier(ids.PortID) + // cfg.Port.Name = "API Port" + // cfg.Port.Description.Default = "This is the API port the server should run on." + // cfg.Port.Default = uint64(shared.DefaultServerApiPort) + // cfg.Port.MinValue = 0 + // cfg.Port.MaxValue = 65535 + // cfg.Port.Value = cfg.Port.Default + // cfg.Port.AffectedContainers = []string{shared.ServiceContainerName} + + // // Options for PortMode + // options := make([]hdconfig.ParameterMetadataOption[PortMode], 3) + // options[0].Name = "Closed" + // options[0].Description.Default = "The API is only accessible to internal Docker container traffic." + // options[0].Value = PortMode_Closed + + // options[1].Name = "Localhost Only" + // options[1].Description.Default = "The API is accessible from internal Docker containers and your own local machine, but no other external machines." + // options[1].Value = PortMode_Localhost + + // options[2].Name = "All External Traffic" + // options[2].Description.Default = "The port is accessible to everything, including external machines.\n\n[orange]Use with caution!" + // options[2].Value = PortMode_External + + // // PortMode + // cfg.PortMode.ID = hdconfig.Identifier(ids.PortModeID) + // cfg.PortMode.Name = "Expose API Port" + // cfg.PortMode.Description.Default = "Determine how the server's HTTP API restricts its access from various sources." + // cfg.PortMode.Options = options + // cfg.PortMode.Default = options[0].Value + // cfg.PortMode.Value = cfg.PortMode.Default + // cfg.PortMode.AffectedContainers = []string{shared.ServiceContainerName} + + return cfg +} + +func ConvertToMetadata(native *nativecfg.NativeExampleConfig) *ExampleConfig { + cfg := NewExampleConfig() + + // cfg.ExampleBool.Value = native.ExampleBool + // cfg.ExampleInt.Value = native.ExampleInt + // cfg.ExampleUint.Value = native.ExampleUint + // cfg.ExampleFloat.Value = native.ExampleFloat + // cfg.ExampleString.Value = native.ExampleString + // cfg.ExampleChoice.Value = native.ExampleChoice + + cfg.SubConfig.SubExampleBool.Value = native.SubConfig.SubExampleBool + cfg.SubConfig.SubExampleChoice.Value = native.SubConfig.SubExampleChoice + + return cfg +} + +func (cfg *ExampleConfig) ConvertToNative() *nativecfg.NativeExampleConfig { + // native := &nativecfg.NativeExampleConfig{} + + // native.ExampleBool = cfg.ExampleBool.Value + // native.ExampleInt = cfg.ExampleInt.Value + // native.ExampleUint = cfg.ExampleUint.Value + // native.ExampleFloat = cfg.ExampleFloat.Value + // native.ExampleString = cfg.ExampleString.Value + // native.ExampleChoice = cfg.ExampleChoice.Value + // native.SubConfig.SubExampleBool = cfg.SubConfig.SubExampleBool.Value + // native.SubConfig.SubExampleChoice = cfg.SubConfig.SubExampleChoice.Value + // return native + return nil +} + +// Configuration manager +type AdapterConfigManager struct { + // The adapter configuration + AdapterConfig *ExampleConfig + + // The native configuration manager + // nativeConfigManager *nativecfg.ConfigManager + + // The path to the adapter configuration file + adapterConfigPath string +} + +// Create a new configuration manager for the adapter +func NewAdapterConfigManager(c *cli.Context) (*AdapterConfigManager, error) { + // configDir := c.String(utils.ConfigDirFlag.Name) + // if configDir == "" { + // return nil, fmt.Errorf("config directory is required") + // } + // return &AdapterConfigManager{ + // // nativeConfigManager: nativecfg.NewConfigManager(filepath.Join(configDir, utils.ServiceConfigFile)), + // adapterConfigPath: filepath.Join(configDir, utils.AdapterConfigFile), + // }, nil + return nil, nil +} + +// Load the configuration from disk +func (m *AdapterConfigManager) LoadConfigFromDisk() (*ExampleConfig, error) { + // Load the native config + // nativeCfg, err := m.nativeConfigManager.LoadConfigFromFile() + // if err != nil { + // return nil, fmt.Errorf("error loading service config: %w", err) + // } + + // // Check if the adapter config exists + // _, err = os.Stat(m.adapterConfigPath) + // if errors.Is(err, fs.ErrNotExist) { + // return nil, nil + // } + + // Load it + bytes, err := os.ReadFile(m.adapterConfigPath) + if err != nil { + return nil, fmt.Errorf("error reading config file [%s]: %w", m.adapterConfigPath, err) + } + + // Deserialize it + cfgInstance := map[string]any{} + err = yaml.Unmarshal(bytes, &cfgInstance) + if err != nil { + return nil, fmt.Errorf("error deserializing adapter config file [%s]: %w", m.adapterConfigPath, err) + } + serverCfg := NewServerConfig() + portInt := cfgInstance[ids.PortID].(int) + serverCfg.Port.Value = uint64(portInt) + serverCfg.PortMode.Value = PortMode(cfgInstance[ids.PortModeID].(string)) + + // Merge the configs + // modCfg := ConvertToMetadata(nativeCfg) + // modCfg.ServerConfig = serverCfg + // m.AdapterConfig = modCfg + // return modCfg, nil + return nil, nil +} + +// Save the configuration to a file. If the config hasn't been loaded yet, this doesn't do anything. +func (m *AdapterConfigManager) SaveConfigToDisk() error { + if m.AdapterConfig == nil { + return nil + } + + // // Save the native config + // nativeCfg := m.AdapterConfig.ConvertToNative() + // m.nativeConfigManager.Config = nativeCfg + // err := m.nativeConfigManager.SaveConfigToFile() + // if err != nil { + // return fmt.Errorf("error saving service config: %w", err) + // } + + // Serialize the adapter config + // modCfg := hdconfig.CreateInstanceFromMetadata(m.AdapterConfig.ServerConfig) + // bytes, err := yaml.Marshal(modCfg) + // if err != nil { + // return fmt.Errorf("error serializing adapter config: %w", err) + // } + + // Write it + // err = os.WriteFile(m.adapterConfigPath, bytes, nativecfg.ConfigFileMode) + // if err != nil { + // return fmt.Errorf("error writing adapter config file [%s]: %w", m.adapterConfigPath, err) + // } + return nil +} diff --git a/adapter/config/ids/ids.go b/adapter/config/ids/ids.go new file mode 100644 index 0000000..90fd979 --- /dev/null +++ b/adapter/config/ids/ids.go @@ -0,0 +1,16 @@ +package ids + +const ( + ExampleBoolID string = "exampleBool" + ExampleIntID string = "exampleInt" + ExampleUintID string = "exampleUint" + ExampleFloatID string = "exampleFloat" + ExampleStringID string = "exampleString" + ExampleChoiceID string = "exampleChoice" + SubConfigID string = "subConfig" + SubExampleBoolID string = "subConfigBool" + SubExampleChoiceID string = "subConfigChoice" + ServerConfigID string = "server" + PortModeID string = "portMode" + PortID string = "port" +) diff --git a/adapter/command/initialize.go b/adapter/config/initialize.go similarity index 98% rename from adapter/command/initialize.go rename to adapter/config/initialize.go index 8500f6f..52d32f5 100644 --- a/adapter/command/initialize.go +++ b/adapter/config/initialize.go @@ -1,4 +1,4 @@ -package command +package config import ( "fmt" diff --git a/adapter/command/upload-deposit-data.go b/adapter/config/upload-deposit-data.go similarity index 97% rename from adapter/command/upload-deposit-data.go rename to adapter/config/upload-deposit-data.go index 848475d..3baf692 100644 --- a/adapter/command/upload-deposit-data.go +++ b/adapter/config/upload-deposit-data.go @@ -1,4 +1,4 @@ -package command +package config import ( "fmt" diff --git a/adapter/hd-module/get-config-instance.go b/adapter/hd-module/get-config-instance.go new file mode 100644 index 0000000..5f36636 --- /dev/null +++ b/adapter/hd-module/get-config-instance.go @@ -0,0 +1,45 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" +) + +func getConfigInstance(c *cli.Context) error { + // Get the request + _, err := utils.HandleKeyedRequest[*utils.KeyedRequest](c) + if err != nil { + return err + } + + // Get the config + cfgMgr, err := config.NewAdapterConfigManager(c) + if err != nil { + return fmt.Errorf("error creating config manager: %w", err) + } + cfg, err := cfgMgr.LoadConfigFromDisk() + if err != nil { + return fmt.Errorf("error loading config: %w", err) + } + + // Handle no saved config + if cfg == nil { + return fmt.Errorf("config has not been initialized yet") + } + + // Create the response + instance := hdconfig.CreateInstanceFromMetadata(cfg) + bytes, err := json.Marshal(instance) + if err != nil { + return fmt.Errorf("error marshalling config: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/utils/auth.go b/adapter/utils/auth.go new file mode 100644 index 0000000..672ac38 --- /dev/null +++ b/adapter/utils/auth.go @@ -0,0 +1,99 @@ +package utils + +import ( + "bufio" + "fmt" + "io/fs" + "os" + + "errors" + + "github.com/goccy/go-json" + "github.com/urfave/cli/v2" +) + +const ( + AuthenticatorMetadataKey string = "authenticator" +) + +// KeyedRequest is a request that contains a key used for authentication +type KeyedRequest struct { + Key string `json:"key"` +} + +// Returns the key for the request +func (r *KeyedRequest) GetKey() string { + return r.Key +} + +// Interface for requests that contain a key +type IKeyedRequest interface { + GetKey() string +} + +// Simple authenticator used for processing incoming requests +type Authenticator struct { + key string +} + +// Creates a new Authenticator instance +func NewAuthenticator(c *cli.Context) (*Authenticator, error) { + keyFile := c.String(KeyFileFlag.Name) + if keyFile == "" { + return nil, fmt.Errorf("secret key file is required") + } + + // Make sure the file exists + _, err := os.Stat(keyFile) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return nil, fmt.Errorf("key file [%s] does not exist", keyFile) + } + return nil, fmt.Errorf("error checking key file [%s]: %w", keyFile, err) + } + + // Read the key file + key, err := os.ReadFile(keyFile) + if err != nil { + return nil, fmt.Errorf("error reading key file [%s]: %w", keyFile, err) + } + + return &Authenticator{ + key: string(key), + }, nil +} + +// Authenticate checks if the provided key matches the stored key +func (a *Authenticator) Authenticate(key string) error { + if key != a.key { + return errors.New("invalid key") + } + return nil +} + +// Handles an incoming keyed request by reading the input, parsing it, and authenticating it +func HandleKeyedRequest[RequestType IKeyedRequest](c *cli.Context) (RequestType, error) { + var data RequestType + + // Read the input + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + return data, fmt.Errorf("error reading input: %w", err) + } + + // Parse the input + err = json.Unmarshal([]byte(input), &data) + if err != nil { + return data, fmt.Errorf("error parsing input: %w", err) + } + + // Authenticate the request + authenticator := c.App.Metadata[AuthenticatorMetadataKey].(*Authenticator) + err = authenticator.Authenticate(data.GetKey()) + if err != nil { + return data, fmt.Errorf("error authenticating request: %w", err) + } + + return data, nil +} diff --git a/adapter/utils/flags.go b/adapter/utils/flags.go index a804479..de93d2e 100644 --- a/adapter/utils/flags.go +++ b/adapter/utils/flags.go @@ -73,6 +73,12 @@ var ( Aliases: []string{"st"}, Usage: "Sign any TXs and print the results, but don't submit it to the network. Useful if you want to save a TX for later or bundle it up with a service like Flashbots.", } + KeyFileFlag *cli.StringFlag = &cli.StringFlag{ + Name: "secret", + Aliases: []string{"s"}, + Usage: "The path to the secret key file for authentication", + Value: "/hd/secret", + } ) func InstantiateFlag[FlagType cli.Flag](prototype FlagType, description string) cli.Flag { diff --git a/adapter/utils/gas.go b/adapter/utils/gas.go index 87c5218..c60529d 100644 --- a/adapter/utils/gas.go +++ b/adapter/utils/gas.go @@ -6,8 +6,8 @@ import ( "math/big" "strconv" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" - "github.com/nodeset-org/hyperdrive/hyperdrive-cli/utils/terminal" "github.com/rocket-pool/node-manager-core/eth" "github.com/rocket-pool/node-manager-core/gas" "github.com/urfave/cli/v2" diff --git a/adapter/utils/prompt.go b/adapter/utils/prompt.go index 4eaf58a..1951733 100644 --- a/adapter/utils/prompt.go +++ b/adapter/utils/prompt.go @@ -9,7 +9,7 @@ import ( "strings" "syscall" - "github.com/nodeset-org/hyperdrive/hyperdrive-cli/utils/terminal" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" "golang.org/x/term" ) diff --git a/go.mod b/go.mod index a6ae126..e9f72cd 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 github.com/mitchellh/go-homedir v1.1.0 - github.com/nodeset-org/hyperdrive v1.1.1 + github.com/nodeset-org/hyperdrive v1.1.2-0.20250106215109-78519b2bef69 github.com/nodeset-org/hyperdrive-daemon v1.1.1 github.com/nodeset-org/nodeset-client-go v1.2.2 github.com/nodeset-org/osha v0.3.1 diff --git a/go.sum b/go.sum index a797e22..b6af530 100644 --- a/go.sum +++ b/go.sum @@ -379,6 +379,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nodeset-org/hyperdrive v1.1.1 h1:PImbcczTqGpx0UPJugQIZkxzlWMJn9O5+138mOrbeKA= github.com/nodeset-org/hyperdrive v1.1.1/go.mod h1:BId3aaponZ2gPeNReLIdB+AAK0sDOKUxV3xAQQrs4fg= +github.com/nodeset-org/hyperdrive v1.1.2-0.20250106215109-78519b2bef69 h1:FJojeBJ3vr7hsIaKzZJDKYPAvxT+kYPUX3ratal9Kmc= +github.com/nodeset-org/hyperdrive v1.1.2-0.20250106215109-78519b2bef69/go.mod h1:nBfN+6oGRe91ZmMrfoQcGCiSGj1ryKfwN5Ap6UfeqwA= github.com/nodeset-org/hyperdrive-daemon v1.1.1 h1:aUQzEKmyuYhVTzn3AQEIdUm335xBYmsCTJfPdGNA5r0= github.com/nodeset-org/hyperdrive-daemon v1.1.1/go.mod h1:tzqdqfRR4Xcy4GtdSiEnfjOFj3+g4TvuZJpYljCQEBM= github.com/nodeset-org/nodeset-client-go v1.2.2 h1:mjJnH5uw/CF5TWdRxdU5+Cw/ERRLz4hl9VsRFVO0Wdo= diff --git a/shared/config/ids/config.go b/shared/config/ids/config.go new file mode 100644 index 0000000..bb4a7d7 --- /dev/null +++ b/shared/config/ids/config.go @@ -0,0 +1,106 @@ +package ids + +import ( + "errors" + "fmt" + "io/fs" + "os" + + "gopkg.in/yaml.v3" +) + +const ( + ConfigFileMode os.FileMode = 0644 +) + +// Example of a choice option, like an enum +type ExampleOption string + +const ( + ExampleOption_One ExampleOption = "one" + ExampleOption_Two ExampleOption = "two" + ExampleOption_Three ExampleOption = "three" +) + +// Example of a configuration for a service +type NativeExampleConfig struct { + ExampleBool bool `json:"exampleBool" yaml:"exampleBool"` + + ExampleInt int64 `json:"exampleInt" yaml:"exampleInt"` + + ExampleUint uint64 `json:"exampleUint" yaml:"exampleUint"` + + ExampleFloat float64 `json:"exampleFloat" yaml:"exampleFloat"` + + ExampleString string `json:"exampleString" yaml:"exampleString"` + + ExampleChoice ExampleOption `json:"exampleChoice" yaml:"exampleChoice"` + + SubConfig NativeSubConfig `json:"subConfig" yaml:"subConfig"` +} + +// Example of a section under the service's top-level configuration +type NativeSubConfig struct { + SubExampleBool bool `json:"subExampleBool" yaml:"subExampleBool"` + + SubExampleChoice ExampleOption `json:"subExampleChoice" yaml:"subExampleChoice"` +} + +// Configuration manager +type ConfigManager struct { + // The configuration + Config *NativeExampleConfig + + // The path to the configuration file + ConfigPath string +} + +// Create a new configuration manager +func NewConfigManager(path string) *ConfigManager { + return &ConfigManager{ + ConfigPath: path, + } +} + +// Load the configuration from a file +func (m *ConfigManager) LoadConfigFromFile() (*NativeExampleConfig, error) { + // Check if the file exists + _, err := os.Stat(m.ConfigPath) + if errors.Is(err, fs.ErrNotExist) { + return nil, nil + } + + // Load it + bytes, err := os.ReadFile(m.ConfigPath) + if err != nil { + return nil, fmt.Errorf("error reading config file [%s]: %w", m.ConfigPath, err) + } + + // Deserialize it + cfg := NativeExampleConfig{} + err = yaml.Unmarshal(bytes, &cfg) + if err != nil { + return nil, fmt.Errorf("error deserializing config file [%s]: %w", m.ConfigPath, err) + } + return &cfg, nil +} + +// Save the configuration to a file. If the config hasn't been loaded yet, this doesn't do anything. +func (m *ConfigManager) SaveConfigToFile() error { + if m.Config == nil { + return nil + } + + // Serialize it + bytes, err := yaml.Marshal(m.Config) + if err != nil { + return fmt.Errorf("error serializing config: %w", err) + } + + // Write it + err = os.WriteFile(m.ConfigPath, bytes, ConfigFileMode) + if err != nil { + return fmt.Errorf("error writing config file [%s]: %w", m.ConfigPath, err) + } + return nil +} From 0dfa3de6fa31548af32464f789dd2fdc921e488f Mon Sep 17 00:00:00 2001 From: huy Date: Fri, 10 Jan 2025 15:07:12 -0800 Subject: [PATCH 20/28] Implement hd commands and app.go --- adapter/app/app.go | 65 ++++++++++++ adapter/hd-module/commands.go | 121 +++++++++++++++++++++++ adapter/hd-module/get-config-metadata.go | 46 +++++++++ adapter/hd-module/get-containers.go | 42 ++++++++ adapter/hd-module/get-log-file.go | 57 +++++++++++ adapter/hd-module/process-config.go | 70 +++++++++++++ adapter/hd-module/run.go | 54 ++++++++++ adapter/hd-module/set-config.go | 48 +++++++++ adapter/hd-module/version.go | 33 +++++++ adapter/utils/consts.go | 15 +++ adapter/utils/flags.go | 12 +++ go.mod | 1 + go.sum | 2 + shared/consts.go | 12 +++ 14 files changed, 578 insertions(+) create mode 100644 adapter/app/app.go create mode 100644 adapter/hd-module/commands.go create mode 100644 adapter/hd-module/get-config-metadata.go create mode 100644 adapter/hd-module/get-containers.go create mode 100644 adapter/hd-module/get-log-file.go create mode 100644 adapter/hd-module/process-config.go create mode 100644 adapter/hd-module/run.go create mode 100644 adapter/hd-module/set-config.go create mode 100644 adapter/hd-module/version.go create mode 100644 adapter/utils/consts.go create mode 100644 shared/consts.go diff --git a/adapter/app/app.go b/adapter/app/app.go new file mode 100644 index 0000000..1b34645 --- /dev/null +++ b/adapter/app/app.go @@ -0,0 +1,65 @@ +package app + +import ( + "fmt" + "os" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/shared" + "github.com/urfave/cli/v2" +) + +func CreateApp() *cli.App { + // Initialize application + app := cli.NewApp() + + // Set application info + app.Name = "Hyperdrive Stakewise Adapter" + app.Usage = "Adapter for the Hyperdrive Stakewise module" + app.Version = shared.StakewiseVersion + app.Authors = []*cli.Author{ + { + Name: "Nodeset", + Email: "info@nodeset.io", + }, + } + app.Copyright = "(c) 2024 NodeSet LLC" + + // Enable Bash Completion + app.EnableBashCompletion = true + + // Set application flags + app.Flags = []cli.Flag{ + utils.ConfigDirFlag, + utils.LogDirFlag, + utils.KeyFileFlag, + } + + // Register commands + config.RegisterCommands(app) + + app.Before = func(c *cli.Context) error { + // Make the authenticator + auth, err := utils.NewAuthenticator(c) + if err != nil { + return err + } + c.App.Metadata[utils.AuthenticatorMetadataKey] = auth + + return nil + } + app.BashComplete = func(c *cli.Context) { + // Load the context and flags prior to autocomplete + err := app.Before(c) + if err != nil { + fmt.Fprint(os.Stderr, err.Error()) + os.Exit(1) + } + + // Run the default autocomplete + cli.DefaultAppComplete(c) + } + + return app +} diff --git a/adapter/hd-module/commands.go b/adapter/hd-module/commands.go new file mode 100644 index 0000000..457a24f --- /dev/null +++ b/adapter/hd-module/commands.go @@ -0,0 +1,121 @@ +package hdmodule + +import ( + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/urfave/cli/v2" +) + +// Handles `hd-module` commands +func RegisterCommands(app *cli.App) { + app.Commands = append(app.Commands, &cli.Command{ + Name: "hd-module", + Aliases: []string{"hd"}, + Usage: "Handle Hyperdrive module commands", + Subcommands: []*cli.Command{ + { + Name: "version", + Aliases: []string{"v"}, + Flags: []cli.Flag{}, + Usage: "Print the module version.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return version() + }, + }, + { + Name: "get-log-file", + Aliases: []string{"l"}, + Flags: []cli.Flag{}, + Usage: "Get the path to a log file.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return getLogFile(c) + }, + }, + { + Name: "get-config-metadata", + Aliases: []string{"c"}, + Flags: []cli.Flag{}, + Usage: "Get the metadata for the module's configuration, representing how to present the parameters to the user.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return getConfigMetadata(c) + }, + }, + { + Name: "get-config-instance", + Aliases: []string{"i"}, + Flags: []cli.Flag{}, + Usage: "Get the instance of the module's configuration, with the values all set.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return getConfigInstance(c) + }, + }, + { + Name: "process-config", + Aliases: []string{"p"}, + Flags: []cli.Flag{}, + Usage: "Process the module's configuration, validating it without saving.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return processConfig(c) + }, + }, + { + Name: "set-config", + Aliases: []string{"s"}, + Flags: []cli.Flag{}, + Usage: "Sets the module's configuration, saving it to disk.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return setConfig(c) + }, + }, + { + Name: "get-containers", + Aliases: []string{"t"}, + Flags: []cli.Flag{}, + Usage: "Get the list of containers owned by this module.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return getContainers(c) + }, + }, + { + Name: "run", + Aliases: []string{"r"}, + Flags: []cli.Flag{}, + Usage: "Run a command.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return run(c) + }, + }, + }, + }) +} diff --git a/adapter/hd-module/get-config-metadata.go b/adapter/hd-module/get-config-metadata.go new file mode 100644 index 0000000..9bc7f84 --- /dev/null +++ b/adapter/hd-module/get-config-metadata.go @@ -0,0 +1,46 @@ +package hdmodule + +import ( + //"encoding/json" + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" +) + +func getConfigMetadata(c *cli.Context) error { + // Get the request + _, err := utils.HandleKeyedRequest[*utils.KeyedRequest](c) + if err != nil { + return err + } + + // Get the config + cfgMgr, err := config.NewAdapterConfigManager(c) + if err != nil { + return fmt.Errorf("error creating config manager: %w", err) + } + cfg, err := cfgMgr.LoadConfigFromDisk() + if err != nil { + return fmt.Errorf("error loading config: %w", err) + } + + // Handle no config file by using the default + if cfg == nil { + cfg = config.NewExampleConfig() + } + + // Create the response + cfgMap := hdconfig.MarshalConfigurationMetadataToMap(cfg) + bytes, err := json.Marshal(cfgMap) + if err != nil { + return fmt.Errorf("error marshalling config: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/hd-module/get-containers.go b/adapter/hd-module/get-containers.go new file mode 100644 index 0000000..bf6c100 --- /dev/null +++ b/adapter/hd-module/get-containers.go @@ -0,0 +1,42 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/shared" + "github.com/urfave/cli/v2" +) + +// Response format for `get-containers` +type getContainersResponse struct { + // The list of containers owned by this module + Containers []string `json:"containers"` +} + +// Handle the `get-containers` command +func getContainers(c *cli.Context) error { + // Get the request + _, err := utils.HandleKeyedRequest[*utils.KeyedRequest](c) + if err != nil { + return err + } + + // Create the response + response := getContainersResponse{ + Containers: []string{ + shared.ServiceContainerName, + }, + } + + // Marshal it + bytes, err := json.Marshal(response) + if err != nil { + return fmt.Errorf("error marshalling get-containers response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/hd-module/get-log-file.go b/adapter/hd-module/get-log-file.go new file mode 100644 index 0000000..0e776d2 --- /dev/null +++ b/adapter/hd-module/get-log-file.go @@ -0,0 +1,57 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/shared" + "github.com/urfave/cli/v2" +) + +// Request format for `get-log-file` +type getLogFileRequest struct { + utils.KeyedRequest + + // The log file source to retrieve + Source string `json:"source"` +} + +// Response format for `get-log-file` +type getLogFileResponse struct { + // The path to the log file + Path string `json:"path"` +} + +// Handle the `get-log-file` command +func getLogFile(c *cli.Context) error { + // Get the request + request, err := utils.HandleKeyedRequest[*getLogFileRequest](c) + if err != nil { + return err + } + + // Get the path + path := "" + switch request.Source { + case "adapter": + path = utils.AdapterLogFile + case shared.ServiceContainerName: + path = shared.ServiceLogFile + } + + // Create the response + response := getLogFileResponse{ + Path: path, + } + + // Marshal it + bytes, err := json.Marshal(response) + if err != nil { + return fmt.Errorf("error marshalling get-log-file response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/hd-module/process-config.go b/adapter/hd-module/process-config.go new file mode 100644 index 0000000..7288620 --- /dev/null +++ b/adapter/hd-module/process-config.go @@ -0,0 +1,70 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config/ids" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" +) + +// Request format for `process-config` +type processConfigRequest struct { + utils.KeyedRequest + + // The config instance to process + Config map[string]any `json:"config"` +} + +// Response format for `process-config` +type processConfigResponse struct { + // A list of errors that occurred during processing, if any + Errors []string `json:"errors"` + + // A list of ports that will be exposed + Ports map[string]uint16 `json:"ports"` +} + +// Handle the `process-config` command +func processConfig(c *cli.Context) error { + // Get the request + request, err := utils.HandleKeyedRequest[*processConfigRequest](c) + if err != nil { + return err + } + + // Get the config + cfg := config.NewExampleConfig() + err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) + if err != nil { + return err + } + + // This is where any examples of validation will go when added + errors := []string{} + + // Get the open ports + ports := map[string]uint16{} + if cfg.ServerConfig.PortMode.Value != config.PortMode_Closed { + ports[ids.ServerConfigID+"/"+ids.PortModeID] = uint16(cfg.ServerConfig.Port.Value) + } + + // Create the response + response := processConfigResponse{ + Errors: errors, + Ports: ports, + } + + // Marshal it + bytes, err := json.Marshal(response) + if err != nil { + return fmt.Errorf("error marshalling process-config response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/hd-module/run.go b/adapter/hd-module/run.go new file mode 100644 index 0000000..8ce5956 --- /dev/null +++ b/adapter/hd-module/run.go @@ -0,0 +1,54 @@ +package hdmodule + +import ( + "fmt" + "os" + "strings" + + "github.com/kballard/go-shellquote" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/app" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/urfave/cli/v2" +) + +// Request format for `run` +type runRequest struct { + utils.KeyedRequest + + // The command to run + Command string `json:"command"` +} + +// Handle the `run` command +func run(c *cli.Context) error { + // Get the request + request, err := utils.HandleKeyedRequest[*runRequest](c) + if err != nil { + return err + } + + // Prevent recursive calls + if strings.HasPrefix(request.Command, "hd-module") || strings.HasPrefix(request.Command, "hd") { + return fmt.Errorf("recursive calls to `run` are not allowed") + } + + //TODO: fix whatever's breaking the key flag in the sub command (do you need to make a new app?) + // Run the app with the new command + args, err := shellquote.Split(request.Command) + args = append([]string{ + os.Args[0], // Adapter path + fmt.Sprintf("--%s", utils.KeyFileFlag.Name), + c.String(utils.KeyFileFlag.Name), + fmt.Sprintf("--%s", utils.ConfigDirFlag.Name), + c.String(utils.ConfigDirFlag.Name), + fmt.Sprintf("--%s", utils.LogDirFlag.Name), + c.String(utils.LogDirFlag.Name), + }, args...) + if err != nil { + return fmt.Errorf("error parsing command: %w", err) + } + + // Create and run a new app + app := app.CreateApp() + return app.Run(args) +} diff --git a/adapter/hd-module/set-config.go b/adapter/hd-module/set-config.go new file mode 100644 index 0000000..dc16d97 --- /dev/null +++ b/adapter/hd-module/set-config.go @@ -0,0 +1,48 @@ +package hdmodule + +import ( + "fmt" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" +) + +// Request format for `set-config` +type setConfigRequest struct { + utils.KeyedRequest + + // The config instance to process + Config map[string]any `json:"config"` +} + +// Handle the `set-config` command +func setConfig(c *cli.Context) error { + // Get the request + request, err := utils.HandleKeyedRequest[*setConfigRequest](c) + if err != nil { + return err + } + + // Get the config + cfg := config.NewExampleConfig() + err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) + if err != nil { + return err + } + + // Make a config manager + cfgMgr, err := config.NewAdapterConfigManager(c) + if err != nil { + return fmt.Errorf("error creating config manager: %w", err) + } + cfgMgr.AdapterConfig = cfg + + // Save it + err = cfgMgr.SaveConfigToDisk() + if err != nil { + return fmt.Errorf("error saving config: %w", err) + } + return nil +} diff --git a/adapter/hd-module/version.go b/adapter/hd-module/version.go new file mode 100644 index 0000000..b4fc8c8 --- /dev/null +++ b/adapter/hd-module/version.go @@ -0,0 +1,33 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + + "github.com/nodeset-org/hyperdrive-stakewise/shared" +) + +// Response format for `version` +type versionResponse struct { + // Version of the module + Version string `json:"version"` +} + +// Handle the `version` command +func version() error { + // Create the response + version := versionResponse{ + Version: shared.StakewiseVersion, + } + + // Marshal it + bytes, err := json.Marshal(version) + if err != nil { + return fmt.Errorf("error marshalling version response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/utils/consts.go b/adapter/utils/consts.go new file mode 100644 index 0000000..55d7b11 --- /dev/null +++ b/adapter/utils/consts.go @@ -0,0 +1,15 @@ +package utils + +const ( + // Name of the log file for the adapter + AdapterLogFile string = "adapter.log" + + // Service log file + ServiceLogFile string = "service.log" + + // Adapter configuration file + AdapterConfigFile string = "adapter-cfg.yaml" + + // Service configuration file + ServiceConfigFile string = "service-cfg.yaml" +) diff --git a/adapter/utils/flags.go b/adapter/utils/flags.go index de93d2e..d1e4ae0 100644 --- a/adapter/utils/flags.go +++ b/adapter/utils/flags.go @@ -79,6 +79,18 @@ var ( Usage: "The path to the secret key file for authentication", Value: "/hd/secret", } + ConfigDirFlag *cli.StringFlag = &cli.StringFlag{ + Name: "config-dir", + Aliases: []string{"c"}, + Usage: "The path to the directory for module configuration files", + Value: "/hd/config", + } + LogDirFlag *cli.StringFlag = &cli.StringFlag{ + Name: "log-dir", + Aliases: []string{"l"}, + Usage: "The path to the directory for module log files", + Value: "/hd/logs", + } ) func InstantiateFlag[FlagType cli.Flag](prototype FlagType, description string) cli.Flag { diff --git a/go.mod b/go.mod index e9f72cd..d912a76 100644 --- a/go.mod +++ b/go.mod @@ -92,6 +92,7 @@ require ( github.com/herumi/bls-eth-go-binary v1.36.1 // indirect github.com/holiman/uint256 v1.3.1 // indirect github.com/ipfs/go-cid v0.4.1 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index b6af530..7e10472 100644 --- a/go.sum +++ b/go.sum @@ -286,6 +286,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= diff --git a/shared/consts.go b/shared/consts.go new file mode 100644 index 0000000..bcf1538 --- /dev/null +++ b/shared/consts.go @@ -0,0 +1,12 @@ +package shared + +const ( + // Name of the service container + ServiceContainerName string = "hyperdrive-stakewise" + + // Name of the log file for the service + ServiceLogFile string = "service.log" + + // Default port for the server API + DefaultServerApiPort uint16 = 8080 +) From 59d722e7905b513d87d0f14b2e9b5f226f713fae Mon Sep 17 00:00:00 2001 From: huy Date: Thu, 30 Jan 2025 16:13:27 -0800 Subject: [PATCH 21/28] bump to newest config design --- adapter/config/config-manager.go | 103 ++++++ adapter/config/config.go | 127 +++++++ adapter/config/he-config.go | 336 ------------------ adapter/config/ids/ids.go | 22 +- adapter/hd-module/commands.go | 17 +- adapter/hd-module/get-config-instance.go | 45 --- adapter/hd-module/get-config-metadata.go | 2 +- ...{process-config.go => process-settings.go} | 4 +- .../{set-config.go => set-settings.go} | 4 +- adapter/hd-module/upgrade-instance.go | 92 +++++ go.mod | 11 +- go.sum | 20 +- shared/config/{ids => }/config.go | 55 ++- shared/config/settings.go | 2 +- shared/config/stakewise-config.go | 3 +- shared/config/templating.go | 2 +- shared/config/types.go | 2 +- 17 files changed, 380 insertions(+), 467 deletions(-) create mode 100644 adapter/config/config-manager.go create mode 100644 adapter/config/config.go delete mode 100644 adapter/config/he-config.go delete mode 100644 adapter/hd-module/get-config-instance.go rename adapter/hd-module/{process-config.go => process-settings.go} (95%) rename adapter/hd-module/{set-config.go => set-settings.go} (92%) create mode 100644 adapter/hd-module/upgrade-instance.go rename shared/config/{ids => }/config.go (55%) diff --git a/adapter/config/config-manager.go b/adapter/config/config-manager.go new file mode 100644 index 0000000..fae85e2 --- /dev/null +++ b/adapter/config/config-manager.go @@ -0,0 +1,103 @@ +package config + +import ( + "fmt" + "os" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config/ids" + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v3" +) + +// Configuration manager +type AdapterConfigManager struct { + // The adapter configuration + AdapterConfig *StakeWiseConfig + + // The native configuration manager + // nativeConfigManager *nativecfg.ConfigManager + + // The path to the adapter configuration file + adapterConfigPath string +} + +// Create a new configuration manager for the adapter +func NewAdapterConfigManager(c *cli.Context) (*AdapterConfigManager, error) { + // configDir := c.String(utils.ConfigDirFlag.Name) + // if configDir == "" { + // return nil, fmt.Errorf("config directory is required") + // } + // return &AdapterConfigManager{ + // // nativeConfigManager: nativecfg.NewConfigManager(filepath.Join(configDir, utils.ServiceConfigFile)), + // adapterConfigPath: filepath.Join(configDir, utils.AdapterConfigFile), + // }, nil + return nil, nil +} + +// Load the configuration from disk +func (m *AdapterConfigManager) LoadConfigFromDisk() (*StakeWiseConfig, error) { + // Load the native config + // nativeCfg, err := m.nativeConfigManager.LoadConfigFromFile() + // if err != nil { + // return nil, fmt.Errorf("error loading service config: %w", err) + // } + + // // Check if the adapter config exists + // _, err = os.Stat(m.adapterConfigPath) + // if errors.Is(err, fs.ErrNotExist) { + // return nil, nil + // } + + // Load it + bytes, err := os.ReadFile(m.adapterConfigPath) + if err != nil { + return nil, fmt.Errorf("error reading config file [%s]: %w", m.adapterConfigPath, err) + } + + // Deserialize it + cfgInstance := map[string]any{} + err = yaml.Unmarshal(bytes, &cfgInstance) + if err != nil { + return nil, fmt.Errorf("error deserializing adapter config file [%s]: %w", m.adapterConfigPath, err) + } + serverCfg := NewServerConfig() + portInt := cfgInstance[ids.PortID].(int) + serverCfg.Port.Value = uint64(portInt) + serverCfg.PortMode.Value = PortMode(cfgInstance[ids.PortModeID].(string)) + + // Merge the configs + // modCfg := ConvertToMetadata(nativeCfg) + // modCfg.ServerConfig = serverCfg + // m.AdapterConfig = modCfg + // return modCfg, nil + return nil, nil +} + +// Save the configuration to a file. If the config hasn't been loaded yet, this doesn't do anything. +func (m *AdapterConfigManager) SaveConfigToDisk() error { + if m.AdapterConfig == nil { + return nil + } + + // // Save the native config + // nativeCfg := m.AdapterConfig.ConvertToNative() + // m.nativeConfigManager.Config = nativeCfg + // err := m.nativeConfigManager.SaveConfigToFile() + // if err != nil { + // return fmt.Errorf("error saving service config: %w", err) + // } + + // Serialize the adapter config + // modCfg := hdconfig.CreateInstanceFromMetadata(m.AdapterConfig.ServerConfig) + // bytes, err := yaml.Marshal(modCfg) + // if err != nil { + // return fmt.Errorf("error serializing adapter config: %w", err) + // } + + // Write it + // err = os.WriteFile(m.adapterConfigPath, bytes, nativecfg.ConfigFileMode) + // if err != nil { + // return fmt.Errorf("error writing adapter config file [%s]: %w", m.adapterConfigPath, err) + // } + return nil +} diff --git a/adapter/config/config.go b/adapter/config/config.go new file mode 100644 index 0000000..44e6c39 --- /dev/null +++ b/adapter/config/config.go @@ -0,0 +1,127 @@ +package config + +import ( + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config/ids" + "github.com/nodeset-org/hyperdrive-stakewise/shared" + sharedconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" + + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/rocket-pool/node-manager-core/config" +) + +const ( + FloatThreshold float64 = 75.0 +) + +type PortMode string + +const ( + PortMode_Closed PortMode = "closed" + PortMode_Localhost PortMode = "localhost" + PortMode_External PortMode = "external" +) + +type StakeWiseConfig struct { + Enabled hdconfig.BoolParameter + ApiPort hdconfig.UintParameter + VerifyDepositsRoot hdconfig.BoolParameter + DaemonContainerTag hdconfig.StringParameter + OperatorContainerTag hdconfig.StringParameter + AdditionalOpFlags hdconfig.StringParameter + + VcCommon *config.ValidatorClientCommonConfig + Lighthouse *config.LighthouseVcConfig + Lodestar *config.LodestarVcConfig + Nimbus *config.NimbusVcConfig + Prysm *config.PrysmVcConfig + Teku *config.TekuVcConfig + + // Internal fields + Version string + // hdCfg *hdconfig.HyperdriveConfig + // networkSettings []*StakeWiseSettings +} + +type StakeWiseConfigSettings struct { + Enabled bool `json:"enabled"` + ApiPort uint16 `json:"apiPort"` + VerifyDepositsRoot bool `json:"verifyDepositsRoot"` + DaemonContainerTag string `json:"daemonContainerTag"` + OperatorContainerTag string `json:"operatorContainerTag"` + AdditionalOpFlags string `json:"additionalOpFlags"` + + // VcCommon *config.ValidatorClientCommonConfig `json:"vcCommon"` + // Lighthouse *config.LighthouseVcConfig `json:"lighthouse"` + // Lodestar *config.LodestarVcConfig `json:"lodestar"` + // Nimbus *config.NimbusVcConfig `json:"nimbus"` + // Prysm *config.PrysmVcConfig `json:"prysm"` + // Teku *config.TekuVcConfig `json:"teku"` + + Version string `json:"Version"` +} + +func NewStakeWiseConfig() *StakeWiseConfig { + cfg := &StakeWiseConfig{} + + cfg.Enabled.ID = hdconfig.Identifier(ids.EnabledID) + cfg.Enabled.Name = "Enabled" + cfg.Enabled.Description.Default = "Toggle for enabling the module" + cfg.Enabled.AffectedContainers = []string{shared.ServiceContainerName} + + cfg.ApiPort.ID = hdconfig.Identifier(ids.ApiPortID) + cfg.ApiPort.Name = "API Port" + cfg.ApiPort.Description.Default = "Port to run the Stakewise API server on" + cfg.ApiPort.AffectedContainers = []string{shared.ServiceContainerName} + + cfg.VerifyDepositsRoot.ID = hdconfig.Identifier(ids.VerifyDepositsRootID) + cfg.VerifyDepositsRoot.Name = "Verify Deposits Root" + cfg.VerifyDepositsRoot.Description.Default = "Toggle for verifying deposit data Merkle roots before saving" + cfg.VerifyDepositsRoot.AffectedContainers = []string{shared.ServiceContainerName} + + cfg.DaemonContainerTag.ID = hdconfig.Identifier(ids.DaemonContainerTagID) + cfg.DaemonContainerTag.Name = "Daemon Container Tag" + cfg.DaemonContainerTag.Description.Default = "The Docker Hub tag for the Stakewise daemon" + cfg.DaemonContainerTag.AffectedContainers = []string{shared.ServiceContainerName} + + cfg.OperatorContainerTag.ID = hdconfig.Identifier(ids.OperatorContainerTagID) + cfg.OperatorContainerTag.Name = "Operator Container Tag" + cfg.OperatorContainerTag.Description.Default = "The Docker Hub tag for the Stakewise operator" + cfg.OperatorContainerTag.AffectedContainers = []string{shared.ServiceContainerName} + + cfg.AdditionalOpFlags.ID = hdconfig.Identifier(ids.AdditionalOpFlagsID) + cfg.AdditionalOpFlags.Name = "Additional Operator Flags" + cfg.AdditionalOpFlags.Description.Default = "Custom command line flags" + cfg.AdditionalOpFlags.AffectedContainers = []string{shared.ServiceContainerName} + + return cfg +} + +func (cfg StakeWiseConfig) GetParameters() []hdconfig.IParameter { + return []hdconfig.IParameter{ + &cfg.Enabled, + &cfg.ApiPort, + &cfg.VerifyDepositsRoot, + &cfg.DaemonContainerTag, + &cfg.OperatorContainerTag, + &cfg.AdditionalOpFlags, + } +} + +func (cfg StakeWiseConfig) GetSections() []hdconfig.ISection { + return []hdconfig.ISection{} +} + +func CreateInstanceFromNativeConfig(native *sharedconfig.NativeStakeWiseConfigSettings) *StakeWiseConfigSettings { + instance := &StakeWiseConfigSettings{ + Enabled: native.Enabled, + ApiPort: native.ApiPort, + } + return instance +} + +func ConvertInstanceToNativeConfig(instance *StakeWiseConfigSettings) *sharedconfig.NativeStakeWiseConfigSettings { + native := &sharedconfig.NativeStakeWiseConfigSettings{ + ApiPort: instance.ApiPort, + } + return native +} diff --git a/adapter/config/he-config.go b/adapter/config/he-config.go deleted file mode 100644 index 98037fe..0000000 --- a/adapter/config/he-config.go +++ /dev/null @@ -1,336 +0,0 @@ -package config - -import ( - "fmt" - "os" - - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config/ids" - nativecfg "github.com/nodeset-org/hyperdrive-stakewise/shared/config/ids" - hdconfig "github.com/nodeset-org/hyperdrive/modules/config" - "github.com/urfave/cli/v2" - "gopkg.in/yaml.v3" -) - -const ( - FloatThreshold float64 = 75.0 -) - -type PortMode string - -const ( - PortMode_Closed PortMode = "closed" - PortMode_Localhost PortMode = "localhost" - PortMode_External PortMode = "external" -) - -type ExampleConfig struct { - // ExampleBool hdconfig.BoolParameterMetadata - - // ExampleInt hdconfig.IntParameterMetadata - - // ExampleUint hdconfig.UintParameterMetadata - - // ExampleFloat hdconfig.FloatParameterMetadata - - // ExampleString hdconfig.StringParameterMetadata - - // ExampleChoice hdconfig.ChoiceParameterMetadata[nativecfg.ExampleOption] - - SubConfig *SubConfig - - ServerConfig *ServerConfig -} - -func NewExampleConfig() *ExampleConfig { - cfg := &ExampleConfig{} - - // // ExampleBool - // cfg.ExampleBool.ID = hdconfig.Identifier(ids.ExampleBoolID) - // cfg.ExampleBool.Name = "Example Boolean" - // cfg.ExampleBool.Description.Default = "This is an example of a boolean parameter. It doesn't directly affect the service, but it does control the behavior of some other config parameters." - // cfg.ExampleBool.AffectedContainers = []string{shared.ServiceContainerName} - // cfg.ExampleBool.Value = cfg.ExampleBool.Default - - // // ExampleInt - // cfg.ExampleInt.ID = hdconfig.Identifier(ids.ExampleIntID) - // cfg.ExampleInt.Name = "Example Integer" - // cfg.ExampleInt.Description.Default = "This is an example of an integer parameter." - // cfg.ExampleInt.AffectedContainers = []string{shared.ServiceContainerName} - // cfg.ExampleInt.Value = cfg.ExampleInt.Default - - // // ExampleUint - // cfg.ExampleUint.ID = hdconfig.Identifier(ids.ExampleUintID) - // cfg.ExampleUint.Name = "Example Unsigned Integer" - // cfg.ExampleUint.Description.Default = "This is an example of an unsigned integer parameter." - // cfg.ExampleUint.AffectedContainers = []string{shared.ServiceContainerName} - // cfg.ExampleUint.Value = cfg.ExampleUint.Default - - // // ExampleFloat - // cfg.ExampleFloat.ID = hdconfig.Identifier(ids.ExampleFloatID) - // cfg.ExampleFloat.Name = "Example Float" - // cfg.ExampleFloat.Description.Default = "This is an example of a float parameter with a minimum and maximum set." - // cfg.ExampleFloat.Default = 50 - // cfg.ExampleFloat.MinValue = 0.0 - // cfg.ExampleFloat.MaxValue = 100.0 - // cfg.ExampleFloat.Value = cfg.ExampleFloat.Default - // cfg.ExampleFloat.AffectedContainers = []string{shared.ServiceContainerName} - - // // ExampleString - // cfg.ExampleString.ID = hdconfig.Identifier(ids.ExampleStringID) - // cfg.ExampleString.Name = "Example String" - // cfg.ExampleString.Description.Default = "This is an example of a string parameter. It has a max length and regex pattern set." - // cfg.ExampleString.MaxLength = 10 - // cfg.ExampleString.Regex = "^[a-zA-Z]*$" - // cfg.ExampleString.Value = cfg.ExampleString.Default - // cfg.ExampleString.AffectedContainers = []string{shared.ServiceContainerName} - - // // Options for ExampleChoice - // options := make([]hdconfig.ParameterMetadataOption[nativecfg.ExampleOption], 3) - // options[0].Name = "One" - // options[0].Description.Default = "This is the first option." - // options[0].Value = nativecfg.ExampleOption_One - - // thresholdString := strconv.FormatFloat(FloatThreshold, 'f', -1, 64) - // options[1].Name = "Two" - // options[1].Description.Default = "This is the second option. It is hidden when ExampleFloat is less than " + thresholdString + "." - // options[1].Description.Template = fmt.Sprintf("{{if lt .GetValue %s %s}}This option is hidden because the float is less than %s.{{else}}This option is visible because the float is greater than or equal to %s.{{end}}", ids.ExampleFloatID, thresholdString, thresholdString, thresholdString) - // options[1].Value = nativecfg.ExampleOption_Two - // options[1].Disabled.Default = true - // options[1].Disabled.Template = "{{if eq .GetValue " + ids.ExampleBoolID + " true}}false{{else}}{{.UseDefault}}{{end}}" - - // options[2].Name = "Three" - // options[2].Description.Default = "This is the third option." - // options[2].Value = nativecfg.ExampleOption_Three - - // // ExampleChoice - // cfg.ExampleChoice.ID = hdconfig.Identifier(ids.ExampleChoiceID) - // cfg.ExampleChoice.Name = "Example Choice" - // cfg.ExampleChoice.Description.Default = "This is an example of a choice parameter between multiple options." - // cfg.ExampleChoice.Options = options - // cfg.ExampleChoice.Default = options[0].Value - // cfg.ExampleChoice.Value = cfg.ExampleChoice.Default - // cfg.ExampleChoice.AffectedContainers = []string{} - - // Subconfigs - cfg.SubConfig = NewSubConfig() - cfg.ServerConfig = NewServerConfig() - - return cfg -} - -type SubConfig struct { - hdconfig.SectionMetadataHeader - - SubExampleBool hdconfig.BoolParameterMetadata - - SubExampleChoice hdconfig.ChoiceParameterMetadata[nativecfg.ExampleOption] -} - -func NewSubConfig() *SubConfig { - cfg := &SubConfig{} - // cfg.ID = hdconfig.Identifier(ids.SubConfigID) - // cfg.Name = "Sub Config" - // cfg.Description.Default = "This is a sub-section of the main configuration." - // cfg.Hidden.Default = true - // cfg.Hidden.Template = "{{if eq .GetValue " + ids.ExampleBoolID + " true}}false{{else}}true{{end}}" - - // // SubExampleBool - // cfg.SubExampleBool.ID = hdconfig.Identifier(ids.SubExampleBoolID) - // cfg.SubExampleBool.Name = "Sub Example Boolean" - // cfg.SubExampleBool.Description.Default = "This is an example of a boolean parameter in a sub-section." - // cfg.SubExampleBool.Value = cfg.SubExampleBool.Default - - // // Options for SubExampleChoice - // options := make([]hdconfig.ParameterMetadataOption[nativecfg.ExampleOption], 2) - // options[0].Name = "One" - // options[0].Description.Default = "This is the first option." - // options[0].Value = nativecfg.ExampleOption_One - - // options[1].Name = "Two" - // options[1].Description.Default = "This is the second option." - // options[1].Value = nativecfg.ExampleOption_Two - - // // SubExampleChoice - // cfg.SubExampleChoice.ID = hdconfig.Identifier(ids.SubExampleChoiceID) - // cfg.SubExampleChoice.Name = "Sub Example Choice" - // cfg.SubExampleChoice.Description.Default = "This is an example of a choice parameter between multiple options in a sub-section." - // cfg.SubExampleChoice.Options = options - // cfg.SubExampleChoice.Default = options[1].Value - // cfg.SubExampleChoice.Value = cfg.SubExampleChoice.Default - - return cfg -} - -type ServerConfig struct { - hdconfig.SectionMetadataHeader - - Port hdconfig.UintParameterMetadata - - PortMode hdconfig.ChoiceParameterMetadata[PortMode] -} - -func NewServerConfig() *ServerConfig { - cfg := &ServerConfig{} - // cfg.ID = hdconfig.Identifier(ids.ServerConfigID) - // cfg.Name = "Service Config" - // cfg.Description.Default = "This is the configuration for the module's service. This isn't used by the service directly, but it is used by Hyperdrive itself in the service's Docker Compose file template to configure the service during its starting process." - - // // Port - // cfg.Port.ID = hdconfig.Identifier(ids.PortID) - // cfg.Port.Name = "API Port" - // cfg.Port.Description.Default = "This is the API port the server should run on." - // cfg.Port.Default = uint64(shared.DefaultServerApiPort) - // cfg.Port.MinValue = 0 - // cfg.Port.MaxValue = 65535 - // cfg.Port.Value = cfg.Port.Default - // cfg.Port.AffectedContainers = []string{shared.ServiceContainerName} - - // // Options for PortMode - // options := make([]hdconfig.ParameterMetadataOption[PortMode], 3) - // options[0].Name = "Closed" - // options[0].Description.Default = "The API is only accessible to internal Docker container traffic." - // options[0].Value = PortMode_Closed - - // options[1].Name = "Localhost Only" - // options[1].Description.Default = "The API is accessible from internal Docker containers and your own local machine, but no other external machines." - // options[1].Value = PortMode_Localhost - - // options[2].Name = "All External Traffic" - // options[2].Description.Default = "The port is accessible to everything, including external machines.\n\n[orange]Use with caution!" - // options[2].Value = PortMode_External - - // // PortMode - // cfg.PortMode.ID = hdconfig.Identifier(ids.PortModeID) - // cfg.PortMode.Name = "Expose API Port" - // cfg.PortMode.Description.Default = "Determine how the server's HTTP API restricts its access from various sources." - // cfg.PortMode.Options = options - // cfg.PortMode.Default = options[0].Value - // cfg.PortMode.Value = cfg.PortMode.Default - // cfg.PortMode.AffectedContainers = []string{shared.ServiceContainerName} - - return cfg -} - -func ConvertToMetadata(native *nativecfg.NativeExampleConfig) *ExampleConfig { - cfg := NewExampleConfig() - - // cfg.ExampleBool.Value = native.ExampleBool - // cfg.ExampleInt.Value = native.ExampleInt - // cfg.ExampleUint.Value = native.ExampleUint - // cfg.ExampleFloat.Value = native.ExampleFloat - // cfg.ExampleString.Value = native.ExampleString - // cfg.ExampleChoice.Value = native.ExampleChoice - - cfg.SubConfig.SubExampleBool.Value = native.SubConfig.SubExampleBool - cfg.SubConfig.SubExampleChoice.Value = native.SubConfig.SubExampleChoice - - return cfg -} - -func (cfg *ExampleConfig) ConvertToNative() *nativecfg.NativeExampleConfig { - // native := &nativecfg.NativeExampleConfig{} - - // native.ExampleBool = cfg.ExampleBool.Value - // native.ExampleInt = cfg.ExampleInt.Value - // native.ExampleUint = cfg.ExampleUint.Value - // native.ExampleFloat = cfg.ExampleFloat.Value - // native.ExampleString = cfg.ExampleString.Value - // native.ExampleChoice = cfg.ExampleChoice.Value - // native.SubConfig.SubExampleBool = cfg.SubConfig.SubExampleBool.Value - // native.SubConfig.SubExampleChoice = cfg.SubConfig.SubExampleChoice.Value - // return native - return nil -} - -// Configuration manager -type AdapterConfigManager struct { - // The adapter configuration - AdapterConfig *ExampleConfig - - // The native configuration manager - // nativeConfigManager *nativecfg.ConfigManager - - // The path to the adapter configuration file - adapterConfigPath string -} - -// Create a new configuration manager for the adapter -func NewAdapterConfigManager(c *cli.Context) (*AdapterConfigManager, error) { - // configDir := c.String(utils.ConfigDirFlag.Name) - // if configDir == "" { - // return nil, fmt.Errorf("config directory is required") - // } - // return &AdapterConfigManager{ - // // nativeConfigManager: nativecfg.NewConfigManager(filepath.Join(configDir, utils.ServiceConfigFile)), - // adapterConfigPath: filepath.Join(configDir, utils.AdapterConfigFile), - // }, nil - return nil, nil -} - -// Load the configuration from disk -func (m *AdapterConfigManager) LoadConfigFromDisk() (*ExampleConfig, error) { - // Load the native config - // nativeCfg, err := m.nativeConfigManager.LoadConfigFromFile() - // if err != nil { - // return nil, fmt.Errorf("error loading service config: %w", err) - // } - - // // Check if the adapter config exists - // _, err = os.Stat(m.adapterConfigPath) - // if errors.Is(err, fs.ErrNotExist) { - // return nil, nil - // } - - // Load it - bytes, err := os.ReadFile(m.adapterConfigPath) - if err != nil { - return nil, fmt.Errorf("error reading config file [%s]: %w", m.adapterConfigPath, err) - } - - // Deserialize it - cfgInstance := map[string]any{} - err = yaml.Unmarshal(bytes, &cfgInstance) - if err != nil { - return nil, fmt.Errorf("error deserializing adapter config file [%s]: %w", m.adapterConfigPath, err) - } - serverCfg := NewServerConfig() - portInt := cfgInstance[ids.PortID].(int) - serverCfg.Port.Value = uint64(portInt) - serverCfg.PortMode.Value = PortMode(cfgInstance[ids.PortModeID].(string)) - - // Merge the configs - // modCfg := ConvertToMetadata(nativeCfg) - // modCfg.ServerConfig = serverCfg - // m.AdapterConfig = modCfg - // return modCfg, nil - return nil, nil -} - -// Save the configuration to a file. If the config hasn't been loaded yet, this doesn't do anything. -func (m *AdapterConfigManager) SaveConfigToDisk() error { - if m.AdapterConfig == nil { - return nil - } - - // // Save the native config - // nativeCfg := m.AdapterConfig.ConvertToNative() - // m.nativeConfigManager.Config = nativeCfg - // err := m.nativeConfigManager.SaveConfigToFile() - // if err != nil { - // return fmt.Errorf("error saving service config: %w", err) - // } - - // Serialize the adapter config - // modCfg := hdconfig.CreateInstanceFromMetadata(m.AdapterConfig.ServerConfig) - // bytes, err := yaml.Marshal(modCfg) - // if err != nil { - // return fmt.Errorf("error serializing adapter config: %w", err) - // } - - // Write it - // err = os.WriteFile(m.adapterConfigPath, bytes, nativecfg.ConfigFileMode) - // if err != nil { - // return fmt.Errorf("error writing adapter config file [%s]: %w", m.adapterConfigPath, err) - // } - return nil -} diff --git a/adapter/config/ids/ids.go b/adapter/config/ids/ids.go index 90fd979..7c91afd 100644 --- a/adapter/config/ids/ids.go +++ b/adapter/config/ids/ids.go @@ -1,16 +1,14 @@ package ids const ( - ExampleBoolID string = "exampleBool" - ExampleIntID string = "exampleInt" - ExampleUintID string = "exampleUint" - ExampleFloatID string = "exampleFloat" - ExampleStringID string = "exampleString" - ExampleChoiceID string = "exampleChoice" - SubConfigID string = "subConfig" - SubExampleBoolID string = "subConfigBool" - SubExampleChoiceID string = "subConfigChoice" - ServerConfigID string = "server" - PortModeID string = "portMode" - PortID string = "port" + PortID = "port" + PortModeID = "portMode" + ServerConfigID = "serverConfig" + + EnabledID = "enabled" + ApiPortID = "apiPort" + VerifyDepositsRootID = "verifyDepositsRoot" + DaemonContainerTagID = "daemonContainerTag" + OperatorContainerTagID = "operatorContainerTag" + AdditionalOpFlagsID = "additionalOpFlags" ) diff --git a/adapter/hd-module/commands.go b/adapter/hd-module/commands.go index 457a24f..d62b1da 100644 --- a/adapter/hd-module/commands.go +++ b/adapter/hd-module/commands.go @@ -51,19 +51,6 @@ func RegisterCommands(app *cli.App) { return getConfigMetadata(c) }, }, - { - Name: "get-config-instance", - Aliases: []string{"i"}, - Flags: []cli.Flag{}, - Usage: "Get the instance of the module's configuration, with the values all set.", - Action: func(c *cli.Context) error { - // Validate args - utils.ValidateArgCount(c, 0) - - // Run - return getConfigInstance(c) - }, - }, { Name: "process-config", Aliases: []string{"p"}, @@ -74,7 +61,7 @@ func RegisterCommands(app *cli.App) { utils.ValidateArgCount(c, 0) // Run - return processConfig(c) + return processSetting(c) }, }, { @@ -87,7 +74,7 @@ func RegisterCommands(app *cli.App) { utils.ValidateArgCount(c, 0) // Run - return setConfig(c) + return setSettings(c) }, }, { diff --git a/adapter/hd-module/get-config-instance.go b/adapter/hd-module/get-config-instance.go deleted file mode 100644 index 5f36636..0000000 --- a/adapter/hd-module/get-config-instance.go +++ /dev/null @@ -1,45 +0,0 @@ -package hdmodule - -import ( - "fmt" - - "github.com/goccy/go-json" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" - hdconfig "github.com/nodeset-org/hyperdrive/modules/config" - "github.com/urfave/cli/v2" -) - -func getConfigInstance(c *cli.Context) error { - // Get the request - _, err := utils.HandleKeyedRequest[*utils.KeyedRequest](c) - if err != nil { - return err - } - - // Get the config - cfgMgr, err := config.NewAdapterConfigManager(c) - if err != nil { - return fmt.Errorf("error creating config manager: %w", err) - } - cfg, err := cfgMgr.LoadConfigFromDisk() - if err != nil { - return fmt.Errorf("error loading config: %w", err) - } - - // Handle no saved config - if cfg == nil { - return fmt.Errorf("config has not been initialized yet") - } - - // Create the response - instance := hdconfig.CreateInstanceFromMetadata(cfg) - bytes, err := json.Marshal(instance) - if err != nil { - return fmt.Errorf("error marshalling config: %w", err) - } - - // Print it - fmt.Println(string(bytes)) - return nil -} diff --git a/adapter/hd-module/get-config-metadata.go b/adapter/hd-module/get-config-metadata.go index 9bc7f84..a74b397 100644 --- a/adapter/hd-module/get-config-metadata.go +++ b/adapter/hd-module/get-config-metadata.go @@ -30,7 +30,7 @@ func getConfigMetadata(c *cli.Context) error { // Handle no config file by using the default if cfg == nil { - cfg = config.NewExampleConfig() + cfg = config.NewStakeWiseConfig() } // Create the response diff --git a/adapter/hd-module/process-config.go b/adapter/hd-module/process-settings.go similarity index 95% rename from adapter/hd-module/process-config.go rename to adapter/hd-module/process-settings.go index 7288620..2d8130f 100644 --- a/adapter/hd-module/process-config.go +++ b/adapter/hd-module/process-settings.go @@ -29,7 +29,7 @@ type processConfigResponse struct { } // Handle the `process-config` command -func processConfig(c *cli.Context) error { +func processSetting(c *cli.Context) error { // Get the request request, err := utils.HandleKeyedRequest[*processConfigRequest](c) if err != nil { @@ -37,7 +37,7 @@ func processConfig(c *cli.Context) error { } // Get the config - cfg := config.NewExampleConfig() + cfg := config.NewStakeWiseConfig() err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) if err != nil { return err diff --git a/adapter/hd-module/set-config.go b/adapter/hd-module/set-settings.go similarity index 92% rename from adapter/hd-module/set-config.go rename to adapter/hd-module/set-settings.go index dc16d97..1a085b0 100644 --- a/adapter/hd-module/set-config.go +++ b/adapter/hd-module/set-settings.go @@ -18,7 +18,7 @@ type setConfigRequest struct { } // Handle the `set-config` command -func setConfig(c *cli.Context) error { +func setSettings(c *cli.Context) error { // Get the request request, err := utils.HandleKeyedRequest[*setConfigRequest](c) if err != nil { @@ -26,7 +26,7 @@ func setConfig(c *cli.Context) error { } // Get the config - cfg := config.NewExampleConfig() + cfg := config.NewStakeWiseConfig() err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) if err != nil { return err diff --git a/adapter/hd-module/upgrade-instance.go b/adapter/hd-module/upgrade-instance.go new file mode 100644 index 0000000..886fb0b --- /dev/null +++ b/adapter/hd-module/upgrade-instance.go @@ -0,0 +1,92 @@ +package hdmodule + +import ( + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + modconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" +) + +// Request format for `upgrade-instance` +type upgradeInstanceRequest struct { + utils.KeyedRequest + + // The current config instance + Instance *modconfig.ModuleInstance `json:"instance"` +} + +// Handle the `upgrade-instance` command +func upgradeInstance(c *cli.Context) error { + return nil + // // Get the request + // request, err := utils.HandleKeyedRequest[*upgradeInstanceRequest](c) + // if err != nil { + // return err + // } + // modInstance := request.Instance + + // // Switch on the instance version + // var settings config.HyperdriveEthereumConfigSettings + // v0_2_0 := semver.MustParse("0.2.0") + // version := semver.MustParse(modInstance.Version) + // if version.LT(v0_2_0) { + // // Upgrade from 0.1.0 to the latest + // oldSettings, err := deserializeSettings_v0_1_0(modInstance.Settings) + // if err != nil { + // return fmt.Errorf("error deserializing settings: %w", err) + // } + // settings = config.UpgradeSettings(oldSettings) + // } else { + // // Deserialize the settings + // settings, err = deserializeSettings_Latest(modInstance.Settings) + // if err != nil { + // return fmt.Errorf("error deserializing settings: %w", err) + // } + // } + + // // Create the response + // response := modconfig.ModuleInstance{ + // Enabled: modInstance.Enabled, + // Version: shared.HyperdriveEthereumVersion, + // } + // response.SetSettingsFromKnownType(settings) + + // // Marshal it + // bytes, err := json.Marshal(response) + // if err != nil { + // return fmt.Errorf("error marshalling upgrade-instance response: %w", err) + // } + + // // Print it + // fmt.Println(string(bytes)) + // return nil +} + +// Deserialize the settings for a v0.1.0 configuration +// func deserializeSettings_v0_1_0(settings map[string]any) (*v0_1_0.ExampleConfigSettings, error) { +// bytes, err := json.Marshal(settings) +// if err != nil { +// return nil, fmt.Errorf("error marshalling settings: %w", err) +// } + +// var cfg v0_1_0.ExampleConfigSettings +// err = json.Unmarshal(bytes, &cfg) +// if err != nil { +// return nil, fmt.Errorf("error unmarshalling settings: %w", err) +// } +// return &cfg, nil +// } + +// Deserialize the settings for the latest configuration +// func deserializeSettings_Latest(settings map[string]any) (*config.ExampleConfigSettings, error) { +// bytes, err := json.Marshal(settings) +// if err != nil { +// return nil, fmt.Errorf("error marshalling settings: %w", err) +// } + +// var cfg config.ExampleConfigSettings +// err = json.Unmarshal(bytes, &cfg) +// if err != nil { +// return nil, fmt.Errorf("error unmarshalling settings: %w", err) +// } +// return &cfg, nil +// } diff --git a/go.mod b/go.mod index d912a76..0e4243b 100644 --- a/go.mod +++ b/go.mod @@ -11,20 +11,22 @@ require ( github.com/goccy/go-json v0.10.3 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mitchellh/go-homedir v1.1.0 - github.com/nodeset-org/hyperdrive v1.1.2-0.20250106215109-78519b2bef69 + github.com/nodeset-org/hyperdrive v1.1.2-0.20250127074045-2c796103a4fc github.com/nodeset-org/hyperdrive-daemon v1.1.1 github.com/nodeset-org/nodeset-client-go v1.2.2 github.com/nodeset-org/osha v0.3.1 github.com/rocket-pool/batch-query v1.0.0 github.com/rocket-pool/node-manager-core v0.5.2-0.20241029172412-6cb22253be3f github.com/stretchr/testify v1.9.0 - github.com/urfave/cli/v2 v2.27.2 + github.com/urfave/cli/v2 v2.27.5 github.com/wealdtech/go-eth2-types/v2 v2.8.2 github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1 golang.org/x/sync v0.7.0 golang.org/x/term v0.22.0 gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -36,6 +38,7 @@ require ( github.com/Microsoft/hcsshim v0.12.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect github.com/btcsuite/btcd v0.24.2 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/btcsuite/btcd/btcutil v1.1.5 // indirect @@ -54,7 +57,7 @@ require ( github.com/containerd/platforms v0.2.1 // indirect github.com/containerd/ttrpc v1.2.5 // indirect github.com/containerd/typeurl/v2 v2.2.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -92,7 +95,6 @@ require ( github.com/herumi/bls-eth-go-binary v1.36.1 // indirect github.com/holiman/uint256 v1.3.1 // indirect github.com/ipfs/go-cid v0.4.1 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -179,7 +181,6 @@ require ( google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 7e10472..482ae09 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -35,6 +35,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= @@ -111,8 +113,8 @@ github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oL github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso= github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= @@ -379,10 +381,8 @@ github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOEL github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nodeset-org/hyperdrive v1.1.1 h1:PImbcczTqGpx0UPJugQIZkxzlWMJn9O5+138mOrbeKA= -github.com/nodeset-org/hyperdrive v1.1.1/go.mod h1:BId3aaponZ2gPeNReLIdB+AAK0sDOKUxV3xAQQrs4fg= -github.com/nodeset-org/hyperdrive v1.1.2-0.20250106215109-78519b2bef69 h1:FJojeBJ3vr7hsIaKzZJDKYPAvxT+kYPUX3ratal9Kmc= -github.com/nodeset-org/hyperdrive v1.1.2-0.20250106215109-78519b2bef69/go.mod h1:nBfN+6oGRe91ZmMrfoQcGCiSGj1ryKfwN5Ap6UfeqwA= +github.com/nodeset-org/hyperdrive v1.1.2-0.20250127074045-2c796103a4fc h1:qBuA2WuUzPWo1eHIhw5SV7qIV31cvNJmudJucqkoxVk= +github.com/nodeset-org/hyperdrive v1.1.2-0.20250127074045-2c796103a4fc/go.mod h1:cqgwg+wtWY1VIkVeLjBz/FOpN7iPvwS8RMAKwwnfDuY= github.com/nodeset-org/hyperdrive-daemon v1.1.1 h1:aUQzEKmyuYhVTzn3AQEIdUm335xBYmsCTJfPdGNA5r0= github.com/nodeset-org/hyperdrive-daemon v1.1.1/go.mod h1:tzqdqfRR4Xcy4GtdSiEnfjOFj3+g4TvuZJpYljCQEBM= github.com/nodeset-org/nodeset-client-go v1.2.2 h1:mjJnH5uw/CF5TWdRxdU5+Cw/ERRLz4hl9VsRFVO0Wdo= @@ -506,8 +506,8 @@ github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24sz github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10 h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg= github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10/go.mod h1:x/Pa0FF5Te9kdrlZKJK82YmAkvL8+f989USgz6Jiw7M= -github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= -github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= github.com/wealdtech/go-bytesutil v1.2.1 h1:TjuRzcG5KaPwaR5JB7L/OgJqMQWvlrblA1n0GfcXFSY= diff --git a/shared/config/ids/config.go b/shared/config/config.go similarity index 55% rename from shared/config/ids/config.go rename to shared/config/config.go index bb4a7d7..d560655 100644 --- a/shared/config/ids/config.go +++ b/shared/config/config.go @@ -1,4 +1,4 @@ -package ids +package config import ( "errors" @@ -13,43 +13,28 @@ const ( ConfigFileMode os.FileMode = 0644 ) -// Example of a choice option, like an enum -type ExampleOption string - -const ( - ExampleOption_One ExampleOption = "one" - ExampleOption_Two ExampleOption = "two" - ExampleOption_Three ExampleOption = "three" -) - -// Example of a configuration for a service -type NativeExampleConfig struct { - ExampleBool bool `json:"exampleBool" yaml:"exampleBool"` - - ExampleInt int64 `json:"exampleInt" yaml:"exampleInt"` - - ExampleUint uint64 `json:"exampleUint" yaml:"exampleUint"` - - ExampleFloat float64 `json:"exampleFloat" yaml:"exampleFloat"` - - ExampleString string `json:"exampleString" yaml:"exampleString"` - - ExampleChoice ExampleOption `json:"exampleChoice" yaml:"exampleChoice"` - - SubConfig NativeSubConfig `json:"subConfig" yaml:"subConfig"` -} - -// Example of a section under the service's top-level configuration -type NativeSubConfig struct { - SubExampleBool bool `json:"subExampleBool" yaml:"subExampleBool"` - - SubExampleChoice ExampleOption `json:"subExampleChoice" yaml:"subExampleChoice"` +type NativeStakeWiseConfigSettings struct { + Enabled bool `json:"enabled"` + ApiPort uint16 `json:"apiPort"` + VerifyDepositsRoot bool `json:"verifyDepositsRoot"` + DaemonContainerTag string `json:"daemonContainerTag"` + OperatorContainerTag string `json:"operatorContainerTag"` + AdditionalOpFlags string `json:"additionalOpFlags"` + + // VcCommon *config.ValidatorClientCommonConfig `json:"vcCommon"` + // Lighthouse *config.LighthouseVcConfig `json:"lighthouse"` + // Lodestar *config.LodestarVcConfig `json:"lodestar"` + // Nimbus *config.NimbusVcConfig `json:"nimbus"` + // Prysm *config.PrysmVcConfig `json:"prysm"` + // Teku *config.TekuVcConfig `json:"teku"` + + Version string `json:"Version"` } // Configuration manager type ConfigManager struct { // The configuration - Config *NativeExampleConfig + Config *NativeStakeWiseConfigSettings // The path to the configuration file ConfigPath string @@ -63,7 +48,7 @@ func NewConfigManager(path string) *ConfigManager { } // Load the configuration from a file -func (m *ConfigManager) LoadConfigFromFile() (*NativeExampleConfig, error) { +func (m *ConfigManager) LoadConfigFromFile() (*NativeStakeWiseConfigSettings, error) { // Check if the file exists _, err := os.Stat(m.ConfigPath) if errors.Is(err, fs.ErrNotExist) { @@ -77,7 +62,7 @@ func (m *ConfigManager) LoadConfigFromFile() (*NativeExampleConfig, error) { } // Deserialize it - cfg := NativeExampleConfig{} + cfg := NativeStakeWiseConfigSettings{} err = yaml.Unmarshal(bytes, &cfg) if err != nil { return nil, fmt.Errorf("error deserializing config file [%s]: %w", m.ConfigPath, err) diff --git a/shared/config/settings.go b/shared/config/settings.go index b3eeb26..e1526bc 100644 --- a/shared/config/settings.go +++ b/shared/config/settings.go @@ -1,4 +1,4 @@ -package swconfig +package config const ( ModuleName string = "stakewise" diff --git a/shared/config/stakewise-config.go b/shared/config/stakewise-config.go index ce1a6fa..c4fb76c 100644 --- a/shared/config/stakewise-config.go +++ b/shared/config/stakewise-config.go @@ -1,5 +1,6 @@ -package swconfig +package config +// TODO(HN): REMOVE ALL THIS import ( "fmt" diff --git a/shared/config/templating.go b/shared/config/templating.go index c08b8ef..852e63e 100644 --- a/shared/config/templating.go +++ b/shared/config/templating.go @@ -1,4 +1,4 @@ -package swconfig +package config import ( "fmt" diff --git a/shared/config/types.go b/shared/config/types.go index da4311a..a2449d4 100644 --- a/shared/config/types.go +++ b/shared/config/types.go @@ -1,4 +1,4 @@ -package swconfig +package config import ( "github.com/rocket-pool/node-manager-core/config" From 703bb0958cafbefd97d2a727d70172e6386dcf0d Mon Sep 17 00:00:00 2001 From: huy Date: Fri, 31 Jan 2025 08:25:22 -0800 Subject: [PATCH 22/28] clean up config --- client/client.go | 15 +- client/global-config.go | 44 +-- shared/config/stakewise-config.go | 588 +++++++++++++++--------------- shared/config/templating.go | 200 +++++----- stakewise-daemon.go | 5 +- 5 files changed, 404 insertions(+), 448 deletions(-) diff --git a/client/client.go b/client/client.go index a06f343..9fa52e3 100644 --- a/client/client.go +++ b/client/client.go @@ -171,11 +171,8 @@ func (c *HyperdriveClient) LoadConfig() (*GlobalConfig, bool, error) { if err != nil { return nil, false, fmt.Errorf("error creating Hyperdrive config: %w", err) } - swCfg, err := swconfig.NewStakeWiseConfig(hdCfg, c.Context.StakeWiseNetworkSettings) - if err != nil { - return nil, false, fmt.Errorf("error creating StakeWise config: %w", err) - } - c.cfg, err = NewGlobalConfig(hdCfg, c.Context.HyperdriveNetworkSettings, swCfg, c.Context.StakeWiseNetworkSettings) + + c.cfg, err = NewGlobalConfig(hdCfg, c.Context.HyperdriveNetworkSettings) if err != nil { return nil, false, fmt.Errorf("error creating global config: %w", err) } @@ -225,14 +222,8 @@ func LoadConfigFromFile(configPath string, hdSettings []*hdconfig.HyperdriveSett return nil, err } - // Get the StakeWise config - swCfg, err := swconfig.NewStakeWiseConfig(hdCfg, swSettings) - if err != nil { - return nil, err - } - // Load the module configs - cfg, err := NewGlobalConfig(hdCfg, hdSettings, swCfg, swSettings) + cfg, err := NewGlobalConfig(hdCfg, hdSettings) if err != nil { return nil, fmt.Errorf("error creating global configuration: %w", err) } diff --git a/client/global-config.go b/client/global-config.go index 768f878..d0ce782 100644 --- a/client/global-config.go +++ b/client/global-config.go @@ -1,16 +1,15 @@ package swclient +// TODO: Talk to Joe about removing this entirely import ( "errors" "fmt" "io/fs" "os" "path/filepath" - "reflect" "github.com/alessio/shellescape" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" - swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" "gopkg.in/yaml.v2" ) @@ -21,10 +20,6 @@ type GlobalConfig struct { // Hyperdrive Hyperdrive *hdconfig.HyperdriveConfig HyperdriveResources *hdconfig.MergedResources - - // StakeWise - StakeWise *swconfig.StakeWiseConfig - StakeWiseResources *swconfig.StakeWiseResources } // Serialize the config and all modules @@ -34,9 +29,7 @@ func (c *GlobalConfig) Serialize() map[string]any { // Get the configs for all of the modules in the system func (c *GlobalConfig) GetAllModuleConfigs() []hdconfig.IModuleConfig { - return []hdconfig.IModuleConfig{ - c.StakeWise, - } + return []hdconfig.IModuleConfig{} } // Saves a config @@ -103,11 +96,10 @@ func SaveConfig(cfg *GlobalConfig, directory string, filename string) error { } // Make a new global config -func NewGlobalConfig(hdCfg *hdconfig.HyperdriveConfig, hdSettings []*hdconfig.HyperdriveSettings, swCfg *swconfig.StakeWiseConfig, swSettings []*swconfig.StakeWiseSettings) (*GlobalConfig, error) { +func NewGlobalConfig(hdCfg *hdconfig.HyperdriveConfig, hdSettings []*hdconfig.HyperdriveSettings) (*GlobalConfig, error) { // Make the config cfg := &GlobalConfig{ Hyperdrive: hdCfg, - StakeWise: swCfg, } // Get the HD resources @@ -124,39 +116,11 @@ func NewGlobalConfig(hdCfg *hdconfig.HyperdriveConfig, hdSettings []*hdconfig.Hy if cfg.HyperdriveResources == nil { return nil, fmt.Errorf("could not find hyperdrive resources for network [%s]", network) } - - // Get the StakeWise resources - for _, setting := range swSettings { - if setting.Key == network { - cfg.StakeWiseResources = setting.StakeWiseResources - break - } - } - if cfg.StakeWiseResources == nil { - return nil, fmt.Errorf("could not find stakewise resources for network [%s]", network) - } - /* - for _, module := range cfg.GetAllModuleConfigs() { - config.ApplyDefaults(module, hdCfg.Network.Value) - } - */ return cfg, nil } // Deserialize the config's modules (assumes the Hyperdrive config itself has already been deserialized) func (c *GlobalConfig) DeserializeModules() error { - // Load Stakewise - stakewiseName := c.StakeWise.GetModuleName() - section, exists := c.Hyperdrive.Modules[stakewiseName] - if exists { - configMap, ok := section.(map[string]any) - if !ok { - return fmt.Errorf("config module section [%s] is not a map, it's a %s", stakewiseName, reflect.TypeOf(section)) - } - err := c.StakeWise.Deserialize(configMap, c.Hyperdrive.Network.Value) - if err != nil { - return fmt.Errorf("error deserializing stakewise configuration: %w", err) - } - } + return nil } diff --git a/shared/config/stakewise-config.go b/shared/config/stakewise-config.go index c4fb76c..f74f270 100644 --- a/shared/config/stakewise-config.go +++ b/shared/config/stakewise-config.go @@ -1,297 +1,297 @@ package config // TODO(HN): REMOVE ALL THIS -import ( - "fmt" - - hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" - hdids "github.com/nodeset-org/hyperdrive-daemon/shared/config/ids" - "github.com/nodeset-org/hyperdrive-stakewise/shared" - "github.com/nodeset-org/hyperdrive-stakewise/shared/config/ids" - "github.com/rocket-pool/node-manager-core/config" -) - -const ( - // Tags - daemonTag string = "nodeset/hyperdrive-stakewise:v" + shared.StakewiseVersion - operatorTag string = "europe-west4-docker.pkg.dev/stakewiselabs/public/v3-operator:v2.1.3" -) - -// Configuration for Stakewise -type StakeWiseConfig struct { - // Toggle for enabling the module - Enabled config.Parameter[bool] - - // Port to run the Stakewise API server on - ApiPort config.Parameter[uint16] - - // Toggle for verifying deposit data Merkle roots before saving - VerifyDepositsRoot config.Parameter[bool] - - // The Docker Hub tag for the Stakewise daemon - DaemonContainerTag config.Parameter[string] - - // The Docker Hub tag for the Stakewise operator - OperatorContainerTag config.Parameter[string] - - // Custom command line flags - AdditionalOpFlags config.Parameter[string] - - // Validator client configs - VcCommon *config.ValidatorClientCommonConfig - Lighthouse *config.LighthouseVcConfig - Lodestar *config.LodestarVcConfig - Nimbus *config.NimbusVcConfig - Prysm *config.PrysmVcConfig - Teku *config.TekuVcConfig - - // Internal fields - Version string - hdCfg *hdconfig.HyperdriveConfig - networkSettings []*StakeWiseSettings -} - -// Generates a new Stakewise config -func NewStakeWiseConfig(hdCfg *hdconfig.HyperdriveConfig, networks []*StakeWiseSettings) (*StakeWiseConfig, error) { - cfg := &StakeWiseConfig{ - hdCfg: hdCfg, - networkSettings: networks, - - Enabled: config.Parameter[bool]{ - ParameterCommon: &config.ParameterCommon{ - ID: ids.StakewiseEnableID, - Name: "Enable", - Description: "Enable support for Stakewise (see more at https://docs.nodeset.io).", - AffectsContainers: []config.ContainerID{ContainerID_StakewiseDaemon, ContainerID_StakewiseOperator, ContainerID_StakewiseValidator}, - CanBeBlank: false, - OverwriteOnUpgrade: false, - }, - Default: map[config.Network]bool{ - config.Network_All: false, - }, - }, - - ApiPort: config.Parameter[uint16]{ - ParameterCommon: &config.ParameterCommon{ - ID: ids.ApiPortID, - Name: "Daemon API Port", - Description: "The port that the Stakewise daemon's API server should run on. Note this is bound to the local machine only; it cannot be accessed by other machines.", - AffectsContainers: []config.ContainerID{ContainerID_StakewiseDaemon}, - CanBeBlank: false, - OverwriteOnUpgrade: false, - }, - Default: map[config.Network]uint16{ - config.Network_All: DefaultApiPort, - }, - }, - - VerifyDepositsRoot: config.Parameter[bool]{ - ParameterCommon: &config.ParameterCommon{ - ID: ids.VerifyDepositRootsID, - Name: "Verify Deposits Root", - Description: "Enable this to verify that the Merkle root of aggregated deposit data returned by the NodeSet server matches the Merkle root stored in the NodeSet vault contract. This is a safety mechanism to ensure the Stakewise Operator container won't try to submit deposits for validators that the NodeSet vault hasn't verified yet.\n\n[orange]Don't disable this unless you know what you're doing.", - AffectsContainers: []config.ContainerID{ContainerID_StakewiseDaemon}, - CanBeBlank: false, - OverwriteOnUpgrade: false, - }, - Default: map[config.Network]bool{ - config.Network_All: true, - }, - }, - - DaemonContainerTag: config.Parameter[string]{ - ParameterCommon: &config.ParameterCommon{ - ID: ids.DaemonContainerTagID, - Name: "Daemon Container Tag", - Description: "The tag name of Hyperdrive's Stakewise Daemon image to use.", - AffectsContainers: []config.ContainerID{ContainerID_StakewiseDaemon}, - CanBeBlank: false, - OverwriteOnUpgrade: true, - }, - Default: map[config.Network]string{ - config.Network_All: daemonTag, - }, - }, - - OperatorContainerTag: config.Parameter[string]{ - ParameterCommon: &config.ParameterCommon{ - ID: ids.OperatorContainerTagID, - Name: "Operator Container Tag", - Description: "The tag name of the Stakewise Operator image to use. See https://github.com/stakewise/v3-operator#using-docker for more details.", - AffectsContainers: []config.ContainerID{ContainerID_StakewiseOperator}, - CanBeBlank: false, - OverwriteOnUpgrade: true, - }, - Default: map[config.Network]string{ - config.Network_All: operatorTag, - }, - }, - - AdditionalOpFlags: config.Parameter[string]{ - ParameterCommon: &config.ParameterCommon{ - ID: ids.AdditionalOpFlagsID, - Name: "Additional Operator Flags", - Description: "Additional custom command line flags you want to pass to the Operator container, to take advantage of other settings that Hyperdrive's configuration doesn't cover.", - AffectsContainers: []config.ContainerID{ContainerID_StakewiseOperator}, - CanBeBlank: true, - OverwriteOnUpgrade: false, - }, - Default: map[config.Network]string{ - config.Network_All: "", - }, - }, - } - - cfg.VcCommon = config.NewValidatorClientCommonConfig() - cfg.Lighthouse = config.NewLighthouseVcConfig() - cfg.Lodestar = config.NewLodestarVcConfig() - cfg.Nimbus = config.NewNimbusVcConfig() - cfg.Prysm = config.NewPrysmVcConfig() - cfg.Teku = config.NewTekuVcConfig() - - // Provision the defaults for each network - for _, network := range networks { - err := config.SetDefaultsForNetworks(cfg, network.DefaultConfigSettings, network.Key) - if err != nil { - return nil, fmt.Errorf("could not set defaults for network %s: %w", network.Key, err) - } - } - - // Apply the default values for the current network - config.ApplyDefaults(cfg, hdCfg.Network.Value) - return cfg, nil -} - -// The title for the config -func (cfg *StakeWiseConfig) GetTitle() string { - return "Stakewise" -} - -// Get the parameters for this config -func (cfg *StakeWiseConfig) GetParameters() []config.IParameter { - return []config.IParameter{ - &cfg.Enabled, - &cfg.ApiPort, - &cfg.VerifyDepositsRoot, - &cfg.DaemonContainerTag, - &cfg.OperatorContainerTag, - &cfg.AdditionalOpFlags, - } -} - -// Get the sections underneath this one -func (cfg *StakeWiseConfig) GetSubconfigs() map[string]config.IConfigSection { - return map[string]config.IConfigSection{ - ids.VcCommonID: cfg.VcCommon, - ids.LighthouseID: cfg.Lighthouse, - ids.LodestarID: cfg.Lodestar, - ids.NimbusID: cfg.Nimbus, - ids.PrysmID: cfg.Prysm, - ids.TekuID: cfg.Teku, - } -} - -// Changes the current network, propagating new parameter settings if they are affected -func (cfg *StakeWiseConfig) ChangeNetwork(oldNetwork config.Network, newNetwork config.Network) { - // Run the changes - config.ChangeNetwork(cfg, oldNetwork, newNetwork) -} - -// Creates a copy of the configuration -func (cfg *StakeWiseConfig) Clone() hdconfig.IModuleConfig { - clone, _ := NewStakeWiseConfig(cfg.hdCfg, cfg.networkSettings) - config.Clone(cfg, clone, cfg.hdCfg.Network.Value) - clone.Version = cfg.Version - return clone -} - -// Updates the default parameters based on the current network value -func (cfg *StakeWiseConfig) UpdateDefaults(network config.Network) { - config.UpdateDefaults(cfg, network) -} - -// Checks to see if the current configuration is valid; if not, returns a list of errors -func (cfg *StakeWiseConfig) Validate() []string { - errors := []string{} - return errors -} - -// Serialize the module config to a map -func (cfg *StakeWiseConfig) Serialize() map[string]any { - cfgMap := config.Serialize(cfg) - cfgMap[hdids.VersionID] = cfg.Version - return cfgMap -} - -// Deserialize the module config from a map -func (cfg *StakeWiseConfig) Deserialize(configMap map[string]any, network config.Network) error { - err := config.Deserialize(cfg, configMap, network) - if err != nil { - return err - } - version, exists := configMap[hdids.VersionID] - if !exists { - // Handle pre-version configs - version = "0.0.1" - } - cfg.Version = version.(string) - return nil -} - -// Get the version of the module config -func (cfg *StakeWiseConfig) GetVersion() string { - return cfg.Version -} - -// Get all loaded network settings -func (cfg *StakeWiseConfig) GetNetworkSettings() []*StakeWiseSettings { - return cfg.networkSettings -} - -// =================== -// === Module Info === -// =================== - -func (cfg *StakeWiseConfig) GetHdClientLogFileName() string { - return ClientLogName -} - -func (cfg *StakeWiseConfig) GetApiLogFileName() string { - return hdconfig.ApiLogName -} - -func (cfg *StakeWiseConfig) GetTasksLogFileName() string { - return hdconfig.TasksLogName -} - -func (cfg *StakeWiseConfig) GetLogNames() []string { - return []string{ - cfg.GetHdClientLogFileName(), - cfg.GetApiLogFileName(), - cfg.GetTasksLogFileName(), - } -} - -// The module name -func (cfg *StakeWiseConfig) GetModuleName() string { - return ModuleName -} - -// The module name -func (cfg *StakeWiseConfig) GetShortName() string { - return ShortModuleName -} - -func (cfg *StakeWiseConfig) GetValidatorContainerTagInfo() map[config.ContainerID]string { - return map[config.ContainerID]string{ - ContainerID_StakewiseValidator: cfg.GetVcContainerTag(), - } -} - -func (cfg *StakeWiseConfig) GetContainersToDeploy() []config.ContainerID { - return []config.ContainerID{ - ContainerID_StakewiseDaemon, - ContainerID_StakewiseOperator, - ContainerID_StakewiseValidator, - } -} +// import ( +// "fmt" + +// hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" +// hdids "github.com/nodeset-org/hyperdrive-daemon/shared/config/ids" +// "github.com/nodeset-org/hyperdrive-stakewise/shared" +// "github.com/nodeset-org/hyperdrive-stakewise/shared/config/ids" +// "github.com/rocket-pool/node-manager-core/config" +// ) + +// const ( +// // Tags +// daemonTag string = "nodeset/hyperdrive-stakewise:v" + shared.StakewiseVersion +// operatorTag string = "europe-west4-docker.pkg.dev/stakewiselabs/public/v3-operator:v2.1.3" +// ) + +// // Configuration for Stakewise +// type StakeWiseConfig struct { +// // Toggle for enabling the module +// Enabled config.Parameter[bool] + +// // Port to run the Stakewise API server on +// ApiPort config.Parameter[uint16] + +// // Toggle for verifying deposit data Merkle roots before saving +// VerifyDepositsRoot config.Parameter[bool] + +// // The Docker Hub tag for the Stakewise daemon +// DaemonContainerTag config.Parameter[string] + +// // The Docker Hub tag for the Stakewise operator +// OperatorContainerTag config.Parameter[string] + +// // Custom command line flags +// AdditionalOpFlags config.Parameter[string] + +// // Validator client configs +// VcCommon *config.ValidatorClientCommonConfig +// Lighthouse *config.LighthouseVcConfig +// Lodestar *config.LodestarVcConfig +// Nimbus *config.NimbusVcConfig +// Prysm *config.PrysmVcConfig +// Teku *config.TekuVcConfig + +// // Internal fields +// Version string +// hdCfg *hdconfig.HyperdriveConfig +// networkSettings []*StakeWiseSettings +// } + +// // Generates a new Stakewise config +// func NewStakeWiseConfig(hdCfg *hdconfig.HyperdriveConfig, networks []*StakeWiseSettings) (*StakeWiseConfig, error) { +// cfg := &StakeWiseConfig{ +// hdCfg: hdCfg, +// networkSettings: networks, + +// Enabled: config.Parameter[bool]{ +// ParameterCommon: &config.ParameterCommon{ +// ID: ids.StakewiseEnableID, +// Name: "Enable", +// Description: "Enable support for Stakewise (see more at https://docs.nodeset.io).", +// AffectsContainers: []config.ContainerID{ContainerID_StakewiseDaemon, ContainerID_StakewiseOperator, ContainerID_StakewiseValidator}, +// CanBeBlank: false, +// OverwriteOnUpgrade: false, +// }, +// Default: map[config.Network]bool{ +// config.Network_All: false, +// }, +// }, + +// ApiPort: config.Parameter[uint16]{ +// ParameterCommon: &config.ParameterCommon{ +// ID: ids.ApiPortID, +// Name: "Daemon API Port", +// Description: "The port that the Stakewise daemon's API server should run on. Note this is bound to the local machine only; it cannot be accessed by other machines.", +// AffectsContainers: []config.ContainerID{ContainerID_StakewiseDaemon}, +// CanBeBlank: false, +// OverwriteOnUpgrade: false, +// }, +// Default: map[config.Network]uint16{ +// config.Network_All: DefaultApiPort, +// }, +// }, + +// VerifyDepositsRoot: config.Parameter[bool]{ +// ParameterCommon: &config.ParameterCommon{ +// ID: ids.VerifyDepositRootsID, +// Name: "Verify Deposits Root", +// Description: "Enable this to verify that the Merkle root of aggregated deposit data returned by the NodeSet server matches the Merkle root stored in the NodeSet vault contract. This is a safety mechanism to ensure the Stakewise Operator container won't try to submit deposits for validators that the NodeSet vault hasn't verified yet.\n\n[orange]Don't disable this unless you know what you're doing.", +// AffectsContainers: []config.ContainerID{ContainerID_StakewiseDaemon}, +// CanBeBlank: false, +// OverwriteOnUpgrade: false, +// }, +// Default: map[config.Network]bool{ +// config.Network_All: true, +// }, +// }, + +// DaemonContainerTag: config.Parameter[string]{ +// ParameterCommon: &config.ParameterCommon{ +// ID: ids.DaemonContainerTagID, +// Name: "Daemon Container Tag", +// Description: "The tag name of Hyperdrive's Stakewise Daemon image to use.", +// AffectsContainers: []config.ContainerID{ContainerID_StakewiseDaemon}, +// CanBeBlank: false, +// OverwriteOnUpgrade: true, +// }, +// Default: map[config.Network]string{ +// config.Network_All: daemonTag, +// }, +// }, + +// OperatorContainerTag: config.Parameter[string]{ +// ParameterCommon: &config.ParameterCommon{ +// ID: ids.OperatorContainerTagID, +// Name: "Operator Container Tag", +// Description: "The tag name of the Stakewise Operator image to use. See https://github.com/stakewise/v3-operator#using-docker for more details.", +// AffectsContainers: []config.ContainerID{ContainerID_StakewiseOperator}, +// CanBeBlank: false, +// OverwriteOnUpgrade: true, +// }, +// Default: map[config.Network]string{ +// config.Network_All: operatorTag, +// }, +// }, + +// AdditionalOpFlags: config.Parameter[string]{ +// ParameterCommon: &config.ParameterCommon{ +// ID: ids.AdditionalOpFlagsID, +// Name: "Additional Operator Flags", +// Description: "Additional custom command line flags you want to pass to the Operator container, to take advantage of other settings that Hyperdrive's configuration doesn't cover.", +// AffectsContainers: []config.ContainerID{ContainerID_StakewiseOperator}, +// CanBeBlank: true, +// OverwriteOnUpgrade: false, +// }, +// Default: map[config.Network]string{ +// config.Network_All: "", +// }, +// }, +// } + +// cfg.VcCommon = config.NewValidatorClientCommonConfig() +// cfg.Lighthouse = config.NewLighthouseVcConfig() +// cfg.Lodestar = config.NewLodestarVcConfig() +// cfg.Nimbus = config.NewNimbusVcConfig() +// cfg.Prysm = config.NewPrysmVcConfig() +// cfg.Teku = config.NewTekuVcConfig() + +// // Provision the defaults for each network +// for _, network := range networks { +// err := config.SetDefaultsForNetworks(cfg, network.DefaultConfigSettings, network.Key) +// if err != nil { +// return nil, fmt.Errorf("could not set defaults for network %s: %w", network.Key, err) +// } +// } + +// // Apply the default values for the current network +// config.ApplyDefaults(cfg, hdCfg.Network.Value) +// return cfg, nil +// } + +// // The title for the config +// func (cfg *StakeWiseConfig) GetTitle() string { +// return "Stakewise" +// } + +// // Get the parameters for this config +// func (cfg *StakeWiseConfig) GetParameters() []config.IParameter { +// return []config.IParameter{ +// &cfg.Enabled, +// &cfg.ApiPort, +// &cfg.VerifyDepositsRoot, +// &cfg.DaemonContainerTag, +// &cfg.OperatorContainerTag, +// &cfg.AdditionalOpFlags, +// } +// } + +// // Get the sections underneath this one +// func (cfg *StakeWiseConfig) GetSubconfigs() map[string]config.IConfigSection { +// return map[string]config.IConfigSection{ +// ids.VcCommonID: cfg.VcCommon, +// ids.LighthouseID: cfg.Lighthouse, +// ids.LodestarID: cfg.Lodestar, +// ids.NimbusID: cfg.Nimbus, +// ids.PrysmID: cfg.Prysm, +// ids.TekuID: cfg.Teku, +// } +// } + +// // Changes the current network, propagating new parameter settings if they are affected +// func (cfg *StakeWiseConfig) ChangeNetwork(oldNetwork config.Network, newNetwork config.Network) { +// // Run the changes +// config.ChangeNetwork(cfg, oldNetwork, newNetwork) +// } + +// // Creates a copy of the configuration +// func (cfg *StakeWiseConfig) Clone() hdconfig.IModuleConfig { +// clone, _ := NewStakeWiseConfig(cfg.hdCfg, cfg.networkSettings) +// config.Clone(cfg, clone, cfg.hdCfg.Network.Value) +// clone.Version = cfg.Version +// return clone +// } + +// // Updates the default parameters based on the current network value +// func (cfg *StakeWiseConfig) UpdateDefaults(network config.Network) { +// config.UpdateDefaults(cfg, network) +// } + +// // Checks to see if the current configuration is valid; if not, returns a list of errors +// func (cfg *StakeWiseConfig) Validate() []string { +// errors := []string{} +// return errors +// } + +// // Serialize the module config to a map +// func (cfg *StakeWiseConfig) Serialize() map[string]any { +// cfgMap := config.Serialize(cfg) +// cfgMap[hdids.VersionID] = cfg.Version +// return cfgMap +// } + +// // Deserialize the module config from a map +// func (cfg *StakeWiseConfig) Deserialize(configMap map[string]any, network config.Network) error { +// err := config.Deserialize(cfg, configMap, network) +// if err != nil { +// return err +// } +// version, exists := configMap[hdids.VersionID] +// if !exists { +// // Handle pre-version configs +// version = "0.0.1" +// } +// cfg.Version = version.(string) +// return nil +// } + +// // Get the version of the module config +// func (cfg *StakeWiseConfig) GetVersion() string { +// return cfg.Version +// } + +// // Get all loaded network settings +// func (cfg *StakeWiseConfig) GetNetworkSettings() []*StakeWiseSettings { +// return cfg.networkSettings +// } + +// // =================== +// // === Module Info === +// // =================== + +// func (cfg *StakeWiseConfig) GetHdClientLogFileName() string { +// return ClientLogName +// } + +// func (cfg *StakeWiseConfig) GetApiLogFileName() string { +// return hdconfig.ApiLogName +// } + +// func (cfg *StakeWiseConfig) GetTasksLogFileName() string { +// return hdconfig.TasksLogName +// } + +// func (cfg *StakeWiseConfig) GetLogNames() []string { +// return []string{ +// cfg.GetHdClientLogFileName(), +// cfg.GetApiLogFileName(), +// cfg.GetTasksLogFileName(), +// } +// } + +// // The module name +// func (cfg *StakeWiseConfig) GetModuleName() string { +// return ModuleName +// } + +// // The module name +// func (cfg *StakeWiseConfig) GetShortName() string { +// return ShortModuleName +// } + +// func (cfg *StakeWiseConfig) GetValidatorContainerTagInfo() map[config.ContainerID]string { +// return map[config.ContainerID]string{ +// ContainerID_StakewiseValidator: cfg.GetVcContainerTag(), +// } +// } + +// func (cfg *StakeWiseConfig) GetContainersToDeploy() []config.ContainerID { +// return []config.ContainerID{ +// ContainerID_StakewiseDaemon, +// ContainerID_StakewiseOperator, +// ContainerID_StakewiseValidator, +// } +// } diff --git a/shared/config/templating.go b/shared/config/templating.go index 852e63e..7dc5e0e 100644 --- a/shared/config/templating.go +++ b/shared/config/templating.go @@ -1,102 +1,102 @@ package config -import ( - "fmt" - - "github.com/rocket-pool/node-manager-core/config" -) - -func (c *StakeWiseConfig) WalletFilename() string { - return WalletFilename -} - -func (c *StakeWiseConfig) PasswordFilename() string { - return PasswordFilename -} - -func (c *StakeWiseConfig) KeystorePasswordFile() string { - return KeystorePasswordFile -} - -func (c *StakeWiseConfig) DaemonContainerName() string { - return string(ContainerID_StakewiseDaemon) -} - -func (c *StakeWiseConfig) DataVolume() string { - return DataVolume -} - -func (c *StakeWiseConfig) OperatorContainerName() string { - return string(ContainerID_StakewiseOperator) -} - -func (c *StakeWiseConfig) VcContainerName() string { - return string(ContainerID_StakewiseValidator) -} - -func (c *StakeWiseConfig) DepositDataFile() string { - return DepositDataFile -} - -// The tag for the daemon container -func (cfg *StakeWiseConfig) GetDaemonContainerTag() string { - return cfg.DaemonContainerTag.Value -} - -// Get the container tag of the selected VC -func (cfg *StakeWiseConfig) GetVcContainerTag() string { - bn := cfg.hdCfg.GetSelectedBeaconNode() - switch bn { - case config.BeaconNode_Lighthouse: - return cfg.Lighthouse.ContainerTag.Value - case config.BeaconNode_Lodestar: - return cfg.Lodestar.ContainerTag.Value - case config.BeaconNode_Nimbus: - return cfg.Nimbus.ContainerTag.Value - case config.BeaconNode_Prysm: - return cfg.Prysm.ContainerTag.Value - case config.BeaconNode_Teku: - return cfg.Teku.ContainerTag.Value - default: - panic(fmt.Sprintf("Unknown Beacon Node %s", bn)) - } -} - -// Gets the additional flags of the selected VC -func (cfg *StakeWiseConfig) GetVcAdditionalFlags() string { - bn := cfg.hdCfg.GetSelectedBeaconNode() - switch bn { - case config.BeaconNode_Lighthouse: - return cfg.Lighthouse.AdditionalFlags.Value - case config.BeaconNode_Lodestar: - return cfg.Lodestar.AdditionalFlags.Value - case config.BeaconNode_Nimbus: - return cfg.Nimbus.AdditionalFlags.Value - case config.BeaconNode_Prysm: - return cfg.Prysm.AdditionalFlags.Value - case config.BeaconNode_Teku: - return cfg.Teku.AdditionalFlags.Value - default: - panic(fmt.Sprintf("Unknown Beacon Node %s", bn)) - } -} - -// Check if any of the services have doppelganger detection enabled -// NOTE: update this with each new service that runs a VC! -func (cfg *StakeWiseConfig) IsDoppelgangerEnabled() bool { - return cfg.VcCommon.DoppelgangerDetection.Value -} - -// Used by text/template to format validator.yml -func (cfg *StakeWiseConfig) Graffiti() (string, error) { - prefix := cfg.hdCfg.GraffitiPrefix() - customGraffiti := cfg.VcCommon.Graffiti.Value - if customGraffiti == "" { - return prefix, nil - } - return fmt.Sprintf("%s (%s)", prefix, customGraffiti), nil -} - -func (cfg *StakeWiseConfig) IsEnabled() bool { - return cfg.Enabled.Value -} +// import ( +// "fmt" + +// "github.com/rocket-pool/node-manager-core/config" +// ) + +// func (c *StakeWiseConfig) WalletFilename() string { +// return WalletFilename +// } + +// func (c *StakeWiseConfig) PasswordFilename() string { +// return PasswordFilename +// } + +// func (c *StakeWiseConfig) KeystorePasswordFile() string { +// return KeystorePasswordFile +// } + +// func (c *StakeWiseConfig) DaemonContainerName() string { +// return string(ContainerID_StakewiseDaemon) +// } + +// func (c *StakeWiseConfig) DataVolume() string { +// return DataVolume +// } + +// func (c *StakeWiseConfig) OperatorContainerName() string { +// return string(ContainerID_StakewiseOperator) +// } + +// func (c *StakeWiseConfig) VcContainerName() string { +// return string(ContainerID_StakewiseValidator) +// } + +// func (c *StakeWiseConfig) DepositDataFile() string { +// return DepositDataFile +// } + +// // The tag for the daemon container +// func (cfg *StakeWiseConfig) GetDaemonContainerTag() string { +// return cfg.DaemonContainerTag.Value +// } + +// // Get the container tag of the selected VC +// func (cfg *StakeWiseConfig) GetVcContainerTag() string { +// bn := cfg.hdCfg.GetSelectedBeaconNode() +// switch bn { +// case config.BeaconNode_Lighthouse: +// return cfg.Lighthouse.ContainerTag.Value +// case config.BeaconNode_Lodestar: +// return cfg.Lodestar.ContainerTag.Value +// case config.BeaconNode_Nimbus: +// return cfg.Nimbus.ContainerTag.Value +// case config.BeaconNode_Prysm: +// return cfg.Prysm.ContainerTag.Value +// case config.BeaconNode_Teku: +// return cfg.Teku.ContainerTag.Value +// default: +// panic(fmt.Sprintf("Unknown Beacon Node %s", bn)) +// } +// } + +// // Gets the additional flags of the selected VC +// func (cfg *StakeWiseConfig) GetVcAdditionalFlags() string { +// bn := cfg.hdCfg.GetSelectedBeaconNode() +// switch bn { +// case config.BeaconNode_Lighthouse: +// return cfg.Lighthouse.AdditionalFlags.Value +// case config.BeaconNode_Lodestar: +// return cfg.Lodestar.AdditionalFlags.Value +// case config.BeaconNode_Nimbus: +// return cfg.Nimbus.AdditionalFlags.Value +// case config.BeaconNode_Prysm: +// return cfg.Prysm.AdditionalFlags.Value +// case config.BeaconNode_Teku: +// return cfg.Teku.AdditionalFlags.Value +// default: +// panic(fmt.Sprintf("Unknown Beacon Node %s", bn)) +// } +// } + +// // Check if any of the services have doppelganger detection enabled +// // NOTE: update this with each new service that runs a VC! +// func (cfg *StakeWiseConfig) IsDoppelgangerEnabled() bool { +// return cfg.VcCommon.DoppelgangerDetection.Value +// } + +// // Used by text/template to format validator.yml +// func (cfg *StakeWiseConfig) Graffiti() (string, error) { +// prefix := cfg.hdCfg.GraffitiPrefix() +// customGraffiti := cfg.VcCommon.Graffiti.Value +// if customGraffiti == "" { +// return prefix, nil +// } +// return fmt.Sprintf("%s (%s)", prefix, customGraffiti), nil +// } + +// func (cfg *StakeWiseConfig) IsEnabled() bool { +// return cfg.Enabled.Value +// } diff --git a/stakewise-daemon.go b/stakewise-daemon.go index 4d1d468..7659085 100644 --- a/stakewise-daemon.go +++ b/stakewise-daemon.go @@ -15,6 +15,7 @@ import ( "github.com/nodeset-org/hyperdrive-daemon/shared" "github.com/nodeset-org/hyperdrive-daemon/shared/auth" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" swcommon "github.com/nodeset-org/hyperdrive-stakewise/common" "github.com/nodeset-org/hyperdrive-stakewise/server" swshared "github.com/nodeset-org/hyperdrive-stakewise/shared" @@ -146,8 +147,8 @@ func main() { stopWg := new(sync.WaitGroup) // Create the service provider - configFactory := func(hdCfg *hdconfig.HyperdriveConfig) (*swconfig.StakeWiseConfig, error) { - return swconfig.NewStakeWiseConfig(hdCfg, settingsList) + configFactory := func(hdCfg *hdconfig.HyperdriveConfig) (*config.StakeWiseConfig, error) { + return config.NewStakeWiseConfig(), nil } sp, err := services.NewModuleServiceProvider(hyperdriveUrl, moduleDir, swconfig.ModuleName, swconfig.ClientLogName, configFactory, hdAuthMgr) if err != nil { From 67f63ffbc1051de773fe1ae40051eb53114225ab Mon Sep 17 00:00:00 2001 From: huy Date: Fri, 31 Jan 2025 10:58:10 -0800 Subject: [PATCH 23/28] wip --- adapter/config/config.go | 19 ++++++++++++------- adapter/hd-module/get-config-metadata.go | 16 ++-------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/adapter/config/config.go b/adapter/config/config.go index 44e6c39..942bfc5 100644 --- a/adapter/config/config.go +++ b/adapter/config/config.go @@ -9,10 +9,6 @@ import ( "github.com/rocket-pool/node-manager-core/config" ) -const ( - FloatThreshold float64 = 75.0 -) - type PortMode string const ( @@ -113,15 +109,24 @@ func (cfg StakeWiseConfig) GetSections() []hdconfig.ISection { func CreateInstanceFromNativeConfig(native *sharedconfig.NativeStakeWiseConfigSettings) *StakeWiseConfigSettings { instance := &StakeWiseConfigSettings{ - Enabled: native.Enabled, - ApiPort: native.ApiPort, + Enabled: native.Enabled, + ApiPort: native.ApiPort, + VerifyDepositsRoot: native.VerifyDepositsRoot, + DaemonContainerTag: native.DaemonContainerTag, + OperatorContainerTag: native.OperatorContainerTag, + AdditionalOpFlags: native.AdditionalOpFlags, } return instance } func ConvertInstanceToNativeConfig(instance *StakeWiseConfigSettings) *sharedconfig.NativeStakeWiseConfigSettings { native := &sharedconfig.NativeStakeWiseConfigSettings{ - ApiPort: instance.ApiPort, + Enabled: instance.Enabled, + ApiPort: instance.ApiPort, + VerifyDepositsRoot: instance.VerifyDepositsRoot, + DaemonContainerTag: instance.DaemonContainerTag, + OperatorContainerTag: instance.OperatorContainerTag, + AdditionalOpFlags: instance.AdditionalOpFlags, } return native } diff --git a/adapter/hd-module/get-config-metadata.go b/adapter/hd-module/get-config-metadata.go index a74b397..6a571c2 100644 --- a/adapter/hd-module/get-config-metadata.go +++ b/adapter/hd-module/get-config-metadata.go @@ -19,22 +19,10 @@ func getConfigMetadata(c *cli.Context) error { } // Get the config - cfgMgr, err := config.NewAdapterConfigManager(c) - if err != nil { - return fmt.Errorf("error creating config manager: %w", err) - } - cfg, err := cfgMgr.LoadConfigFromDisk() - if err != nil { - return fmt.Errorf("error loading config: %w", err) - } - - // Handle no config file by using the default - if cfg == nil { - cfg = config.NewStakeWiseConfig() - } + cfg := config.NewStakeWiseConfig() // Create the response - cfgMap := hdconfig.MarshalConfigurationMetadataToMap(cfg) + cfgMap := hdconfig.MarshalConfigurationToMap(cfg) bytes, err := json.Marshal(cfgMap) if err != nil { return fmt.Errorf("error marshalling config: %w", err) From 2f1dde390f6aa5ab8188bbdb0b31db1102b68334 Mon Sep 17 00:00:00 2001 From: huy Date: Fri, 31 Jan 2025 13:13:08 -0800 Subject: [PATCH 24/28] fixing compiler errors --- adapter/config/claim-rewards.go | 120 ++++++------- adapter/config/config-manager.go | 110 ++++++------ adapter/config/exit.go | 228 +++++++++++------------- adapter/config/generate-deposit-data.go | 101 +++++------ adapter/config/generate-keys.go | 205 ++++++++++----------- adapter/config/get-node-status.go | 98 +++++----- adapter/config/initialize.go | 67 ++++--- adapter/config/upload-deposit-data.go | 46 +++-- adapter/hd-module/process-settings.go | 66 ++++--- adapter/hd-module/set-settings.go | 54 +++--- common/service-provider.go | 12 +- shared/config/resources.go | 2 +- 12 files changed, 527 insertions(+), 582 deletions(-) diff --git a/adapter/config/claim-rewards.go b/adapter/config/claim-rewards.go index a28367c..0ecb527 100644 --- a/adapter/config/claim-rewards.go +++ b/adapter/config/claim-rewards.go @@ -1,79 +1,71 @@ package config import ( - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/tx" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" - swclient "github.com/nodeset-org/hyperdrive-stakewise/client" - "github.com/rocket-pool/node-manager-core/eth" "github.com/urfave/cli/v2" ) func claimRewards(c *cli.Context) error { // Get the client - hd, err := swclient.NewHyperdriveClientFromCtx(c) - if err != nil { - return err - } - sw, err := swclient.NewStakewiseClientFromCtx(c, hd) - if err != nil { - return err - } - cfg, _, err := hd.LoadConfig() - if err != nil { - return fmt.Errorf("error loading Hyperdrive config: %w", err) - } - if !cfg.StakeWise.Enabled.Value { - fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - return nil - } + // hd, err := swclient.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } - // Check if there's a node address ready - _, ready, err := wallet.CheckIfAddressReady(hd) - if err != nil { - return err - } - if !ready { - return nil - } + // // Check if there's a node address ready + // _, ready, err := wallet.CheckIfAddressReady(hd) + // if err != nil { + // return err + // } + // if !ready { + // return nil + // } - // Get the list of rewards available - resp, err := sw.Api.Wallet.ClaimRewards() - if err != nil { - return err - } - fmt.Println("Your withdrawable rewards:") - fmt.Printf("%.4f %s (%s)\n", eth.WeiToEth(resp.Data.WithdrawableToken), resp.Data.TokenSymbol, resp.Data.TokenName) - fmt.Printf("%.4f ETH\n", eth.WeiToEth(resp.Data.WithdrawableNativeToken)) - fmt.Println() - fmt.Println("NOTE: this list only shows rewards that StakeWise has already returned to NodeSet. Your share may include more rewards, but StakeWise hasn't returned yet.") - fmt.Println() + // // Get the list of rewards available + // resp, err := sw.Api.Wallet.ClaimRewards() + // if err != nil { + // return err + // } + // fmt.Println("Your withdrawable rewards:") + // fmt.Printf("%.4f %s (%s)\n", eth.WeiToEth(resp.Data.WithdrawableToken), resp.Data.TokenSymbol, resp.Data.TokenName) + // fmt.Printf("%.4f ETH\n", eth.WeiToEth(resp.Data.WithdrawableNativeToken)) + // fmt.Println() + // fmt.Println("NOTE: this list only shows rewards that StakeWise has already returned to NodeSet. Your share may include more rewards, but StakeWise hasn't returned yet.") + // fmt.Println() - // Check if both balances are zero - sum := big.NewInt(0) - sum.Add(sum, resp.Data.WithdrawableNativeToken) - sum.Add(sum, resp.Data.WithdrawableToken) - if sum.Cmp(common.Big0) == 0 { - fmt.Println("You don't have any rewards to claim.") - return nil - } + // // Check if both balances are zero + // sum := big.NewInt(0) + // sum.Add(sum, resp.Data.WithdrawableNativeToken) + // sum.Add(sum, resp.Data.WithdrawableToken) + // if sum.Cmp(common.Big0) == 0 { + // fmt.Println("You don't have any rewards to claim.") + // return nil + // } - // Run the TX - validated, err := tx.HandleTx(c, hd, resp.Data.TxInfo, - "Are you sure you want to claim rewards?", - "claiming rewards", - "Claiming rewards...", - ) - if err != nil { - return err - } - if !validated { - return nil - } + // // Run the TX + // validated, err := tx.HandleTx(c, hd, resp.Data.TxInfo, + // "Are you sure you want to claim rewards?", + // "claiming rewards", + // "Claiming rewards...", + // ) + // if err != nil { + // return err + // } + // if !validated { + // return nil + // } - fmt.Println("Rewards successfully claimed.") + // fmt.Println("Rewards successfully claimed.") return nil } diff --git a/adapter/config/config-manager.go b/adapter/config/config-manager.go index fae85e2..0bdf379 100644 --- a/adapter/config/config-manager.go +++ b/adapter/config/config-manager.go @@ -1,10 +1,15 @@ package config import ( + "errors" "fmt" + "io/fs" "os" + "path/filepath" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config/ids" + sharedconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" ) @@ -12,10 +17,10 @@ import ( // Configuration manager type AdapterConfigManager struct { // The adapter configuration - AdapterConfig *StakeWiseConfig + AdapterConfig *StakeWiseConfigSettings // The native configuration manager - // nativeConfigManager *nativecfg.ConfigManager + nativeConfigManager *sharedconfig.ConfigManager // The path to the adapter configuration file adapterConfigPath string @@ -23,54 +28,44 @@ type AdapterConfigManager struct { // Create a new configuration manager for the adapter func NewAdapterConfigManager(c *cli.Context) (*AdapterConfigManager, error) { - // configDir := c.String(utils.ConfigDirFlag.Name) - // if configDir == "" { - // return nil, fmt.Errorf("config directory is required") - // } - // return &AdapterConfigManager{ - // // nativeConfigManager: nativecfg.NewConfigManager(filepath.Join(configDir, utils.ServiceConfigFile)), - // adapterConfigPath: filepath.Join(configDir, utils.AdapterConfigFile), - // }, nil - return nil, nil + configDir := c.String(utils.ConfigDirFlag.Name) + if configDir == "" { + return nil, fmt.Errorf("config directory is required") + } + return &AdapterConfigManager{ + nativeConfigManager: sharedconfig.NewConfigManager(filepath.Join(configDir, utils.ServiceConfigFile)), + adapterConfigPath: filepath.Join(configDir, utils.AdapterConfigFile), + }, nil } // Load the configuration from disk -func (m *AdapterConfigManager) LoadConfigFromDisk() (*StakeWiseConfig, error) { +func (m *AdapterConfigManager) LoadConfigFromDisk() (*StakeWiseConfigSettings, error) { // Load the native config - // nativeCfg, err := m.nativeConfigManager.LoadConfigFromFile() - // if err != nil { - // return nil, fmt.Errorf("error loading service config: %w", err) - // } - - // // Check if the adapter config exists - // _, err = os.Stat(m.adapterConfigPath) - // if errors.Is(err, fs.ErrNotExist) { - // return nil, nil - // } - - // Load it - bytes, err := os.ReadFile(m.adapterConfigPath) + nativeCfg, err := m.nativeConfigManager.LoadConfigFromFile() if err != nil { - return nil, fmt.Errorf("error reading config file [%s]: %w", m.adapterConfigPath, err) + return nil, fmt.Errorf("error loading service config: %w", err) } - // Deserialize it - cfgInstance := map[string]any{} - err = yaml.Unmarshal(bytes, &cfgInstance) - if err != nil { - return nil, fmt.Errorf("error deserializing adapter config file [%s]: %w", m.adapterConfigPath, err) + // Check if the adapter config exists + _, err = os.Stat(m.adapterConfigPath) + if errors.Is(err, fs.ErrNotExist) { + return nil, nil } - serverCfg := NewServerConfig() - portInt := cfgInstance[ids.PortID].(int) - serverCfg.Port.Value = uint64(portInt) - serverCfg.PortMode.Value = PortMode(cfgInstance[ids.PortModeID].(string)) - // Merge the configs - // modCfg := ConvertToMetadata(nativeCfg) - // modCfg.ServerConfig = serverCfg - // m.AdapterConfig = modCfg - // return modCfg, nil - return nil, nil + // Load it + // bytes, err := os.ReadFile(m.adapterConfigPath) + // if err != nil { + // return nil, fmt.Errorf("error reading config file [%s]: %w", m.adapterConfigPath, err) + // } + + // Deserialize it + modCfg := CreateInstanceFromNativeConfig(nativeCfg) + // err = yaml.Unmarshal(bytes, &modCfg.ServerConfig) + // if err != nil { + // return nil, fmt.Errorf("error deserializing adapter config file [%s]: %w", m.adapterConfigPath, err) + // } + m.AdapterConfig = modCfg + return modCfg, nil } // Save the configuration to a file. If the config hasn't been loaded yet, this doesn't do anything. @@ -79,25 +74,24 @@ func (m *AdapterConfigManager) SaveConfigToDisk() error { return nil } - // // Save the native config - // nativeCfg := m.AdapterConfig.ConvertToNative() - // m.nativeConfigManager.Config = nativeCfg - // err := m.nativeConfigManager.SaveConfigToFile() - // if err != nil { - // return fmt.Errorf("error saving service config: %w", err) - // } + // Save the native config + nativeCfg := ConvertInstanceToNativeConfig(m.AdapterConfig) + m.nativeConfigManager.Config = nativeCfg + err := m.nativeConfigManager.SaveConfigToFile() + if err != nil { + return fmt.Errorf("error saving service config: %w", err) + } // Serialize the adapter config - // modCfg := hdconfig.CreateInstanceFromMetadata(m.AdapterConfig.ServerConfig) - // bytes, err := yaml.Marshal(modCfg) - // if err != nil { - // return fmt.Errorf("error serializing adapter config: %w", err) - // } + bytes, err := yaml.Marshal(m.AdapterConfig) + if err != nil { + return fmt.Errorf("error serializing adapter config: %w", err) + } // Write it - // err = os.WriteFile(m.adapterConfigPath, bytes, nativecfg.ConfigFileMode) - // if err != nil { - // return fmt.Errorf("error writing adapter config file [%s]: %w", m.adapterConfigPath, err) - // } + err = os.WriteFile(m.adapterConfigPath, bytes, sharedconfig.ConfigFileMode) + if err != nil { + return fmt.Errorf("error writing adapter config file [%s]: %w", m.adapterConfigPath, err) + } return nil } diff --git a/adapter/config/exit.go b/adapter/config/exit.go index 2006897..82fc34b 100644 --- a/adapter/config/exit.go +++ b/adapter/config/exit.go @@ -1,16 +1,6 @@ package config import ( - "fmt" - "time" - - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" - swclient "github.com/nodeset-org/hyperdrive-stakewise/client" - nutils "github.com/rocket-pool/node-manager-core/cli/utils" - - "github.com/rocket-pool/node-manager-core/beacon" "github.com/urfave/cli/v2" ) @@ -33,115 +23,115 @@ var ( ) func exit(c *cli.Context) error { - // Get the client - hd, err := swclient.NewHyperdriveClientFromCtx(c) - if err != nil { - return err - } - sw, err := swclient.NewStakewiseClientFromCtx(c, hd) - if err != nil { - return err - } - cfg, _, err := hd.LoadConfig() - if err != nil { - return fmt.Errorf("error loading Hyperdrive config: %w", err) - } - if !cfg.StakeWise.Enabled.Value { - fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - return nil - } - - // Check wallet status - _, ready, err := wallet.CheckIfWalletReady(hd) - if err != nil { - return err - } - if !ready { - return nil - } - - // Get all active validators - activeValidatorResponse, err := sw.Api.Status.GetValidatorStatuses() - if err != nil { - return fmt.Errorf("error while getting active validators: %w", err) - } - var activeValidators []beacon.ValidatorPubkey - for _, state := range activeValidatorResponse.Data.States { - if state.BeaconStatus == beacon.ValidatorState_ActiveOngoing { - activeValidators = append(activeValidators, state.Pubkey) - } - } - if len(activeValidators) == 0 { - fmt.Println("None of your validators are active, so they cannot be exited.") - return nil - } - - // Get selected validators - options := make([]nutils.SelectionOption[beacon.ValidatorPubkey], len(activeValidators)) - for i, pubkey := range activeValidators { - option := &options[i] - option.Element = &activeValidators[i] - option.ID = activeValidators[i].HexWithPrefix() - option.Display = fmt.Sprintf("%s (active since %s)", pubkey, time.Unix(0, 0)) // Placeholder, fill in with status details - } - selectedValidators, err := utils.GetMultiselectIndices(c, pubkeysFlag.Name, options, "Please select a validator to exit:") - if err != nil { - return fmt.Errorf("error determining validator selection: %w", err) - } - - // Get the epoch if set - var epochPtr *uint64 - if c.IsSet(epochFlag.Name) { - epoch := c.Uint64(epochFlag.Name) - epochPtr = &epoch - } - - // Get the no broadcast flag - noBroadcastBool := false - if c.IsSet(noBroadcastFlag.Name) { - noBroadcastBool = c.Bool(noBroadcastFlag.Name) - } - - // Get the pubkeys - pubkeys := make([]beacon.ValidatorPubkey, len(selectedValidators)) - for i, validator := range selectedValidators { - pubkeys[i] = *validator - } - - if !noBroadcastBool { - // Show a warning message - fmt.Printf("%sNOTE:\n", terminal.ColorYellow) - fmt.Println("You are about to exit your validator(s). This will tell each one to stop all activities on the Beacon Chain.") - fmt.Println("Please continue to run them until each one you've exited has been processed by the exit queue. It will no longer earn staking rewards after this point.") - fmt.Printf("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (typically after a few days).%s\n", terminal.ColorReset) - - // Prompt for confirmation - if !(c.Bool(utils.YesFlag.Name) || utils.ConfirmWithIAgree(fmt.Sprintf("Are you sure you want to exit %d validator(s)? This action cannot be undone!", len(selectedValidators)))) { - fmt.Println("Cancelled.") - return nil - } - } - - // Get signed exit messages - response, err := sw.Api.Validator.Exit(pubkeys, epochPtr, noBroadcastBool) - if err != nil { - return fmt.Errorf("error while getting validator exit messages: %w", err) - } - - // Log successand return if not broadcasting - if !noBroadcastBool { - fmt.Println("Successfully exited the selected validator(s). It will take some time before their status is reflected on the Beacon Chain.") - return nil - } - - // Print them all - fmt.Printf("Exit epoch: %d\n", response.Data.Epoch) - fmt.Println() - for _, info := range response.Data.ExitInfos { - fmt.Printf("Validator %d (%s):\n", info.Index, info.Pubkey.HexWithPrefix()) - fmt.Printf("\tSignature: %s\n", info.Signature.HexWithPrefix()) - fmt.Println() - } + // // Get the client + // hd, err := swclient.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + + // // Check wallet status + // _, ready, err := wallet.CheckIfWalletReady(hd) + // if err != nil { + // return err + // } + // if !ready { + // return nil + // } + + // // Get all active validators + // activeValidatorResponse, err := sw.Api.Status.GetValidatorStatuses() + // if err != nil { + // return fmt.Errorf("error while getting active validators: %w", err) + // } + // var activeValidators []beacon.ValidatorPubkey + // for _, state := range activeValidatorResponse.Data.States { + // if state.BeaconStatus == beacon.ValidatorState_ActiveOngoing { + // activeValidators = append(activeValidators, state.Pubkey) + // } + // } + // if len(activeValidators) == 0 { + // fmt.Println("None of your validators are active, so they cannot be exited.") + // return nil + // } + + // // Get selected validators + // options := make([]nutils.SelectionOption[beacon.ValidatorPubkey], len(activeValidators)) + // for i, pubkey := range activeValidators { + // option := &options[i] + // option.Element = &activeValidators[i] + // option.ID = activeValidators[i].HexWithPrefix() + // option.Display = fmt.Sprintf("%s (active since %s)", pubkey, time.Unix(0, 0)) // Placeholder, fill in with status details + // } + // selectedValidators, err := utils.GetMultiselectIndices(c, pubkeysFlag.Name, options, "Please select a validator to exit:") + // if err != nil { + // return fmt.Errorf("error determining validator selection: %w", err) + // } + + // // Get the epoch if set + // var epochPtr *uint64 + // if c.IsSet(epochFlag.Name) { + // epoch := c.Uint64(epochFlag.Name) + // epochPtr = &epoch + // } + + // // Get the no broadcast flag + // noBroadcastBool := false + // if c.IsSet(noBroadcastFlag.Name) { + // noBroadcastBool = c.Bool(noBroadcastFlag.Name) + // } + + // // Get the pubkeys + // pubkeys := make([]beacon.ValidatorPubkey, len(selectedValidators)) + // for i, validator := range selectedValidators { + // pubkeys[i] = *validator + // } + + // if !noBroadcastBool { + // // Show a warning message + // fmt.Printf("%sNOTE:\n", terminal.ColorYellow) + // fmt.Println("You are about to exit your validator(s). This will tell each one to stop all activities on the Beacon Chain.") + // fmt.Println("Please continue to run them until each one you've exited has been processed by the exit queue. It will no longer earn staking rewards after this point.") + // fmt.Printf("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (typically after a few days).%s\n", terminal.ColorReset) + + // // Prompt for confirmation + // if !(c.Bool(utils.YesFlag.Name) || utils.ConfirmWithIAgree(fmt.Sprintf("Are you sure you want to exit %d validator(s)? This action cannot be undone!", len(selectedValidators)))) { + // fmt.Println("Cancelled.") + // return nil + // } + // } + + // // Get signed exit messages + // response, err := sw.Api.Validator.Exit(pubkeys, epochPtr, noBroadcastBool) + // if err != nil { + // return fmt.Errorf("error while getting validator exit messages: %w", err) + // } + + // // Log successand return if not broadcasting + // if !noBroadcastBool { + // fmt.Println("Successfully exited the selected validator(s). It will take some time before their status is reflected on the Beacon Chain.") + // return nil + // } + + // // Print them all + // fmt.Printf("Exit epoch: %d\n", response.Data.Epoch) + // fmt.Println() + // for _, info := range response.Data.ExitInfos { + // fmt.Printf("Validator %d (%s):\n", info.Index, info.Pubkey.HexWithPrefix()) + // fmt.Printf("\tSignature: %s\n", info.Signature.HexWithPrefix()) + // fmt.Println() + // } // Return return nil diff --git a/adapter/config/generate-deposit-data.go b/adapter/config/generate-deposit-data.go index 78fdded..e119ac2 100644 --- a/adapter/config/generate-deposit-data.go +++ b/adapter/config/generate-deposit-data.go @@ -1,66 +1,61 @@ package config import ( - "encoding/json" - "fmt" - - swclient "github.com/nodeset-org/hyperdrive-stakewise/client" - "github.com/rocket-pool/node-manager-core/beacon" "github.com/urfave/cli/v2" ) func generateDepositData(c *cli.Context) error { - // Get the client - hd, err := swclient.NewHyperdriveClientFromCtx(c) - if err != nil { - return err - } - sw, err := swclient.NewStakewiseClientFromCtx(c, hd) - if err != nil { - return err - } - cfg, _, err := hd.LoadConfig() - if err != nil { - return fmt.Errorf("error loading Hyperdrive config: %w", err) - } - if !cfg.StakeWise.Enabled.Value { - fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - return nil - } + // // Get the client + // hd, err := swclient.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } - // Parse the pubkeys - pubkeyStrings := c.StringSlice(generatePubkeyFlag.Name) - pubkeys := make([]beacon.ValidatorPubkey, len(pubkeyStrings)) - for i, pubkeyString := range pubkeyStrings { - pubkey, err := beacon.HexToValidatorPubkey(pubkeyString) - if err != nil { - return fmt.Errorf("error parsing pubkey [%s]: %w", pubkeyString, err) - } - pubkeys[i] = pubkey - } + // // Parse the pubkeys + // pubkeyStrings := c.StringSlice(generatePubkeyFlag.Name) + // pubkeys := make([]beacon.ValidatorPubkey, len(pubkeyStrings)) + // for i, pubkeyString := range pubkeyStrings { + // pubkey, err := beacon.HexToValidatorPubkey(pubkeyString) + // if err != nil { + // return fmt.Errorf("error parsing pubkey [%s]: %w", pubkeyString, err) + // } + // pubkeys[i] = pubkey + // } - // Generate the deposit data - fmt.Println("Generating deposit data...") - response, err := sw.Api.Nodeset.GenerateDepositData(pubkeys) - if err != nil { - return err - } + // // Generate the deposit data + // fmt.Println("Generating deposit data...") + // response, err := sw.Api.Nodeset.GenerateDepositData(pubkeys) + // if err != nil { + // return err + // } - // Serialize the deposit data - var bytes []byte - shouldIndent := c.Bool(generateIndentFlag.Name) - if shouldIndent { - bytes, err = json.MarshalIndent(response.Data.Deposits, "", " ") - } else { - bytes, err = json.Marshal(response.Data.Deposits) - } - if err != nil { - return fmt.Errorf("error serializing deposit data: %w", err) - } + // // Serialize the deposit data + // var bytes []byte + // shouldIndent := c.Bool(generateIndentFlag.Name) + // if shouldIndent { + // bytes, err = json.MarshalIndent(response.Data.Deposits, "", " ") + // } else { + // bytes, err = json.Marshal(response.Data.Deposits) + // } + // if err != nil { + // return fmt.Errorf("error serializing deposit data: %w", err) + // } - // Print the deposit data - fmt.Println("Deposit data:") - fmt.Println() - fmt.Println(string(bytes)) + // // Print the deposit data + // fmt.Println("Deposit data:") + // fmt.Println() + // fmt.Println(string(bytes)) return nil } diff --git a/adapter/config/generate-keys.go b/adapter/config/generate-keys.go index 3aaa956..50dd1d5 100644 --- a/adapter/config/generate-keys.go +++ b/adapter/config/generate-keys.go @@ -1,126 +1,117 @@ package config import ( - "fmt" - "time" - - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" - swclient "github.com/nodeset-org/hyperdrive-stakewise/client" - swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" - "github.com/rocket-pool/node-manager-core/utils/input" "github.com/urfave/cli/v2" ) func generateKeys(c *cli.Context) error { - // Get the client - hd, err := swclient.NewHyperdriveClientFromCtx(c) - if err != nil { - return err - } - sw, err := swclient.NewStakewiseClientFromCtx(c, hd) - if err != nil { - return err - } - cfg, _, err := hd.LoadConfig() - if err != nil { - return fmt.Errorf("error loading Hyperdrive config: %w", err) - } - if !cfg.StakeWise.Enabled.Value { - fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - return nil - } - noRestart := c.Bool(generateKeysNoRestartFlag.Name) + // // Get the client + // hd, err := swclient.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + // noRestart := c.Bool(generateKeysNoRestartFlag.Name) - // Check wallet status - _, ready, err := wallet.CheckIfWalletReady(hd) - if err != nil { - return err - } - if !ready { - return nil - } + // // Check wallet status + // _, ready, err := wallet.CheckIfWalletReady(hd) + // if err != nil { + // return err + // } + // if !ready { + // return nil + // } - // Get the count - count := c.Uint64(generateKeysCountFlag.Name) - if count == 0 { - countString := utils.Prompt("How many keys would you like to generate?", "^\\d+$", "Invalid count, try again") - count, err = input.ValidateUint("count", countString) - if err != nil { - return fmt.Errorf("invalid count [%s]: %w", countString, err) - } - } + // // Get the count + // count := c.Uint64(generateKeysCountFlag.Name) + // if count == 0 { + // countString := utils.Prompt("How many keys would you like to generate?", "^\\d+$", "Invalid count, try again") + // count, err = input.ValidateUint("count", countString) + // if err != nil { + // return fmt.Errorf("invalid count [%s]: %w", countString, err) + // } + // } - fmt.Println("Note: key generation is an expensive process, this may take a long time! Progress will be printed as each key is generated.") - fmt.Println() + // fmt.Println("Note: key generation is an expensive process, this may take a long time! Progress will be printed as each key is generated.") + // fmt.Println() - // Generate the new keys - startTime := time.Now() - latestTime := startTime - for i := uint64(0); i < count; i++ { - response, err := sw.Api.Wallet.GenerateKeys(1, false) - if err != nil { - return fmt.Errorf("error generating keys: %w", err) - } - if len(response.Data.Pubkeys) == 0 { - return fmt.Errorf("server did not return any pubkeys") - } + // // Generate the new keys + // startTime := time.Now() + // latestTime := startTime + // for i := uint64(0); i < count; i++ { + // response, err := sw.Api.Wallet.GenerateKeys(1, false) + // if err != nil { + // return fmt.Errorf("error generating keys: %w", err) + // } + // if len(response.Data.Pubkeys) == 0 { + // return fmt.Errorf("server did not return any pubkeys") + // } - elapsed := time.Since(latestTime) - latestTime = time.Now() - pubkey := response.Data.Pubkeys[0] - fmt.Printf("Generated %s (%d/%d) in %s\n", pubkey.HexWithPrefix(), (i + 1), count, elapsed) - } - fmt.Printf("Completed in %s.\n", time.Since(startTime)) - fmt.Println() + // elapsed := time.Since(latestTime) + // latestTime = time.Now() + // pubkey := response.Data.Pubkeys[0] + // fmt.Printf("Generated %s (%d/%d) in %s\n", pubkey.HexWithPrefix(), (i + 1), count, elapsed) + // } + // fmt.Printf("Completed in %s.\n", time.Since(startTime)) + // fmt.Println() - // Restart the Stakewise Operator - if noRestart { - fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Stakewise Operator container at your earliest convenience in order to deposit your new keys once it's your turn. Failure to do so will prevent your validators from ever being activated.%s\n", terminal.ColorYellow, terminal.ColorReset) - } else { - fmt.Print("Restarting Stakewise Operator... ") - _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseOperator)) - if err != nil { - fmt.Println("error") - fmt.Printf("%sWARNING: error restarting stakewise operator: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) - fmt.Println("Please restart your Stakewise Operator container in order to be able to deposit for your new keys,") - } else { - fmt.Println("done!") - } - } - fmt.Println() + // // Restart the Stakewise Operator + // if noRestart { + // fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Stakewise Operator container at your earliest convenience in order to deposit your new keys once it's your turn. Failure to do so will prevent your validators from ever being activated.%s\n", terminal.ColorYellow, terminal.ColorReset) + // } else { + // fmt.Print("Restarting Stakewise Operator... ") + // _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseOperator)) + // if err != nil { + // fmt.Println("error") + // fmt.Printf("%sWARNING: error restarting stakewise operator: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) + // fmt.Println("Please restart your Stakewise Operator container in order to be able to deposit for your new keys,") + // } else { + // fmt.Println("done!") + // } + // } + // fmt.Println() - // Restart the VC - if noRestart { - fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Validator Client at your earliest convenience in order to attest with your new keys. Failure to do so will result in any new validators being offline and *losing ETH* until you restart it.%s\n", terminal.ColorYellow, terminal.ColorReset) - } else { - fmt.Print("Restarting Validator Client... ") - _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseValidator)) - if err != nil { - fmt.Println("error") - fmt.Printf("%sWARNING: error restarting validator client: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) - fmt.Println("Please restart your Validator Client in order to attest with your new keys!") - } else { - fmt.Println("done!") - } - } - fmt.Println() + // // Restart the VC + // if noRestart { + // fmt.Printf("%sYou have automatic restarting turned off.\nPlease restart your Validator Client at your earliest convenience in order to attest with your new keys. Failure to do so will result in any new validators being offline and *losing ETH* until you restart it.%s\n", terminal.ColorYellow, terminal.ColorReset) + // } else { + // fmt.Print("Restarting Validator Client... ") + // _, err = hd.Api.Service.RestartContainer(string(swconfig.ContainerID_StakewiseValidator)) + // if err != nil { + // fmt.Println("error") + // fmt.Printf("%sWARNING: error restarting validator client: %s%s\n", terminal.ColorRed, err.Error(), terminal.ColorReset) + // fmt.Println("Please restart your Validator Client in order to attest with your new keys!") + // } else { + // fmt.Println("done!") + // } + // } + // fmt.Println() - // Upload to the server - newKeysUploaded, err := utils.UploadDepositData(c, hd, sw) - if err != nil { - return err - } + // // Upload to the server + // newKeysUploaded, err := utils.UploadDepositData(c, hd, sw) + // if err != nil { + // return err + // } - if newKeysUploaded { - if !noRestart { - fmt.Println() - fmt.Println("Your new keys are now ready for use. When one of them is selected for activation, your system will deposit it and begin attesting automatically.") - } else { - fmt.Println("Your new keys are uploaded, but you *must* restart your Validator Client at your earliest convenience to begin attesting once they are selected for depositing.") - } - } + // if newKeysUploaded { + // if !noRestart { + // fmt.Println() + // fmt.Println("Your new keys are now ready for use. When one of them is selected for activation, your system will deposit it and begin attesting automatically.") + // } else { + // fmt.Println("Your new keys are uploaded, but you *must* restart your Validator Client at your earliest convenience to begin attesting once they are selected for depositing.") + // } + // } return nil } diff --git a/adapter/config/get-node-status.go b/adapter/config/get-node-status.go index aaf8bd6..54280d0 100644 --- a/adapter/config/get-node-status.go +++ b/adapter/config/get-node-status.go @@ -3,8 +3,6 @@ package config import ( "fmt" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" - swclient "github.com/nodeset-org/hyperdrive-stakewise/client" swtypes "github.com/nodeset-org/hyperdrive-stakewise/shared/types" "github.com/rocket-pool/node-manager-core/beacon" @@ -12,60 +10,60 @@ import ( ) func getNodeStatus(c *cli.Context) error { - // Get the client - hd, err := swclient.NewHyperdriveClientFromCtx(c) - if err != nil { - return err - } - sw, err := swclient.NewStakewiseClientFromCtx(c, hd) - if err != nil { - return err - } - cfg, _, err := hd.LoadConfig() - if err != nil { - return fmt.Errorf("error loading Hyperdrive config: %w", err) - } - if !cfg.StakeWise.Enabled.Value { - fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - return nil - } + // // Get the client + // hd, err := swclient.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } - // Check the registration status first - shouldContinue, err := utils.CheckRegistrationStatus(c, hd) - if err != nil { - return fmt.Errorf("error checking nodeset registration status: %w", err) - } - if !shouldContinue { - return nil - } + // // Check the registration status first + // shouldContinue, err := utils.CheckRegistrationStatus(c, hd) + // if err != nil { + // return fmt.Errorf("error checking nodeset registration status: %w", err) + // } + // if !shouldContinue { + // return nil + // } - // Get the validator statuses - response, err := sw.Api.Status.GetValidatorStatuses() - if err != nil { - fmt.Printf("error fetching validator statuses: %v\n", err) - return err - } + // // Get the validator statuses + // response, err := sw.Api.Status.GetValidatorStatuses() + // if err != nil { + // fmt.Printf("error fetching validator statuses: %v\n", err) + // return err + // } - if len(response.Data.States) == 0 { - fmt.Println("You do not have any validators.") - return nil - } + // if len(response.Data.States) == 0 { + // fmt.Println("You do not have any validators.") + // return nil + // } - for _, state := range response.Data.States { - fmt.Printf("%s:\n", state.Pubkey.HexWithPrefix()) + // for _, state := range response.Data.States { + // fmt.Printf("%s:\n", state.Pubkey.HexWithPrefix()) - // Print Beacon status - if state.Index == "" { - fmt.Println("\tBeacon State: Not seen by Beacon Chain yet") - } else { - fmt.Printf("\tBeacon Index: %s\n", state.Index) - fmt.Printf("\tBeacon State: %s\n", getBeaconStatusLabel(state.BeaconStatus)) - } + // // Print Beacon status + // if state.Index == "" { + // fmt.Println("\tBeacon State: Not seen by Beacon Chain yet") + // } else { + // fmt.Printf("\tBeacon Index: %s\n", state.Index) + // fmt.Printf("\tBeacon State: %s\n", getBeaconStatusLabel(state.BeaconStatus)) + // } - // Print NodeSet status - fmt.Printf("\tNodeSet State: %s\n", getNodeSetStateLabel(state.NodesetStatus)) - fmt.Println() - } + // // Print NodeSet status + // fmt.Printf("\tNodeSet State: %s\n", getNodeSetStateLabel(state.NodesetStatus)) + // fmt.Println() + // } return nil } diff --git a/adapter/config/initialize.go b/adapter/config/initialize.go index 52d32f5..b4eab4a 100644 --- a/adapter/config/initialize.go +++ b/adapter/config/initialize.go @@ -1,49 +1,44 @@ package config import ( - "fmt" - - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/terminal" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils/wallet" - swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/urfave/cli/v2" ) func initialize(c *cli.Context) error { - // Get client + // // Get client - hd, err := swclient.NewHyperdriveClientFromCtx(c) - if err != nil { - return err - } - sw, err := swclient.NewStakewiseClientFromCtx(c, hd) - if err != nil { - return err - } - cfg, _, err := hd.LoadConfig() - if err != nil { - return fmt.Errorf("error loading Hyperdrive config: %w", err) - } - if !cfg.StakeWise.Enabled.Value { - fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - return nil - } + // hd, err := swclient.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } - // Check wallet status - _, ready, err := wallet.CheckIfWalletReady(hd) - if err != nil { - return err - } - if !ready { - return nil - } + // // Check wallet status + // _, ready, err := wallet.CheckIfWalletReady(hd) + // if err != nil { + // return err + // } + // if !ready { + // return nil + // } - // Initialize the Stakewise wallet - swResponse, err := sw.Api.Wallet.Initialize() - if err != nil { - return err - } + // // Initialize the Stakewise wallet + // swResponse, err := sw.Api.Wallet.Initialize() + // if err != nil { + // return err + // } - fmt.Printf("Your node wallet has been successfully copied to the Stakewise module with address %s%s%s.", terminal.ColorBlue, swResponse.Data.AccountAddress.Hex(), terminal.ColorReset) + // fmt.Printf("Your node wallet has been successfully copied to the Stakewise module with address %s%s%s.", terminal.ColorBlue, swResponse.Data.AccountAddress.Hex(), terminal.ColorReset) return nil } diff --git a/adapter/config/upload-deposit-data.go b/adapter/config/upload-deposit-data.go index 3baf692..981c3a2 100644 --- a/adapter/config/upload-deposit-data.go +++ b/adapter/config/upload-deposit-data.go @@ -1,33 +1,31 @@ package config import ( - "fmt" - - "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" - swclient "github.com/nodeset-org/hyperdrive-stakewise/client" "github.com/urfave/cli/v2" ) func uploadDepositData(c *cli.Context) error { - // Get the client - hd, err := swclient.NewHyperdriveClientFromCtx(c) - if err != nil { - return err - } - sw, err := swclient.NewStakewiseClientFromCtx(c, hd) - if err != nil { - return err - } - cfg, _, err := hd.LoadConfig() - if err != nil { - return fmt.Errorf("error loading Hyperdrive config: %w", err) - } - if !cfg.StakeWise.Enabled.Value { - fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") - return nil - } + // // Get the client + // hd, err := swclient.NewHyperdriveClientFromCtx(c) + // if err != nil { + // return err + // } + // sw, err := swclient.NewStakewiseClientFromCtx(c, hd) + // if err != nil { + // return err + // } + // cfg, _, err := hd.LoadConfig() + // if err != nil { + // return fmt.Errorf("error loading Hyperdrive config: %w", err) + // } + // if !cfg.StakeWise.Enabled.Value { + // fmt.Println("The StakeWise module is not enabled in your Hyperdrive configuration.") + // return nil + // } + + // // Upload to the server + // _, err = utils.UploadDepositData(c, hd, sw) + // return err - // Upload to the server - _, err = utils.UploadDepositData(c, hd, sw) - return err + return nil } diff --git a/adapter/hd-module/process-settings.go b/adapter/hd-module/process-settings.go index 2d8130f..76878f7 100644 --- a/adapter/hd-module/process-settings.go +++ b/adapter/hd-module/process-settings.go @@ -1,13 +1,7 @@ package hdmodule import ( - "fmt" - - "github.com/goccy/go-json" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config/ids" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" - hdconfig "github.com/nodeset-org/hyperdrive/modules/config" "github.com/urfave/cli/v2" ) @@ -30,41 +24,41 @@ type processConfigResponse struct { // Handle the `process-config` command func processSetting(c *cli.Context) error { - // Get the request - request, err := utils.HandleKeyedRequest[*processConfigRequest](c) - if err != nil { - return err - } + // // Get the request + // request, err := utils.HandleKeyedRequest[*processConfigRequest](c) + // if err != nil { + // return err + // } - // Get the config - cfg := config.NewStakeWiseConfig() - err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) - if err != nil { - return err - } + // // Get the config + // cfg := config.NewStakeWiseConfig() + // err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) + // if err != nil { + // return err + // } - // This is where any examples of validation will go when added - errors := []string{} + // // This is where any examples of validation will go when added + // errors := []string{} - // Get the open ports - ports := map[string]uint16{} - if cfg.ServerConfig.PortMode.Value != config.PortMode_Closed { - ports[ids.ServerConfigID+"/"+ids.PortModeID] = uint16(cfg.ServerConfig.Port.Value) - } + // // Get the open ports + // ports := map[string]uint16{} + // if cfg.ServerConfig.PortMode.Value != config.PortMode_Closed { + // ports[ids.ServerConfigID+"/"+ids.PortModeID] = uint16(cfg.ServerConfig.Port.Value) + // } - // Create the response - response := processConfigResponse{ - Errors: errors, - Ports: ports, - } + // // Create the response + // response := processConfigResponse{ + // Errors: errors, + // Ports: ports, + // } - // Marshal it - bytes, err := json.Marshal(response) - if err != nil { - return fmt.Errorf("error marshalling process-config response: %w", err) - } + // // Marshal it + // bytes, err := json.Marshal(response) + // if err != nil { + // return fmt.Errorf("error marshalling process-config response: %w", err) + // } - // Print it - fmt.Println(string(bytes)) + // // Print it + // fmt.Println(string(bytes)) return nil } diff --git a/adapter/hd-module/set-settings.go b/adapter/hd-module/set-settings.go index 1a085b0..dadce2b 100644 --- a/adapter/hd-module/set-settings.go +++ b/adapter/hd-module/set-settings.go @@ -1,11 +1,7 @@ package hdmodule import ( - "fmt" - - "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" - hdconfig "github.com/nodeset-org/hyperdrive/modules/config" "github.com/urfave/cli/v2" ) @@ -19,30 +15,30 @@ type setConfigRequest struct { // Handle the `set-config` command func setSettings(c *cli.Context) error { - // Get the request - request, err := utils.HandleKeyedRequest[*setConfigRequest](c) - if err != nil { - return err - } - - // Get the config - cfg := config.NewStakeWiseConfig() - err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) - if err != nil { - return err - } - - // Make a config manager - cfgMgr, err := config.NewAdapterConfigManager(c) - if err != nil { - return fmt.Errorf("error creating config manager: %w", err) - } - cfgMgr.AdapterConfig = cfg - - // Save it - err = cfgMgr.SaveConfigToDisk() - if err != nil { - return fmt.Errorf("error saving config: %w", err) - } + // // Get the request + // request, err := utils.HandleKeyedRequest[*setConfigRequest](c) + // if err != nil { + // return err + // } + + // // Get the config + // cfg := config.NewStakeWiseConfig() + // err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) + // if err != nil { + // return err + // } + + // // Make a config manager + // cfgMgr, err := config.NewAdapterConfigManager(c) + // if err != nil { + // return fmt.Errorf("error creating config manager: %w", err) + // } + // cfgMgr.AdapterConfig = cfg + + // // Save it + // err = cfgMgr.SaveConfigToDisk() + // if err != nil { + // return fmt.Errorf("error saving config: %w", err) + // } return nil } diff --git a/common/service-provider.go b/common/service-provider.go index 0cf5e92..7edad73 100644 --- a/common/service-provider.go +++ b/common/service-provider.go @@ -5,6 +5,8 @@ import ( "fmt" "reflect" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-daemon/module-utils/services" swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" "github.com/rocket-pool/node-manager-core/wallet" @@ -13,7 +15,7 @@ import ( // Provides the StakeWise module config and resources type IStakeWiseConfigProvider interface { // Gets the StakeWise config - GetConfig() *swconfig.StakeWiseConfig + GetConfig() *config.StakeWiseConfig // Gets the StakeWise resources GetResources() *swconfig.MergedResources @@ -48,7 +50,7 @@ type IStakeWiseServiceProvider interface { type stakeWiseServiceProvider struct { services.IModuleServiceProvider - swCfg *swconfig.StakeWiseConfig + swCfg *config.StakeWiseConfig wallet *Wallet resources *swconfig.MergedResources depositDataManager *DepositDataManager @@ -57,7 +59,7 @@ type stakeWiseServiceProvider struct { // Create a new service provider with Stakewise daemon-specific features func NewStakeWiseServiceProvider(sp services.IModuleServiceProvider, settingsList []*swconfig.StakeWiseSettings) (IStakeWiseServiceProvider, error) { // Create the resources - swCfg, ok := sp.GetModuleConfig().(*swconfig.StakeWiseConfig) + swCfg, ok := sp.GetModuleConfig().(*config.StakeWiseConfig) if !ok { return nil, fmt.Errorf("stakewise config is not the correct type, it's a %s", reflect.TypeOf(swCfg)) } @@ -83,7 +85,7 @@ func NewStakeWiseServiceProvider(sp services.IModuleServiceProvider, settingsLis } // Create a new service provider with Stakewise daemon-specific features, using custom services instead of loading them from the module service provider. -func NewStakeWiseServiceProviderFromCustomServices(sp services.IModuleServiceProvider, cfg *swconfig.StakeWiseConfig, resources *swconfig.MergedResources) (IStakeWiseServiceProvider, error) { +func NewStakeWiseServiceProviderFromCustomServices(sp services.IModuleServiceProvider, cfg *config.StakeWiseConfig, resources *swconfig.MergedResources) (IStakeWiseServiceProvider, error) { // Create the wallet wallet, err := NewWallet(sp) if err != nil { @@ -107,7 +109,7 @@ func NewStakeWiseServiceProviderFromCustomServices(sp services.IModuleServicePro return stakewiseSp, nil } -func (s *stakeWiseServiceProvider) GetConfig() *swconfig.StakeWiseConfig { +func (s *stakeWiseServiceProvider) GetConfig() *config.StakeWiseConfig { return s.swCfg } diff --git a/shared/config/resources.go b/shared/config/resources.go index 8764361..ea71513 100644 --- a/shared/config/resources.go +++ b/shared/config/resources.go @@ -1,4 +1,4 @@ -package swconfig +package config import ( "errors" From 1ab36b72a40bf50f912018605749b789d52cf0f7 Mon Sep 17 00:00:00 2001 From: huy Date: Mon, 3 Feb 2025 09:18:17 -0800 Subject: [PATCH 25/28] WIP --- client/client.go | 97 +++++++++++++------------- server/service/get-network-settings.go | 3 +- testing/node.go | 10 ++- testing/test-manager.go | 7 +- 4 files changed, 57 insertions(+), 60 deletions(-) diff --git a/client/client.go b/client/client.go index 9fa52e3..63db13c 100644 --- a/client/client.go +++ b/client/client.go @@ -16,7 +16,7 @@ import ( hdclient "github.com/nodeset-org/hyperdrive-daemon/client" "github.com/nodeset-org/hyperdrive-daemon/shared/auth" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" - swclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" + adapterclient "github.com/nodeset-org/hyperdrive-stakewise/adapter/client" swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" "github.com/rocket-pool/node-manager-core/api/client" @@ -83,7 +83,7 @@ func NewHyperdriveClientFromHyperdriveCtx(hdCtx *context.HyperdriveContext) (*Hy var tracer *httptrace.ClientTrace if hdCtx.HttpTraceFile != nil { var err error - tracer, err = swclient.CreateTracer(hdCtx.HttpTraceFile, logger) + tracer, err = adapterclient.CreateTracer(hdCtx.HttpTraceFile, logger) if err != nil { logger.Error("Error creating HTTP trace", log.Err(err)) } @@ -245,50 +245,51 @@ func NewStakewiseClientFromCtx(c *cli.Context, hdClient *HyperdriveClient) (*Sta // Create new Stakewise client from a custom context // Only use this function from commands that may work if the Daemon service doesn't exist func NewStakewiseClientFromHyperdriveCtx(hdCtx *context.HyperdriveContext, hdClient *HyperdriveClient) (*StakewiseClient, error) { - logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, swconfig.ModuleName)) - - // Create the tracer if required - var tracer *httptrace.ClientTrace - if hdCtx.HttpTraceFile != nil { - var err error - tracer, err = swclient.CreateTracer(hdCtx.HttpTraceFile, logger) - if err != nil { - logger.Error("Error creating HTTP trace", log.Err(err)) - } - } - - // Make the client - swClient := &StakewiseClient{ - Context: hdCtx, - Logger: logger, - } - - // Get the API URL - url := hdCtx.ApiUrl - if url == nil { - var err error - url, err = url.Parse(fmt.Sprintf("http://localhost:%d/%s", hdClient.cfg.StakeWise.ApiPort.Value, swconfig.ApiClientRoute)) - if err != nil { - return nil, fmt.Errorf("error parsing StakeWise API URL: %w", err) - } - } else { - host := fmt.Sprintf("%s://%s:%d/%s", url.Scheme, url.Hostname(), hdClient.cfg.StakeWise.ApiPort.Value, swconfig.ApiClientRoute) - var err error - url, err = url.Parse(host) - if err != nil { - return nil, fmt.Errorf("error parsing StakeWise API URL: %w", err) - } - } - - // Create the auth manager - authPath := filepath.Join(hdCtx.UserDirPath, swApiKeyRelPath) - err := auth.GenerateAuthKeyIfNotPresent(authPath, auth.DefaultKeyLength) - if err != nil { - return nil, fmt.Errorf("error generating StakeWise module API key: %w", err) - } - authMgr := auth.NewAuthorizationManager(authPath, cliIssuer, auth.DefaultRequestLifespan) - - // Create the API client - swClient.Api = NewApiClient(url, logger, tracer, authMgr) - return swClient, nil + return &StakewiseClient{}, nil + // logger := log.NewTerminalLogger(hdCtx.DebugEnabled, terminalLogColor).With(slog.String(log.OriginKey, swconfig.ModuleName)) + + // // Create the tracer if required + // var tracer *httptrace.ClientTrace + // if hdCtx.HttpTraceFile != nil { + // var err error + // tracer, err = adapterclient.CreateTracer(hdCtx.HttpTraceFile, logger) + // if err != nil { + // logger.Error("Error creating HTTP trace", log.Err(err)) + // } + // } + + // // Make the client + // swClient := &StakewiseClient{ + // Context: hdCtx, + // Logger: logger, + // } + + // // Get the API URL + // url := hdCtx.ApiUrl + // if url == nil { + // var err error + // url, err = url.Parse(fmt.Sprintf("http://localhost:%d/%s", hdClient.cfg.StakeWise.ApiPort.Value, swconfig.ApiClientRoute)) + // if err != nil { + // return nil, fmt.Errorf("error parsing StakeWise API URL: %w", err) + // } + // } else { + // host := fmt.Sprintf("%s://%s:%d/%s", url.Scheme, url.Hostname(), hdClient.cfg.StakeWise.ApiPort.Value, swconfig.ApiClientRoute) + // var err error + // url, err = url.Parse(host) + // if err != nil { + // return nil, fmt.Errorf("error parsing StakeWise API URL: %w", err) + // } + // } + + // // Create the auth manager + // authPath := filepath.Join(hdCtx.UserDirPath, swApiKeyRelPath) + // err := auth.GenerateAuthKeyIfNotPresent(authPath, auth.DefaultKeyLength) + // if err != nil { + // return nil, fmt.Errorf("error generating StakeWise module API key: %w", err) + // } + // authMgr := auth.NewAuthorizationManager(authPath, cliIssuer, auth.DefaultRequestLifespan) + + // // Create the API client + // swClient.Api = NewApiClient(url, logger, tracer, authMgr) + // return swClient, nil } diff --git a/server/service/get-network-settings.go b/server/service/get-network-settings.go index 68ecfcc..df8c905 100644 --- a/server/service/get-network-settings.go +++ b/server/service/get-network-settings.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/gorilla/mux" "github.com/nodeset-org/hyperdrive-daemon/module-utils/server" + "github.com/nodeset-org/hyperdrive-stakewise/client" swapi "github.com/nodeset-org/hyperdrive-stakewise/shared/api" "github.com/rocket-pool/node-manager-core/api/types" "github.com/rocket-pool/node-manager-core/wallet" @@ -45,7 +46,7 @@ func (c *serviceGetNetworkSettingsContext) PrepareData(data *swapi.ServiceGetNet sp := c.handler.serviceProvider hdCfg := sp.GetHyperdriveConfig() swCfg := sp.GetConfig() - settingsList := swCfg.GetNetworkSettings() + settingsList := client.GetNetworkSettings() network := hdCfg.Network.Value for _, settings := range settingsList { if settings.Key == network { diff --git a/testing/node.go b/testing/node.go index bd7b5a4..e5c49aa 100644 --- a/testing/node.go +++ b/testing/node.go @@ -12,6 +12,7 @@ import ( "github.com/nodeset-org/hyperdrive-daemon/shared/auth" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" hdtesting "github.com/nodeset-org/hyperdrive-daemon/testing" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" swclient "github.com/nodeset-org/hyperdrive-stakewise/client" swcommon "github.com/nodeset-org/hyperdrive-stakewise/common" swserver "github.com/nodeset-org/hyperdrive-stakewise/server" @@ -115,15 +116,12 @@ func (n *StakeWiseNode) CreateSubNode(hdNode *hdtesting.HyperdriveNode, address // Make Constellation resources resources := getTestResources(hdSp.GetResources(), deploymentName) - csCfg, err := swconfig.NewStakeWiseConfig(hdCfg, []*swconfig.StakeWiseSettings{}) - if err != nil { - return nil, fmt.Errorf("error creating Constellation config: %v", err) - } - csCfg.ApiPort.Value = port + csCfg := config.NewStakeWiseConfig() + // csCfg.ApiPort.Value = port // Make sure the module directory exists moduleDir := filepath.Join(hdCfg.UserDataPath.Value, hdconfig.ModulesName, swconfig.ModuleName) - err = os.MkdirAll(moduleDir, 0755) + err := os.MkdirAll(moduleDir, 0755) if err != nil { return nil, fmt.Errorf("error creating data and modules directories [%s]: %v", moduleDir, err) } diff --git a/testing/test-manager.go b/testing/test-manager.go index f198a09..e093d56 100644 --- a/testing/test-manager.go +++ b/testing/test-manager.go @@ -8,6 +8,7 @@ import ( hdservices "github.com/nodeset-org/hyperdrive-daemon/module-utils/services" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" hdtesting "github.com/nodeset-org/hyperdrive-daemon/testing" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" swcommon "github.com/nodeset-org/hyperdrive-stakewise/common" swconfig "github.com/nodeset-org/hyperdrive-stakewise/shared/config" "github.com/rocket-pool/node-manager-core/log" @@ -43,11 +44,7 @@ func NewStakeWiseTestManager() (*StakeWiseTestManager, error) { // Make StakeWise resources resources := getTestResources(hdSp.GetResources(), deploymentName) - swCfg, err := swconfig.NewStakeWiseConfig(hdCfg, []*swconfig.StakeWiseSettings{}) - if err != nil { - closeTestManager(tm) - return nil, fmt.Errorf("error creating StakeWise config: %v", err) - } + swCfg := config.NewStakeWiseConfig() // Make the module directory moduleDir := filepath.Join(hdCfg.UserDataPath.Value, hdconfig.ModulesName, swconfig.ModuleName) From 6d46ab9b39a2ce5e16552231a3c2f7ce7be829b9 Mon Sep 17 00:00:00 2001 From: huy Date: Mon, 3 Feb 2025 09:25:03 -0800 Subject: [PATCH 26/28] uncommnet setSettings and processSettings --- adapter/hd-module/process-settings.go | 65 ++++++++++++++++----------- adapter/hd-module/set-settings.go | 62 ++++++++++++++----------- adapter/utils/consts.go | 9 ++++ go.mod | 1 + go.sum | 4 ++ 5 files changed, 87 insertions(+), 54 deletions(-) diff --git a/adapter/hd-module/process-settings.go b/adapter/hd-module/process-settings.go index 76878f7..d066817 100644 --- a/adapter/hd-module/process-settings.go +++ b/adapter/hd-module/process-settings.go @@ -1,6 +1,12 @@ package hdmodule import ( + "fmt" + + hdconfig "github.com/nodeset-org/hyperdrive/shared/config" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" "github.com/urfave/cli/v2" ) @@ -10,7 +16,7 @@ type processConfigRequest struct { utils.KeyedRequest // The config instance to process - Config map[string]any `json:"config"` + Settings *hdconfig.HyperdriveSettings `json:"settings"` } // Response format for `process-config` @@ -24,41 +30,46 @@ type processConfigResponse struct { // Handle the `process-config` command func processSetting(c *cli.Context) error { - // // Get the request - // request, err := utils.HandleKeyedRequest[*processConfigRequest](c) - // if err != nil { - // return err - // } + // Get the request + request, err := utils.HandleKeyedRequest[*processConfigRequest](c) + if err != nil { + return err + } - // // Get the config - // cfg := config.NewStakeWiseConfig() - // err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) - // if err != nil { - // return err - // } + // Construct the module settings from the Hyperdrive config + modInstance, exists := request.Settings.Modules[utils.FullyQualifiedModuleName] + if !exists { + return fmt.Errorf("could not find settings for %s", utils.FullyQualifiedModuleName) + } + var settings config.StakeWiseConfigSettings + err = modInstance.DeserializeSettingsIntoKnownType(&settings) + if err != nil { + return fmt.Errorf("error loading settings: %w", err) + } - // // This is where any examples of validation will go when added - // errors := []string{} + // This is where any examples of validation will go when added + errors := []string{} // // Get the open ports - // ports := map[string]uint16{} + ports := map[string]uint16{} + // if cfg.ServerConfig.PortMode.Value != config.PortMode_Closed { // ports[ids.ServerConfigID+"/"+ids.PortModeID] = uint16(cfg.ServerConfig.Port.Value) // } - // // Create the response - // response := processConfigResponse{ - // Errors: errors, - // Ports: ports, - // } + // Create the response + response := processConfigResponse{ + Errors: errors, + Ports: ports, + } - // // Marshal it - // bytes, err := json.Marshal(response) - // if err != nil { - // return fmt.Errorf("error marshalling process-config response: %w", err) - // } + // Marshal it + bytes, err := json.Marshal(response) + if err != nil { + return fmt.Errorf("error marshalling process-config response: %w", err) + } - // // Print it - // fmt.Println(string(bytes)) + // Print it + fmt.Println(string(bytes)) return nil } diff --git a/adapter/hd-module/set-settings.go b/adapter/hd-module/set-settings.go index dadce2b..50586d7 100644 --- a/adapter/hd-module/set-settings.go +++ b/adapter/hd-module/set-settings.go @@ -1,44 +1,52 @@ package hdmodule import ( + "fmt" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + hdconfig "github.com/nodeset-org/hyperdrive/shared/config" "github.com/urfave/cli/v2" ) // Request format for `set-config` -type setConfigRequest struct { +type setSettingsRequest struct { utils.KeyedRequest // The config instance to process - Config map[string]any `json:"config"` + Settings *hdconfig.HyperdriveSettings `json:"settings"` } // Handle the `set-config` command func setSettings(c *cli.Context) error { - // // Get the request - // request, err := utils.HandleKeyedRequest[*setConfigRequest](c) - // if err != nil { - // return err - // } - - // // Get the config - // cfg := config.NewStakeWiseConfig() - // err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) - // if err != nil { - // return err - // } - - // // Make a config manager - // cfgMgr, err := config.NewAdapterConfigManager(c) - // if err != nil { - // return fmt.Errorf("error creating config manager: %w", err) - // } - // cfgMgr.AdapterConfig = cfg - - // // Save it - // err = cfgMgr.SaveConfigToDisk() - // if err != nil { - // return fmt.Errorf("error saving config: %w", err) - // } + // Get the request + request, err := utils.HandleKeyedRequest[*setSettingsRequest](c) + if err != nil { + return err + } + + // Construct the module settings from the Hyperdrive config + modInstance, exists := request.Settings.Modules[utils.FullyQualifiedModuleName] + if !exists { + return fmt.Errorf("could not find config for %s", utils.FullyQualifiedModuleName) + } + var settings config.StakeWiseConfigSettings + err = modInstance.DeserializeSettingsIntoKnownType(&settings) + if err != nil { + return fmt.Errorf("error loading settings: %w", err) + } + + // Make a config manager + cfgMgr, err := config.NewAdapterConfigManager(c) + if err != nil { + return fmt.Errorf("error creating config manager: %w", err) + } + cfgMgr.AdapterConfig = &settings + + // Save it + err = cfgMgr.SaveConfigToDisk() + if err != nil { + return fmt.Errorf("error saving config: %w", err) + } return nil } diff --git a/adapter/utils/consts.go b/adapter/utils/consts.go index 55d7b11..a21dddb 100644 --- a/adapter/utils/consts.go +++ b/adapter/utils/consts.go @@ -12,4 +12,13 @@ const ( // Service configuration file ServiceConfigFile string = "service-cfg.yaml" + + // Module name as it appears in the descriptor + ModuleName string = "hyperdrive-stakewise" + + // Author name as it appears in the descriptor + AuthorName string = "NodeSet" + + // Fully qualified module name + FullyQualifiedModuleName string = AuthorName + "/" + ModuleName ) diff --git a/go.mod b/go.mod index 0e4243b..97cf606 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( ) require ( + al.essio.dev/pkg/shellescape v1.5.1 // indirect filippo.io/age v1.2.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20240716105424-66b64c4bb379 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect diff --git a/go.sum b/go.sum index 482ae09..027a3eb 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho= +al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -237,6 +239,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= From 91f83963e72d47f07079385239aabc711147f080 Mon Sep 17 00:00:00 2001 From: huy Date: Mon, 3 Feb 2025 12:45:29 -0800 Subject: [PATCH 27/28] upgradeInstance --- adapter/hd-module/commands.go | 13 +++++ adapter/hd-module/upgrade-instance.go | 80 +++++++++++++++------------ 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/adapter/hd-module/commands.go b/adapter/hd-module/commands.go index d62b1da..bc6cfeb 100644 --- a/adapter/hd-module/commands.go +++ b/adapter/hd-module/commands.go @@ -51,6 +51,19 @@ func RegisterCommands(app *cli.App) { return getConfigMetadata(c) }, }, + { + Name: "upgrade-instance", + Aliases: []string{"u"}, + Flags: []cli.Flag{}, + Usage: "Upgrade an instance of the module's configuration to the latest version - used when the configuration was generated with an older version of this module.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return upgradeInstance(c) + }, + }, { Name: "process-config", Aliases: []string{"p"}, diff --git a/adapter/hd-module/upgrade-instance.go b/adapter/hd-module/upgrade-instance.go index 886fb0b..9521121 100644 --- a/adapter/hd-module/upgrade-instance.go +++ b/adapter/hd-module/upgrade-instance.go @@ -1,7 +1,12 @@ package hdmodule import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/shared" modconfig "github.com/nodeset-org/hyperdrive/modules/config" "github.com/urfave/cli/v2" ) @@ -16,16 +21,15 @@ type upgradeInstanceRequest struct { // Handle the `upgrade-instance` command func upgradeInstance(c *cli.Context) error { - return nil - // // Get the request - // request, err := utils.HandleKeyedRequest[*upgradeInstanceRequest](c) - // if err != nil { - // return err - // } - // modInstance := request.Instance + // Get the request + request, err := utils.HandleKeyedRequest[*upgradeInstanceRequest](c) + if err != nil { + return err + } + modInstance := request.Instance - // // Switch on the instance version - // var settings config.HyperdriveEthereumConfigSettings + // Switch on the instance version + var settings *config.StakeWiseConfigSettings // v0_2_0 := semver.MustParse("0.2.0") // version := semver.MustParse(modInstance.Version) // if version.LT(v0_2_0) { @@ -43,22 +47,28 @@ func upgradeInstance(c *cli.Context) error { // } // } - // // Create the response - // response := modconfig.ModuleInstance{ - // Enabled: modInstance.Enabled, - // Version: shared.HyperdriveEthereumVersion, - // } - // response.SetSettingsFromKnownType(settings) + // Deserialize the settings + settings, err = deserializeSettings_Latest(modInstance.Settings) + if err != nil { + return fmt.Errorf("error deserializing settings: %w", err) + } - // // Marshal it - // bytes, err := json.Marshal(response) - // if err != nil { - // return fmt.Errorf("error marshalling upgrade-instance response: %w", err) - // } + // Create the response + response := modconfig.ModuleInstance{ + Enabled: modInstance.Enabled, + Version: shared.StakewiseVersion, + } + response.SetSettingsFromKnownType(settings) - // // Print it - // fmt.Println(string(bytes)) - // return nil + // Marshal it + bytes, err := json.Marshal(response) + if err != nil { + return fmt.Errorf("error marshalling upgrade-instance response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil } // Deserialize the settings for a v0.1.0 configuration @@ -77,16 +87,16 @@ func upgradeInstance(c *cli.Context) error { // } // Deserialize the settings for the latest configuration -// func deserializeSettings_Latest(settings map[string]any) (*config.ExampleConfigSettings, error) { -// bytes, err := json.Marshal(settings) -// if err != nil { -// return nil, fmt.Errorf("error marshalling settings: %w", err) -// } +func deserializeSettings_Latest(settings map[string]any) (*config.StakeWiseConfigSettings, error) { + bytes, err := json.Marshal(settings) + if err != nil { + return nil, fmt.Errorf("error marshalling settings: %w", err) + } -// var cfg config.ExampleConfigSettings -// err = json.Unmarshal(bytes, &cfg) -// if err != nil { -// return nil, fmt.Errorf("error unmarshalling settings: %w", err) -// } -// return &cfg, nil -// } + var cfg config.StakeWiseConfigSettings + err = json.Unmarshal(bytes, &cfg) + if err != nil { + return nil, fmt.Errorf("error unmarshalling settings: %w", err) + } + return &cfg, nil +} From e1571de9df216ba2eed63ec9cb2c5452e815282b Mon Sep 17 00:00:00 2001 From: huy Date: Mon, 3 Feb 2025 15:58:12 -0800 Subject: [PATCH 28/28] TODO before coming back later --- adapter/config/config.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/adapter/config/config.go b/adapter/config/config.go index 942bfc5..23f02a0 100644 --- a/adapter/config/config.go +++ b/adapter/config/config.go @@ -25,6 +25,7 @@ type StakeWiseConfig struct { OperatorContainerTag hdconfig.StringParameter AdditionalOpFlags hdconfig.StringParameter + // TODO: (HN) - Recreate all this stuff VcCommon *config.ValidatorClientCommonConfig Lighthouse *config.LighthouseVcConfig Lodestar *config.LodestarVcConfig @@ -104,7 +105,9 @@ func (cfg StakeWiseConfig) GetParameters() []hdconfig.IParameter { } func (cfg StakeWiseConfig) GetSections() []hdconfig.ISection { - return []hdconfig.ISection{} + return []hdconfig.ISection{ + // TODO: Clients need to get added here after porting + } } func CreateInstanceFromNativeConfig(native *sharedconfig.NativeStakeWiseConfigSettings) *StakeWiseConfigSettings {