Repository navigation
feat: wire up proposer_preferences gossip topic #9275
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
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
d817b48
feat: wire up proposer_preferences gossip topic
nflaig b37696f
review
nflaig 3126b04
Merge branch 'unstable' into nflaig/proposer-preferences-gossip
nflaig f5a3f03
keep spec ordering
nflaig c1e5809
review
nflaig e5b606c
more review
nflaig 8dc4854
Merge branch 'unstable' into nflaig/proposer-preferences-gossip
nflaig 47b9e95
Merge branch 'unstable' into nflaig/proposer-preferences-gossip
nflaig File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
39 changes: 39 additions & 0 deletions
39
packages/beacon-node/src/chain/errors/proposerPreferences.ts
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import {Slot, ValidatorIndex} from "@lodestar/types"; | ||
| import {GossipActionError} from "./gossipValidation.js"; | ||
|
|
||
| export enum ProposerPreferencesErrorCode { | ||
| INVALID_EPOCH = "PROPOSER_PREFERENCES_ERROR_INVALID_EPOCH", | ||
| PROPOSAL_SLOT_PASSED = "PROPOSER_PREFERENCES_ERROR_PROPOSAL_SLOT_PASSED", | ||
| INVALID_PROPOSER = "PROPOSER_PREFERENCES_ERROR_INVALID_PROPOSER", | ||
| ALREADY_KNOWN = "PROPOSER_PREFERENCES_ERROR_ALREADY_KNOWN", | ||
| INVALID_SIGNATURE = "PROPOSER_PREFERENCES_ERROR_INVALID_SIGNATURE", | ||
| } | ||
|
|
||
| export type ProposerPreferencesErrorType = | ||
| | { | ||
| code: ProposerPreferencesErrorCode.INVALID_EPOCH; | ||
| proposalSlot: Slot; | ||
| currentEpoch: number; | ||
| } | ||
| | { | ||
| code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED; | ||
| proposalSlot: Slot; | ||
| currentSlot: Slot; | ||
| } | ||
| | { | ||
| code: ProposerPreferencesErrorCode.INVALID_PROPOSER; | ||
| proposalSlot: Slot; | ||
| validatorIndex: ValidatorIndex; | ||
| } | ||
| | { | ||
| code: ProposerPreferencesErrorCode.ALREADY_KNOWN; | ||
| proposalSlot: Slot; | ||
| validatorIndex: ValidatorIndex; | ||
| } | ||
| | { | ||
| code: ProposerPreferencesErrorCode.INVALID_SIGNATURE; | ||
| proposalSlot: Slot; | ||
| validatorIndex: ValidatorIndex; | ||
| }; | ||
|
|
||
| export class ProposerPreferencesError extends GossipActionError<ProposerPreferencesErrorType> {} |
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
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
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
29 changes: 29 additions & 0 deletions
29
packages/beacon-node/src/chain/seenCache/seenProposerPreferences.ts
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import {Slot, ValidatorIndex} from "@lodestar/types"; | ||
| import {MapDef} from "@lodestar/utils"; | ||
|
|
||
| /** | ||
| * Tracks signed proposer preferences we've already seen per (proposal_slot, validator_index). | ||
| */ | ||
| export class SeenProposerPreferences { | ||
| private readonly validatorIndexesBySlot = new MapDef<Slot, Set<ValidatorIndex>>(() => new Set<ValidatorIndex>()); | ||
|
|
||
| isKnown(proposalSlot: Slot, validatorIndex: ValidatorIndex): boolean { | ||
| return this.validatorIndexesBySlot.get(proposalSlot)?.has(validatorIndex) === true; | ||
| } | ||
|
|
||
| add(proposalSlot: Slot, validatorIndex: ValidatorIndex): void { | ||
| this.validatorIndexesBySlot.getOrDefault(proposalSlot).add(validatorIndex); | ||
| } | ||
|
|
||
| /** | ||
| * Entries are only load-bearing while `proposal_slot > state.slot`. Once the slot has passed the | ||
| * `[IGNORE] proposal_slot > state.slot` gossip rule takes over, so drop them on each slot tick. | ||
| */ | ||
| prune(currentSlot: Slot): void { | ||
| for (const slot of this.validatorIndexesBySlot.keys()) { | ||
| if (slot < currentSlot) { | ||
| this.validatorIndexesBySlot.delete(slot); | ||
| } | ||
| } | ||
| } | ||
| } |
91 changes: 91 additions & 0 deletions
91
packages/beacon-node/src/chain/validation/proposerPreferences.ts
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| import {SLOTS_PER_EPOCH} from "@lodestar/params"; | ||
| import { | ||
| computeEpochAtSlot, | ||
| createSingleSignatureSetFromComponents, | ||
| getProposerPreferencesSigningRoot, | ||
| isStatePostGloas, | ||
| } from "@lodestar/state-transition"; | ||
| import {gloas} from "@lodestar/types"; | ||
| import {GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode} from "../errors/index.js"; | ||
| import {IBeaconChain} from "../index.js"; | ||
| import {RegenCaller} from "../regen/index.js"; | ||
|
|
||
| /** | ||
| * Validates a gossiped `SignedProposerPreferences` per | ||
| * https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md#proposer_preferences | ||
| */ | ||
| export async function validateGossipProposerPreferences( | ||
| chain: IBeaconChain, | ||
| signedProposerPreferences: gloas.SignedProposerPreferences | ||
| ): Promise<void> { | ||
| const preferences = signedProposerPreferences.message; | ||
| const {proposalSlot, validatorIndex} = preferences; | ||
| const proposalEpoch = computeEpochAtSlot(proposalSlot); | ||
|
|
||
| // [IGNORE] `preferences.proposal_slot` is in the current or next epoch. | ||
| const currentEpoch = chain.clock.currentEpoch; | ||
| if (proposalEpoch < currentEpoch || proposalEpoch > currentEpoch + 1) { | ||
| throw new ProposerPreferencesError(GossipAction.IGNORE, { | ||
| code: ProposerPreferencesErrorCode.INVALID_EPOCH, | ||
| proposalSlot, | ||
| currentEpoch, | ||
| }); | ||
| } | ||
|
|
||
| // [IGNORE] `preferences.proposal_slot` has not already passed. | ||
| const currentSlot = chain.clock.currentSlot; | ||
| if (proposalSlot <= currentSlot) { | ||
| throw new ProposerPreferencesError(GossipAction.IGNORE, { | ||
| code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED, | ||
| proposalSlot, | ||
| currentSlot, | ||
| }); | ||
| } | ||
|
|
||
| const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.validateGossipProposerPreferences); | ||
| if (!isStatePostGloas(state)) { | ||
| throw new Error(`Expected gloas+ state for proposer preferences validation, got fork=${state.forkName}`); | ||
| } | ||
|
|
||
| // [REJECT] `preferences.validator_index` is present at the correct slot in the current or next | ||
| // epoch's portion of `state.proposer_lookahead` — i.e. `is_valid_proposal_slot(state, preferences)` | ||
| // returns True. | ||
| const epochOffset = proposalEpoch - state.epoch; | ||
| const proposers = epochOffset === 0 ? state.currentProposers : state.nextProposers; | ||
| const expectedProposer = proposers[proposalSlot % SLOTS_PER_EPOCH]; | ||
| if (epochOffset < 0 || epochOffset > 1 || expectedProposer !== validatorIndex) { | ||
| throw new ProposerPreferencesError(GossipAction.REJECT, { | ||
| code: ProposerPreferencesErrorCode.INVALID_PROPOSER, | ||
| proposalSlot, | ||
| validatorIndex, | ||
| }); | ||
| } | ||
|
|
||
| // [IGNORE] The `signed_proposer_preferences` is the first valid message received from the validator | ||
| // with index `preferences.validator_index` and the given slot `preferences.proposal_slot`. | ||
| if (chain.seenProposerPreferences.isKnown(proposalSlot, validatorIndex)) { | ||
| throw new ProposerPreferencesError(GossipAction.IGNORE, { | ||
| code: ProposerPreferencesErrorCode.ALREADY_KNOWN, | ||
| proposalSlot, | ||
| validatorIndex, | ||
| }); | ||
| } | ||
|
|
||
| // [REJECT] `signed_proposer_preferences.signature` is valid with respect to the validator's public key. | ||
| const signatureSet = createSingleSignatureSetFromComponents( | ||
| chain.pubkeyCache.getOrThrow(validatorIndex), | ||
| getProposerPreferencesSigningRoot(chain.config, preferences), | ||
| signedProposerPreferences.signature | ||
| ); | ||
|
|
||
| if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true}))) { | ||
| throw new ProposerPreferencesError(GossipAction.REJECT, { | ||
| code: ProposerPreferencesErrorCode.INVALID_SIGNATURE, | ||
| proposalSlot, | ||
| validatorIndex, | ||
| }); | ||
| } | ||
|
|
||
| // Valid | ||
| chain.seenProposerPreferences.add(proposalSlot, validatorIndex); | ||
| } |
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,11 @@ const linearGossipQueueOpts: { | |
| type: QueueType.FIFO, | ||
| dropOpts: {type: DropType.count, count: 1}, | ||
| }, | ||
| [GossipType.proposer_preferences]: { | ||
| maxLength: 1024, | ||
|
Member
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. this queue is likely way too long but not sure that really matters much |
||
| type: QueueType.FIFO, | ||
| dropOpts: {type: DropType.count, count: 1}, | ||
| }, | ||
| }; | ||
|
|
||
| const indexedGossipQueueOpts: { | ||
|
|
||
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.
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.
could be more if there is a epoch boundary reorg which causes dependent root to change but this also depends on how clients will broadcast these, either way this seems fine