Repository navigation
feat(validator): publish proposer preferences (gloas) - #707
MegaRedHand wants to merge 9 commits into
Conversation
…nces Signed proposer preferences must carry the shuffling dependent root, which only v2 of the proposer duties endpoint reports (v1's root is one epoch later). Add both calls to the beacon node trait, with the fallback sending preferences to every node since each one gossips what it accepts.
…main The gossip validator verifies a preference at the fork version of the epoch before its proposal, while the validator guide signs with the head state. The two differ only in the first epoch of a fork, where the gossip rule is what peers accept, so sign for that one.
From gloas a proposer's fee recipient and gas limit travel in signed proposer preferences, and a node ignores trustless bids for a slot whose proposer published none. A per-slot service signs one per owned proposal in the current and next gloas epoch, keyed by (slot, dependent root) so a reorg re-publishes, and sends it to every node.
…oas bids by the boost factor Wire the preferences service into the slot loop, unconditionally since from gloas they carry every proposal's fee recipient. produceBlockV4 now sends the configured builder_boost_factor in its body, so with --builder-proposals a pooled p2p bid can win on value; without it the factor stays 0 as before.
…'s gossip rules The signing domain is the part of the preferences flow most likely to drift between the signer and the verifier, so drive the validator client's own signer and HTTP client against the real route and gossip rules.
Document the per-slot preferences, their two counters, the signing domain departure from the validator guide, and that the boost factor now also weighs gloas p2p bids.
… their reporting A node answers 400 for a whole call when any entry fails, and one it holds for the same slot and root with other content is such a failure, so a batch kept every entry unpublished and re-sent until that slot passed (a restart after a fee recipient change triggers it). Send each preference alone and remember each success. Also: a failed v2 duties fetch no longer warns "duty likely missed" twice a slot (the service warns once per epoch and counts it), the fork header is the target epoch's, the poll is spawned ahead of the proposal, the signing-domain explanation is corrected (get_domain reads state.fork, so the guide and the gossip rule differ only for a proposal in a fork's first epoch signed during it), v2 dependent slot reuses the state transition's definition, and the end-to-end test now reads its duty and dependent root through the client.
A preference a node keeps refusing is retried every slot until its slot passes, which warned up to two epochs of slots per entry. Warn on the first refusal of each (slot, dependent root), log repeats at debug, and keep counting every failure. Reword the failures metric to what it now counts.
…split the fetch counter try_all warned per node on every refusal, so a preference a node kept refusing still warned every slot despite the per-entry dedup; add a quiet variant for it. Poll after the proposal rather than before, so the next epoch duties fetch does not compete with block production. Count failed v2 duty fetches on their own counter, since a syncing node is not a refused preference.
🤖 Claude Code ReviewReview of PR 707: validator client publishes signed proposer preferencesI read the diff and the checked-out Correctness
Minor points
Tests No blocking issues. Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Codex Code ReviewI reviewed the PR diff and the touched Rust paths in context. Overall this looks careful and the added tests materially improve confidence around proposer-preferences signing and v2 duties. I did not find a clear consensus-breaking bug in the new logic.
Findings
What looks good
Looks good to me aside from the minor maintainability/UX notes above. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Kimi Code ReviewI'll review this PR which implements proposer preferences for the Gloas fork. Let me analyze the changes systematically. Overall AssessmentThis is a well-structured PR adding Gloas fork proposer preferences support. The core logic is sound, but I've identified several issues ranging from minor to potentially serious. Critical Issues1. Potential Division by Zero in
|
| Priority | Issue | Location | Fix |
|---|---|---|---|
| Critical | builder_boost_factor serialized as string not number |
http.rs:94 |
Remove quotes: "{factor}" → {factor} |
| High | Signing domain uses MIN_SEED_LOOKAHEAD (4) vs epoch-1; comment/code mismatch |
signing.rs:339 |
Verify spec, fix code or comment |
| Medium | Mutex poison recovery hides bugs | preferences.rs:121 |
Use parking_lot or log recovery |
| Medium | Sequential epoch processing | preferences.rs:181 |
Consider join! |
| Medium | Sequential preference submission | preferences.rs:252 |
Consider limited parallelism |
| Low | AtomicBool ordering could be relaxed |
preferences.rs:178 |
compare_exchange_weak |
| Low | Test logic duplicates production | preferences.rs:437 |
Share domain computation |
The PR is well-tested and the architecture is sound. The string-vs-number JSON serialization is the most critical fix needed before merge.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
First gloas step of the builder work: the validator client publishes signed proposer preferences (fee recipient, target gas limit) for its gloas proposals. Stacked on #705.
Why: from gloas, beacon-APIs makes
SignedProposerPreferencesthe source of a proposer's fee recipient and gas limit, supersedingprepare_beacon_proposer. A proposer without preferences also gets no trustless bids: p2p bids for its slot are ignored.What it adds
dependent_rootis the shuffling dependent root the preferences must carry; the duty loop keeps v1. Signs each own duty whose slot has not started and which was not yet published for that(slot, dependent_root). Submits one preference per request to every node, so one refusal does not hold back the others. A reorg that moves the dependent root re-publishes--builder-proposals, as in Lighthouse: from gloas the preferences carry the fee recipient for every proposal. Validators with no fee recipient are skipped, with one warning per epochDOMAIN_PROPOSER_PREFERENCESat the fork version ofproposal_epoch - 1, which is what the gossip rule verifies against. The validator guide's head-state signer differs only for a proposal in a fork's first epoch signed during that epoch; ours is the one peers following the gossip rule accept. ethlambda's beacon node accepts bothEth-Consensus-Version--builder-proposals,produceBlockV4's body carries the configuredbuilder_boost_factor, so pooled p2p bids can win on value. Without it the factor stays0ethlambda_validator_proposer_preferences_published_total,..._failures_total(preferences a node refused or did not take),..._duty_fetch_failures_total(failed v2 duty fetches). A refusal is warned once per preference, then logged at debugKnown limitations
Tests
make lintpasses, as do the validator lib, rpc lib and binary tests.