Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4c09570
initial commit implementing https://github.com/ethereum/keymanager-AP…
james-prysm Jul 29, 2026
27698ca
gaz
james-prysm Jul 30, 2026
f48d53b
Merge branch 'develop' into km88-proposer-settings
james-prysm Jul 31, 2026
602d824
Merge branch 'develop' into km88-proposer-settings
james-prysm Aug 3, 2026
f4c3354
updating based on removal of p2p boost config
james-prysm Aug 3, 2026
df8afee
self review
james-prysm Aug 5, 2026
cc92577
Merge branch 'develop' into km88-proposer-settings
james-prysm Aug 5, 2026
ea814aa
make changelog more concise
james-prysm Aug 6, 2026
4f25f60
rolling back some flag overrides
james-prysm Aug 7, 2026
40486bd
reverting change
james-prysm Aug 7, 2026
aa6247b
clarifying changelog
james-prysm Aug 7, 2026
586904e
self review on comments
james-prysm Aug 7, 2026
ba99fc3
moving functions around after self review
james-prysm Aug 7, 2026
d9b6989
more bug fixes and missing items after self review
james-prysm Aug 7, 2026
0d863aa
Merge branch 'develop' into km88-proposer-settings
james-prysm Aug 7, 2026
3429854
Update validator/rpc/structs.go
james-prysm Aug 10, 2026
29ea6a0
Update validator/rpc/handlers_validator_config.go
james-prysm Aug 10, 2026
2a365bc
remove proxy that is no longer used
james-prysm Aug 10, 2026
e68f2fe
remove enabled tag and use builders tag instead
james-prysm Aug 11, 2026
4ea3e1a
gaz
james-prysm Aug 11, 2026
9698cf1
self review points, don't migrate gas limit in v1 and take in in v2 i…
james-prysm Aug 11, 2026
bf4fd8c
fixing test order
james-prysm Aug 11, 2026
3cd047b
adding gloas gates for endpoints, fixing bugs after self review, make…
james-prysm Aug 11, 2026
da52e2e
gaz
james-prysm Aug 11, 2026
1526ec2
Merge branch 'develop' into km88-proposer-settings
james-prysm Aug 12, 2026
ae648dc
jun's comments
james-prysm Aug 12, 2026
3234435
adding test
james-prysm Aug 12, 2026
57a9174
terence's comments
james-prysm Aug 13, 2026
934cc8d
Merge branch 'develop' into km88-proposer-settings
james-prysm Aug 13, 2026
1ebba34
Merge branch 'develop' into km88-proposer-settings
james-prysm Aug 13, 2026
25d6063
adding test
james-prysm Aug 13, 2026
7a95158
Merge branch 'develop' into km88-proposer-settings
james-prysm Aug 14, 2026
3d761b1
self review, making sure fork transitions are smoother
james-prysm Aug 14, 2026
3a3c114
Merge branch 'develop' into km88-proposer-settings
james-prysm Aug 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion beacon-chain/rpc/prysm/v1alpha1/validator/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ type Server struct {
lastBidSlot primitives.Slot
lastBidSource bidSource // Guarded by lastBidLock, set during Gloas block build, read when proposing.
lastBidBuilderURL string // Guarded by lastBidLock, winning Builder-API URL for lastBidSlot.
maxExecutionPayments sync.Map // validator pubkey [48]byte -> max execution payment (Gwei uint64).
maxExecutionPayments sync.Map // pubkey [48]byte -> Gwei uint64, min-collapsed per-entry caps; TODO(gloas): re-key per builder identity with beacon-APIs #630.
}

// Deprecated: The gRPC API will remain the default and fully supported through v8 (expected in 2026) but will be eventually removed in favor of REST API.
Expand Down
25 changes: 25 additions & 0 deletions changelog/james-prysm_keymanager-builders-endpoints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
### Added

- Add `GET`/`POST`/`DELETE /eth/v1/validator/{pubkey}/builders` keymanager endpoints for per-key builder configuration (keymanager-APIs #88). The endpoints respond 501 on networks without a scheduled gloas fork, where builder configuration cannot take effect.

### Changed

- Add v2 proposer settings (`"version": 2`) for configuring gloas builders; see the keymanager-APIs specification for the schema.
- Proposer settings sources without a `version` are treated as version 2 when they contain v2 builder fields.
- In v2 proposer settings, a nonempty `builders` list opts a key into mev-boost registration before the gloas fork, and an explicit empty list opts it out.
- v1 builder settings are not migrated to v2: at the gloas fork fee recipients and graffiti carry over, while v1 builder content — including its gas limits — is dropped and replaced with defaults, with a warning. Gas limits apply post-fork only when explicitly set at the option level, so validators follow future chain-default gas limit increases unless they opt out.
- The gas-limit keymanager API writes the option-level gas limit and no longer requires an enabled builder; deleting a gas limit unsets it (following the chain default) instead of pinning the current default value. Builder registration resolves fee recipients and participation independently, so a key with an enabled builder and only a default fee recipient now registers.
- Setting a fee recipient, gas limit, or graffiti no longer snapshots `default_config`'s builder settings onto that key; the key keeps following the default as it changes.

### Deprecated

- `--with-builder` generates legacy (pre-gloas) mev-boost builder settings, which are discontinued at the gloas fork; the command now warns when the flag is used.
- `--enable-builder` and `--suggested-gas-limit` produce only legacy (pre-gloas) content: they still drive mev-boost registrations before the fork, but never override v2 proposer settings or the gas limit schedule, and warn that they have no effect after gloas. An explicitly configured gas limit below the scheduled network gas limit is honored with a once-per-epoch warning.

### Fixed

- Fix the minimal slashing protection database decoding unset builder settings fields as explicit zero values; both validator DB backends now read the same stored settings identically.

### Removed

- Remove the unused `relays` field from the builder config; a settings file that still contains it is unaffected.
3 changes: 2 additions & 1 deletion cmd/prysmctl/validator/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ var (
WithBuilderFlag = &cli.BoolFlag{
Name: "with-builder",
Aliases: []string{"wb"},
Usage: "adds default builder options to proposer settings output, used for enabling mev-boost and relays",
Usage: "adds legacy (pre-gloas) mev-boost builder options to the proposer settings output; " +
"these are discontinued at the gloas fork, where builder participation requires v2 settings with a builders list",
}

DefaultFeeRecipientFlag = &cli.StringFlag{
Expand Down
1 change: 1 addition & 0 deletions cmd/prysmctl/validator/proposer_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func getProposerSettings(c *cli.Context, r io.Reader) error {
log.Infof("The default fee recipient is set to %s", defaultFeeRecipient)
var builderSettings *validatorpb.BuilderConfig
if c.Bool(WithBuilderFlag.Name) {
log.Warnf("--%s generates legacy (pre-gloas) mev-boost settings; they are discontinued at the gloas fork. Gloas builder participation requires v2 proposer settings with a builders list.", WithBuilderFlag.Name)
builderSettings = &validatorpb.BuilderConfig{
Enabled: true,
GasLimit: validatorType.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
Expand Down
6 changes: 5 additions & 1 deletion config/proposer/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ go_library(

go_test(
name = "go_default_test",
srcs = ["settings_test.go"],
srcs = [
"effective_builder_config_test.go",
"settings_test.go",
],
embed = [":go_default_library"],
deps = [
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/validator:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
Expand Down
98 changes: 98 additions & 0 deletions config/proposer/effective_builder_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package proposer

import (
"testing"

"github.com/OffchainLabs/prysm/v7/consensus-types/validator"
"github.com/OffchainLabs/prysm/v7/testing/require"
)

// These cases mirror the test vectors proposed upstream on keymanager-APIs #87
// for builder-config inheritance granularity.
func TestEffectiveBuilderConfig(t *testing.T) {
entryA := &BuilderEntry{URL: "https://a"}
entryB := &BuilderEntry{URL: "https://b"}
entryC := &BuilderEntry{URL: "https://c"}

t.Run("nil per-key returns default", func(t *testing.T) {
def := &BuilderConfig{Enabled: true}
require.Equal(t, def, effectiveBuilderConfig(nil, def))
})
t.Run("nil default returns per-key", func(t *testing.T) {
perKey := &BuilderConfig{Enabled: true}
require.Equal(t, perKey, effectiveBuilderConfig(perKey, nil))
})
t.Run("min_bid inherits when per-key omits it", func(t *testing.T) {
def := &BuilderConfig{Enabled: true, Builders: []*BuilderEntry{entryA, entryB}, MinBid: uint64ValPtr(5000000)}
perKey := &BuilderConfig{Enabled: true, Builders: []*BuilderEntry{entryC}}
eff := effectiveBuilderConfig(perKey, def)
require.NotNil(t, eff.MinBid)
require.Equal(t, validator.Uint64(5000000), *eff.MinBid)
require.Equal(t, 1, len(eff.Builders))
require.Equal(t, "https://c", eff.Builders[0].URL)
})
t.Run("explicit zero max payment is preserved, not inherited over", func(t *testing.T) {
def := &BuilderConfig{MaxExecutionPayment: uint64ValPtr(1000000000)}
perKey := &BuilderConfig{Enabled: true, MaxExecutionPayment: uint64ValPtr(0)}
eff := effectiveBuilderConfig(perKey, def)
require.NotNil(t, eff.MaxExecutionPayment)
require.Equal(t, validator.Uint64(0), *eff.MaxExecutionPayment)
})
t.Run("unset max payment inherits default", func(t *testing.T) {
def := &BuilderConfig{MaxExecutionPayment: uint64ValPtr(1000000000)}
perKey := &BuilderConfig{Enabled: true}
eff := effectiveBuilderConfig(perKey, def)
require.NotNil(t, eff.MaxExecutionPayment)
require.Equal(t, validator.Uint64(1000000000), *eff.MaxExecutionPayment)
})
t.Run("explicit per-key disable wins over enabled default", func(t *testing.T) {
def := &BuilderConfig{Enabled: true}
perKey := &BuilderConfig{Enabled: false, MinBid: uint64ValPtr(1)}
require.Equal(t, false, effectiveBuilderConfig(perKey, def).IsEnabled())
})
t.Run("present per-key builder config is authoritative on enabled", func(t *testing.T) {
// A per-key config with enabled false does not inherit an enabled default;
// whole-config inheritance happens only when the per-key builder config is nil.
def := &BuilderConfig{Enabled: true}
perKey := &BuilderConfig{MinBid: uint64ValPtr(1)}
require.Equal(t, false, effectiveBuilderConfig(perKey, def).IsEnabled())
require.Equal(t, true, effectiveBuilderConfig(nil, def).IsEnabled())
})
t.Run("present builders list replaces, never unions", func(t *testing.T) {
def := &BuilderConfig{Builders: []*BuilderEntry{entryA, entryB}}
perKey := &BuilderConfig{Builders: []*BuilderEntry{entryC}}
eff := effectiveBuilderConfig(perKey, def)
require.Equal(t, 1, len(eff.Builders))
require.Equal(t, "https://c", eff.Builders[0].URL)
})
t.Run("absent builders list inherits default list", func(t *testing.T) {
def := &BuilderConfig{Builders: []*BuilderEntry{entryA, entryB}}
perKey := &BuilderConfig{Enabled: true}
require.Equal(t, 2, len(effectiveBuilderConfig(perKey, def).Builders))
})
t.Run("zero gas limit inherits default gas limit", func(t *testing.T) {
def := &BuilderConfig{GasLimit: validator.Uint64(30000000)}
perKey := &BuilderConfig{Enabled: true}
require.Equal(t, validator.Uint64(30000000), effectiveBuilderConfig(perKey, def).GasLimit)
})
t.Run("boost factor inherits per field", func(t *testing.T) {
def := &BuilderConfig{MinBid: uint64ValPtr(5000000), BuilderBoostFactor: uint64ValPtr(90)}
perKey := &BuilderConfig{Enabled: true, BuilderBoostFactor: uint64ValPtr(120)}
eff := effectiveBuilderConfig(perKey, def)
require.Equal(t, validator.Uint64(5000000), *eff.MinBid)
require.Equal(t, validator.Uint64(120), *eff.BuilderBoostFactor)
})
t.Run("both-set min_bid: per-key wins", func(t *testing.T) {
def := &BuilderConfig{MinBid: uint64ValPtr(5000000)}
perKey := &BuilderConfig{MinBid: uint64ValPtr(7000000)}
require.Equal(t, validator.Uint64(7000000), *effectiveBuilderConfig(perKey, def).MinBid)
})
t.Run("nonzero per-key gas limit wins over default", func(t *testing.T) {
def := &BuilderConfig{GasLimit: validator.Uint64(30000000)}
perKey := &BuilderConfig{GasLimit: validator.Uint64(45000000)}
require.Equal(t, validator.Uint64(45000000), effectiveBuilderConfig(perKey, def).GasLimit)
})
t.Run("nil nil is nil", func(t *testing.T) {
require.Equal(t, (*BuilderConfig)(nil), effectiveBuilderConfig(nil, nil))
})
}
Loading
Loading