Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/hyperdrive 2.0 module #49

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ build/
# Binaries
hyperdrive-stakewise
logs/

.DS_Store
94 changes: 94 additions & 0 deletions adapter/client/tracer.go
Original file line number Diff line number Diff line change
@@ -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("<error writing to HTTP trace file>", 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("<error writing to HTTP trace file>", 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("<error writing to HTTP trace file>", 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("<error writing to HTTP trace file>", log.Err(err))
}
}
tracer.GotFirstResponseByte = func() {
logger.Debug("HTTP Got First Response Byte")
err := writeToTraceFile(file, "GotFirstResponseByte")
if err != nil {
logger.Debug("<error writing to HTTP trace file>", 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("<error writing to HTTP trace file>", 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("<error writing to HTTP trace file>", 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
}
79 changes: 79 additions & 0 deletions adapter/command/claim-rewards.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package command

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
}

// 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()

// 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
}
156 changes: 156 additions & 0 deletions adapter/command/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package command

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{
{
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...)
}
Loading
Loading