Validate and canonicalize mux validator pubkeys - #910
Open
pucedoteth wants to merge 1 commit into
Open
Conversation
The mux map is keyed on the raw `validator_pubkeys` strings from the
config file, and getHeader looks a validator up with the raw pubkey
segment from the request URL:
pubkey = vars["pubkey"]
...
if mux, ok := m.muxMap[pubkey]; ok {
Neither side is validated or normalized, so two mistakes silently
disable a mux instead of failing:
- A malformed pubkey (truncated, mistyped, missing 0x, or empty) is
accepted at startup and can never match a request.
- A correctly formed pubkey written in a different case never matches,
because hex is case-insensitive but map keys are not.
In both cases mev-boost starts cleanly, reports the mux as loaded, and
then quietly serves that validator from the default relay set. Nothing
logs a warning. The same comparison also defeats the existing duplicate
check: the same pubkey in two muxes in different cases is not reported
by ErrDuplicateValidatorPubkey and silently lands in whichever entry is
processed last.
Relay pubkeys already go through utils.HexToPubkey in
types.NewRelayEntry; only the mux path skips validation.
Validate the encoding and lowercase the key when building the map, and
fold the case of the lookup key in GetConfigForValidator. The check is
syntactic rather than a BLS point decompression, since these strings are
only ever lookup keys and the getHeader pubkey is not curve-checked
either -- this keeps well-formed placeholder keys, such as the one in
the existing cli config test, working.
Note this now rejects a mux config that was previously accepted: a
malformed validator pubkey is a startup error instead of a mux that
never fires.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The mux map is keyed on the raw
validator_pubkeysstrings from the config file, andgetHeaderlooks a validator up with the raw pubkey segment from the request URL:Neither side is validated or normalized, so two operator mistakes silently disable a mux instead of failing:
0xprefix, or even the empty string. It can never match a request.In both cases mev-boost starts cleanly, reports the mux as loaded, and then quietly serves that validator from the default relay set. Nothing logs a warning. For a mux configured to restrict a validator to a specific relay set, that is a silent routing change.
The same raw comparison also defeats the duplicate check that already exists: the same pubkey listed in two muxes in different cases is not caught by
ErrDuplicateValidatorPubkey, and silently lands in whichever entry is processed last.Relay pubkeys already go through
utils.HexToPubkeyintypes.NewRelayEntry— only the mux path skips validation.Change
ValidateMuxEntriesvalidates the encoding and stores the lowercased key.GetConfigForValidatorfolds the case of the lookup key.The check is syntactic (0x-prefixed, 48 bytes of hex) rather than a BLS point decompression. These strings are only ever lookup keys, and the
getHeaderpubkey is not curve-checked either, so requiring a valid curve point would be stricter than the matching logic — and would reject well-formed placeholder keys, including the one in the existingTestParseConfigWithMuxfixture. Folding rather than parsing on lookup also keeps this off thegetHeaderhot path.Behavior change
This rejects a mux config that was previously accepted: a malformed validator pubkey is now a startup error rather than a mux that never fires. That is the point of the change, but it is a visible difference for anyone currently running a typo, so flagging it explicitly. Happy to downgrade it to a warning-and-skip if you would rather not fail closed on startup.
Test plan
Four new tests, all failing before the change (verified by reverting only
config/mux_config.goandserver/service.goand keeping the tests):TestValidateMuxEntriesRejectsMalformedPubkeymalformed validator pubkey "0xdeadbeef" was accepted(also"",not-a-pubkey, over-long)TestValidateMuxEntriesCanonicalizesPubkeysmux map is not keyed by the canonical lowercase pubkeyTestValidateMuxEntriesDetectsDuplicateAcrossCaseTestGetConfigForValidatorIsCaseInsensitiveexpected the mux relay ..., got the default setgo test ./...passes (including the existingTestParseConfigWithMux), plusgo test -raceon the changed packages,go vet, andgofmt -d -s.🤖 Written with Claude Code. Every result above comes from a local build and test run.