From ddd8bf0400205c5a5a610f5be07741962e036df4 Mon Sep 17 00:00:00 2001 From: Fornax <23104993+0xfornax@users.noreply.github.com> Date: Sun, 4 Jan 2026 20:55:27 -0300 Subject: [PATCH 01/13] Fix parameter type --- bindings/network/revenues.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/network/revenues.go b/bindings/network/revenues.go index b526eb670..cf4bea432 100644 --- a/bindings/network/revenues.go +++ b/bindings/network/revenues.go @@ -36,7 +36,7 @@ func CalculateSplit(rp *rocketpool.RocketPool, sinceBlock uint64, opts *bind.Cal return RevenueSplit{}, err } revenueSplit := new(RevenueSplit) - if err := rocketNetworkRevenues.Call(opts, revenueSplit, "calculateSplit", big.NewInt(int64(sinceBlock))); err != nil { + if err := rocketNetworkRevenues.Call(opts, revenueSplit, "calculateSplit", sinceBlock); err != nil { return RevenueSplit{}, fmt.Errorf("error calculating the revenue split %w", err) } return *revenueSplit, nil From e6ccc4b9582ff2cd189404b408141b7c1dce2497 Mon Sep 17 00:00:00 2001 From: Fornax <23104993+0xfornax@users.noreply.github.com> Date: Sun, 4 Jan 2026 22:56:02 -0300 Subject: [PATCH 02/13] Set the v11 interval for devnet-6 --- shared/services/rewards/generator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/services/rewards/generator.go b/shared/services/rewards/generator.go index ee6eb548b..5fb14242c 100644 --- a/shared/services/rewards/generator.go +++ b/shared/services/rewards/generator.go @@ -52,7 +52,7 @@ const ( MainnetV10Interval uint64 = 30 MainnetV11Interval uint64 = 9000 // TODO: schedule v11 // Devnet intervals - DevnetV11Interval uint64 = 9 + DevnetV11Interval uint64 = 7 // Testnet intervals TestnetV10Interval uint64 = 0 From 8c9e110c59f774c9c0c3bbeac432b95911188a46 Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Mon, 5 Jan 2026 15:48:19 -0300 Subject: [PATCH 03/13] Fix early return, missing start blocks and times when sp balance is zero --- shared/services/rewards/generator-impl-v11.go | 11 ++++++----- shared/services/rewards/generator-impl-v8.go | 11 ++++++----- shared/services/rewards/generator-impl-v9-v10.go | 11 ++++++----- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/shared/services/rewards/generator-impl-v11.go b/shared/services/rewards/generator-impl-v11.go index 45df577bb..d7c7febfa 100644 --- a/shared/services/rewards/generator-impl-v11.go +++ b/shared/services/rewards/generator-impl-v11.go @@ -489,17 +489,13 @@ func (r *treeGeneratorImpl_v11) calculateEthRewards(checkBeaconPerformance bool) r.log.Printlnf("%s Smoothing Pool Balance:\t%s\t(%.3f)", r.logPrefix, r.smoothingPoolBalance.String(), eth.WeiToEth(r.smoothingPoolBalance)) r.log.Printlnf("%s Earmarked Voter Share:\t%s\t(%.3f)", r.logPrefix, r.networkState.NetworkDetails.SmoothingPoolPendingVoterShare.String(), eth.WeiToEth(r.networkState.NetworkDetails.SmoothingPoolPendingVoterShare)) - // Ignore the ETH calculation if there are no rewards - if r.smoothingPoolBalance.Cmp(common.Big0) == 0 { - return nil - } - if r.rewardsFile.Index == 0 { // This is the first interval, Smoothing Pool rewards are ignored on the first interval since it doesn't have a discrete start time return nil } // Get the start time of this interval based on the event from the previous one + // This must be done even if there are no smoothing pool rewards, to set the start blocks and timestamps //previousIntervalEvent, err := GetRewardSnapshotEvent(r.rp, r.cfg, r.rewardsFile.Index-1, r.opts) // This is immutable so querying at the head is fine and mitigates issues around calls for pruned EL state previousIntervalEvent, err := r.rp.GetRewardSnapshotEvent(r.previousRewardsPoolAddresses, r.rewardsFile.Index-1, r.opts) if err != nil { @@ -510,6 +506,11 @@ func (r *treeGeneratorImpl_v11) calculateEthRewards(checkBeaconPerformance bool) return err } + // Ignore the ETH calculation if there are no rewards + if r.smoothingPoolBalance.Cmp(common.Big0) == 0 { + return nil + } + r.elStartTime = time.Unix(int64(startElBlockHeader.Time), 0) r.elEndTime = time.Unix(int64(r.elSnapshotHeader.Time), 0) r.intervalSeconds = big.NewInt(int64(r.elEndTime.Sub(r.elStartTime) / time.Second)) diff --git a/shared/services/rewards/generator-impl-v8.go b/shared/services/rewards/generator-impl-v8.go index baedb82d7..045062055 100644 --- a/shared/services/rewards/generator-impl-v8.go +++ b/shared/services/rewards/generator-impl-v8.go @@ -522,17 +522,13 @@ func (r *treeGeneratorImpl_v8) calculateEthRewards(checkBeaconPerformance bool) r.smoothingPoolBalance = r.networkState.NetworkDetails.SmoothingPoolBalance r.log.Printlnf("%s Smoothing Pool Balance: %s (%.3f)", r.logPrefix, r.smoothingPoolBalance.String(), eth.WeiToEth(r.smoothingPoolBalance)) - // Ignore the ETH calculation if there are no rewards - if r.smoothingPoolBalance.Cmp(common.Big0) == 0 { - return nil - } - if r.rewardsFile.Index == 0 { // This is the first interval, Smoothing Pool rewards are ignored on the first interval since it doesn't have a discrete start time return nil } // Get the start time of this interval based on the event from the previous one + // This must be done even if there are no smoothing pool rewards, to set the start blocks and timestamps //previousIntervalEvent, err := GetRewardSnapshotEvent(r.rp, r.cfg, r.rewardsFile.Index-1, r.opts) // This is immutable so querying at the head is fine and mitigates issues around calls for pruned EL state previousIntervalEvent, err := r.rp.GetRewardSnapshotEvent(r.previousRewardsPoolAddresses, r.rewardsFile.Index-1, nil) if err != nil { @@ -543,6 +539,11 @@ func (r *treeGeneratorImpl_v8) calculateEthRewards(checkBeaconPerformance bool) return err } + // Ignore the ETH calculation if there are no rewards + if r.smoothingPoolBalance.Cmp(common.Big0) == 0 { + return nil + } + r.elStartTime = time.Unix(int64(startElBlockHeader.Time), 0) r.elEndTime = time.Unix(int64(r.elSnapshotHeader.Time), 0) r.intervalSeconds = big.NewInt(int64(r.elEndTime.Sub(r.elStartTime) / time.Second)) diff --git a/shared/services/rewards/generator-impl-v9-v10.go b/shared/services/rewards/generator-impl-v9-v10.go index 792024ad0..ae9e690d6 100644 --- a/shared/services/rewards/generator-impl-v9-v10.go +++ b/shared/services/rewards/generator-impl-v9-v10.go @@ -450,17 +450,13 @@ func (r *treeGeneratorImpl_v9_v10) calculateEthRewards(checkBeaconPerformance bo r.smoothingPoolBalance = r.networkState.NetworkDetails.SmoothingPoolBalance r.log.Printlnf("%s Smoothing Pool Balance: %s (%.3f)", r.logPrefix, r.smoothingPoolBalance.String(), eth.WeiToEth(r.smoothingPoolBalance)) - // Ignore the ETH calculation if there are no rewards - if r.smoothingPoolBalance.Cmp(common.Big0) == 0 { - return nil - } - if r.rewardsFile.Index == 0 { // This is the first interval, Smoothing Pool rewards are ignored on the first interval since it doesn't have a discrete start time return nil } // Get the start time of this interval based on the event from the previous one + // This must be done even if there are no smoothing pool rewards, to set the start blocks and timestamps //previousIntervalEvent, err := GetRewardSnapshotEvent(r.rp, r.cfg, r.rewardsFile.Index-1, r.opts) // This is immutable so querying at the head is fine and mitigates issues around calls for pruned EL state previousIntervalEvent, err := r.rp.GetRewardSnapshotEvent(r.previousRewardsPoolAddresses, r.rewardsFile.Index-1, r.opts) if err != nil { @@ -471,6 +467,11 @@ func (r *treeGeneratorImpl_v9_v10) calculateEthRewards(checkBeaconPerformance bo return err } + // Ignore the ETH calculation if there are no rewards + if r.smoothingPoolBalance.Cmp(common.Big0) == 0 { + return nil + } + r.elStartTime = time.Unix(int64(startElBlockHeader.Time), 0) r.elEndTime = time.Unix(int64(r.elSnapshotHeader.Time), 0) r.intervalSeconds = big.NewInt(int64(r.elEndTime.Sub(r.elStartTime) / time.Second)) From b2984002890924c2963432f24b768dc44093939d Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Mon, 5 Jan 2026 18:24:16 -0300 Subject: [PATCH 04/13] Print the minipool status --- rocketpool-cli/minipool/status.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rocketpool-cli/minipool/status.go b/rocketpool-cli/minipool/status.go index 6e88a69e0..a3bde6045 100644 --- a/rocketpool-cli/minipool/status.go +++ b/rocketpool-cli/minipool/status.go @@ -161,6 +161,7 @@ func printMinipoolDetails(minipool api.MinipoolDetails, latestDelegate common.Ad } else { fmt.Printf("%sInfractions: %d%s\n", colorRed, minipool.Penalties, colorReset) } + fmt.Printf("Status: %s\n", minipool.Status.Status.String()) fmt.Printf("Status updated: %s\n", minipool.Status.StatusTime.Format(TimeFormat)) fmt.Printf("Node fee: %f%%\n", minipool.Node.Fee*100) fmt.Printf("Node deposit: %.6f ETH\n", math.RoundDown(eth.WeiToEth(minipool.Node.DepositBalance), 6)) From a7aee25b2b817bcfc32cd3ee880658e974b5a346 Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:51:32 -0300 Subject: [PATCH 05/13] Add missing check when the validator is not found yet --- rocketpool/watchtower/dissolve-invalid-credentials.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rocketpool/watchtower/dissolve-invalid-credentials.go b/rocketpool/watchtower/dissolve-invalid-credentials.go index 54d01f3ec..9bf421f6e 100644 --- a/rocketpool/watchtower/dissolve-invalid-credentials.go +++ b/rocketpool/watchtower/dissolve-invalid-credentials.go @@ -104,7 +104,10 @@ func (t *dissolveInvalidCredentials) dissolveInvalidCredentialValidators(state * t.log.Printlnf("Error fetching validator %s from beacon state: %s", validatorFromState.Index, err) continue } - if validatorFromState.Index != "" && !bytes.Equal(validatorFromState.WithdrawalCredentials.Bytes(), expectedWithdrawalAddress.Bytes()) { + if !validatorFromState.Exists { + continue + } + if !bytes.Equal(validatorFromState.WithdrawalCredentials.Bytes(), expectedWithdrawalAddress.Bytes()) { t.log.Printlnf("Validator %s has an invalid credential %s while the expected is %s. Dissolving...", validatorFromState.Index, validatorFromState.WithdrawalCredentials, expectedWithdrawalAddress.Bytes()) t.dissolveMegapoolValidator(validator) } From 359a9dfa3a30dc46812dda8ff5a49a28eb0a3c6b Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Tue, 6 Jan 2026 12:49:11 -0300 Subject: [PATCH 06/13] Check user capital before using it --- shared/services/rewards/generator-impl-v11.go | 2 +- shared/services/state/network-state.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/shared/services/rewards/generator-impl-v11.go b/shared/services/rewards/generator-impl-v11.go index d7c7febfa..eefcd8db0 100644 --- a/shared/services/rewards/generator-impl-v11.go +++ b/shared/services/rewards/generator-impl-v11.go @@ -162,7 +162,7 @@ func (r *treeGeneratorImpl_v11) generateTree(rp RewardsExecutionClient, networkN if nodeInfo.Megapool == nil { continue } - r.epsilon.Add(r.epsilon, big.NewInt(int64(nodeInfo.Megapool.ActiveValidatorCount))) + r.epsilon.Add(r.epsilon, big.NewInt(int64(len(nodeInfo.Megapool.Validators)))) } } } diff --git a/shared/services/state/network-state.go b/shared/services/state/network-state.go index 773226017..ff66c22a1 100644 --- a/shared/services/state/network-state.go +++ b/shared/services/state/network-state.go @@ -725,7 +725,9 @@ func (s *NetworkState) GetEligibleBorrowedEth(node *rpstate.NativeNodeDetails) * if node.MegapoolDeployed { megapool := s.MegapoolDetails[node.MegapoolAddress] - eligibleBorrowedEth.Add(eligibleBorrowedEth, megapool.UserCapital) + if megapool.UserCapital != nil { + eligibleBorrowedEth.Add(eligibleBorrowedEth, megapool.UserCapital) + } } return eligibleBorrowedEth From d33d18f3886c6dc48b398984c5eaab48a211881c Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:54:55 -0300 Subject: [PATCH 07/13] Increase epsilon as cumulative error can exceed the validator count --- shared/services/rewards/generator-impl-v11.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/services/rewards/generator-impl-v11.go b/shared/services/rewards/generator-impl-v11.go index eefcd8db0..46e73d2a2 100644 --- a/shared/services/rewards/generator-impl-v11.go +++ b/shared/services/rewards/generator-impl-v11.go @@ -165,6 +165,8 @@ func (r *treeGeneratorImpl_v11) generateTree(rp RewardsExecutionClient, networkN r.epsilon.Add(r.epsilon, big.NewInt(int64(len(nodeInfo.Megapool.Validators)))) } } + // Cumulative error can exceed the validator count + r.epsilon.Mul(r.epsilon, big.NewInt(2)) } // Calculate the RPL rewards From 933f505d8db28e5a66c6bad5b6c5bbb8a658848c Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Tue, 25 Nov 2025 17:41:12 -0300 Subject: [PATCH 08/13] Add commit-boost --- rocketpool-cli/service/service.go | 12 ++ shared/services/config/commit-boost-config.go | 163 ++++++++++++++++++ shared/services/config/rocket-pool-config.go | 52 ++++-- .../assets/install/override/commit-boost.yml | 8 + .../assets/install/scripts/start-bn.sh | 20 +-- .../install/scripts/start-commit-boost.sh | 12 ++ .../assets/install/scripts/start-vc.sh | 10 +- .../templates/commit-boost-config.tmpl | 22 +++ .../install/templates/commit-boost.tmpl | 31 ++++ .../assets/install/templates/eth2.tmpl | 2 +- .../assets/install/templates/validator.tmpl | 4 +- shared/types/config/types.go | 1 + 12 files changed, 309 insertions(+), 28 deletions(-) create mode 100644 shared/services/config/commit-boost-config.go create mode 100644 shared/services/rocketpool/assets/install/override/commit-boost.yml create mode 100755 shared/services/rocketpool/assets/install/scripts/start-commit-boost.sh create mode 100644 shared/services/rocketpool/assets/install/templates/commit-boost-config.tmpl create mode 100644 shared/services/rocketpool/assets/install/templates/commit-boost.tmpl diff --git a/rocketpool-cli/service/service.go b/rocketpool-cli/service/service.go index e637e3647..7c3a6a076 100644 --- a/rocketpool-cli/service/service.go +++ b/rocketpool-cli/service/service.go @@ -1179,12 +1179,24 @@ func serviceVersion(c *cli.Context) error { mevBoostString = "Disabled" } + var commitBoostString string + if cfg.EnableCommitBoost.Value.(bool) { + if cfg.CommitBoost.Mode.Value.(cfgtypes.Mode) == cfgtypes.Mode_Local { + commitBoostString = fmt.Sprintf("Enabled (Local Mode)\n\tImage: %s", cfg.CommitBoost.ContainerTag.Value.(string)) + } else { + commitBoostString = "Enabled (External Mode)" + } + } else { + commitBoostString = "Disabled" + } + // Print version info fmt.Printf("Rocket Pool client version: %s\n", c.App.Version) fmt.Printf("Rocket Pool service version: %s\n", serviceVersion) fmt.Printf("Selected Eth 1.0 client: %s\n", eth1ClientString) fmt.Printf("Selected Eth 2.0 client: %s\n", eth2ClientString) fmt.Printf("MEV-Boost client: %s\n", mevBoostString) + fmt.Printf("Commit-Boost client: %s\n", commitBoostString) return nil } diff --git a/shared/services/config/commit-boost-config.go b/shared/services/config/commit-boost-config.go new file mode 100644 index 000000000..3bde52089 --- /dev/null +++ b/shared/services/config/commit-boost-config.go @@ -0,0 +1,163 @@ +package config + +import ( + "fmt" + + "github.com/rocket-pool/smartnode/shared/types/config" +) + +// Constants +const ( + CommitBoostConfigFile string = "cb_config.toml" + commitBoostProdTag string = "ghcr.io/commit-boost/pbs:v0.9.2" + commitBoostTestTag string = "ghcr.io/commit-boost/pbs:v0.9.2" +) + +// Configuration for Commit-Boost's service +type CommitBoostConfig struct { + // Ownership mode + Mode config.Parameter `yaml:"mode,omitempty"` + + // The URL of an external MEV-Boost client + ExternalUrl config.Parameter `yaml:"externalUrl"` + + // The Docker Hub tag for Commit-Boost + ContainerTag config.Parameter `yaml:"containerTag,omitempty"` + + // Custom command line flags + AdditionalFlags config.Parameter `yaml:"additionalFlags,omitempty"` + + parentConfig *RocketPoolConfig `yaml:"-"` + + // The port that Commit-Boost should serve its API on + Port config.Parameter `yaml:"port,omitempty"` + + // Toggle for forwarding the HTTP port outside of Docker + OpenRpcPort config.Parameter `yaml:"openRpcPort,omitempty"` +} + +// Generates a new Commit-Boost PBS service configuration +func NewCommitBoostConfig(cfg *RocketPoolConfig) *CommitBoostConfig { + portModes := config.PortModes("") + + return &CommitBoostConfig{ + parentConfig: cfg, + + Mode: config.Parameter{ + ID: "mode", + Name: "Commit-Boost Mode", + Description: "Choose whether to let the Smartnode manage your Commit-Boost instance (Locally Managed), or if you manage your own outside of the Smartnode stack (Externally Managed).", + Type: config.ParameterType_Choice, + Default: map[config.Network]interface{}{config.Network_All: config.Mode_Local}, + AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + Options: []config.ParameterOption{{ + Name: "Locally Managed", + Description: "Allow the Smartnode to manage the Commit-Boost client for you", + Value: config.Mode_Local, + }, { + Name: "Externally Managed", + Description: "Use an existing Commit-Boost client that you manage on your own", + Value: config.Mode_External, + }}, + }, + Port: config.Parameter{ + ID: "port", + Name: "Port", + Description: "The port that Commit-Boost should serve its API on.", + Type: config.ParameterType_Uint16, + Default: map[config.Network]interface{}{config.Network_All: uint16(18550)}, + AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + }, + + OpenRpcPort: config.Parameter{ + ID: "openRpcPort", + Name: "Expose API Port", + Description: "Expose the API port to other processes on your machine, or to your local network so other local machines can access Commit-Boost's API.", + Type: config.ParameterType_Choice, + Default: map[config.Network]interface{}{config.Network_All: config.RPC_Closed}, + AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + Options: portModes, + }, + + ContainerTag: config.Parameter{ + ID: "containerTag", + Name: "Container Tag", + Description: "The tag name of the Commit-Boost container you want to use.", + AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, + CanBeBlank: false, + OverwriteOnUpgrade: true, + Type: config.ParameterType_String, + Default: map[config.Network]interface{}{ + config.Network_Mainnet: commitBoostProdTag, + config.Network_Devnet: commitBoostTestTag, + config.Network_Testnet: commitBoostTestTag, + }, + }, + AdditionalFlags: config.Parameter{ + ID: "additionalFlags", + Name: "Additional Flags", + Description: "Additional custom command line flags you want to pass to Commit-Boost, to take advantage of other settings that Hyperdrive's configuration doesn't cover.", + AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, + CanBeBlank: true, + OverwriteOnUpgrade: false, + Type: config.ParameterType_String, + Default: map[config.Network]interface{}{ + config.Network_All: "", + }, + }, + ExternalUrl: config.Parameter{ + ID: "externalUrl", + Name: "External URL", + Description: "The URL of the external Commit-Boost client or provider", + Type: config.ParameterType_String, + Default: map[config.Network]interface{}{config.Network_All: ""}, + }, + } +} + +// The title for the config +func (cfg *CommitBoostConfig) GetConfigTitle() string { + return "Commit-Boost Settings" +} + +// Get the Parameters for this config +func (cfg *CommitBoostConfig) GetParameters() []*config.Parameter { + return []*config.Parameter{ + &cfg.ContainerTag, + &cfg.AdditionalFlags, + } +} + +// Get the filename for the Commit-Boost PBS config +func (cfg *CommitBoostConfig) GetCommitBoostConfigFilename() string { + return CommitBoostConfigFile +} + +func (cfg *CommitBoostConfig) GetCommitBoostOpenPorts() string { + portMode := cfg.OpenRpcPort.Value.(config.RPCMode) + if !portMode.Open() { + return "" + } + port := cfg.Port.Value.(uint16) + return fmt.Sprintf("\"%s\"", portMode.DockerPortMapping(port)) +} + +// Get the chain name for the Commit-Boost config file +func (cfg *CommitBoostConfig) GetChainName(network config.Network) (string, error) { + switch network { + case config.Network_Mainnet: + return "Mainnet", nil + case config.Network_Devnet: + return "Devnet", nil + case config.Network_Testnet: + return "Testnet", nil + default: + return "", fmt.Errorf("unsupported network %s for Commit-Boost PBS config", network) + } +} diff --git a/shared/services/config/rocket-pool-config.go b/shared/services/config/rocket-pool-config.go index a259807c2..7a5c98f2e 100644 --- a/shared/services/config/rocket-pool-config.go +++ b/shared/services/config/rocket-pool-config.go @@ -35,6 +35,7 @@ const ( ExporterContainerName string = "exporter" GrafanaContainerName string = "grafana" MevBoostContainerName string = "mev-boost" + CommitBoostContainerName string = "commit-boost" NodeContainerName string = "node" PrometheusContainerName string = "prometheus" AlertmanagerContainerName string = "alertmanager" @@ -127,6 +128,10 @@ type RocketPoolConfig struct { EnableMevBoost config.Parameter `yaml:"enableMevBoost,omitempty"` MevBoost *MevBoostConfig `yaml:"mevBoost,omitempty"` + // Commit-Boost + EnableCommitBoost config.Parameter `yaml:"enableCommitBoost,omitempty"` + CommitBoost *CommitBoostConfig `yaml:"commitBoostConfig,omitempty"` + // Addons GraffitiWallWriter addontypes.SmartnodeAddon `yaml:"addon-gww,omitempty"` RescueNode addontypes.SmartnodeAddon `yaml:"addon-rescue-node,omitempty"` @@ -447,6 +452,16 @@ func NewRocketPoolConfig(rpDir string, isNativeMode bool) *RocketPoolConfig { CanBeBlank: false, OverwriteOnUpgrade: true, }, + EnableCommitBoost: config.Parameter{ + ID: "enableCommitBoost", + Name: "Enable Commit-Boost", + Description: "Enable Commit-Boost, which connects your validator to one or more relays of your choice. The relays act as intermediaries between you and professional block builders that find and extract Commit opportunities. The builders will give you a healthy tip in return, which tends to be worth more than blocks you built on your own.\n\n[orange]NOTE: This toggle is temporary during the early Merge days while relays are still being created. It will be removed in the future.", + Type: config.ParameterType_Bool, + Default: map[config.Network]interface{}{config.Network_All: true}, + AffectsContainers: []config.ContainerID{config.ContainerID_Eth2, config.ContainerID_CommitBoost}, + CanBeBlank: false, + OverwriteOnUpgrade: true, + }, } // Set the defaults for choices @@ -480,7 +495,7 @@ func NewRocketPoolConfig(rpDir string, isNativeMode bool) *RocketPoolConfig { cfg.BitflyNodeMetrics = NewBitflyNodeMetricsConfig(cfg) cfg.Native = NewNativeConfig(cfg) cfg.MevBoost = NewMevBoostConfig(cfg) - + cfg.CommitBoost = NewCommitBoostConfig(cfg) // Addons cfg.GraffitiWallWriter = addons.NewGraffitiWallWriter() cfg.RescueNode = addons.NewRescueNode() @@ -553,6 +568,7 @@ func (cfg *RocketPoolConfig) GetParameters() []*config.Parameter { &cfg.ExporterMetricsPort, &cfg.WatchtowerMetricsPort, &cfg.EnableMevBoost, + &cfg.EnableCommitBoost, } } @@ -586,6 +602,7 @@ func (cfg *RocketPoolConfig) GetSubconfigs() map[string]config.Config { "bitflyNodeMetrics": cfg.BitflyNodeMetrics, "native": cfg.Native, "mevBoost": cfg.MevBoost, + "commitBoostConfig": cfg.CommitBoost, "addons-gww": cfg.GraffitiWallWriter.GetConfig(), "addons-rescue-node": cfg.RescueNode.GetConfig(), } @@ -1150,17 +1167,22 @@ func (cfg *RocketPoolConfig) FeeRecipientFile() string { return GlobalFeeRecipientFilename } -// Used by text/template to format validator.yml -func (cfg *RocketPoolConfig) MevBoostUrl() string { - if !cfg.EnableMevBoost.Value.(bool) { - return "" - } +// Used by text/template to format mev-boost.yml +func (cfg *RocketPoolConfig) PbsUrl() string { + if cfg.EnableMevBoost.Value.(bool) { - if cfg.MevBoost.Mode.Value == config.Mode_Local { - return fmt.Sprintf("http://%s:%d", MevBoostContainerName, cfg.MevBoost.Port.Value) + if cfg.MevBoost.Mode.Value == config.Mode_Local { + return fmt.Sprintf("http://%s:%d", MevBoostContainerName, cfg.MevBoost.Port.Value) + } + return cfg.MevBoost.ExternalUrl.Value.(string) } - - return cfg.MevBoost.ExternalUrl.Value.(string) + if cfg.EnableCommitBoost.Value.(bool) { + if cfg.CommitBoost.Mode.Value == config.Mode_Local { + return fmt.Sprintf("http://%s:%d", CommitBoostContainerName, cfg.CommitBoost.Port.Value) + } + return cfg.CommitBoost.ExternalUrl.Value.(string) + } + return "" } // Gets the tag of the ec container @@ -1425,6 +1447,16 @@ func (cfg *RocketPoolConfig) GetMevBoostOpenPorts() string { return fmt.Sprintf("\"%s\"", portMode.DockerPortMapping(port)) } +// Used by text/template to format commit-boost.yml +func (cfg *RocketPoolConfig) GetCommitBoostOpenPorts() string { + portMode := cfg.CommitBoost.OpenRpcPort.Value.(config.RPCMode) + if !portMode.Open() { + return "" + } + port := cfg.CommitBoost.Port.Value.(uint16) + return fmt.Sprintf("\"%s\"", portMode.DockerPortMapping(port)) +} + // TODO: remove this code on the next Prysm release - so users can still rollback from 6.0.4 // Used by text/template to select an entrypoint based on which consensus client is used. func (cfg *RocketPoolConfig) GetEth2Entrypoint() string { diff --git a/shared/services/rocketpool/assets/install/override/commit-boost.yml b/shared/services/rocketpool/assets/install/override/commit-boost.yml new file mode 100644 index 000000000..707b56df7 --- /dev/null +++ b/shared/services/rocketpool/assets/install/override/commit-boost.yml @@ -0,0 +1,8 @@ +# Enter your own customizations for the Commit-Boost container here. These changes will persist after upgrades, so you only need to do them once. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + commit-boost: + x-rp-comment: Add your customizations below this line diff --git a/shared/services/rocketpool/assets/install/scripts/start-bn.sh b/shared/services/rocketpool/assets/install/scripts/start-bn.sh index 07b591c64..6863f59f0 100755 --- a/shared/services/rocketpool/assets/install/scripts/start-bn.sh +++ b/shared/services/rocketpool/assets/install/scripts/start-bn.sh @@ -85,8 +85,8 @@ if [ "$CC_CLIENT" = "lighthouse" ]; then CMD="$CMD --execution-timeout-multiplier 2" fi - if [ ! -z "$MEV_BOOST_URL" ]; then - CMD="$CMD --builder $MEV_BOOST_URL" + if [ ! -z "$PBS_URL" ]; then + CMD="$CMD --builder $PBS_URL" fi if [ ! -z "$BN_MAX_PEERS" ]; then @@ -142,8 +142,8 @@ if [ "$CC_CLIENT" = "lodestar" ]; then CMD="$CMD --terminal-total-difficulty-override $TTD_OVERRIDE" fi - if [ ! -z "$MEV_BOOST_URL" ]; then - CMD="$CMD --builder --builder.urls $MEV_BOOST_URL" + if [ ! -z "$PBS_URL" ]; then + CMD="$CMD --builder --builder.urls $PBS_URL" fi if [ ! -z "$BN_MAX_PEERS" ]; then @@ -214,8 +214,8 @@ if [ "$CC_CLIENT" = "nimbus" ]; then CMD="$CMD --suggested-gas-limit=$BN_SUGGESTED_BLOCK_GAS_LIMIT" fi - if [ ! -z "$MEV_BOOST_URL" ]; then - CMD="$CMD --payload-builder --payload-builder-url=$MEV_BOOST_URL" + if [ ! -z "$PBS_URL" ]; then + CMD="$CMD --payload-builder --payload-builder-url=$PBS_URL" fi if [ ! -z "$BN_MAX_PEERS" ]; then @@ -271,8 +271,8 @@ if [ "$CC_CLIENT" = "prysm" ]; then --blob-storage-layout=by-epoch \ $BN_ADDITIONAL_FLAGS" - if [ ! -z "$MEV_BOOST_URL" ]; then - CMD="$CMD --http-mev-relay $MEV_BOOST_URL" + if [ ! -z "$PBS_URL" ]; then + CMD="$CMD --http-mev-relay $PBS_URL" fi if [ ! -z "$BN_MAX_PEERS" ]; then @@ -327,8 +327,8 @@ if [ "$CC_CLIENT" = "teku" ]; then CMD="$CMD --data-storage-mode=archive" fi - if [ ! -z "$MEV_BOOST_URL" ]; then - CMD="$CMD --builder-endpoint=$MEV_BOOST_URL" + if [ ! -z "$PBS_URL" ]; then + CMD="$CMD --builder-endpoint=$PBS_URL" fi if [ ! -z "$BN_MAX_PEERS" ]; then diff --git a/shared/services/rocketpool/assets/install/scripts/start-commit-boost.sh b/shared/services/rocketpool/assets/install/scripts/start-commit-boost.sh new file mode 100755 index 000000000..063fc7b3f --- /dev/null +++ b/shared/services/rocketpool/assets/install/scripts/start-commit-boost.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# This script launches Commit-Boost PBS clients for Rocket Pool's docker stack; + +# Commit-Boost PBS doesn't accept command line arguments, everything must be in the config file which is +# specified by an environment variable. +export CB_CONFIG="/cb_config.toml" +if [ ! -f "$CB_CONFIG" ]; then + echo "Commit-Boost config file not found at $CB_CONFIG. Please ensure the config file is correctly mounted." + exit 1 +fi +CMD="/usr/local/bin/commit-boost-pbs" +exec ${CMD} \ No newline at end of file diff --git a/shared/services/rocketpool/assets/install/scripts/start-vc.sh b/shared/services/rocketpool/assets/install/scripts/start-vc.sh index 03f24db7d..6ed6398f0 100755 --- a/shared/services/rocketpool/assets/install/scripts/start-vc.sh +++ b/shared/services/rocketpool/assets/install/scripts/start-vc.sh @@ -69,7 +69,7 @@ if [ "$CC_CLIENT" = "lighthouse" ]; then CMD="$CMD --enable-doppelganger-protection" fi - if [ "$ENABLE_MEV_BOOST" = "true" ]; then + if [ "$ENABLE_PBS" = "true" ]; then CMD="$CMD --builder-proposals --prefer-builder-proposals" fi @@ -130,7 +130,7 @@ if [ ! -z "$VC_SUGGESTED_BLOCK_GAS_LIMIT" ]; then CMD="$CMD --doppelgangerProtection" fi - if [ "$ENABLE_MEV_BOOST" = "true" ]; then + if [ "$ENABLE_PBS" = "true" ]; then CMD="$CMD --builder" fi @@ -174,7 +174,7 @@ if [ "$CC_CLIENT" = "nimbus" ]; then --block-monitor-type=event \ $VC_ADDITIONAL_FLAGS" - if [ "$ENABLE_MEV_BOOST" = "true" ]; then + if [ "$ENABLE_PBS" = "true" ]; then CMD="$CMD --payload-builder" fi @@ -224,7 +224,7 @@ if [ "$CC_CLIENT" = "prysm" ]; then CMD="$CMD --suggested-gas-limit=$VC_SUGGESTED_BLOCK_GAS_LIMIT" fi - if [ "$ENABLE_MEV_BOOST" = "true" ]; then + if [ "$ENABLE_PBS" = "true" ]; then CMD="$CMD --enable-builder" fi @@ -285,7 +285,7 @@ if [ "$CC_CLIENT" = "teku" ]; then CMD="$CMD --doppelganger-detection-enabled" fi - if [ "$ENABLE_MEV_BOOST" = "true" ]; then + if [ "$ENABLE_PBS" = "true" ]; then CMD="$CMD --validators-builder-registration-default-enabled=true" if [ ! -z "$BN_SUGGESTED_BLOCK_GAS_LIMIT" ]; then CMD="$CMD --validators-builder-registration-default-gas-limit=$BN_SUGGESTED_BLOCK_GAS_LIMIT" diff --git a/shared/services/rocketpool/assets/install/templates/commit-boost-config.tmpl b/shared/services/rocketpool/assets/install/templates/commit-boost-config.tmpl new file mode 100644 index 000000000..19a6355bd --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/commit-boost-config.tmpl @@ -0,0 +1,22 @@ +{{- $networkName := .RocketPool.Network -}} +chain = "{{.RocketPool.CommitBoostConfig.GetChainName $networkName}}" + +[pbs] +docker_image = "{{.RocketPool.CommitBoostConfig.ContainerTag}}" +host = "0.0.0.0" +port = 18550 +relay_check = true +wait_all_registrations = true + +{{- range $relay := .RocketPool.CommitBoostConfig.GetEnabledPbsRelays $networkName}} + +[[relays]] +id = "{{$relay.ID}}" +url = "{{index $relay.Urls $networkName}}" +{{- end -}} +{{- $customRelays := Split .RocketPool.CommitBoostConfig.ExternalUrl.Value "," -}} +{{- range $customRelay := $customRelays}} + +[[relays]] +url = "{{$customRelay}}" +{{- end -}} \ No newline at end of file diff --git a/shared/services/rocketpool/assets/install/templates/commit-boost.tmpl b/shared/services/rocketpool/assets/install/templates/commit-boost.tmpl new file mode 100644 index 000000000..4eba4e152 --- /dev/null +++ b/shared/services/rocketpool/assets/install/templates/commit-boost.tmpl @@ -0,0 +1,31 @@ +# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY +# If you want to overwrite some of these values with your own customizations, +# please add them to `override/commit-boost.yml`. +# +# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration +# for more information on overriding specific parameters of docker-compose files. + +services: + commit-boost: + image: {{.CommitBoost.ContainerTag}} + container_name: {{.Smartnode.ProjectName}}_commit-boost + restart: unless-stopped + ports: [{{.GetCommitBoostOpenPorts}}] + volumes: + - {{.RocketPoolDirectory}}/scripts:/setup:ro + networks: + - net + environment: + - NETWORK={{.Smartnode.Network}} + - COMMIT_BOOST_PORT={{.CommitBoost.Port}} + - COMMIT_BOOST_ADDITIONAL_FLAGS={{.CommitBoost.AdditionalFlags}} + entrypoint: sh + command: "/setup/start-commit-boost.sh" + cap_drop: + - all + cap_add: + - dac_override + security_opt: + - no-new-privileges +networks: + net: diff --git a/shared/services/rocketpool/assets/install/templates/eth2.tmpl b/shared/services/rocketpool/assets/install/templates/eth2.tmpl index 5688c50af..78cf7edbf 100644 --- a/shared/services/rocketpool/assets/install/templates/eth2.tmpl +++ b/shared/services/rocketpool/assets/install/templates/eth2.tmpl @@ -72,7 +72,7 @@ services: - BITFLY_NODE_METRICS_SECRET={{.BitflyNodeMetrics.Secret}} - BITFLY_NODE_METRICS_ENDPOINT={{.BitflyNodeMetrics.Endpoint}} - BITFLY_NODE_METRICS_MACHINE_NAME={{.BitflyNodeMetrics.MachineName}} - - MEV_BOOST_URL={{.MevBoostUrl}} + - PBS_URL={{.PbsUrl}} - RETH_ADDRESS={{.Smartnode.GetRethAddress.Hex}} - GRAFFITI={{.Graffiti}} - ADDON_GWW_ENABLED={{.GraffitiWallWriter.GetEnabledParameter}} diff --git a/shared/services/rocketpool/assets/install/templates/validator.tmpl b/shared/services/rocketpool/assets/install/templates/validator.tmpl index fc5e1ff9f..7785c58c1 100644 --- a/shared/services/rocketpool/assets/install/templates/validator.tmpl +++ b/shared/services/rocketpool/assets/install/templates/validator.tmpl @@ -50,8 +50,8 @@ services: - BITFLY_NODE_METRICS_MACHINE_NAME={{.BitflyNodeMetrics.MachineName}} - GRAFFITI={{.Graffiti}} - ADDON_GWW_ENABLED={{.GraffitiWallWriter.GetEnabledParameter}} - - MEV_BOOST_URL={{.MevBoostUrl}} - - ENABLE_MEV_BOOST={{.EnableMevBoost}} + - PBS_URL={{.PbsUrl}} + - ENABLE_PBS={{.EnableMevBoost}} {{- if eq .ConsensusClient.String "teku"}} - TEKU_USE_SLASHING_PROTECTION={{.Teku.UseSlashingProtection}} {{- end}} diff --git a/shared/types/config/types.go b/shared/types/config/types.go index 5bc43ce8b..ec4477bd2 100644 --- a/shared/types/config/types.go +++ b/shared/types/config/types.go @@ -27,6 +27,7 @@ const ( ContainerID_Alertmanager ContainerID = "alertmanager" ContainerID_Exporter ContainerID = "exporter" ContainerID_MevBoost ContainerID = "mev-boost" + ContainerID_CommitBoost ContainerID = "commit-boost" ) // Enum to describe which network the system is on From a7828185a64f35aaa79a62d9cc2d4898347149ef Mon Sep 17 00:00:00 2001 From: Fornax <23104993+0xfornax@users.noreply.github.com> Date: Thu, 27 Nov 2025 22:41:59 -0300 Subject: [PATCH 09/13] wip --- .../service/config/settings-commit-boost.go | 156 ++++++++++++++++++ .../service/config/settings-home.go | 3 + 2 files changed, 159 insertions(+) create mode 100644 rocketpool-cli/service/config/settings-commit-boost.go diff --git a/rocketpool-cli/service/config/settings-commit-boost.go b/rocketpool-cli/service/config/settings-commit-boost.go new file mode 100644 index 000000000..b2c792277 --- /dev/null +++ b/rocketpool-cli/service/config/settings-commit-boost.go @@ -0,0 +1,156 @@ +package config + +import ( + "github.com/rivo/tview" + "github.com/rocket-pool/smartnode/shared/services/config" + cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" +) + +// The page wrapper for the Commit-boost config +type CommitBoostConfigPage struct { + home *settingsHome + page *page + layout *standardLayout + masterConfig *config.RocketPoolConfig + enableBox *parameterizedFormItem + modeBox *parameterizedFormItem + selectionModeBox *parameterizedFormItem + localItems []*parameterizedFormItem + externalItems []*parameterizedFormItem + regulatedAllMevBox *parameterizedFormItem + unregulatedAllMevBox *parameterizedFormItem + flashbotsBox *parameterizedFormItem + bloxrouteMaxProfitBox *parameterizedFormItem + bloxrouteRegulatedBox *parameterizedFormItem + ultrasoundBox *parameterizedFormItem + ultrasoundFilteredBox *parameterizedFormItem + aestusBox *parameterizedFormItem + titanGlobalBox *parameterizedFormItem + titanRegionalBox *parameterizedFormItem + btcsOfacBox *parameterizedFormItem +} + +// Creates a new page for the Commit-Boost settings +func NewCommitBoostConfigPage(home *settingsHome) *CommitBoostConfigPage { + + configPage := &CommitBoostConfigPage{ + home: home, + masterConfig: home.md.Config, + } + configPage.createContent() + + configPage.page = newPage( + home.homePage, + "settings-commit-boost", + "Commit-Boost", + "Select this to configure the settings for the Commit-Boost client, the source of blocks with MEV rewards for your minipools.\n\n", + configPage.layout.grid, + ) + + return configPage + +} + +// Get the underlying page +func (configPage *CommitBoostConfigPage) getPage() *page { + return configPage.page +} + +// Creates the content for the Commit-Boost settings page +func (configPage *CommitBoostConfigPage) createContent() { + + // Create the layout + configPage.layout = newStandardLayout() + configPage.layout.createForm(&configPage.masterConfig.Smartnode.Network, "Commit-Boost Settings") + configPage.layout.setupEscapeReturnHomeHandler(configPage.home.md, configPage.home.homePage) + + // Set up the form items + configPage.enableBox = createParameterizedCheckbox(&configPage.masterConfig.EnableCommitBoost) + configPage.modeBox = createParameterizedDropDown(&configPage.masterConfig.CommitBoost.Mode, configPage.layout.descriptionBox) + + localParams := []*cfgtypes.Parameter{ + &configPage.masterConfig.CommitBoost.Port, + &configPage.masterConfig.CommitBoost.OpenRpcPort, + &configPage.masterConfig.CommitBoost.ContainerTag, + &configPage.masterConfig.CommitBoost.AdditionalFlags, + } + externalParams := []*cfgtypes.Parameter{&configPage.masterConfig.CommitBoost.ExternalUrl} + + configPage.localItems = createParameterizedFormItems(localParams, configPage.layout.descriptionBox) + configPage.externalItems = createParameterizedFormItems(externalParams, configPage.layout.descriptionBox) + + configPage.flashbotsBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.FlashbotsRelay) + configPage.bloxrouteMaxProfitBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.BloxRouteMaxProfitRelay) + configPage.bloxrouteRegulatedBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.BloxRouteRegulatedRelay) + configPage.ultrasoundBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.UltrasoundRelay) + configPage.ultrasoundFilteredBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.UltrasoundFilteredRelay) + configPage.aestusBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.AestusRelay) + configPage.titanGlobalBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.TitanGlobalRelay) + configPage.titanRegionalBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.TitanRegionalRelay) + configPage.btcsOfacBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.BtcsOfacRelay) + + // Map the parameters to the form items in the layout + configPage.layout.mapParameterizedFormItems(configPage.enableBox, configPage.modeBox, configPage.selectionModeBox) + configPage.layout.mapParameterizedFormItems(configPage.flashbotsBox, configPage.bloxrouteMaxProfitBox, configPage.bloxrouteRegulatedBox, configPage.ultrasoundBox, configPage.ultrasoundFilteredBox, configPage.aestusBox, configPage.titanGlobalBox, configPage.titanRegionalBox, configPage.btcsOfacBox) + configPage.layout.mapParameterizedFormItems(configPage.localItems...) + configPage.layout.mapParameterizedFormItems(configPage.externalItems...) + + // Set up the setting callbacks + configPage.enableBox.item.(*tview.Checkbox).SetChangedFunc(func(checked bool) { + if configPage.masterConfig.EnableCommitBoost.Value == checked { + return + } + configPage.masterConfig.EnableCommitBoost.Value = checked + configPage.handleLayoutChanged() + }) + configPage.modeBox.item.(*DropDown).SetSelectedFunc(func(text string, index int) { + if configPage.masterConfig.CommitBoost.Mode.Value == configPage.masterConfig.CommitBoost.Mode.Options[index].Value { + return + } + configPage.masterConfig.CommitBoost.Mode.Value = configPage.masterConfig.CommitBoost.Mode.Options[index].Value + configPage.handleModeChanged() + }) + + // Do the initial draw + configPage.handleLayoutChanged() +} + +// Handle all of the form changes when the MEV-Boost mode has changed +func (configPage *CommitBoostConfigPage) handleModeChanged() { + configPage.layout.form.Clear(true) + configPage.layout.form.AddFormItem(configPage.enableBox.item) + if configPage.masterConfig.EnableCommitBoost.Value == true { + configPage.layout.form.AddFormItem(configPage.modeBox.item) + + selectedMode := configPage.masterConfig.CommitBoost.Mode.Value.(cfgtypes.Mode) + switch selectedMode { + case cfgtypes.Mode_Local: + configPage.handleSelectionModeChanged() + case cfgtypes.Mode_External: + if configPage.masterConfig.ExecutionClientMode.Value.(cfgtypes.Mode) == cfgtypes.Mode_Local { + // Only show these to Docker users, not Hybrid users + configPage.layout.addFormItems(configPage.externalItems) + } + } + } + + configPage.layout.refresh() +} + +// Handle all of the form changes when the relay selection mode has changed +func (configPage *CommitBoostConfigPage) handleSelectionModeChanged() { + configPage.layout.form.Clear(true) + configPage.layout.form.AddFormItem(configPage.enableBox.item) + configPage.layout.form.AddFormItem(configPage.modeBox.item) + + configPage.layout.form.AddFormItem(configPage.selectionModeBox.item) + + configPage.layout.addFormItems(configPage.localItems) +} + +// Handle a bulk redraw request +func (configPage *CommitBoostConfigPage) handleLayoutChanged() { + + // Rebuild the parameter maps based on the selected network + configPage.handleModeChanged() +} diff --git a/rocketpool-cli/service/config/settings-home.go b/rocketpool-cli/service/config/settings-home.go index 44185fd23..70893bd0b 100644 --- a/rocketpool-cli/service/config/settings-home.go +++ b/rocketpool-cli/service/config/settings-home.go @@ -19,6 +19,7 @@ type settingsHome struct { fallbackPage *FallbackConfigPage ccPage *ConsensusConfigPage mevBoostPage *MevBoostConfigPage + commitBoostPage *CommitBoostConfigPage metricsPage *MetricsConfigPage alertingPage *AlertingConfigPage addonsPage *AddonsPage @@ -45,6 +46,7 @@ func newSettingsHome(md *mainDisplay) *settingsHome { home.ccPage = NewConsensusConfigPage(home) home.fallbackPage = NewFallbackConfigPage(home) home.mevBoostPage = NewMevBoostConfigPage(home) + home.commitBoostPage = NewCommitBoostConfigPage(home) home.metricsPage = NewMetricsConfigPage(home) home.alertingPage = NewAlertingConfigPage(home) home.addonsPage = NewAddonsPage(home) @@ -54,6 +56,7 @@ func newSettingsHome(md *mainDisplay) *settingsHome { home.ccPage, home.fallbackPage, home.mevBoostPage, + home.commitBoostPage, home.metricsPage, home.alertingPage, home.addonsPage, From 233ad5bdf3a554f8f418b28b925bb1a72fefce65 Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Tue, 6 Jan 2026 20:41:07 -0300 Subject: [PATCH 10/13] wip --- .../service/config/settings-commit-boost.go | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/rocketpool-cli/service/config/settings-commit-boost.go b/rocketpool-cli/service/config/settings-commit-boost.go index b2c792277..86427b6e9 100644 --- a/rocketpool-cli/service/config/settings-commit-boost.go +++ b/rocketpool-cli/service/config/settings-commit-boost.go @@ -14,7 +14,6 @@ type CommitBoostConfigPage struct { masterConfig *config.RocketPoolConfig enableBox *parameterizedFormItem modeBox *parameterizedFormItem - selectionModeBox *parameterizedFormItem localItems []*parameterizedFormItem externalItems []*parameterizedFormItem regulatedAllMevBox *parameterizedFormItem @@ -90,7 +89,7 @@ func (configPage *CommitBoostConfigPage) createContent() { configPage.btcsOfacBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.BtcsOfacRelay) // Map the parameters to the form items in the layout - configPage.layout.mapParameterizedFormItems(configPage.enableBox, configPage.modeBox, configPage.selectionModeBox) + configPage.layout.mapParameterizedFormItems(configPage.enableBox, configPage.modeBox) configPage.layout.mapParameterizedFormItems(configPage.flashbotsBox, configPage.bloxrouteMaxProfitBox, configPage.bloxrouteRegulatedBox, configPage.ultrasoundBox, configPage.ultrasoundFilteredBox, configPage.aestusBox, configPage.titanGlobalBox, configPage.titanRegionalBox, configPage.btcsOfacBox) configPage.layout.mapParameterizedFormItems(configPage.localItems...) configPage.layout.mapParameterizedFormItems(configPage.externalItems...) @@ -122,12 +121,23 @@ func (configPage *CommitBoostConfigPage) handleModeChanged() { if configPage.masterConfig.EnableCommitBoost.Value == true { configPage.layout.form.AddFormItem(configPage.modeBox.item) - selectedMode := configPage.masterConfig.CommitBoost.Mode.Value.(cfgtypes.Mode) + var selectedMode cfgtypes.Mode + if configPage.masterConfig.CommitBoost.Mode.Value != nil { + selectedMode = configPage.masterConfig.CommitBoost.Mode.Value.(cfgtypes.Mode) + } else { + selectedMode = cfgtypes.Mode_Local + } switch selectedMode { case cfgtypes.Mode_Local: configPage.handleSelectionModeChanged() case cfgtypes.Mode_External: - if configPage.masterConfig.ExecutionClientMode.Value.(cfgtypes.Mode) == cfgtypes.Mode_Local { + var execMode cfgtypes.Mode + if configPage.masterConfig.ExecutionClientMode.Value != nil { + execMode = configPage.masterConfig.ExecutionClientMode.Value.(cfgtypes.Mode) + } else { + execMode = cfgtypes.Mode_Local + } + if execMode == cfgtypes.Mode_Local { // Only show these to Docker users, not Hybrid users configPage.layout.addFormItems(configPage.externalItems) } @@ -143,8 +153,6 @@ func (configPage *CommitBoostConfigPage) handleSelectionModeChanged() { configPage.layout.form.AddFormItem(configPage.enableBox.item) configPage.layout.form.AddFormItem(configPage.modeBox.item) - configPage.layout.form.AddFormItem(configPage.selectionModeBox.item) - configPage.layout.addFormItems(configPage.localItems) } From bac4513c9e0fcb49f3546435d30c80ecdfc82fa1 Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Wed, 11 Feb 2026 15:09:19 -0300 Subject: [PATCH 11/13] Finish commit-boost config --- .../service/config/settings-commit-boost.go | 73 +++-- shared/services/config/commit-boost-config.go | 294 +++++++++++++++++- shared/services/config/rocket-pool-config.go | 27 +- .../templates/commit-boost-config.tmpl | 14 +- .../install/templates/commit-boost.tmpl | 1 + shared/services/rocketpool/client.go | 14 + 6 files changed, 385 insertions(+), 38 deletions(-) diff --git a/rocketpool-cli/service/config/settings-commit-boost.go b/rocketpool-cli/service/config/settings-commit-boost.go index 86427b6e9..88e476b46 100644 --- a/rocketpool-cli/service/config/settings-commit-boost.go +++ b/rocketpool-cli/service/config/settings-commit-boost.go @@ -14,19 +14,13 @@ type CommitBoostConfigPage struct { masterConfig *config.RocketPoolConfig enableBox *parameterizedFormItem modeBox *parameterizedFormItem + selectionModeBox *parameterizedFormItem localItems []*parameterizedFormItem externalItems []*parameterizedFormItem - regulatedAllMevBox *parameterizedFormItem - unregulatedAllMevBox *parameterizedFormItem flashbotsBox *parameterizedFormItem bloxrouteMaxProfitBox *parameterizedFormItem bloxrouteRegulatedBox *parameterizedFormItem - ultrasoundBox *parameterizedFormItem - ultrasoundFilteredBox *parameterizedFormItem - aestusBox *parameterizedFormItem - titanGlobalBox *parameterizedFormItem titanRegionalBox *parameterizedFormItem - btcsOfacBox *parameterizedFormItem } // Creates a new page for the Commit-Boost settings @@ -42,7 +36,7 @@ func NewCommitBoostConfigPage(home *settingsHome) *CommitBoostConfigPage { home.homePage, "settings-commit-boost", "Commit-Boost", - "Select this to configure the settings for the Commit-Boost client, the source of blocks with MEV rewards for your minipools.\n\n", + "Select this to configure the settings for the Commit-Boost PBS client, the source of blocks with MEV rewards for your validators.\n\nCommit-Boost is a powerful PBS client that offers detailed reporting and transparency around block building, very low latency, and a responsive development team.\n\n", configPage.layout.grid, ) @@ -66,8 +60,10 @@ func (configPage *CommitBoostConfigPage) createContent() { // Set up the form items configPage.enableBox = createParameterizedCheckbox(&configPage.masterConfig.EnableCommitBoost) configPage.modeBox = createParameterizedDropDown(&configPage.masterConfig.CommitBoost.Mode, configPage.layout.descriptionBox) + configPage.selectionModeBox = createParameterizedDropDown(&configPage.masterConfig.CommitBoost.RelaySelectionMode, configPage.layout.descriptionBox) localParams := []*cfgtypes.Parameter{ + &configPage.masterConfig.CommitBoost.CustomRelays, &configPage.masterConfig.CommitBoost.Port, &configPage.masterConfig.CommitBoost.OpenRpcPort, &configPage.masterConfig.CommitBoost.ContainerTag, @@ -78,19 +74,15 @@ func (configPage *CommitBoostConfigPage) createContent() { configPage.localItems = createParameterizedFormItems(localParams, configPage.layout.descriptionBox) configPage.externalItems = createParameterizedFormItems(externalParams, configPage.layout.descriptionBox) - configPage.flashbotsBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.FlashbotsRelay) - configPage.bloxrouteMaxProfitBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.BloxRouteMaxProfitRelay) - configPage.bloxrouteRegulatedBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.BloxRouteRegulatedRelay) - configPage.ultrasoundBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.UltrasoundRelay) - configPage.ultrasoundFilteredBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.UltrasoundFilteredRelay) - configPage.aestusBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.AestusRelay) - configPage.titanGlobalBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.TitanGlobalRelay) - configPage.titanRegionalBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.TitanRegionalRelay) - configPage.btcsOfacBox = createParameterizedCheckbox(&configPage.masterConfig.MevBoost.BtcsOfacRelay) + // Relay checkboxes - using CommitBoost's own relay parameters + configPage.flashbotsBox = createParameterizedCheckbox(&configPage.masterConfig.CommitBoost.FlashbotsRelay) + configPage.bloxrouteMaxProfitBox = createParameterizedCheckbox(&configPage.masterConfig.CommitBoost.BloxRouteMaxProfitRelay) + configPage.bloxrouteRegulatedBox = createParameterizedCheckbox(&configPage.masterConfig.CommitBoost.BloxRouteRegulatedRelay) + configPage.titanRegionalBox = createParameterizedCheckbox(&configPage.masterConfig.CommitBoost.TitanRegionalRelay) // Map the parameters to the form items in the layout - configPage.layout.mapParameterizedFormItems(configPage.enableBox, configPage.modeBox) - configPage.layout.mapParameterizedFormItems(configPage.flashbotsBox, configPage.bloxrouteMaxProfitBox, configPage.bloxrouteRegulatedBox, configPage.ultrasoundBox, configPage.ultrasoundFilteredBox, configPage.aestusBox, configPage.titanGlobalBox, configPage.titanRegionalBox, configPage.btcsOfacBox) + configPage.layout.mapParameterizedFormItems(configPage.enableBox, configPage.modeBox, configPage.selectionModeBox) + configPage.layout.mapParameterizedFormItems(configPage.flashbotsBox, configPage.bloxrouteMaxProfitBox, configPage.bloxrouteRegulatedBox, configPage.titanRegionalBox) configPage.layout.mapParameterizedFormItems(configPage.localItems...) configPage.layout.mapParameterizedFormItems(configPage.externalItems...) @@ -109,12 +101,19 @@ func (configPage *CommitBoostConfigPage) createContent() { configPage.masterConfig.CommitBoost.Mode.Value = configPage.masterConfig.CommitBoost.Mode.Options[index].Value configPage.handleModeChanged() }) + configPage.selectionModeBox.item.(*DropDown).SetSelectedFunc(func(text string, index int) { + if configPage.masterConfig.CommitBoost.RelaySelectionMode.Value == configPage.masterConfig.CommitBoost.RelaySelectionMode.Options[index].Value { + return + } + configPage.masterConfig.CommitBoost.RelaySelectionMode.Value = configPage.masterConfig.CommitBoost.RelaySelectionMode.Options[index].Value + configPage.handleSelectionModeChanged() + }) // Do the initial draw configPage.handleLayoutChanged() } -// Handle all of the form changes when the MEV-Boost mode has changed +// Handle all of the form changes when the Commit-Boost mode has changed func (configPage *CommitBoostConfigPage) handleModeChanged() { configPage.layout.form.Clear(true) configPage.layout.form.AddFormItem(configPage.enableBox.item) @@ -145,6 +144,14 @@ func (configPage *CommitBoostConfigPage) handleModeChanged() { } configPage.layout.refresh() + + // Show the external mode warning after refresh() so it doesn't get overwritten + // by the form's ChangedFunc callback triggered during SetFocus(0) inside refresh() + if configPage.masterConfig.EnableCommitBoost.Value == true { + if mode, ok := configPage.masterConfig.CommitBoost.Mode.Value.(cfgtypes.Mode); ok && mode == cfgtypes.Mode_External { + configPage.layout.descriptionBox.SetText("[orange]NOTE: You have externally-managed client mode selected and Commit-Boost enabled. You must have Commit-Boost enabled in your externally-managed Beacon Node's configuration for this to function properly - otherwise you may not be able to publish blocks and will miss significant rewards!") + } + } } // Handle all of the form changes when the relay selection mode has changed @@ -153,7 +160,33 @@ func (configPage *CommitBoostConfigPage) handleSelectionModeChanged() { configPage.layout.form.AddFormItem(configPage.enableBox.item) configPage.layout.form.AddFormItem(configPage.modeBox.item) + configPage.layout.form.AddFormItem(configPage.selectionModeBox.item) + selectedMode := configPage.masterConfig.CommitBoost.RelaySelectionMode.Value.(config.PbsRelaySelectionMode) + switch selectedMode { + case config.PbsRelaySelectionMode_All: + // "Use All Relays" mode - no individual checkboxes needed + + case config.PbsRelaySelectionMode_Manual: + // Show available relay checkboxes for the current network + availableRelays := configPage.masterConfig.CommitBoost.GetAvailableRelays() + for _, relay := range availableRelays { + switch relay.ID { + case cfgtypes.MevRelayID_Flashbots: + configPage.layout.form.AddFormItem(configPage.flashbotsBox.item) + case cfgtypes.MevRelayID_BloxrouteMaxProfit: + configPage.layout.form.AddFormItem(configPage.bloxrouteMaxProfitBox.item) + case cfgtypes.MevRelayID_BloxrouteRegulated: + configPage.layout.form.AddFormItem(configPage.bloxrouteRegulatedBox.item) + case cfgtypes.MevRelayID_TitanRegional: + configPage.layout.form.AddFormItem(configPage.titanRegionalBox.item) + } + } + } + + // Show local settings (custom relays, port, container tag, etc.) configPage.layout.addFormItems(configPage.localItems) + + configPage.layout.refresh() } // Handle a bulk redraw request diff --git a/shared/services/config/commit-boost-config.go b/shared/services/config/commit-boost-config.go index 3bde52089..d170d7696 100644 --- a/shared/services/config/commit-boost-config.go +++ b/shared/services/config/commit-boost-config.go @@ -2,23 +2,57 @@ package config import ( "fmt" + "strings" "github.com/rocket-pool/smartnode/shared/types/config" ) // Constants const ( - CommitBoostConfigFile string = "cb_config.toml" - commitBoostProdTag string = "ghcr.io/commit-boost/pbs:v0.9.2" - commitBoostTestTag string = "ghcr.io/commit-boost/pbs:v0.9.2" + CommitBoostConfigFile string = "cb_config.toml" + CommitBoostConfigTemplate string = "commit-boost-config" + commitBoostProdTag string = "ghcr.io/commit-boost/pbs:v0.9.2" + commitBoostTestTag string = "ghcr.io/commit-boost/pbs:v0.9.2" ) +// Relay selection mode for Commit-Boost PBS +type PbsRelaySelectionMode string + +const ( + PbsRelaySelectionMode_All PbsRelaySelectionMode = "all" + PbsRelaySelectionMode_Manual PbsRelaySelectionMode = "manual" +) + +// A relay entry ready for template rendering +type PbsRelayInfo struct { + ID string + URL string +} + // Configuration for Commit-Boost's service type CommitBoostConfig struct { // Ownership mode Mode config.Parameter `yaml:"mode,omitempty"` - // The URL of an external MEV-Boost client + // The mode for relay selection + RelaySelectionMode config.Parameter `yaml:"relaySelectionMode,omitempty"` + + // Flashbots relay + FlashbotsRelay config.Parameter `yaml:"cbFlashbotsEnabled,omitempty"` + + // bloXroute max profit relay + BloxRouteMaxProfitRelay config.Parameter `yaml:"cbBloxRouteMaxProfitEnabled,omitempty"` + + // bloXroute regulated relay + BloxRouteRegulatedRelay config.Parameter `yaml:"cbBloxRouteRegulatedEnabled,omitempty"` + + // Titan regional relay + TitanRegionalRelay config.Parameter `yaml:"cbTitanRegionalEnabled,omitempty"` + + // Custom relays provided by the user (comma-separated URLs) + CustomRelays config.Parameter `yaml:"customRelays,omitempty"` + + // The URL of an external Commit-Boost client ExternalUrl config.Parameter `yaml:"externalUrl"` // The Docker Hub tag for Commit-Boost @@ -27,21 +61,36 @@ type CommitBoostConfig struct { // Custom command line flags AdditionalFlags config.Parameter `yaml:"additionalFlags,omitempty"` - parentConfig *RocketPoolConfig `yaml:"-"` - // The port that Commit-Boost should serve its API on Port config.Parameter `yaml:"port,omitempty"` // Toggle for forwarding the HTTP port outside of Docker OpenRpcPort config.Parameter `yaml:"openRpcPort,omitempty"` + + /////////////////////////// + // Non-editable settings // + /////////////////////////// + + parentConfig *RocketPoolConfig `yaml:"-"` + relays []config.MevRelay `yaml:"-"` + relayMap map[config.MevRelayID]config.MevRelay `yaml:"-"` } // Generates a new Commit-Boost PBS service configuration func NewCommitBoostConfig(cfg *RocketPoolConfig) *CommitBoostConfig { + // Generate the relays + relays := createCommitBoostRelays() + relayMap := map[config.MevRelayID]config.MevRelay{} + for _, relay := range relays { + relayMap[relay.ID] = relay + } + portModes := config.PortModes("") return &CommitBoostConfig{ parentConfig: cfg, + relays: relays, + relayMap: relayMap, Mode: config.Parameter{ ID: "mode", @@ -62,6 +111,44 @@ func NewCommitBoostConfig(cfg *RocketPoolConfig) *CommitBoostConfig { Value: config.Mode_External, }}, }, + + RelaySelectionMode: config.Parameter{ + ID: "relaySelectionMode", + Name: "Relay Selection Mode", + Description: "Select how the TUI shows you the options for which PBS relays to enable.\n\nNote that all of the built-in relays support regional sanction lists (such as the US OFAC list) and are compliant with regulations.", + Type: config.ParameterType_Choice, + Default: map[config.Network]interface{}{config.Network_All: PbsRelaySelectionMode_All}, + AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + Options: []config.ParameterOption{{ + Name: "Use All Relays", + Description: "Use this if you simply want to enable all of the built-in relays without needing to read about each individual relay. If new relays get added, you'll automatically start using those too.\n\nNote that all of the built-in relays support regional sanction lists (such as the US OFAC list) and are compliant with regulations.", + Value: PbsRelaySelectionMode_All, + }, { + Name: "Manual Mode", + Description: "Each relay will be shown, and you can enable each one individually as you see fit.\nUse this if you already know about the relays and want to customize the ones you will use.", + Value: PbsRelaySelectionMode_Manual, + }}, + }, + + // Explicit relay params + FlashbotsRelay: generateCbRelayParameter("cbFlashbotsEnabled", relayMap[config.MevRelayID_Flashbots]), + BloxRouteMaxProfitRelay: generateCbRelayParameter("cbBloxRouteMaxProfitEnabled", relayMap[config.MevRelayID_BloxrouteMaxProfit]), + BloxRouteRegulatedRelay: generateCbRelayParameter("cbBloxRouteRegulatedEnabled", relayMap[config.MevRelayID_BloxrouteRegulated]), + TitanRegionalRelay: generateCbRelayParameter("cbTitanRegionalEnabled", relayMap[config.MevRelayID_TitanRegional]), + + CustomRelays: config.Parameter{ + ID: "customRelays", + Name: "Custom Relays", + Description: "Add custom relay URLs to Commit-Boost that aren't part of the built-in set. You can add multiple relays by separating each one with a comma. Any relay URLs can be used as long as they match your selected Ethereum network.\n\nFor a comprehensive list of available relays, we recommend the list maintained by ETHStaker:\nhttps://github.com/eth-educators/ethstaker-guides/blob/main/MEV-relay-list.md", + Type: config.ParameterType_String, + Default: map[config.Network]interface{}{config.Network_All: ""}, + AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, + CanBeBlank: true, + OverwriteOnUpgrade: false, + }, + Port: config.Parameter{ ID: "port", Name: "Port", @@ -99,10 +186,11 @@ func NewCommitBoostConfig(cfg *RocketPoolConfig) *CommitBoostConfig { config.Network_Testnet: commitBoostTestTag, }, }, + AdditionalFlags: config.Parameter{ ID: "additionalFlags", Name: "Additional Flags", - Description: "Additional custom command line flags you want to pass to Commit-Boost, to take advantage of other settings that Hyperdrive's configuration doesn't cover.", + Description: "Additional custom command line flags you want to pass to Commit-Boost, to take advantage of other settings that the Smartnode's configuration doesn't cover.", AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, CanBeBlank: true, OverwriteOnUpgrade: false, @@ -111,10 +199,11 @@ func NewCommitBoostConfig(cfg *RocketPoolConfig) *CommitBoostConfig { config.Network_All: "", }, }, + ExternalUrl: config.Parameter{ ID: "externalUrl", Name: "External URL", - Description: "The URL of the external Commit-Boost client or provider", + Description: "The URL of the external Commit-Boost client or provider.", Type: config.ParameterType_String, Default: map[config.Network]interface{}{config.Network_All: ""}, }, @@ -129,8 +218,18 @@ func (cfg *CommitBoostConfig) GetConfigTitle() string { // Get the Parameters for this config func (cfg *CommitBoostConfig) GetParameters() []*config.Parameter { return []*config.Parameter{ + &cfg.Mode, + &cfg.RelaySelectionMode, + &cfg.FlashbotsRelay, + &cfg.BloxRouteMaxProfitRelay, + &cfg.BloxRouteRegulatedRelay, + &cfg.TitanRegionalRelay, + &cfg.CustomRelays, + &cfg.Port, + &cfg.OpenRpcPort, &cfg.ContainerTag, &cfg.AdditionalFlags, + &cfg.ExternalUrl, } } @@ -161,3 +260,182 @@ func (cfg *CommitBoostConfig) GetChainName(network config.Network) (string, erro return "", fmt.Errorf("unsupported network %s for Commit-Boost PBS config", network) } } + +// Get the chain name for the current network (for use in templates) +func (cfg *CommitBoostConfig) GetCurrentChainName() string { + network := cfg.parentConfig.Smartnode.Network.Value.(config.Network) + name, _ := cfg.GetChainName(network) + return name +} + +// Get the relays that are available for the current network +func (cfg *CommitBoostConfig) GetAvailableRelays() []config.MevRelay { + relays := []config.MevRelay{} + currentNetwork := cfg.parentConfig.Smartnode.Network.Value.(config.Network) + for _, relay := range cfg.relays { + if relay.Urls.UrlExists(currentNetwork) { + relays = append(relays, relay) + } + } + return relays +} + +// Get the enabled PBS relays, formatted for template rendering. +func (cfg *CommitBoostConfig) GetEnabledPbsRelayInfo() []PbsRelayInfo { + currentNetwork := cfg.parentConfig.Smartnode.Network.Value.(config.Network) + relays := cfg.getEnabledRelays() + result := []PbsRelayInfo{} + for _, relay := range relays { + if url, ok := relay.Urls[currentNetwork]; ok && url != "" { + result = append(result, PbsRelayInfo{ + ID: string(relay.ID), + URL: url, + }) + } + } + return result +} + +// Get which PBS relays are enabled based on the relay selection mode +func (cfg *CommitBoostConfig) getEnabledRelays() []config.MevRelay { + relays := []config.MevRelay{} + currentNetwork := cfg.parentConfig.Smartnode.Network.Value.(config.Network) + + switch cfg.RelaySelectionMode.Value.(PbsRelaySelectionMode) { + case PbsRelaySelectionMode_All: + for _, relay := range cfg.relays { + if relay.Urls.UrlExists(currentNetwork) { + relays = append(relays, relay) + } + } + + case PbsRelaySelectionMode_Manual: + relays = cfg.maybeAddRelay(relays, cfg.FlashbotsRelay, config.MevRelayID_Flashbots, currentNetwork) + relays = cfg.maybeAddRelay(relays, cfg.BloxRouteMaxProfitRelay, config.MevRelayID_BloxrouteMaxProfit, currentNetwork) + relays = cfg.maybeAddRelay(relays, cfg.BloxRouteRegulatedRelay, config.MevRelayID_BloxrouteRegulated, currentNetwork) + relays = cfg.maybeAddRelay(relays, cfg.TitanRegionalRelay, config.MevRelayID_TitanRegional, currentNetwork) + } + + return relays +} + +func (cfg *CommitBoostConfig) maybeAddRelay(relays []config.MevRelay, relayParam config.Parameter, relayID config.MevRelayID, currentNetwork config.Network) []config.MevRelay { + if relayParam.Value == true { + if cfg.relayMap[relayID].Urls.UrlExists(currentNetwork) { + relays = append(relays, cfg.relayMap[relayID]) + } + } + return relays +} + +// Get custom relay URLs (comma-separated in CustomRelays) as a slice +func (cfg *CommitBoostConfig) GetCustomRelays() []string { + customRelays, ok := cfg.CustomRelays.Value.(string) + if !ok || customRelays == "" { + return nil + } + result := []string{} + for _, relay := range strings.Split(customRelays, ",") { + trimmed := strings.TrimSpace(relay) + if trimmed != "" { + result = append(result, trimmed) + } + } + return result +} + +// Get the relay string for all enabled relays (built-in + custom), comma-separated +func (cfg *CommitBoostConfig) GetRelayString() string { + relayUrls := []string{} + currentNetwork := cfg.parentConfig.Smartnode.Network.Value.(config.Network) + + relays := cfg.getEnabledRelays() + for _, relay := range relays { + relayUrls = append(relayUrls, relay.Urls[currentNetwork]) + } + if customRelays := cfg.GetCustomRelays(); len(customRelays) > 0 { + relayUrls = append(relayUrls, customRelays...) + } + + return strings.Join(relayUrls, ",") +} + +// Get the container tag value as a string (for use in templates) +func (cfg *CommitBoostConfig) GetContainerTag() string { + return fmt.Sprint(cfg.ContainerTag.Value) +} + +// Create the default Commit-Boost PBS relays. +// Following hyperdrive's approach, only regulated/compliant relays are included. +func createCommitBoostRelays() []config.MevRelay { + relays := []config.MevRelay{ + // Flashbots + { + ID: config.MevRelayID_Flashbots, + Name: "Flashbots", + Description: "Flashbots is the developer of MEV-Boost, and one of the best-known and most trusted relays in the space.", + Urls: map[config.Network]string{ + config.Network_Mainnet: "https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net?id=rocketpool", + config.Network_Testnet: "https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@boost-relay-hoodi.flashbots.net?id=rocketpool", + config.Network_Devnet: "https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@boost-relay-hoodi.flashbots.net?id=rocketpool", + }, + Regulated: true, + }, + + // bloXroute Max Profit + { + ID: config.MevRelayID_BloxrouteMaxProfit, + Name: "bloXroute Max Profit", + Description: "Select this to enable the \"max profit\" relay from bloXroute.", + Urls: map[config.Network]string{ + config.Network_Mainnet: "https://0x8b5d2e73e2a3a55c6c87b8b6eb92e0149a125c852751db1422fa951e42a09b82c142c3ea98d0d9930b056a3bc9896b8f@bloxroute.max-profit.blxrbdn.com?id=rocketpool", + config.Network_Testnet: "https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.hoodi.blxrbdn.com?id=rocketpool", + config.Network_Devnet: "https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.hoodi.blxrbdn.com?id=rocketpool", + }, + Regulated: true, + }, + + // bloXroute Regulated + { + ID: config.MevRelayID_BloxrouteRegulated, + Name: "bloXroute Regulated", + Description: "Select this to enable the \"regulated\" relay from bloXroute.", + Urls: map[config.Network]string{ + config.Network_Mainnet: "https://0xb0b07cd0abef743db4260b0ed50619cf6ad4d82064cb4fbec9d3ec530f7c5e6793d9f286c4e082c0244ffb9f2658fe88@bloxroute.regulated.blxrbdn.com?id=rocketpool", + }, + Regulated: true, + }, + + // Titan Regional + { + ID: config.MevRelayID_TitanRegional, + Name: "Titan Regional", + Description: "Titan Relay is a neutral, Rust-based PBS Relay optimized for low latency throughput, geographical distribution, and robustness. This is the regulated (filtering) version.", + Urls: map[config.Network]string{ + config.Network_Mainnet: "https://0x8c4ed5e24fe5c6ae21018437bde147693f68cda427cd1122cf20819c30eda7ed74f72dece09bb313f2a1855595ab677d@regional.titanrelay.xyz", + config.Network_Testnet: "https://0xaa58208899c6105603b74396734a6263cc7d947f444f396a90f7b7d3e65d102aec7e5e5291b27e08d02c50a050825c2f@hoodi.titanrelay.xyz", + config.Network_Devnet: "https://0xaa58208899c6105603b74396734a6263cc7d947f444f396a90f7b7d3e65d102aec7e5e5291b27e08d02c50a050825c2f@hoodi.titanrelay.xyz", + }, + Regulated: true, + }, + } + + return relays +} + +// Generate one of the relay parameters for Commit-Boost +func generateCbRelayParameter(id string, relay config.MevRelay) config.Parameter { + description := fmt.Sprintf("[lime]NOTE: You can enable multiple options.\n\n[white]%s\n\n", relay.Description) + description += "Complies with Regulations: YES\n" + + return config.Parameter{ + ID: id, + Name: fmt.Sprintf("Enable %s", relay.Name), + Description: description, + Type: config.ParameterType_Bool, + Default: map[config.Network]interface{}{config.Network_All: false}, + AffectsContainers: []config.ContainerID{config.ContainerID_CommitBoost}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + } +} diff --git a/shared/services/config/rocket-pool-config.go b/shared/services/config/rocket-pool-config.go index 7a5c98f2e..f18e8403c 100644 --- a/shared/services/config/rocket-pool-config.go +++ b/shared/services/config/rocket-pool-config.go @@ -445,7 +445,7 @@ func NewRocketPoolConfig(rpDir string, isNativeMode bool) *RocketPoolConfig { EnableMevBoost: config.Parameter{ ID: "enableMevBoost", Name: "Enable MEV-Boost", - Description: "Enable MEV-Boost, which connects your validator to one or more relays of your choice. The relays act as intermediaries between you and professional block builders that find and extract MEV opportunities. The builders will give you a healthy tip in return, which tends to be worth more than blocks you built on your own.\n\n[orange]NOTE: This toggle is temporary during the early Merge days while relays are still being created. It will be removed in the future.", + Description: "Enable MEV-Boost, which connects your validator to one or more relays of your choice. The relays act as intermediaries between you and professional block builders that find and extract MEV opportunities. The builders will give you a healthy tip in return, which tends to be worth more than blocks you built on your own.\n\n", Type: config.ParameterType_Bool, Default: map[config.Network]interface{}{config.Network_All: true}, AffectsContainers: []config.ContainerID{config.ContainerID_Eth2, config.ContainerID_MevBoost}, @@ -455,7 +455,7 @@ func NewRocketPoolConfig(rpDir string, isNativeMode bool) *RocketPoolConfig { EnableCommitBoost: config.Parameter{ ID: "enableCommitBoost", Name: "Enable Commit-Boost", - Description: "Enable Commit-Boost, which connects your validator to one or more relays of your choice. The relays act as intermediaries between you and professional block builders that find and extract Commit opportunities. The builders will give you a healthy tip in return, which tends to be worth more than blocks you built on your own.\n\n[orange]NOTE: This toggle is temporary during the early Merge days while relays are still being created. It will be removed in the future.", + Description: "Enable Commit-Boost, which connects your validator to one or more relays of your choice. The relays act as intermediaries between you and professional block builders that find and extract opportunities. The builders will give you a healthy tip in return, which tends to be worth more than blocks you built on your own.\n\n", Type: config.ParameterType_Bool, Default: map[config.Network]interface{}{config.Network_All: true}, AffectsContainers: []config.ContainerID{config.ContainerID_Eth2, config.ContainerID_CommitBoost}, @@ -1602,6 +1602,29 @@ func (cfg *RocketPoolConfig) Validate() []string { } } + // Check if both MEV-Boost and Commit-Boost are enabled at the same time + if cfg.EnableMevBoost.Value == true && cfg.EnableCommitBoost.Value == true { + errors = append(errors, "You have both MEV-Boost and Commit-Boost enabled. Please disable one of them — only one PBS (Proposer-Builder Separation) client can be active at a time.") + } + + // Validate Commit-Boost settings + if !cfg.IsNativeMode && cfg.EnableCommitBoost.Value == true { + switch cfg.CommitBoost.Mode.Value.(config.Mode) { + case config.Mode_Local: + relayInfo := cfg.CommitBoost.GetEnabledPbsRelayInfo() + customRelays := cfg.CommitBoost.GetCustomRelays() + if len(relayInfo) == 0 && len(customRelays) == 0 { + errors = append(errors, "You have Commit-Boost enabled in local mode but don't have any relays enabled and no custom relays set. Please enable at least one relay or add a custom relay URL.") + } + case config.Mode_External: + if cfg.ExecutionClientMode.Value.(config.Mode) == config.Mode_Local && cfg.CommitBoost.ExternalUrl.Value.(string) == "" { + errors = append(errors, "You have Commit-Boost enabled in external mode but don't have a URL set. Please enter the external Commit-Boost server URL to use it.") + } + default: + errors = append(errors, "You do not have a Commit-Boost mode configured. You must either select a mode in the `rocketpool service config` UI, or disable Commit-Boost.") + } + } + // Technically not required since native mode doesn't support addons, but defensively check to make sure a native mode // user hasn't tried to configure the rescue node via the TUI if cfg.RescueNode.GetEnabledParameter().Value.(bool) { diff --git a/shared/services/rocketpool/assets/install/templates/commit-boost-config.tmpl b/shared/services/rocketpool/assets/install/templates/commit-boost-config.tmpl index 19a6355bd..ad896e3b4 100644 --- a/shared/services/rocketpool/assets/install/templates/commit-boost-config.tmpl +++ b/shared/services/rocketpool/assets/install/templates/commit-boost-config.tmpl @@ -1,22 +1,20 @@ -{{- $networkName := .RocketPool.Network -}} -chain = "{{.RocketPool.CommitBoostConfig.GetChainName $networkName}}" +chain = "{{.CommitBoost.GetCurrentChainName}}" [pbs] -docker_image = "{{.RocketPool.CommitBoostConfig.ContainerTag}}" +docker_image = "{{.CommitBoost.GetContainerTag}}" host = "0.0.0.0" port = 18550 relay_check = true wait_all_registrations = true -{{- range $relay := .RocketPool.CommitBoostConfig.GetEnabledPbsRelays $networkName}} +{{- range $relay := .CommitBoost.GetEnabledPbsRelayInfo}} [[relays]] id = "{{$relay.ID}}" -url = "{{index $relay.Urls $networkName}}" +url = "{{$relay.URL}}" {{- end -}} -{{- $customRelays := Split .RocketPool.CommitBoostConfig.ExternalUrl.Value "," -}} -{{- range $customRelay := $customRelays}} +{{- range $customRelay := .CommitBoost.GetCustomRelays}} [[relays]] url = "{{$customRelay}}" -{{- end -}} \ No newline at end of file +{{- end -}} diff --git a/shared/services/rocketpool/assets/install/templates/commit-boost.tmpl b/shared/services/rocketpool/assets/install/templates/commit-boost.tmpl index 4eba4e152..9ff76bfd4 100644 --- a/shared/services/rocketpool/assets/install/templates/commit-boost.tmpl +++ b/shared/services/rocketpool/assets/install/templates/commit-boost.tmpl @@ -13,6 +13,7 @@ services: ports: [{{.GetCommitBoostOpenPorts}}] volumes: - {{.RocketPoolDirectory}}/scripts:/setup:ro + - {{.RocketPoolDirectory}}/runtime/cb_config.toml:/cb_config.toml:ro networks: - net environment: diff --git a/shared/services/rocketpool/client.go b/shared/services/rocketpool/client.go index f14cb758a..e7eba56fa 100644 --- a/shared/services/rocketpool/client.go +++ b/shared/services/rocketpool/client.go @@ -1183,6 +1183,20 @@ func (c *Client) deployTemplates(cfg *config.RocketPoolConfig, rocketpoolDir str toDeploy = append(toDeploy, config.MevBoostContainerName) } + // Check if we are running the Commit-Boost container locally + if cfg.EnableCommitBoost.Value == true && cfg.CommitBoost.Mode.Value.(cfgtypes.Mode) == cfgtypes.Mode_Local { + toDeploy = append(toDeploy, config.CommitBoostContainerName) + + // Render the Commit-Boost PBS config file (cb_config.toml) + cbConfigTmpl := template.Template{ + Src: filepath.Join(templatesFolder, config.CommitBoostConfigTemplate+".tmpl"), + Dst: filepath.Join(runtimeFolder, config.CommitBoostConfigFile), + } + if err := cbConfigTmpl.Write(cfg); err != nil { + return []string{}, fmt.Errorf("could not render Commit-Boost config file: %w", err) + } + } + for _, containerName := range toDeploy { containers, err := composePaths.File(containerName).Write(cfg) if err != nil { From c151b520180c3238a4e610f5a3ab463c16394ecf Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Wed, 11 Feb 2026 15:44:03 -0300 Subject: [PATCH 12/13] Remove overwrite on upgrade --- shared/services/config/commit-boost-config.go | 11 ++++++----- shared/services/config/rocket-pool-config.go | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/shared/services/config/commit-boost-config.go b/shared/services/config/commit-boost-config.go index d170d7696..d0d1942ad 100644 --- a/shared/services/config/commit-boost-config.go +++ b/shared/services/config/commit-boost-config.go @@ -11,8 +11,8 @@ import ( const ( CommitBoostConfigFile string = "cb_config.toml" CommitBoostConfigTemplate string = "commit-boost-config" - commitBoostProdTag string = "ghcr.io/commit-boost/pbs:v0.9.2" - commitBoostTestTag string = "ghcr.io/commit-boost/pbs:v0.9.2" + commitBoostProdTag string = "ghcr.io/commit-boost/pbs:v0.9.3" + commitBoostTestTag string = "ghcr.io/commit-boost/pbs:v0.9.3" ) // Relay selection mode for Commit-Boost PBS @@ -247,15 +247,16 @@ func (cfg *CommitBoostConfig) GetCommitBoostOpenPorts() string { return fmt.Sprintf("\"%s\"", portMode.DockerPortMapping(port)) } -// Get the chain name for the Commit-Boost config file +// Get the chain name for the Commit-Boost config file. +// Commit-Boost expects actual Ethereum network names, not generic labels. func (cfg *CommitBoostConfig) GetChainName(network config.Network) (string, error) { switch network { case config.Network_Mainnet: return "Mainnet", nil case config.Network_Devnet: - return "Devnet", nil + return "Hoodi", nil case config.Network_Testnet: - return "Testnet", nil + return "Hoodi", nil default: return "", fmt.Errorf("unsupported network %s for Commit-Boost PBS config", network) } diff --git a/shared/services/config/rocket-pool-config.go b/shared/services/config/rocket-pool-config.go index 01273a140..f49eb8182 100644 --- a/shared/services/config/rocket-pool-config.go +++ b/shared/services/config/rocket-pool-config.go @@ -450,7 +450,7 @@ func NewRocketPoolConfig(rpDir string, isNativeMode bool) *RocketPoolConfig { Default: map[config.Network]interface{}{config.Network_All: true}, AffectsContainers: []config.ContainerID{config.ContainerID_Eth2, config.ContainerID_MevBoost}, CanBeBlank: false, - OverwriteOnUpgrade: true, + OverwriteOnUpgrade: false, }, EnableCommitBoost: config.Parameter{ ID: "enableCommitBoost", @@ -460,7 +460,7 @@ func NewRocketPoolConfig(rpDir string, isNativeMode bool) *RocketPoolConfig { Default: map[config.Network]interface{}{config.Network_All: true}, AffectsContainers: []config.ContainerID{config.ContainerID_Eth2, config.ContainerID_CommitBoost}, CanBeBlank: false, - OverwriteOnUpgrade: true, + OverwriteOnUpgrade: false, }, } From 4b6a357e54faa8a279d62fcc98bddacdfca64a70 Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Wed, 11 Feb 2026 15:47:41 -0300 Subject: [PATCH 13/13] Add isPBSEnabled --- shared/services/config/rocket-pool-config.go | 5 +++++ .../rocketpool/assets/install/templates/validator.tmpl | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/shared/services/config/rocket-pool-config.go b/shared/services/config/rocket-pool-config.go index f49eb8182..85430ea0e 100644 --- a/shared/services/config/rocket-pool-config.go +++ b/shared/services/config/rocket-pool-config.go @@ -1189,6 +1189,11 @@ func (cfg *RocketPoolConfig) FeeRecipientFile() string { return GlobalFeeRecipientFilename } +// Used by text/template to check if any PBS client (MEV-Boost or Commit-Boost) is enabled +func (cfg *RocketPoolConfig) IsPbsEnabled() bool { + return cfg.EnableMevBoost.Value.(bool) || cfg.EnableCommitBoost.Value.(bool) +} + // Used by text/template to format mev-boost.yml func (cfg *RocketPoolConfig) PbsUrl() string { if cfg.EnableMevBoost.Value.(bool) { diff --git a/shared/services/rocketpool/assets/install/templates/validator.tmpl b/shared/services/rocketpool/assets/install/templates/validator.tmpl index 77ec54ca6..302bee54d 100644 --- a/shared/services/rocketpool/assets/install/templates/validator.tmpl +++ b/shared/services/rocketpool/assets/install/templates/validator.tmpl @@ -44,7 +44,7 @@ services: - GRAFFITI={{.Graffiti}} - ADDON_GWW_ENABLED={{.GraffitiWallWriter.GetEnabledParameter}} - PBS_URL={{.PbsUrl}} - - ENABLE_PBS={{.EnableMevBoost}} + - ENABLE_PBS={{.IsPbsEnabled}} {{- if eq .ConsensusClient.String "teku"}} - TEKU_USE_SLASHING_PROTECTION={{.Teku.UseSlashingProtection}} {{- end}}