From 1f392438e0d76ed1a9d62e9b6ecfc88019d5cf0d Mon Sep 17 00:00:00 2001 From: Tuyen Nguyen <vutuyen2636@gmail.com> Date: Wed, 27 Nov 2024 09:18:22 +0700 Subject: [PATCH] fix: sync cached isCompoundingValidatorArr at epoch transition --- .../src/cache/epochTransitionCache.ts | 25 +++++++++++++------ .../epoch/processEffectiveBalanceUpdates.ts | 7 +++--- .../src/epoch/processPendingDeposits.ts | 3 +++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/state-transition/src/cache/epochTransitionCache.ts b/packages/state-transition/src/cache/epochTransitionCache.ts index d86431c72eee..16279e851188 100644 --- a/packages/state-transition/src/cache/epochTransitionCache.ts +++ b/packages/state-transition/src/cache/epochTransitionCache.ts @@ -9,7 +9,12 @@ import {Epoch, RootHex, ValidatorIndex, phase0} from "@lodestar/types"; import {intDiv, toRootHex} from "@lodestar/utils"; import {processPendingAttestations} from "../epoch/processPendingAttestations.js"; -import {CachedBeaconStateAllForks, CachedBeaconStateAltair, CachedBeaconStatePhase0} from "../index.js"; +import { + CachedBeaconStateAllForks, + CachedBeaconStateAltair, + CachedBeaconStatePhase0, + hasCompoundingWithdrawalCredential, +} from "../index.js"; import {computeBaseRewardPerIncrement} from "../util/altair.js"; import { FLAG_CURR_HEAD_ATTESTER, @@ -133,11 +138,7 @@ export interface EpochTransitionCache { flags: number[]; - /** - * Validators in the current epoch, should use it for read-only value instead of accessing state.validators directly. - * Note that during epoch processing, validators could be updated so need to use it with care. - */ - validators: phase0.Validator[]; + isCompoundingValidatorArr: boolean[]; /** * balances array will be populated by processRewardsAndPenalties() and consumed by processEffectiveBalanceUpdates(). @@ -209,6 +210,8 @@ const inclusionDelays = new Array<number>(); /** WARNING: reused, never gc'd */ const flags = new Array<number>(); /** WARNING: reused, never gc'd */ +const isCompoundingValidatorArr = new Array<boolean>(); +/** WARNING: reused, never gc'd */ const nextEpochShufflingActiveValidatorIndices = new Array<number>(); export function beforeProcessEpoch( @@ -262,6 +265,10 @@ export function beforeProcessEpoch( // TODO: optimize by combining the two loops // likely will require splitting into phase0 and post-phase0 versions + if (forkSeq >= ForkSeq.electra) { + isCompoundingValidatorArr.length = validatorCount; + } + // Clone before being mutated in processEffectiveBalanceUpdates epochCtx.beforeEpochTransition(); @@ -298,6 +305,10 @@ export function beforeProcessEpoch( flags[i] = flag; + if (forkSeq >= ForkSeq.electra) { + isCompoundingValidatorArr[i] = hasCompoundingWithdrawalCredential(validator.withdrawalCredentials); + } + if (isActiveCurr) { totalActiveStakeByIncrement += effectiveBalancesByIncrements[i]; } else { @@ -511,7 +522,7 @@ export function beforeProcessEpoch( proposerIndices, inclusionDelays, flags, - validators, + isCompoundingValidatorArr, // Will be assigned in processRewardsAndPenalties() balances: undefined, }; diff --git a/packages/state-transition/src/epoch/processEffectiveBalanceUpdates.ts b/packages/state-transition/src/epoch/processEffectiveBalanceUpdates.ts index d55f37aba178..b98e5cce9f35 100644 --- a/packages/state-transition/src/epoch/processEffectiveBalanceUpdates.ts +++ b/packages/state-transition/src/epoch/processEffectiveBalanceUpdates.ts @@ -5,10 +5,11 @@ import { HYSTERESIS_QUOTIENT, HYSTERESIS_UPWARD_MULTIPLIER, MAX_EFFECTIVE_BALANCE, + MAX_EFFECTIVE_BALANCE_ELECTRA, + MIN_ACTIVATION_BALANCE, TIMELY_TARGET_FLAG_INDEX, } from "@lodestar/params"; import {BeaconStateAltair, CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js"; -import {getMaxEffectiveBalance} from "../util/validator.js"; /** Same to https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.5/specs/altair/beacon-chain.md#has_flag */ const TIMELY_TARGET = 1 << TIMELY_TARGET_FLAG_INDEX; @@ -43,7 +44,7 @@ export function processEffectiveBalanceUpdates( // and updated in processPendingDeposits() and processPendingConsolidations() // so it's recycled here for performance. const balances = cache.balances ?? state.balances.getAll(); - const currentEpochValidators = cache.validators; + const isCompoundingValidatorArr = cache.isCompoundingValidatorArr; let numUpdate = 0; for (let i = 0, len = balances.length; i < len; i++) { @@ -58,7 +59,7 @@ export function processEffectiveBalanceUpdates( effectiveBalanceLimit = MAX_EFFECTIVE_BALANCE; } else { // from electra, effectiveBalanceLimit is per validator - effectiveBalanceLimit = getMaxEffectiveBalance(currentEpochValidators[i].withdrawalCredentials); + effectiveBalanceLimit = isCompoundingValidatorArr[i] ? MAX_EFFECTIVE_BALANCE_ELECTRA : MIN_ACTIVATION_BALANCE; } if ( diff --git a/packages/state-transition/src/epoch/processPendingDeposits.ts b/packages/state-transition/src/epoch/processPendingDeposits.ts index 866dcc2510ad..e00e1ef93c8c 100644 --- a/packages/state-transition/src/epoch/processPendingDeposits.ts +++ b/packages/state-transition/src/epoch/processPendingDeposits.ts @@ -3,6 +3,7 @@ import {PendingDeposit} from "@lodestar/types/lib/electra/types.js"; import {addValidatorToRegistry, isValidDepositSignature} from "../block/processDeposit.js"; import {CachedBeaconStateElectra, EpochTransitionCache} from "../types.js"; import {increaseBalance} from "../util/balance.js"; +import {hasCompoundingWithdrawalCredential} from "../util/electra.js"; import {computeStartSlotAtEpoch} from "../util/epoch.js"; import {getActivationExitChurnLimit} from "../util/validator.js"; @@ -106,6 +107,8 @@ function applyPendingDeposit( // Verify the deposit signature (proof of possession) which is not checked by the deposit contract if (isValidDepositSignature(state.config, pubkey, withdrawalCredentials, amount, signature)) { addValidatorToRegistry(ForkSeq.electra, state, pubkey, withdrawalCredentials, amount); + const newValidatorIndex = state.validators.length - 1; + cache.isCompoundingValidatorArr[newValidatorIndex] = hasCompoundingWithdrawalCredential(withdrawalCredentials); } } else { // Increase balance