diff --git a/packages/state-transition/src/util/seed.ts b/packages/state-transition/src/util/seed.ts index 129cf6bfaf7..b96b0616e7b 100644 --- a/packages/state-transition/src/util/seed.ts +++ b/packages/state-transition/src/util/seed.ts @@ -12,7 +12,7 @@ import { SYNC_COMMITTEE_SIZE, } from "@lodestar/params"; import {Bytes32, DomainType, Epoch, ValidatorIndex} from "@lodestar/types"; -import {assert, bytesToBigInt, intToBytes} from "@lodestar/utils"; +import {assert, bytesToBigInt, bytesToInt, intToBytes} from "@lodestar/utils"; import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js"; import {BeaconStateAllForks} from "../types.js"; import {computeStartSlotAtEpoch} from "./epoch.js"; @@ -57,30 +57,40 @@ export function computeProposerIndex( throw Error("Validator indices must not be empty"); } - // TODO: Inline outside this function - const MAX_RANDOM_BYTE = 2 ** 8 - 1; - const MAX_EFFECTIVE_BALANCE_INCREMENT = - fork >= ForkSeq.electra - ? MAX_EFFECTIVE_BALANCE_ELECTRA / EFFECTIVE_BALANCE_INCREMENT - : MAX_EFFECTIVE_BALANCE / EFFECTIVE_BALANCE_INCREMENT; - - let i = 0; - /* eslint-disable-next-line no-constant-condition */ - while (true) { - const candidateIndex = indices[computeShuffledIndex(i % indices.length, indices.length, seed)]; - const randByte = digest( - Buffer.concat([ - seed, - // - intToBytes(Math.floor(i / 32), 8, "le"), - ]) - )[i % 32]; - - const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex]; - if (effectiveBalanceIncrement * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randByte) { - return candidateIndex; + if (fork >= ForkSeq.electra) { + const MAX_RANDOM_VALUE = 2 ** 16 - 1; + const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE_ELECTRA / EFFECTIVE_BALANCE_INCREMENT; + + let i = 0; + while (true) { + const candidateIndex = indices[computeShuffledIndex(i % indices.length, indices.length, seed)]; + const randomBytes = digest(Buffer.concat([seed, intToBytes(Math.floor(i / 16), 8, "le")])); + const offset = (i % 16) * 2; + const randomValue = bytesToInt(randomBytes.subarray(offset, offset + 2)); + + const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex]; + if (effectiveBalanceIncrement * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomValue) { + return candidateIndex; + } + + i += 1; + } + } else { + const MAX_RANDOM_BYTE = 2 ** 8 - 1; + const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE / EFFECTIVE_BALANCE_INCREMENT; + + let i = 0; + while (true) { + const candidateIndex = indices[computeShuffledIndex(i % indices.length, indices.length, seed)]; + const randomByte = digest(Buffer.concat([seed, intToBytes(Math.floor(i / 32), 8, "le")]))[i % 32]; + + const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex]; + if (effectiveBalanceIncrement * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomByte) { + return candidateIndex; + } + + i += 1; } - i += 1; } } @@ -100,37 +110,55 @@ export function getNextSyncCommitteeIndices( activeValidatorIndices: ArrayLike, effectiveBalanceIncrements: EffectiveBalanceIncrements ): ValidatorIndex[] { - // TODO: Bechmark if it's necessary to inline outside of this function - const MAX_RANDOM_BYTE = 2 ** 8 - 1; - const MAX_EFFECTIVE_BALANCE_INCREMENT = - fork >= ForkSeq.electra - ? MAX_EFFECTIVE_BALANCE_ELECTRA / EFFECTIVE_BALANCE_INCREMENT - : MAX_EFFECTIVE_BALANCE / EFFECTIVE_BALANCE_INCREMENT; - - const epoch = computeEpochAtSlot(state.slot) + 1; - - const activeValidatorCount = activeValidatorIndices.length; - const seed = getSeed(state, epoch, DOMAIN_SYNC_COMMITTEE); - let i = 0; const syncCommitteeIndices = []; - while (syncCommitteeIndices.length < SYNC_COMMITTEE_SIZE) { - const shuffledIndex = computeShuffledIndex(i % activeValidatorCount, activeValidatorCount, seed); - const candidateIndex = activeValidatorIndices[shuffledIndex]; - const randByte = digest( - Buffer.concat([ - seed, - // - intToBytes(Math.floor(i / 32), 8, "le"), - ]) - )[i % 32]; - - const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex]; - if (effectiveBalanceIncrement * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randByte) { - syncCommitteeIndices.push(candidateIndex); + + if (fork >= ForkSeq.electra) { + const MAX_RANDOM_VALUE = 2 ** 16 - 1; + const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE_ELECTRA / EFFECTIVE_BALANCE_INCREMENT; + + const epoch = computeEpochAtSlot(state.slot) + 1; + const activeValidatorCount = activeValidatorIndices.length; + const seed = getSeed(state, epoch, DOMAIN_SYNC_COMMITTEE); + + let i = 0; + while (syncCommitteeIndices.length < SYNC_COMMITTEE_SIZE) { + const shuffledIndex = computeShuffledIndex(i % activeValidatorCount, activeValidatorCount, seed); + const candidateIndex = activeValidatorIndices[shuffledIndex]; + const randomBytes = digest(Buffer.concat([seed, intToBytes(Math.floor(i / 16), 8, "le")])); + const offset = (i % 16) * 2; + const randomValue = bytesToInt(randomBytes.subarray(offset, offset + 2)); + + const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex]; + if (effectiveBalanceIncrement * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomValue) { + syncCommitteeIndices.push(candidateIndex); + } + + i += 1; } + } else { + const MAX_RANDOM_BYTE = 2 ** 8 - 1; + const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE / EFFECTIVE_BALANCE_INCREMENT; + + const epoch = computeEpochAtSlot(state.slot) + 1; + + const activeValidatorCount = activeValidatorIndices.length; + const seed = getSeed(state, epoch, DOMAIN_SYNC_COMMITTEE); - i++; + let i = 0; + while (syncCommitteeIndices.length < SYNC_COMMITTEE_SIZE) { + const shuffledIndex = computeShuffledIndex(i % activeValidatorCount, activeValidatorCount, seed); + const candidateIndex = activeValidatorIndices[shuffledIndex]; + const randomByte = digest(Buffer.concat([seed, intToBytes(Math.floor(i / 32), 8, "le")]))[i % 32]; + + const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex]; + if (effectiveBalanceIncrement * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomByte) { + syncCommitteeIndices.push(candidateIndex); + } + + i += 1; + } } + return syncCommitteeIndices; }