-
-
Notifications
You must be signed in to change notification settings - Fork 291
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
Implement support for validator next-epoch proposer duties #3782
Changes from 21 commits
9a0a792
5a1426a
bcdfbb6
4846918
18cde25
9bee893
5a632b1
0a1aba3
ac83ab8
fafca52
7d08ada
f0a9c3f
520c749
d54e124
02a722a
b7e526a
72fd6ea
12aa60d
b6b1b8c
c75d926
3c252a7
162a66b
8b58292
99e973f
be65566
ab35439
1ec1ccb
736cef7
9a51aa4
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 |
---|---|---|
|
@@ -3,6 +3,7 @@ import {BLSSignature, CommitteeIndex, Epoch, Slot, ValidatorIndex, phase0, SyncP | |
import {createIBeaconConfig, IBeaconConfig, IChainConfig} from "@chainsafe/lodestar-config"; | ||
import { | ||
ATTESTATION_SUBNET_COUNT, | ||
DOMAIN_BEACON_PROPOSER, | ||
EFFECTIVE_BALANCE_INCREMENT, | ||
FAR_FUTURE_EPOCH, | ||
GENESIS_EPOCH, | ||
|
@@ -18,8 +19,9 @@ import { | |
getChurnLimit, | ||
isActiveValidator, | ||
isAggregatorFromCommitteeLength, | ||
computeProposers, | ||
computeSyncPeriodAtEpoch, | ||
getSeed, | ||
computeProposers, | ||
} from "../util"; | ||
import {computeEpochShuffling, IEpochShuffling} from "../util/epochShuffling"; | ||
import {EffectiveBalanceIncrements, getEffectiveBalanceIncrementsWithLen} from "./effectiveBalanceIncrements"; | ||
|
@@ -95,6 +97,17 @@ export class EpochContext { | |
* 32 x Number | ||
*/ | ||
proposers: ValidatorIndex[]; | ||
|
||
/** | ||
* Map of Indexes of the block proposers keyed by the next epoch. | ||
* | ||
* We allow requesting proposal duties only one epoch in the future | ||
* Note: There is a small probability that returned validators differs | ||
* than what is returned when the epoch is reached. | ||
* | ||
* 32 x Number | ||
*/ | ||
nextEpochProposers: Map<Epoch, ValidatorIndex[]> = new Map<Epoch, ValidatorIndex[]>(); | ||
/** | ||
* Shuffling of validator indexes. Immutable through the epoch, then it's replaced entirely. | ||
* Note: Per spec definition, shuffling will always be defined. They are never called before loadState() | ||
|
@@ -150,6 +163,9 @@ export class EpochContext { | |
epoch: Epoch; | ||
syncPeriod: SyncPeriod; | ||
|
||
currentProposerSeed: Uint8Array; | ||
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. Why does needs to be persisted? What's the advantage of keeping this around? |
||
nextProposerSeed: Uint8Array; | ||
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. Same here, what's the advantage of keeping this around? Why not compute on demand and discard after caching 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. The issue in that, is related to what you observed in the comment here
Computing the seed via 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. Then please add comments 🙏 all non-obvious decisions must be rationalized in committed comments |
||
|
||
constructor(data: { | ||
config: IBeaconConfig; | ||
pubkey2index: PubkeyIndexMap; | ||
|
@@ -170,6 +186,8 @@ export class EpochContext { | |
nextSyncCommitteeIndexed: SyncCommitteeCache; | ||
epoch: Epoch; | ||
syncPeriod: SyncPeriod; | ||
currentProposerSeed: Uint8Array; | ||
nextProposerSeed: Uint8Array; | ||
}) { | ||
this.config = data.config; | ||
this.pubkey2index = data.pubkey2index; | ||
|
@@ -190,6 +208,8 @@ export class EpochContext { | |
this.nextSyncCommitteeIndexed = data.nextSyncCommitteeIndexed; | ||
this.epoch = data.epoch; | ||
this.syncPeriod = data.syncPeriod; | ||
this.currentProposerSeed = data.currentProposerSeed; | ||
this.nextProposerSeed = data.nextProposerSeed; | ||
} | ||
|
||
/** | ||
|
@@ -269,9 +289,14 @@ export class EpochContext { | |
: computeEpochShuffling(state, previousActiveIndices, previousEpoch); | ||
const nextShuffling = computeEpochShuffling(state, nextActiveIndices, nextEpoch); | ||
|
||
const currentProposerSeed = getSeed(state, currentShuffling.epoch, DOMAIN_BEACON_PROPOSER); | ||
const nextProposerSeed = getSeed(state, nextShuffling.epoch, DOMAIN_BEACON_PROPOSER); | ||
|
||
// Allow to create CachedBeaconState for empty states | ||
const proposers = | ||
state.validators.length > 0 ? computeProposers(state, currentShuffling, effectiveBalanceIncrements) : []; | ||
state.validators.length > 0 | ||
? computeProposers(currentProposerSeed, currentShuffling, effectiveBalanceIncrements) | ||
: []; | ||
|
||
// Only after altair, compute the indices of the current sync committee | ||
const afterAltairFork = currentEpoch >= config.ALTAIR_FORK_EPOCH; | ||
|
@@ -334,6 +359,8 @@ export class EpochContext { | |
nextSyncCommitteeIndexed, | ||
epoch: currentEpoch, | ||
syncPeriod: computeSyncPeriodAtEpoch(currentEpoch), | ||
currentProposerSeed, | ||
nextProposerSeed, | ||
}); | ||
} | ||
|
||
|
@@ -369,6 +396,8 @@ export class EpochContext { | |
nextSyncCommitteeIndexed: this.nextSyncCommitteeIndexed, | ||
epoch: this.epoch, | ||
syncPeriod: this.syncPeriod, | ||
currentProposerSeed: this.currentProposerSeed, | ||
nextProposerSeed: this.nextProposerSeed, | ||
}); | ||
} | ||
|
||
|
@@ -389,7 +418,10 @@ export class EpochContext { | |
const nextEpoch = currEpoch + 1; | ||
|
||
this.nextShuffling = computeEpochShuffling(state, epochProcess.nextEpochShufflingActiveValidatorIndices, nextEpoch); | ||
this.proposers = computeProposers(state, this.currentShuffling, this.effectiveBalanceIncrements); | ||
this.currentProposerSeed = getSeed(state, this.currentShuffling.epoch, DOMAIN_BEACON_PROPOSER); | ||
this.nextProposerSeed = getSeed(state, this.nextShuffling.epoch, DOMAIN_BEACON_PROPOSER); | ||
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. we only need to call 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. The JSDoc for 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. Also here you can comment (with |
||
|
||
this.proposers = computeProposers(this.currentProposerSeed, this.currentShuffling, this.effectiveBalanceIncrements); | ||
|
||
// TODO: DEDUPLICATE from createEpochContext | ||
// | ||
|
@@ -467,6 +499,18 @@ export class EpochContext { | |
return (committeesSinceEpochStart + committeeIndex) % ATTESTATION_SUBNET_COUNT; | ||
} | ||
|
||
getNextEpochBeaconProposer(): ValidatorIndex[] { | ||
const nextShuffling = this.nextShuffling; | ||
let nextProposers = this.nextEpochProposers.get(nextShuffling.epoch); | ||
if (!nextProposers) { | ||
nextProposers = computeProposers(this.nextProposerSeed, nextShuffling, this.effectiveBalanceIncrements); | ||
// Only keep proposers for one epoch in the future, so clear before setting new value | ||
this.nextEpochProposers.clear(); | ||
this.nextEpochProposers.set(nextShuffling.epoch, nextProposers); | ||
dapplion marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return nextProposers; | ||
} | ||
|
||
getBeaconProposer(slot: Slot): ValidatorIndex { | ||
const epoch = computeEpochAtSlot(slot); | ||
if (epoch !== this.currentShuffling.epoch) { | ||
|
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.
Cache exactly 1 or 0 next proposers for the next epoch of this epochCtx. We should research the safety of +2, +3 epoch duties before generalizing.
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.
Yeah. That is what is being done. Are you hinting at something else?
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.
If there's exactly 1 possible value for that why do a map?
is more intuitive