Skip to content
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

feat: support electra devnet-1 #6892

Merged
merged 10 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
capella,
deneb,
Wei,
electra,
} from "@lodestar/types";
import {
CachedBeaconStateAllForks,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
EpochTransitionCache,
BeaconStateAllForks,
beforeProcessEpoch,
CachedBeaconStateAltair,
} from "@lodestar/state-transition";
import * as epochFns from "@lodestar/state-transition/epoch";
import {ssz} from "@lodestar/types";
Expand Down Expand Up @@ -40,7 +41,10 @@ const epochTransitionFns: Record<string, EpochTransitionFn> = {
rewards_and_penalties: epochFns.processRewardsAndPenalties,
slashings: epochFns.processSlashings,
slashings_reset: epochFns.processSlashingsReset,
sync_committee_updates: epochFns.processSyncCommitteeUpdates as EpochTransitionFn,
sync_committee_updates: (state, _) => {
const fork = state.config.getForkSeq(state.slot);
epochFns.processSyncCommitteeUpdates(fork, state as CachedBeaconStateAltair);
},
historical_summaries_update: epochFns.processHistoricalSummariesUpdate as EpochTransitionFn,
pending_balance_deposits: epochFns.processPendingBalanceDeposits as EpochTransitionFn,
pending_consolidations: epochFns.processPendingConsolidations as EpochTransitionFn,
Expand Down
6 changes: 5 additions & 1 deletion packages/beacon-node/test/spec/presets/operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,13 @@ const operationFns: Record<string, BlockProcessFn<CachedBeaconStateAllForks>> =
blockFns.processWithdrawalRequest(ForkSeq.electra, state as CachedBeaconStateElectra, testCase.withdrawal_request);
},

deposit_request: (state, testCase: {deposit_request: electra.DepositRequest}) => {
blockFns.processDepositRequest(ForkSeq.electra, state as CachedBeaconStateElectra, testCase.deposit_request);
},

consolidation_request: (state, testCase: {consolidation_request: electra.ConsolidationRequest}) => {
blockFns.processConsolidationRequest(state as CachedBeaconStateElectra, testCase.consolidation_request);
},

};

export type BlockProcessFn<T extends CachedBeaconStateAllForks> = (state: T, testCase: any) => void;
Expand Down Expand Up @@ -151,6 +154,7 @@ const operations: TestRunnerFn<OperationsTestCase, BeaconStateAllForks> = (fork,
address_change: ssz.capella.SignedBLSToExecutionChange,
// Electra
withdrawal_request: ssz.electra.WithdrawalRequest,
deposit_request: ssz.electra.DepositRequest,
consolidation_request: ssz.electra.ConsolidationRequest,
},
shouldError: (testCase) => testCase.post === undefined,
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/test/spec/specTestVersioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {DownloadTestsOptions} from "@lodestar/spec-test-util/downloadTests";
const __dirname = path.dirname(fileURLToPath(import.meta.url));

export const ethereumConsensusSpecsTests: DownloadTestsOptions = {
specVersion: "v1.5.0-alpha.2",
specVersion: "v1.5.0-alpha.3",
// Target directory is the host package root: 'packages/*/spec-tests'
outputDir: path.join(__dirname, "../../spec-tests"),
specTestsRepoUrl: "https://github.com/ethereum/consensus-spec-tests",
Expand Down
3 changes: 2 additions & 1 deletion packages/beacon-node/test/spec/utils/specTestIterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ export const defaultSkipOpts: SkipOpts = {
/^capella\/light_client\/single_merkle_proof\/BeaconBlockBody.*/,
/^deneb\/light_client\/single_merkle_proof\/BeaconBlockBody.*/,
],
skippedTests: [],
// TODO Electra: Review this test in the next spec test release
skippedTests: [/incorrect_not_enough_consolidation_churn_available/],
ensi321 marked this conversation as resolved.
Show resolved Hide resolved
skippedRunners: ["merkle_proof", "networking"],
};

Expand Down
8 changes: 7 additions & 1 deletion packages/config/src/forkConfig/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
isForkExecution,
isForkBlobs,
} from "@lodestar/params";
import {Slot, allForks, Version, ssz} from "@lodestar/types";
import {Slot, allForks, Version, ssz, Epoch} from "@lodestar/types";
import {ChainConfig} from "../chainConfig/index.js";
import {ForkConfig, ForkInfo} from "./types.js";

Expand Down Expand Up @@ -80,6 +80,9 @@ export function createForkConfig(config: ChainConfig): ForkConfig {
// Fork convenience methods
getForkInfo(slot: Slot): ForkInfo {
const epoch = Math.floor(Math.max(slot, 0) / SLOTS_PER_EPOCH);
return this.getForkInfoFromEpoch(epoch);
},
getForkInfoFromEpoch(epoch: Epoch): ForkInfo {
// NOTE: forks must be sorted by descending epoch, latest fork first
for (const fork of forksDescendingEpochOrder) {
if (epoch >= fork.epoch) return fork;
Expand All @@ -92,6 +95,9 @@ export function createForkConfig(config: ChainConfig): ForkConfig {
getForkSeq(slot: Slot): ForkSeq {
return this.getForkInfo(slot).seq;
},
getForkSeqFromEpoch(epoch: Epoch): ForkSeq {
return this.getForkInfoFromEpoch(epoch).seq;
},
getForkVersion(slot: Slot): Version {
return this.getForkInfo(slot).version;
},
Expand Down
5 changes: 4 additions & 1 deletion packages/config/src/forkConfig/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ export type ForkConfig = {

/** Get the hard-fork info for the active fork at `slot` */
getForkInfo(slot: Slot): ForkInfo;

/** Get the hard-fork info for the active fork at `epoch` */
getForkInfoFromEpoch(epoch: Epoch): ForkInfo;
/** Get the hard-fork name at a given slot */
getForkName(slot: Slot): ForkName;
/** Get the hard-fork sequence number at a given slot */
getForkSeq(slot: Slot): ForkSeq;
/** Get the hard-fork sequence number at a given epoch */
getForkSeqFromEpoch(epoch: Epoch): ForkSeq;
/** Get the hard-fork version at a given slot */
getForkVersion(slot: Slot): Version;
/** Get SSZ types by hard-fork */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export function processConsolidationRequest(
state: CachedBeaconStateElectra,
consolidationRequest: electra.ConsolidationRequest
): void {

// If the pending consolidations queue is full, consolidation requests are ignored
if (state.pendingConsolidations.length >= PENDING_CONSOLIDATIONS_LIMIT) {
return;
Expand All @@ -30,11 +29,11 @@ export function processConsolidationRequest(
}

// Verify that source != target, so a consolidation cannot be used as an exit.
if (sourceIndex === targetIndex){
if (sourceIndex === targetIndex) {
return;
}

const sourceValidator = state.validators.getReadonly(sourceIndex);
const sourceValidator = state.validators.get(sourceIndex);
ensi321 marked this conversation as resolved.
Show resolved Hide resolved
const targetValidator = state.validators.getReadonly(targetIndex);
const sourceWithdrawalAddress = sourceValidator.withdrawalCredentials.subarray(12);
const currentEpoch = state.epochCtx.epoch;
Expand Down Expand Up @@ -71,4 +70,4 @@ export function processConsolidationRequest(
targetIndex,
});
state.pendingConsolidations.push(pendingConsolidation);
}
}
2 changes: 1 addition & 1 deletion packages/state-transition/src/block/processOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function processOperations(
const stateElectra = state as CachedBeaconStateElectra;
const bodyElectra = body as electra.BeaconBlockBody;

for (const depositRequest of bodyElectra.executionPayload.depositReceipts) {
for (const depositRequest of bodyElectra.executionPayload.depositRequests) {
processDepositRequest(fork, stateElectra, depositRequest);
}

Expand Down
15 changes: 13 additions & 2 deletions packages/state-transition/src/cache/epochCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,12 @@ export class EpochCache {
// Allow to create CachedBeaconState for empty states, or no active validators
const proposers =
currentShuffling.activeIndices.length > 0
? computeProposers(currentProposerSeed, currentShuffling, effectiveBalanceIncrements)
? computeProposers(
config.getForkSeqFromEpoch(currentEpoch),
currentProposerSeed,
currentShuffling,
effectiveBalanceIncrements
)
: [];

const proposersNextEpoch: ProposersDeferred = {
Expand Down Expand Up @@ -571,7 +576,12 @@ export class EpochCache {
this.proposersPrevEpoch = this.proposers;

const currentProposerSeed = getSeed(state, this.currentShuffling.epoch, DOMAIN_BEACON_PROPOSER);
this.proposers = computeProposers(currentProposerSeed, this.currentShuffling, this.effectiveBalanceIncrements);
this.proposers = computeProposers(
this.config.getForkSeqFromEpoch(currEpoch),
currentProposerSeed,
this.currentShuffling,
this.effectiveBalanceIncrements
);

// Only pre-compute the seed since it's very cheap. Do the expensive computeProposers() call only on demand.
this.proposersNextEpoch = {computed: false, seed: getSeed(state, this.nextShuffling.epoch, DOMAIN_BEACON_PROPOSER)};
Expand Down Expand Up @@ -768,6 +778,7 @@ export class EpochCache {
getBeaconProposersNextEpoch(): ValidatorIndex[] {
if (!this.proposersNextEpoch.computed) {
const indexes = computeProposers(
this.config.getForkSeqFromEpoch(this.epoch + 1),
this.proposersNextEpoch.seed,
this.nextShuffling,
this.effectiveBalanceIncrements
Expand Down
2 changes: 1 addition & 1 deletion packages/state-transition/src/epoch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export function processEpoch(
const timer = metrics?.epochTransitionStepTime.startTimer({
step: EpochTransitionStep.processSyncCommitteeUpdates,
});
processSyncCommitteeUpdates(state as CachedBeaconStateAltair);
processSyncCommitteeUpdates(fork, state as CachedBeaconStateAltair);
timer?.();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {getCurrentEpoch} from "../util/epoch.js";
* For each eligible `deposit`, call `increaseBalance()`.
* Remove the processed deposits from `state.pendingBalanceDeposits`.
* Update `state.depositBalanceToConsume` for the next epoch
*
*
* TODO Electra: Update ssz library to support batch push to `pendingBalanceDeposits`
*/
export function processPendingBalanceDeposits(state: CachedBeaconStateElectra): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import bls from "@chainsafe/bls";
import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD} from "@lodestar/params";
import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD, ForkSeq} from "@lodestar/params";
import {ssz} from "@lodestar/types";
import {getNextSyncCommitteeIndices} from "../util/seed.js";
import {CachedBeaconStateAltair} from "../types.js";
Expand All @@ -10,14 +10,15 @@ import {CachedBeaconStateAltair} from "../types.js";
* PERF: Once every `EPOCHS_PER_SYNC_COMMITTEE_PERIOD`, do an expensive operation to compute the next committee.
* Calculating the next sync committee has a proportional cost to $VALIDATOR_COUNT
*/
export function processSyncCommitteeUpdates(state: CachedBeaconStateAltair): void {
export function processSyncCommitteeUpdates(fork: ForkSeq, state: CachedBeaconStateAltair): void {
const nextEpoch = state.epochCtx.epoch + 1;

if (nextEpoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD === 0) {
const activeValidatorIndices = state.epochCtx.nextShuffling.activeIndices;
const {effectiveBalanceIncrements} = state.epochCtx;

const nextSyncCommitteeIndices = getNextSyncCommitteeIndices(
fork,
state,
activeValidatorIndices,
effectiveBalanceIncrements
Expand Down
4 changes: 2 additions & 2 deletions packages/state-transition/src/signatureSets/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {ForkSeq} from "@lodestar/params";
import {allForks, altair, capella, electra} from "@lodestar/types";
import {allForks, altair, capella} from "@lodestar/types";
import {ISignatureSet} from "../util/index.js";
import {CachedBeaconStateAllForks, CachedBeaconStateAltair, CachedBeaconStateElectra} from "../types.js";
import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "../types.js";
import {getSyncCommitteeSignatureSet} from "../block/processSyncCommittee.js";
import {getProposerSlashingsSignatureSets} from "./proposerSlashings.js";
import {getAttesterSlashingsSignatureSets} from "./attesterSlashings.js";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export function upgradeStateToAltair(statePhase0: CachedBeaconStatePhase0): Cach
stateAltair.inactivityScores = ssz.altair.InactivityScores.toViewDU(newZeroedArray(validatorCount));

const {syncCommittee, indices} = getNextSyncCommittee(
ForkSeq.altair,
stateAltair,
stateAltair.epochCtx.nextShuffling.activeIndices,
stateAltair.epochCtx.effectiveBalanceIncrements
Expand Down
2 changes: 2 additions & 0 deletions packages/state-transition/src/util/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ export function executionPayloadToPayloadHeader(
ssz.electra.DepositRequests.hashTreeRoot((payload as electra.ExecutionPayload).depositRequests);
(bellatrixPayloadFields as electra.ExecutionPayloadHeader).withdrawalRequestsRoot =
ssz.electra.WithdrawalRequests.hashTreeRoot((payload as electra.ExecutionPayload).withdrawalRequests);
(bellatrixPayloadFields as electra.ExecutionPayloadHeader).consolidationRequestsRoot =
ssz.electra.ConsolidationRequests.hashTreeRoot((payload as electra.ExecutionPayload).consolidationRequests);
}

return bellatrixPayloadFields;
Expand Down
8 changes: 6 additions & 2 deletions packages/state-transition/src/util/genesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {EpochCacheImmutableData} from "../cache/epochCache.js";
import {processDeposit} from "../block/processDeposit.js";
import {increaseBalance} from "../index.js";
import {computeEpochAtSlot} from "./epoch.js";
import {getActiveValidatorIndices} from "./validator.js";
import {getActiveValidatorIndices, getValidatorMaxEffectiveBalance} from "./validator.js";
import {getTemporaryBlockHeader} from "./blockRoot.js";
import {newFilledArray} from "./array.js";
import {getNextSyncCommittee} from "./syncCommittee.js";
Expand Down Expand Up @@ -193,7 +193,10 @@ export function applyDeposits(
}

const balance = balancesArr[i];
const effectiveBalance = Math.min(balance - (balance % EFFECTIVE_BALANCE_INCREMENT), MAX_EFFECTIVE_BALANCE);
const effectiveBalance = Math.min(
balance - (balance % EFFECTIVE_BALANCE_INCREMENT),
getValidatorMaxEffectiveBalance(validator.withdrawalCredentials)
);

validator.effectiveBalance = effectiveBalance;
epochCtx.effectiveBalanceIncrementsSet(i, effectiveBalance);
Expand Down Expand Up @@ -263,6 +266,7 @@ export function initializeBeaconStateFromEth1(

if (fork >= ForkSeq.altair) {
const {syncCommittee} = getNextSyncCommittee(
fork,
state,
activeValidatorIndices,
state.epochCtx.effectiveBalanceIncrements
Expand Down
19 changes: 14 additions & 5 deletions packages/state-transition/src/util/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
DOMAIN_SYNC_COMMITTEE,
EFFECTIVE_BALANCE_INCREMENT,
EPOCHS_PER_HISTORICAL_VECTOR,
ForkSeq,
MAX_EFFECTIVE_BALANCE,
MAX_EFFECTIVE_BALANCE_ELECTRA,
MIN_SEED_LOOKAHEAD,
SHUFFLE_ROUND_COUNT,
SLOTS_PER_EPOCH,
Expand All @@ -20,6 +22,7 @@ import {computeEpochAtSlot} from "./epoch.js";
* Compute proposer indices for an epoch
*/
export function computeProposers(
fork: ForkSeq,
epochSeed: Uint8Array,
shuffling: {epoch: Epoch; activeIndices: ArrayLike<ValidatorIndex>},
effectiveBalanceIncrements: EffectiveBalanceIncrements
Expand All @@ -29,6 +32,7 @@ export function computeProposers(
for (let slot = startSlot; slot < startSlot + SLOTS_PER_EPOCH; slot++) {
proposers.push(
computeProposerIndex(
fork,
effectiveBalanceIncrements,
shuffling.activeIndices,
digest(Buffer.concat([epochSeed, intToBytes(slot, 8)]))
Expand All @@ -44,6 +48,7 @@ export function computeProposers(
* SLOW CODE - 🐢
*/
export function computeProposerIndex(
fork: ForkSeq,
effectiveBalanceIncrements: EffectiveBalanceIncrements,
indices: ArrayLike<ValidatorIndex>,
seed: Uint8Array
Expand All @@ -54,7 +59,10 @@ export function computeProposerIndex(

// TODO: Inline outside this function
const MAX_RANDOM_BYTE = 2 ** 8 - 1;
const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE / EFFECTIVE_BALANCE_INCREMENT;
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 */
Expand All @@ -73,9 +81,6 @@ export function computeProposerIndex(
return candidateIndex;
}
i += 1;
if (i === indices.length) {
ensi321 marked this conversation as resolved.
Show resolved Hide resolved
return -1;
}
}
}

Expand All @@ -90,13 +95,17 @@ export function computeProposerIndex(
* SLOW CODE - 🐢
*/
export function getNextSyncCommitteeIndices(
fork: ForkSeq,
state: BeaconStateAllForks,
activeValidatorIndices: ArrayLike<ValidatorIndex>,
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 = MAX_EFFECTIVE_BALANCE / EFFECTIVE_BALANCE_INCREMENT;
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;

Expand Down
4 changes: 3 additions & 1 deletion packages/state-transition/src/util/syncCommittee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import bls from "@chainsafe/bls";
import {
BASE_REWARD_FACTOR,
EFFECTIVE_BALANCE_INCREMENT,
ForkSeq,
SLOTS_PER_EPOCH,
SYNC_COMMITTEE_SIZE,
SYNC_REWARD_WEIGHT,
Expand All @@ -19,11 +20,12 @@ import {getNextSyncCommitteeIndices} from "./seed.js";
* SLOW CODE - 🐢
*/
export function getNextSyncCommittee(
fork: ForkSeq,
state: BeaconStateAllForks,
activeValidatorIndices: ArrayLike<ValidatorIndex>,
effectiveBalanceIncrements: EffectiveBalanceIncrements
): {indices: ValidatorIndex[]; syncCommittee: altair.SyncCommittee} {
const indices = getNextSyncCommitteeIndices(state, activeValidatorIndices, effectiveBalanceIncrements);
const indices = getNextSyncCommitteeIndices(fork, state, activeValidatorIndices, effectiveBalanceIncrements);

// Using the index2pubkey cache is slower because it needs the serialized pubkey.
const pubkeys = indices.map((index) => state.validators.getReadonly(index).pubkey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ function benchmarkAltairEpochSteps(stateOg: LazyValue<CachedBeaconStateAllForks>
id: `${stateId} - altair processSyncCommitteeUpdates`,
convergeFactor: 1 / 100, // Very unstable make it converge faster
beforeEach: () => stateOg.value.clone() as CachedBeaconStateAltair,
fn: (state) => processSyncCommitteeUpdates(state),
fn: (state) => processSyncCommitteeUpdates(ForkSeq.altair, state),
});

itBench<StateEpoch, StateEpoch>({
Expand Down
Loading
Loading