From 0dcf30b79ed4fae939a2dcc3e3d4c016c7f7e550 Mon Sep 17 00:00:00 2001 From: Jacob Shufro Date: Sun, 1 Jun 2025 10:46:02 -0400 Subject: [PATCH 1/2] Add a port for node webserver/api --- rocketpool/node/node.go | 2 +- shared/services/config/smartnode-config.go | 31 +++++++++++++++++++ .../assets/install/templates/node.tmpl | 1 + shared/types/config/port-modes.go | 13 ++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/rocketpool/node/node.go b/rocketpool/node/node.go index 0daff0c53..4287bbfbe 100644 --- a/rocketpool/node/node.go +++ b/rocketpool/node/node.go @@ -51,7 +51,7 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { app.Commands = append(app.Commands, cli.Command{ Name: name, Aliases: aliases, - Usage: "Run Rocket Pool node activity daemon", + Usage: "Run Rocket Pool node activity daemon/webserver", Action: func(c *cli.Context) error { return run(c) }, diff --git a/shared/services/config/smartnode-config.go b/shared/services/config/smartnode-config.go index 2dc4d9656..35a5ae730 100644 --- a/shared/services/config/smartnode-config.go +++ b/shared/services/config/smartnode-config.go @@ -112,6 +112,12 @@ type SmartnodeConfig struct { // Threshold for automatic vote power initialization transactions AutoInitVPThreshold config.Parameter `yaml:"autoInitVPThreshold,omitempty"` + // Port for the node's webserver + APIPort config.Parameter `yaml:"apiPort,omitempty"` + + // Whether to expose the node's API port to the local network + OpenAPIPort config.Parameter `yaml:"openAPIPort,omitempty"` + /////////////////////////// // Non-editable settings // /////////////////////////// @@ -413,6 +419,29 @@ func NewSmartnodeConfig(cfg *RocketPoolConfig) *SmartnodeConfig { OverwriteOnUpgrade: true, }, + APIPort: config.Parameter{ + ID: "apiPort", + Name: "API Port", + Description: "The port your Smartnode's API should listen on.", + Type: config.ParameterType_Uint16, + Default: map[config.Network]interface{}{config.Network_All: uint16(8280)}, + AffectsContainers: []config.ContainerID{config.ContainerID_Node}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + }, + + OpenAPIPort: config.Parameter{ + ID: "openAPIPort", + Name: "Expose API Port", + Description: "Expose the API port to other processes on your machine. For security reasons, you cannot expose the API port except to localhost. It is recommended to keep this CLOSED.", + Type: config.ParameterType_Choice, + Default: map[config.Network]interface{}{config.Network_All: config.RPC_Closed}, + AffectsContainers: []config.ContainerID{config.ContainerID_Node}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + Options: config.RestrictedPortModes(), + }, + txWatchUrl: map[config.Network]string{ config.Network_Mainnet: "https://etherscan.io/tx", config.Network_Devnet: "https://hoodi.etherscan.io/tx", @@ -635,6 +664,8 @@ func (cfg *SmartnodeConfig) GetParameters() []*config.Parameter { &cfg.ArchiveECUrl, &cfg.WatchtowerMaxFeeOverride, &cfg.WatchtowerPrioFeeOverride, + &cfg.APIPort, + &cfg.OpenAPIPort, } } diff --git a/shared/services/rocketpool/assets/install/templates/node.tmpl b/shared/services/rocketpool/assets/install/templates/node.tmpl index 1fe7025d4..9d94fa0e9 100644 --- a/shared/services/rocketpool/assets/install/templates/node.tmpl +++ b/shared/services/rocketpool/assets/install/templates/node.tmpl @@ -10,6 +10,7 @@ services: image: {{.Smartnode.GetSmartnodeContainerTag}} container_name: {{.Smartnode.ProjectName}}_node restart: unless-stopped + ports: [{{.GetNodeOpenPorts}}] volumes: - /var/run/docker.sock:/var/run/docker.sock - {{.RocketPoolDirectory}}:/.rocketpool diff --git a/shared/types/config/port-modes.go b/shared/types/config/port-modes.go index 936474fde..6d78e7dfb 100644 --- a/shared/types/config/port-modes.go +++ b/shared/types/config/port-modes.go @@ -55,3 +55,16 @@ func PortModes(warningOverride string) []ParameterOption { Value: RPC_OpenExternal, }} } + +func RestrictedPortModes() []ParameterOption { + + return []ParameterOption{{ + Name: "Closed", + Description: "Do not allow connections to the port", + Value: RPC_Closed, + }, { + Name: "Open to Localhost", + Description: "Allow connections from this host only", + Value: RPC_OpenLocalhost, + }} +} From 5005a80f517e4fad825de46547fb2883b7e03755 Mon Sep 17 00:00:00 2001 From: Jacob Shufro Date: Tue, 15 Jul 2025 09:47:26 -0400 Subject: [PATCH 2/2] WIP --- rocketpool/node/http.go | 46 +++++++++++++++++++++++++++++++++ rocketpool/node/node.go | 7 +++++ rocketpool/node/routes/state.go | 1 + 3 files changed, 54 insertions(+) create mode 100644 rocketpool/node/http.go create mode 100644 rocketpool/node/routes/state.go diff --git a/rocketpool/node/http.go b/rocketpool/node/http.go new file mode 100644 index 000000000..526d8ea17 --- /dev/null +++ b/rocketpool/node/http.go @@ -0,0 +1,46 @@ +package node + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/rocket-pool/smartnode/shared/services/config" + cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" + "google.golang.org/genproto/googleapis/maps/routes/v1" +) + +type httpServer struct { + server *http.Server +} + +func startHTTP(ctx context.Context, cfg *config.RocketPoolConfig) { + host := "127.0.0.1" + if cfg.Smartnode.OpenAPIPort.Value == cfgtypes.RPC_OpenLocalhost { + host = "0.0.0.0" + } + + port, ok := cfg.Smartnode.APIPort.Value.(uint16) + if !ok { + log.Fatalf("Error getting API port: %v", err) + } + + httpServer := &httpServer{} + + server := &http.Server{ + Addr: fmt.Sprintf("%s:%d", host, port), + Handler: httpServer, + } + + httpServer.server = server +} + +func (s *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + w.WriteHeader(http.StatusOK) + w.Write([]byte("Hello, World!")) +} + +func (s *httpServer) addRoute(r *routes.Route) { +} diff --git a/rocketpool/node/node.go b/rocketpool/node/node.go index 4287bbfbe..210f0ea1c 100644 --- a/rocketpool/node/node.go +++ b/rocketpool/node/node.go @@ -1,6 +1,7 @@ package node import ( + "context" "fmt" "math/big" "net/http" @@ -119,6 +120,12 @@ func run(c *cli.Context) error { m := state.NewNetworkStateManager(rp, cfg.Smartnode.GetStateManagerContracts(), bc, &updateLog) stateLocker := collectors.NewStateLocker() + // Create a context for the daemon + ctx := context.Background() + + // Start the HTTP server + startHTTP(ctx, cfg) + // Initialize tasks manageFeeRecipient, err := newManageFeeRecipient(c, log.NewColorLogger(ManageFeeRecipientColor)) if err != nil { diff --git a/rocketpool/node/routes/state.go b/rocketpool/node/routes/state.go new file mode 100644 index 000000000..0db51ae52 --- /dev/null +++ b/rocketpool/node/routes/state.go @@ -0,0 +1 @@ +package routes