Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 3 additions & 2 deletions rocketpool-cli/service/config/settings-native-smartnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func NewNativeSmartnodeConfigPage(home *settingsNativeHome) *NativeSmartnodeConf
configPage.page = newPage(
home.homePage,
"settings-native-smartnode",
"Smartnode and TX Fees",
"Smart Node and TX Fees",
"Select this to configure the settings for the Smart Node itself, including the defaults and limits on transaction fees.",
configPage.layout.grid,
)
Expand All @@ -43,7 +43,8 @@ func (configPage *NativeSmartnodeConfigPage) createContent() {
layout.setupEscapeReturnHomeHandler(configPage.home.md, configPage.home.homePage)

// Set up the form items
formItems := createParameterizedFormItems(masterConfig.Smartnode.GetParameters(), layout.descriptionBox)
params := append(masterConfig.Smartnode.GetParameters(), &masterConfig.EnableIPv6, &masterConfig.Alertmanager.ShowAlertsOnCLI)
formItems := createParameterizedFormItems(params, layout.descriptionBox)
for _, formItem := range formItems {
if formItem.parameter.ID == config.ProjectNameID {
// Ignore the project name ID since it doesn't apply to native mode
Expand Down
5 changes: 3 additions & 2 deletions rocketpool-cli/service/config/settings-smartnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func NewSmartnodeConfigPage(home *settingsHome) *SmartnodeConfigPage {
configPage.page = newPage(
home.homePage,
"settings-smartnode",
"Smartnode and TX Fees",
"Smart Node and TX Fees",
"Select this to configure the settings for the Smart Node itself, including the defaults and limits on transaction fees.",
configPage.layout.grid,
)
Expand Down Expand Up @@ -67,7 +67,8 @@ func (configPage *SmartnodeConfigPage) createContent() {
})

// Set up the form items
formItems := createParameterizedFormItems(masterConfig.Smartnode.GetParameters(), layout.descriptionBox)
params := append(masterConfig.Smartnode.GetParameters(), &masterConfig.EnableIPv6, &masterConfig.Alertmanager.ShowAlertsOnCLI)
formItems := createParameterizedFormItems(params, layout.descriptionBox)
for _, formItem := range formItems {
layout.form.AddFormItem(formItem.item)
layout.parameters[formItem.item] = formItem
Expand Down
12 changes: 12 additions & 0 deletions rocketpool-cli/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,12 @@ func configureService(configPath string, isNative, yes bool, composeFiles []stri
})
}

// Warn if IPv6 is enabled but no public IPv6 address is available
if md.Config.IsIPv6Enabled() && md.Config.GetExternalIpv6() == "" {
color.YellowPrintln("Warning: IPv6 is enabled but no public IPv6 address was detected. Your node will not be reachable by IPv6 peers.")
fmt.Println()
}

// Query for service start if this is old and there are containers to change
if len(md.ContainersToRestart) > 0 {
fmt.Println("The following containers must be restarted for the changes to take effect:")
Expand Down Expand Up @@ -519,6 +525,12 @@ func startService(params startServiceParams) error {
return fmt.Errorf("No configuration detected. Please run `rocketpool service config` to set up your Smart Node before running it.")
}

// Warn if IPv6 is enabled but no public IPv6 address is available
if cfg.IsIPv6Enabled() && cfg.GetExternalIpv6() == "" {
color.YellowPrintln("Warning: IPv6 is enabled but no public IPv6 address was detected. Your node will not be reachable by IPv6 peers.")
fmt.Println()
}

// Check if this is a new install
isUpdate, err := rp.IsFirstRun()
if err != nil {
Expand Down
39 changes: 37 additions & 2 deletions shared/services/config/rocket-pool-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ type RocketPoolConfig struct {
ConsensusClient config.Parameter `yaml:"consensusClient,omitempty"`
ExternalConsensusClient config.Parameter `yaml:"externalConsensusClient,omitempty"`

// IPv6 networking
EnableIPv6 config.Parameter `yaml:"enableIPv6,omitempty"`

// Metrics settings
EnableMetrics config.Parameter `yaml:"enableMetrics,omitempty"`
EnableODaoMetrics config.Parameter `yaml:"enableODaoMetrics,omitempty"`
Expand Down Expand Up @@ -343,6 +346,17 @@ func NewRocketPoolConfig(rpDir string, isNativeMode bool) *RocketPoolConfig {
}},
},

EnableIPv6: config.Parameter{
ID: "enableIPv6",
Name: "Enable IPv6",
Description: "Enables dual-stack (IPv4 + IPv6) networking for the Smart Node. When enabled, your Ethereum clients will listen on both IPv4 and IPv6 and can peer with IPv6 nodes in addition to IPv4. Enable this if your machine has only an IPv6 address, or if you want your node to participate in IPv6 peering.",
Type: config.ParameterType_Bool,
Default: map[config.Network]interface{}{config.Network_All: false},
AffectsContainers: []config.ContainerID{config.ContainerID_Api, config.ContainerID_Node, config.ContainerID_Watchtower, config.ContainerID_Eth1, config.ContainerID_Eth2, config.ContainerID_Validator, config.ContainerID_Grafana, config.ContainerID_Prometheus, config.ContainerID_Alertmanager, config.ContainerID_Exporter, config.ContainerID_MevBoost, config.ContainerID_CommitBoost},
CanBeBlank: false,
OverwriteOnUpgrade: false,
},

EnableMetrics: config.Parameter{
ID: "enableMetrics",
Name: "Enable Metrics",
Expand Down Expand Up @@ -558,6 +572,7 @@ func (cfg *RocketPoolConfig) GetParameters() []*config.Parameter {
&cfg.ConsensusClientMode,
&cfg.ConsensusClient,
&cfg.ExternalConsensusClient,
&cfg.EnableIPv6,
&cfg.EnableMetrics,
&cfg.EnableODaoMetrics,
&cfg.EnableBitflyNodeMetrics,
Expand Down Expand Up @@ -1312,6 +1327,12 @@ func (cfg *RocketPoolConfig) GetECAdditionalFlags() (string, error) {
return "", fmt.Errorf("Unknown Execution Client %s", string(cfg.ExecutionClient.Value.(config.ExecutionClient)))
}

// IsIPv6Enabled returns true if IPv6 support is enabled for the Docker network.
// Used by text/template to conditionally add enable_ipv6 to compose network definitions.
func (cfg *RocketPoolConfig) IsIPv6Enabled() bool {
return cfg.EnableIPv6.Value.(bool)
}

// Used by text/template to format eth1.yml
func (cfg *RocketPoolConfig) GetExternalIp() string {
// Get the external IP address
Expand All @@ -1322,13 +1343,27 @@ func (cfg *RocketPoolConfig) GetExternalIp() string {
return ""
}

if ip.To4() == nil {
fmt.Println("Warning: external IP address is v6; if you're using Nimbus or Besu, it may have trouble finding peers:")
if ip.To4() == nil && !cfg.IsIPv6Enabled() {
fmt.Println("Warning: your external IP address is IPv6. If you haven't enabled IPv6 support in your configuration, your node may have trouble finding peers. Run 'rocketpool service config' and enable IPv6 under 'Smart Node and TX Fees'.")
}

return ip.String()
}

// Used by text/template to format eth2.yml when IPv6 is enabled.
// Lodestar requires --enr.ip6 and Teku requires --p2p-advertised-ips to advertise IPv6 in their ENR.
// Returns the external IPv6 address, or empty string if unavailable.
func (cfg *RocketPoolConfig) GetExternalIpv6() string {
consensusConfig := externalip.ConsensusConfig{Timeout: 3 * time.Second}
ip6Consensus := externalip.DefaultConsensus(&consensusConfig, nil)
ip6Consensus.UseIPProtocol(6)
ip, err := ip6Consensus.ExternalIP()
if err != nil || ip.To4() != nil {
return ""
}
return ip.String()
}

// Gets the tag of the cc container
// Used by text/template to format eth2.yml
func (cfg *RocketPoolConfig) GetBeaconContainerTag() (string, error) {
Expand Down
22 changes: 19 additions & 3 deletions shared/services/config/teku-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import (
)

const (
tekuTagTest string = "consensys/teku:26.4.0"
tekuTagProd string = "consensys/teku:26.4.0"
defaultTekuMaxPeers uint16 = 100
tekuTagTest string = "consensys/teku:26.4.0"
tekuTagProd string = "consensys/teku:26.4.0"
defaultTekuMaxPeers uint16 = 100
defaultTekuP2pIpv6Port uint16 = 9090
)

// Configuration for Teku
Expand Down Expand Up @@ -36,6 +37,9 @@ type TekuConfig struct {
// Custom command line flags for the BN
AdditionalBnFlags config.Parameter `yaml:"additionalBnFlags,omitempty"`

// The IPv6 P2P port for Teku (Teku uses a separate port for IPv6)
P2pIpv6Port config.Parameter `yaml:"p2pIpv6Port,omitempty"`

// Custom command line flags for the VC
AdditionalVcFlags config.Parameter `yaml:"additionalVcFlags,omitempty"`
}
Expand Down Expand Up @@ -120,6 +124,17 @@ func NewTekuConfig(cfg *RocketPoolConfig) *TekuConfig {
OverwriteOnUpgrade: false,
},

P2pIpv6Port: config.Parameter{
ID: "p2pIpv6Port",
Name: "P2P IPv6 Port",
Description: "The port Teku uses for P2P communication over IPv6. Teku requires a dedicated port for IPv6 (separate from the main P2P port). Only used when IPv6 support is enabled.",
Type: config.ParameterType_Uint16,
Default: map[config.Network]interface{}{config.Network_All: defaultTekuP2pIpv6Port},
AffectsContainers: []config.ContainerID{config.ContainerID_Eth2},
CanBeBlank: false,
OverwriteOnUpgrade: false,
},

AdditionalVcFlags: config.Parameter{
ID: "additionalVcFlags",
Name: "Additional Validator Client Flags",
Expand All @@ -142,6 +157,7 @@ func (cfg *TekuConfig) GetParameters() []*config.Parameter {
&cfg.UseSlashingProtection,
&cfg.ContainerTag,
&cfg.AdditionalBnFlags,
&cfg.P2pIpv6Port,
&cfg.AdditionalVcFlags,
}
}
Expand Down
11 changes: 7 additions & 4 deletions shared/services/connectivity/check-port-connectivity.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ var publicIPResolvers = []struct {
addr string // host:port (IP literal)
hostname string // special hostname that returns the caller's public IP
}{
{"208.67.222.222:53", "myip.opendns.com"}, // OpenDNS primary
{"208.67.220.220:53", "myip.opendns.com"}, // OpenDNS secondary
{"216.239.32.10:53", "o-o.myaddr.l.google.com"}, // Google ns1
{"208.67.222.222:53", "myip.opendns.com"}, // OpenDNS primary (IPv4)
{"208.67.220.220:53", "myip.opendns.com"}, // OpenDNS secondary (IPv4)
{"216.239.32.10:53", "o-o.myaddr.l.google.com"}, // Google ns1 (IPv4)
{"[2620:119:35::35]:53", "myip.opendns.com"}, // OpenDNS primary (IPv6)
{"[2620:119:53::53]:53", "myip.opendns.com"}, // OpenDNS secondary (IPv6)
{"[2001:4860:4860::8888]:53", "o-o.myaddr.l.google.com"}, // Google ns1 (IPv6)
}

// Check port connectivity task
Expand Down Expand Up @@ -178,7 +181,7 @@ func getPublicIP() (string, error) {
// isPortReachableNATReflection attempts a TCP connection to host:port and returns true if
// the connection succeeds within portCheckTimeout.
func isPortReachableNATReflection(host string, port uint16) bool {
address := fmt.Sprintf("%s:%d", host, port)
address := net.JoinHostPort(host, fmt.Sprintf("%d", port))
conn, err := net.DialTimeout("tcp", address, portCheckTimeout)
if err != nil {
return false
Expand Down
24 changes: 23 additions & 1 deletion shared/services/rocketpool/assets/install/scripts/start-bn.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ if [ "$CC_CLIENT" = "lighthouse" ]; then
CMD="$CMD --monitoring-endpoint $BITFLY_NODE_METRICS_ENDPOINT?apikey=$BITFLY_NODE_METRICS_SECRET&machine=$BITFLY_NODE_METRICS_MACHINE_NAME"
fi

if [ "$ENABLE_IPV6" = "true" ]; then
CMD="$CMD --listen-address 0.0.0.0 --listen-address :: --port6 $BN_P2P_PORT --enr-udp6-port $BN_P2P_PORT --quic-port6 ${BN_P2P_QUIC_PORT:-8001}"
fi

exec ${CMD}

fi
Expand Down Expand Up @@ -142,7 +146,14 @@ if [ "$CC_CLIENT" = "lodestar" ]; then
else
CMD="$CMD --enr.ip $EXTERNAL_IP --nat"
fi
fi
fi

if [ "$ENABLE_IPV6" = "true" ]; then
CMD="$CMD --listenAddress 0.0.0.0 --listenAddress6 :: --port6 $BN_P2P_PORT --quicPort6 ${BN_P2P_QUIC_PORT:-8001}"
if [ ! -z "$EXTERNAL_IP6" ]; then
CMD="$CMD --enr.ip6 $EXTERNAL_IP6"
fi
fi

if [ ! -z "$CHECKPOINT_SYNC_URL" ]; then
CMD="$CMD --checkpointSyncUrl $CHECKPOINT_SYNC_URL"
Expand Down Expand Up @@ -313,6 +324,17 @@ if [ "$CC_CLIENT" = "teku" ]; then
CMD="$CMD --metrics-publish-endpoint=$BITFLY_NODE_METRICS_ENDPOINT?apikey=$BITFLY_NODE_METRICS_SECRET&machine=$BITFLY_NODE_METRICS_MACHINE_NAME"
fi

if [ "$ENABLE_IPV6" = "true" ]; then
CMD="$CMD --p2p-interface=0.0.0.0,:: --p2p-port-ipv6=$BN_IPV6_P2P_PORT"
if [ ! -z "$EXTERNAL_IP6" ]; then
if [ ! -z "$EXTERNAL_IP" ] && ! expr "$EXTERNAL_IP" : '.*:' >/dev/null; then
CMD="$CMD --p2p-advertised-ips $EXTERNAL_IP,$EXTERNAL_IP6 --p2p-advertised-port-ipv6=$BN_IPV6_P2P_PORT"
else
CMD="$CMD --p2p-advertised-ips $EXTERNAL_IP6 --p2p-advertised-port-ipv6=$BN_IPV6_P2P_PORT"
fi
fi
fi

if [ "$TEKU_JVM_HEAP_SIZE" -gt "0" ]; then
CMD="env JAVA_OPTS=\"-Xmx${TEKU_JVM_HEAP_SIZE}m\" $CMD"
fi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ services:
- net
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
volumes:
alertmanager-data:
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ services:
- no-new-privileges
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,6 @@ services:
- no-new-privileges
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
volumes:
eth1clientdata:
23 changes: 23 additions & 0 deletions shared/services/rocketpool/assets/install/templates/eth2.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,32 @@ services:
stop_grace_period: 3m
{{- $p2p := (or .ConsensusCommon.P2pPort.String "9001")}}
ports:
{{- if .IsIPv6Enabled}}
- "[::]:{{$p2p}}:{{$p2p}}/udp"
- "[::]:{{$p2p}}:{{$p2p}}/tcp"
{{- else}}
- "{{$p2p}}:{{$p2p}}/udp"
- "{{$p2p}}:{{$p2p}}/tcp"
{{- end}}
{{- if eq .ConsensusClient.String "lighthouse"}}
{{- if .IsIPv6Enabled}}
- "[::]:{{.Lighthouse.P2pQuicPort}}:{{.Lighthouse.P2pQuicPort}}/udp"
{{- else}}
- "{{.Lighthouse.P2pQuicPort}}:{{.Lighthouse.P2pQuicPort}}/udp"
{{- end}}
{{- else if eq .ConsensusClient.String "prysm"}}
- "{{.Prysm.P2pQuicPort}}:{{.Prysm.P2pQuicPort}}/udp"
{{- else if eq .ConsensusClient.String "lodestar"}}
{{- if .IsIPv6Enabled}}
- "[::]:{{.Lodestar.P2pQuicPort}}:{{.Lodestar.P2pQuicPort}}/udp"
{{- else}}
- "{{.Lodestar.P2pQuicPort}}:{{.Lodestar.P2pQuicPort}}/udp"
{{- end}}
{{- end}}
{{- if and (eq .ConsensusClient.String "teku") .IsIPv6Enabled}}
- "{{.Teku.P2pIpv6Port}}:{{.Teku.P2pIpv6Port}}/udp"
- "{{.Teku.P2pIpv6Port}}:{{.Teku.P2pIpv6Port}}/tcp"
{{- end}}
{{- range $entry := .GetBnOpenPorts}}
- "{{$entry}}"
{{- end}}
Expand Down Expand Up @@ -66,6 +83,10 @@ services:
- ENABLE_METRICS={{.EnableMetrics}}
- BN_METRICS_PORT={{.BnMetricsPort}}
- EXTERNAL_IP={{.GetExternalIp}}
{{- if .IsIPv6Enabled}}
- EXTERNAL_IP6={{.GetExternalIpv6}}
{{- end}}
- ENABLE_IPV6={{.IsIPv6Enabled}}
- CHECKPOINT_SYNC_URL={{.ConsensusCommon.CheckpointSyncProvider}}
- DOPPELGANGER_DETECTION={{.IsDoppelgangerEnabled}}
- BN_ADDITIONAL_FLAGS={{.GetBNAdditionalFlags}}
Expand All @@ -82,6 +103,7 @@ services:
{{- if eq .ConsensusClient.String "teku"}}
- TEKU_JVM_HEAP_SIZE={{.Teku.JvmHeapSize}}
- TEKU_ARCHIVE_MODE={{.Teku.ArchiveMode}}
- BN_IPV6_P2P_PORT={{.Teku.P2pIpv6Port}}
{{- else if eq .ConsensusClient.String "prysm"}}
- BN_RPC_PORT={{.Prysm.RpcPort}}
- BN_P2P_QUIC_PORT={{.Prysm.P2pQuicPort}}
Expand Down Expand Up @@ -109,5 +131,6 @@ services:
{{- end}}
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
volumes:
eth2clientdata:
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ services:
network_mode: host
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ services:
- net
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
volumes:
grafana-storage:
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ services:
- no-new-privileges
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ services:
- no-new-privileges
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ services:
- "host.docker.internal:host-gateway"
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
volumes:
prometheus-data:
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ services:
- no-new-privileges
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ services:
- no-new-privileges
networks:
net:
enable_ipv6: {{ .IsIPv6Enabled }}