-
-
Notifications
You must be signed in to change notification settings - Fork 465
fix: derive proposer duties dependent root from state #9498
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1295,7 +1295,7 @@ export function getValidatorApi( | |||||
| // It should be set to the latest block applied to `self` or the genesis block root. | ||||||
| const dependentRoot = | ||||||
| // In v2 the dependent root is different after fulu due to deterministic proposer lookahead | ||||||
| proposerShufflingDecisionRoot(opts?.v2 ? config.getForkName(startSlot) : ForkName.phase0, state, epoch) || | ||||||
| proposerShufflingDecisionRoot(opts?.v2 ? config.getForkName(startSlot) : ForkName.phase0, state) || | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By removing
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| (await getGenesisBlockRoot(state)); | ||||||
|
|
||||||
| return { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,30 +17,24 @@ import {computeStartSlotAtEpoch} from "./epoch.js"; | |
| import {EpochShuffling} from "./epochShuffling.js"; | ||
|
|
||
| /** | ||
| * Block root that decided the proposer shuffling for `proposalEpoch` (keys that shuffling). | ||
| * Computed for the requested epoch, not `state.epoch`, so it is correct when `state` is one | ||
| * epoch off the requested epoch (e.g. serving next-epoch duties from the current state). | ||
| * Block root that decided the proposer shuffling for the state used to serve proposer duties. | ||
| * | ||
| * Returns `null` when the genesis block decides its own shuffling (caller falls back to the | ||
| * genesis block root). | ||
| */ | ||
| export function proposerShufflingDecisionRoot( | ||
| fork: ForkName, | ||
| state: IBeaconStateView, | ||
| proposalEpoch: Epoch | ||
| ): Root | null { | ||
| const decisionSlot = proposerShufflingDecisionSlot(fork, proposalEpoch); | ||
| export function proposerShufflingDecisionRoot(fork: ForkName, state: IBeaconStateView): Root | null { | ||
| const decisionSlot = proposerShufflingDecisionSlot(fork, state); | ||
| if (state.slot === decisionSlot) { | ||
| return null; | ||
| } | ||
| return state.getBlockRootAtSlot(decisionSlot); | ||
| } | ||
|
|
||
| /** Slot whose block root keys the proposer shuffling for `proposalEpoch`. */ | ||
| function proposerShufflingDecisionSlot(fork: ForkName, proposalEpoch: Epoch): Slot { | ||
| /** Slot whose block root keys the proposer shuffling for `state`. */ | ||
| function proposerShufflingDecisionSlot(fork: ForkName, state: IBeaconStateView): Slot { | ||
| // Post-Fulu the shuffling is decided one epoch earlier (deterministic proposer lookahead, | ||
| // MIN_SEED_LOOKAHEAD = 1); pre-Fulu it is the last block before `proposalEpoch`. | ||
| const decisionEpoch = isForkPostFulu(fork) ? proposalEpoch - 1 : proposalEpoch; | ||
| // MIN_SEED_LOOKAHEAD = 1); pre-Fulu it is the last block before `state.epoch`. | ||
| const decisionEpoch = isForkPostFulu(fork) ? state.epoch - 1 : state.epoch; | ||
| return Math.max(computeStartSlotAtEpoch(decisionEpoch) - 1, 0); | ||
| } | ||
|
Comment on lines
+20
to
39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To prevent returning the wrong dependent root for previous epoch requests (where * Block root that decided the proposer shuffling for `proposalEpoch` (keys that shuffling).
* Computed for the requested epoch, not `state.epoch`, so it is correct when `state` is one
* epoch off the requested epoch (e.g. serving next-epoch duties from the current state).
*
* Returns `null` when the genesis block decides its own shuffling (caller falls back to the
* genesis block root).
*/
export function proposerShufflingDecisionRoot(
fork: ForkName,
state: IBeaconStateView,
proposalEpoch: Epoch
): Root | null {
const decisionSlot = proposerShufflingDecisionSlot(fork, proposalEpoch, state.slot);
if (state.slot === decisionSlot) {
return null;
}
return state.getBlockRootAtSlot(decisionSlot);
}
/** Slot whose block root keys the proposer shuffling for `proposalEpoch`. */
function proposerShufflingDecisionSlot(fork: ForkName, proposalEpoch: Epoch, currentSlot: Slot): Slot {
// Post-Fulu the shuffling is decided one epoch earlier (deterministic proposer lookahead,
// MIN_SEED_LOOKAHEAD = 1); pre-Fulu it is the last block before `proposalEpoch`.
const decisionEpoch = isForkPostFulu(fork) ? proposalEpoch - 1 : proposalEpoch;
const decisionSlot = Math.max(computeStartSlotAtEpoch(decisionEpoch) - 1, 0);
if (decisionSlot >= currentSlot) {
const currentEpoch = Math.floor(currentSlot / SLOTS_PER_EPOCH);
return Math.max(computeStartSlotAtEpoch(currentEpoch) - 1, 0);
}
return decisionSlot;
}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Closing #9498 — #9499 takes the same direction your suggestion pointed at (keep |
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When v2 post-Fulu duties for epoch
N+1are served from a state at epochN, this call now derives the root fromstate.epochand returnsblock_root(start(N-1)-1). Those duties come fromstate.nextProposers, which is keyed bystate.nextDecisionRoot/ the v2 spec rootblock_root(start(N)-1); the Gloas proposer-preference validator also checksheadState.nextDecisionRootforproposalEpoch - 1. As a result, validator clients that poll next-epoch v2 duties will sign preferences with a dependent root the beacon node rejects as unknown.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed — closing #9498 in favor of #9499 which keeps
proposalEpochas a parameter and adopts the Lighthouse-style head-root fallback for the future-decision-slot case. That preserves the v2 post-Fulublock_root(start(N) - 1)semantics you flagged here.