Repository navigation
feat(validator): persist per-validator fee recipient, graffiti and gas limit in validator_definitions.yml - #780
pablodeymo wants to merge 1 commit into
Conversation
… entry in validator_definitions.yml, in Lighthouse's layout (suggested_fee_recipient, graffiti, gas_limit), so a restart runs with what the keymanager API was given instead of putting every validator back on the command line's defaults. The keymanager's settings routes now write the file under the same definitions lock the keystore routes take, and before the in-memory change: a failed write is a 500 that changes nothing. Startup seeds the settings from the enabled entries, refusing a malformed value the way a bad keystore is refused. A re-imported key keeps the overrides its entry held, and a deleted key's entry takes them with it. The fields may also be written by hand, and a Lighthouse file reads as is.
🤖 Grok Code ReviewI'll inspect the surrounding keymanager and definitions code so the review is grounded in how locks, saves, and startup seeding actually work. Automated review by Grok (xAI) · grok-4.7 · custom prompt |
🤖 Claude Code ReviewReview: persisting per-validator proposer overrides in
|
🤖 Codex Code Review
Tests could not run because Rustup attempted to write to its read-only temporary directory. Automated review by OpenAI Codex · gpt-6.1-sol · custom prompt |
🤖 Gemini Code ReviewThe changes look solid and well-tested. Key highlights:
Automated review by Gemini · gemini-4-argon · custom prompt |
🤖 Kimi Code ReviewNo blocking correctness or security issues found. The persist-before-memory ordering in One non-blocking performance consideration:
Automated review by Kimi (Moonshot AI) · kimi-k3.1-pro · custom prompt |
🤖 DeepSeek Code ReviewThis PR looks like a solid improvement to validator settings persistence. It doesn’t touch consensus/fork-choice/state-transition code, so the review below focuses on the validator and persistence paths. Findings
Overall, the persist-before-apply approach, lock ordering, and tests for failed writes are good. The main concern is the Lighthouse file round-trip behavior. Automated review by DeepSeek · deepseek-v4-pro · custom prompt |
🗒️ Description / Motivation
feerecipient,graffitiandgas_limitroutes overrode one validator's settings in memory only: a restart put every validator back on the command line's defaults, fee recipient included, and nothing told the operator. Lighthouse persists the three on each entry ofvalidator_definitions.yml; Teku, Prysm and Lodestar read a proposer-config file. This was the last high-severity item in the client-gap list.validator_definitions.yml, in Lighthouse's exact layout, so a file written by either client reads in the other, and the keymanager, the file and startup agree on one record.What Changed
crates/validator/src/keys/definitions.rs:ValidatorDefinitiongainssuggested_fee_recipient(0xhex),graffiti(text) andgas_limit(integer), each#[serde(default, skip_serializing_if = "Option::is_none")], so an existing file parses unchanged and an entry without overrides is written exactly as before.overrides()parses the three (a malformed value is aKeystoreerror naming the entry and the field),apply(SettingChange)writes one change in the file's form,with_overrides_ofcarries them across a rebuilt entry, andValidatorDefinitions::seed_overridesinstalls every enabled entry's into aProposerSettings.crates/validator/src/proposer_settings.rs:Overridesis public, withSettingChange(one change to one of the three,Nonemeaning back to the default),ProposerSettings::apply,seedandoverrides; the sixset_*/clear_*are now wrappers.fee_recipient_from_text/to_textmoved here from the route (the zero-address refusal with them) so the file and the API parse an address one way. The module doc no longer says "in memory only": the file is the record, this is the cache.crates/validator/src/http_api/proposer.rs: everyPOST/DELETEgoes through onepersist_and_apply:definitions_lockfirst (the order the keystore routes take),storeread lock for the held check, open the file, apply the change to the key's entry, save, then apply in memory. A failed read or write is a 500 and memory is unchanged, so what the operator sees is what the next restart loads. A held key with no entry in the file (the file was edited under a running client) is a 500 naming the file rather than a memory-only change.GETs are unchanged.crates/validator/src/http_api/keystores.rs: a re-imported key (password rotation, retry) rebuilt its entry from the new keystore and would have dropped the overrides; they are carried over. A fresh import starts with none.crates/validator/src/lib.rs: after the keys load, the definitions are opened once more and seeded into the settings; a malformed override refuses startup, as a bad keystore does.docs/cli.md: the keymanager section's "held in memory only" paragraph replaced with how they persist, that the fields may be written by hand, and that a Lighthouse file reads as is.Correctness / Behavior Guarantees
definitions_lock, thenstore), so the settings routes cannot deadlock against an import or delete and their open-mutate-save cycles never interleave.ValidatorStore::loadskipping its key.POST feerecipient(400), now from the shared parser, and is refused in the file too.description,builder_*,type) are read and ignored, as every unknown key already was.Tests Added / Run
definitions.rs: an entry without overrides is written without the fields; overrides round-trip through the file; a Lighthouse-written entry (with its extra fields) parses with its three overrides; a malformed address and a 33-byte graffiti are errors naming the field; clearing removes the field; seeding installs enabled entries' overrides only.proposer_settings.rs: seeding replaces a key's overrides wholesale; the address parser round-trips and refuses the zero address and malformed input.http_api/proposer.rs: everyPOSTlands in the file and everyDELETEremoves the field; persisted overrides survive a restart (a freshProposerSettingsseeded from the file answers them); a key missing from the file is a 500 with memory unchanged; an unwritable directory is a 500 with memory and file unchanged (unix, skipped as root).http_api/keystores.rs: a re-imported key keeps its persisted graffiti and the file holds one entry.cargo test -p ethlambda-validator --profile release-fast: 339 passed.cargo test -p ethlambda --profile release-fast: 264 passed.make lint: clean.cargo fmt --check: clean.Related Issues / PRs
ValidatorDefinition's path fields into a flattenedsigningenum and reworkskeystores.rs; the three fields here are additive and self-contained, so whichever lands second has a line-adjacent conflict in the struct and the import'sUndo::Replacedarm, nothing structural.--proposer-settings-file, Lighthouse'sbuilder_*fields, and a--graffiti-file.✅ Verification Checklist
make fmt— cleanmake lint(clippy with-D warnings) — cleanmake test— the validator crate and the binary were run atrelease-fast(603 tests); CI runs the rest