From ea43dc5948111837816a355377c871315c69881b Mon Sep 17 00:00:00 2001 From: harkamal Date: Wed, 24 Jan 2024 17:38:11 +0530 Subject: [PATCH 1/9] feat: placeholder PR for electra add types stub and epoch config fix types --- .../test/spec/presets/fork.test.ts | 2 + .../test/spec/presets/transition.test.ts | 8 + .../upgradeLightClientHeader.test.ts | 33 +++- .../test/unit/network/fork.test.ts | 13 +- packages/beacon-node/test/utils/config.ts | 8 + .../config/src/chainConfig/configs/mainnet.ts | 4 + .../config/src/chainConfig/configs/minimal.ts | 4 + packages/config/src/chainConfig/types.ts | 6 + packages/config/src/forkConfig/index.ts | 10 +- packages/light-client/src/spec/utils.ts | 4 + packages/params/src/forkName.ts | 2 + .../test/unit/upgradeState.test.ts | 8 + packages/types/src/allForks/sszTypes.ts | 38 +++++ packages/types/src/electra/index.ts | 3 + packages/types/src/electra/sszTypes.ts | 148 ++++++++++++++++++ packages/types/src/electra/types.ts | 29 ++++ packages/types/src/sszTypes.ts | 1 + packages/types/src/types.ts | 1 + packages/validator/src/util/params.ts | 5 + 19 files changed, 323 insertions(+), 4 deletions(-) create mode 100644 packages/types/src/electra/index.ts create mode 100644 packages/types/src/electra/sszTypes.ts create mode 100644 packages/types/src/electra/types.ts diff --git a/packages/beacon-node/test/spec/presets/fork.test.ts b/packages/beacon-node/test/spec/presets/fork.test.ts index 228ab6a38935..c880d24bbbe3 100644 --- a/packages/beacon-node/test/spec/presets/fork.test.ts +++ b/packages/beacon-node/test/spec/presets/fork.test.ts @@ -35,6 +35,8 @@ const fork: TestRunnerFn = (forkNext) => { return slotFns.upgradeStateToCapella(preState as CachedBeaconStateBellatrix); case ForkName.deneb: return slotFns.upgradeStateToDeneb(preState as CachedBeaconStateCapella); + case ForkName.electra: + throw Error("not Implemented"); } }, options: { diff --git a/packages/beacon-node/test/spec/presets/transition.test.ts b/packages/beacon-node/test/spec/presets/transition.test.ts index 77919d76c3b1..1f98dbb41f58 100644 --- a/packages/beacon-node/test/spec/presets/transition.test.ts +++ b/packages/beacon-node/test/spec/presets/transition.test.ts @@ -102,6 +102,14 @@ function getTransitionConfig(fork: ForkName, forkEpoch: number): Partial${toFork}`, function () { + lcHeaderByFork[fromFork].beacon.slot = testSlots[fromFork]; + lcHeaderByFork[toFork].beacon.slot = testSlots[fromFork]; + + expect(() => { + upgradeLightClientHeader(config, toFork, lcHeaderByFork[fromFork]); + }).toThrow("Not Implemented"); + }); + } + + // Since electra is not implemented for loop is till deneb (Object.values(ForkName).length-1) + // Once electra is implemnted run for loop till Object.values(ForkName).length + + // for (let i = ForkSeq.altair; i < Object.values(ForkName).length; i++) { + + for (let i = ForkSeq.altair; i < Object.values(ForkName).length - 1; i++) { for (let j = i; j > 0; j--) { const fromFork = ForkName[ForkSeq[i] as ForkName]; const toFork = ForkName[ForkSeq[j] as ForkName]; diff --git a/packages/beacon-node/test/unit/network/fork.test.ts b/packages/beacon-node/test/unit/network/fork.test.ts index be748d2e8185..bbe1c0870d30 100644 --- a/packages/beacon-node/test/unit/network/fork.test.ts +++ b/packages/beacon-node/test/unit/network/fork.test.ts @@ -9,12 +9,14 @@ function getForkConfig({ bellatrix, capella, deneb, + electra, }: { phase0: number; altair: number; bellatrix: number; capella: number; deneb: number; + electra: number; }): BeaconConfig { const forks: Record = { phase0: { @@ -57,6 +59,14 @@ function getForkConfig({ prevVersion: Buffer.from([0, 0, 0, 3]), prevForkName: ForkName.capella, }, + electra: { + name: ForkName.electra, + seq: ForkSeq.electra, + epoch: electra, + version: Buffer.from([0, 0, 0, 5]), + prevVersion: Buffer.from([0, 0, 0, 4]), + prevForkName: ForkName.deneb, + }, }; const forksAscendingEpochOrder = Object.values(forks); const forksDescendingEpochOrder = Object.values(forks).reverse(); @@ -133,9 +143,10 @@ const testScenarios = [ for (const testScenario of testScenarios) { const {phase0, altair, bellatrix, capella, testCases} = testScenario; const deneb = Infinity; + const electra = Infinity; describe(`network / fork: phase0: ${phase0}, altair: ${altair}, bellatrix: ${bellatrix} capella: ${capella}`, () => { - const forkConfig = getForkConfig({phase0, altair, bellatrix, capella, deneb}); + const forkConfig = getForkConfig({phase0, altair, bellatrix, capella, deneb, electra}); const forks = forkConfig.forks; for (const testCase of testCases) { const {epoch, currentFork, nextFork, activeForks} = testCase; diff --git a/packages/beacon-node/test/utils/config.ts b/packages/beacon-node/test/utils/config.ts index 54c058d30722..2aad1c14c03e 100644 --- a/packages/beacon-node/test/utils/config.ts +++ b/packages/beacon-node/test/utils/config.ts @@ -31,5 +31,13 @@ export function getConfig(fork: ForkName, forkEpoch = 0): ChainForkConfig { CAPELLA_FORK_EPOCH: 0, DENEB_FORK_EPOCH: forkEpoch, }); + case ForkName.electra: + return createChainForkConfig({ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: forkEpoch, + }); } } diff --git a/packages/config/src/chainConfig/configs/mainnet.ts b/packages/config/src/chainConfig/configs/mainnet.ts index 883688ca821b..0de1bee666ec 100644 --- a/packages/config/src/chainConfig/configs/mainnet.ts +++ b/packages/config/src/chainConfig/configs/mainnet.ts @@ -49,6 +49,10 @@ export const chainConfig: ChainConfig = { DENEB_FORK_VERSION: b("0x04000000"), DENEB_FORK_EPOCH: 269568, // March 13, 2024, 01:55:35pm UTC + // Electra + ELECTRA_FORK_VERSION: b("0x05000000"), + ELECTRA_FORK_EPOCH: Infinity, + // Time parameters // --------------------------------------------------------------- // 12 seconds diff --git a/packages/config/src/chainConfig/configs/minimal.ts b/packages/config/src/chainConfig/configs/minimal.ts index 23cd14e763ec..c99a76d1ee40 100644 --- a/packages/config/src/chainConfig/configs/minimal.ts +++ b/packages/config/src/chainConfig/configs/minimal.ts @@ -46,6 +46,10 @@ export const chainConfig: ChainConfig = { DENEB_FORK_VERSION: b("0x04000001"), DENEB_FORK_EPOCH: Infinity, + // Electra + ELECTRA_FORK_VERSION: b("0x05000001"), + ELECTRA_FORK_EPOCH: Infinity, + // Time parameters // --------------------------------------------------------------- // [customized] Faster for testing purposes diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 657c8a6c14b4..0d306aa6545a 100644 --- a/packages/config/src/chainConfig/types.ts +++ b/packages/config/src/chainConfig/types.ts @@ -40,6 +40,9 @@ export type ChainConfig = { // DENEB DENEB_FORK_VERSION: Uint8Array; DENEB_FORK_EPOCH: number; + // ELECTRA + ELECTRA_FORK_VERSION: Uint8Array; + ELECTRA_FORK_EPOCH: number; // Time parameters SECONDS_PER_SLOT: number; @@ -99,6 +102,9 @@ export const chainConfigTypes: SpecTypes = { // DENEB DENEB_FORK_VERSION: "bytes", DENEB_FORK_EPOCH: "number", + // ELECTRA + ELECTRA_FORK_VERSION: "bytes", + ELECTRA_FORK_EPOCH: "number", // Time parameters SECONDS_PER_SLOT: "number", diff --git a/packages/config/src/forkConfig/index.ts b/packages/config/src/forkConfig/index.ts index d630f1ddfc88..efb27ca1505f 100644 --- a/packages/config/src/forkConfig/index.ts +++ b/packages/config/src/forkConfig/index.ts @@ -55,10 +55,18 @@ export function createForkConfig(config: ChainConfig): ForkConfig { prevVersion: config.CAPELLA_FORK_VERSION, prevForkName: ForkName.capella, }; + const electra: ForkInfo = { + name: ForkName.electra, + seq: ForkSeq.electra, + epoch: config.ELECTRA_FORK_EPOCH, + version: config.ELECTRA_FORK_VERSION, + prevVersion: config.DENEB_FORK_VERSION, + prevForkName: ForkName.deneb, + }; /** Forks in order order of occurence, `phase0` first */ // Note: Downstream code relies on proper ordering. - const forks = {phase0, altair, bellatrix, capella, deneb}; + const forks = {phase0, altair, bellatrix, capella, deneb, electra}; // Prevents allocating an array on every getForkInfo() call const forksAscendingEpochOrder = Object.values(forks); diff --git a/packages/light-client/src/spec/utils.ts b/packages/light-client/src/spec/utils.ts index 2a5720a1f637..ed4b9e6e961d 100644 --- a/packages/light-client/src/spec/utils.ts +++ b/packages/light-client/src/spec/utils.ts @@ -103,6 +103,10 @@ export function upgradeLightClientHeader( // Break if no further upgradation is required else fall through if (ForkSeq[targetFork] <= ForkSeq.deneb) break; + + // eslint-disable-next-line no-fallthrough + case ForkName.electra: + throw Error("Not Implemented"); } return upgradedHeader; } diff --git a/packages/params/src/forkName.ts b/packages/params/src/forkName.ts index 142684c313f4..bbb72a7972fa 100644 --- a/packages/params/src/forkName.ts +++ b/packages/params/src/forkName.ts @@ -7,6 +7,7 @@ export enum ForkName { bellatrix = "bellatrix", capella = "capella", deneb = "deneb", + electra = "electra", } /** @@ -18,6 +19,7 @@ export enum ForkSeq { bellatrix = 2, capella = 3, deneb = 4, + electra = 5, } export type ForkPreLightClient = ForkName.phase0; diff --git a/packages/state-transition/test/unit/upgradeState.test.ts b/packages/state-transition/test/unit/upgradeState.test.ts index 2ea8eef182ac..75ba415c1bea 100644 --- a/packages/state-transition/test/unit/upgradeState.test.ts +++ b/packages/state-transition/test/unit/upgradeState.test.ts @@ -55,5 +55,13 @@ function getConfig(fork: ForkName, forkEpoch = 0): ChainForkConfig { CAPELLA_FORK_EPOCH: 0, DENEB_FORK_EPOCH: forkEpoch, }); + case ForkName.electra: + return createChainForkConfig({ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: forkEpoch, + }); } } diff --git a/packages/types/src/allForks/sszTypes.ts b/packages/types/src/allForks/sszTypes.ts index 7174bc52e89c..6030215ac8ca 100644 --- a/packages/types/src/allForks/sszTypes.ts +++ b/packages/types/src/allForks/sszTypes.ts @@ -3,6 +3,7 @@ import {ssz as altair} from "../altair/index.js"; import {ssz as bellatrix} from "../bellatrix/index.js"; import {ssz as capella} from "../capella/index.js"; import {ssz as deneb} from "../deneb/index.js"; +import {ssz as electra} from "../electra/index.js"; /** * Index the ssz types that differ by fork @@ -44,6 +45,13 @@ export const allForks = { BeaconState: deneb.BeaconState, Metadata: altair.Metadata, }, + electra: { + BeaconBlockBody: electra.BeaconBlockBody, + BeaconBlock: electra.BeaconBlock, + SignedBeaconBlock: electra.SignedBeaconBlock, + BeaconState: electra.BeaconState, + Metadata: altair.Metadata, + }, }; /** @@ -85,6 +93,17 @@ export const allForksExecution = { SignedBuilderBid: deneb.SignedBuilderBid, SSEPayloadAttributes: deneb.SSEPayloadAttributes, }, + electra: { + BeaconBlockBody: electra.BeaconBlockBody, + BeaconBlock: electra.BeaconBlock, + SignedBeaconBlock: electra.SignedBeaconBlock, + BeaconState: electra.BeaconState, + ExecutionPayload: electra.ExecutionPayload, + ExecutionPayloadHeader: electra.ExecutionPayloadHeader, + BuilderBid: electra.BuilderBid, + SignedBuilderBid: electra.SignedBuilderBid, + SSEPayloadAttributes: electra.SSEPayloadAttributes, + }, }; /** @@ -107,6 +126,11 @@ export const allForksBlinded = { BeaconBlock: deneb.BlindedBeaconBlock, SignedBeaconBlock: deneb.SignedBlindedBeaconBlock, }, + electra: { + BeaconBlockBody: electra.BlindedBeaconBlockBody, + BeaconBlock: electra.BlindedBeaconBlock, + SignedBeaconBlock: electra.SignedBlindedBeaconBlock, + }, }; export const allForksLightClient = { @@ -150,6 +174,16 @@ export const allForksLightClient = { LightClientOptimisticUpdate: deneb.LightClientOptimisticUpdate, LightClientStore: deneb.LightClientStore, }, + electra: { + BeaconBlock: electra.BeaconBlock, + BeaconBlockBody: electra.BeaconBlockBody, + LightClientHeader: electra.LightClientHeader, + LightClientBootstrap: electra.LightClientBootstrap, + LightClientUpdate: electra.LightClientUpdate, + LightClientFinalityUpdate: electra.LightClientFinalityUpdate, + LightClientOptimisticUpdate: electra.LightClientOptimisticUpdate, + LightClientStore: electra.LightClientStore, + }, }; export const allForksBlobs = { @@ -157,4 +191,8 @@ export const allForksBlobs = { BlobSidecar: deneb.BlobSidecar, ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, }, + electra: { + BlobSidecar: electra.BlobSidecar, + ExecutionPayloadAndBlobsBundle: electra.ExecutionPayloadAndBlobsBundle, + }, }; diff --git a/packages/types/src/electra/index.ts b/packages/types/src/electra/index.ts new file mode 100644 index 000000000000..7856cd729620 --- /dev/null +++ b/packages/types/src/electra/index.ts @@ -0,0 +1,3 @@ +export * from "./types.js"; +export * as ts from "./types.js"; +export * as ssz from "./sszTypes.js"; diff --git a/packages/types/src/electra/sszTypes.ts b/packages/types/src/electra/sszTypes.ts new file mode 100644 index 000000000000..30690a499845 --- /dev/null +++ b/packages/types/src/electra/sszTypes.ts @@ -0,0 +1,148 @@ +import {ContainerType} from "@chainsafe/ssz"; +import {ssz as primitiveSsz} from "../primitive/index.js"; +import {ssz as denebSsz} from "../deneb/index.js"; + +const {BLSSignature} = primitiveSsz; + +export const ExecutionPayload = new ContainerType( + { + ...denebSsz.ExecutionPayload.fields, + }, + {typeName: "ExecutionPayload", jsonCase: "eth2"} +); + +export const ExecutionPayloadHeader = new ContainerType( + { + ...denebSsz.ExecutionPayloadHeader.fields, + }, + {typeName: "ExecutionPayloadHeader", jsonCase: "eth2"} +); + +export const BeaconBlockBody = new ContainerType( + { + ...denebSsz.BeaconBlockBody.fields, + }, + {typeName: "BeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const BeaconBlock = new ContainerType( + { + ...denebSsz.BeaconBlock.fields, + }, + {typeName: "BeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const SignedBeaconBlock = new ContainerType( + { + message: BeaconBlock, + signature: BLSSignature, + }, + {typeName: "SignedBeaconBlock", jsonCase: "eth2"} +); + +export const BlobSidecar = new ContainerType( + { + ...denebSsz.BlobSidecar.fields, + }, + {typeName: "BlobSidecar", jsonCase: "eth2"} +); + +export const BlindedBeaconBlockBody = new ContainerType( + { + ...denebSsz.BlindedBeaconBlockBody.fields, + }, + {typeName: "BlindedBeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const BlindedBeaconBlock = new ContainerType( + { + ...denebSsz.BlindedBeaconBlock.fields, + }, + {typeName: "BlindedBeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const SignedBlindedBeaconBlock = new ContainerType( + { + message: BlindedBeaconBlock, + signature: BLSSignature, + }, + {typeName: "SignedBlindedBeaconBlock", jsonCase: "eth2"} +); + +export const BuilderBid = new ContainerType( + { + ...denebSsz.BuilderBid.fields, + }, + {typeName: "BuilderBid", jsonCase: "eth2"} +); + +export const SignedBuilderBid = new ContainerType( + { + message: BuilderBid, + signature: BLSSignature, + }, + {typeName: "SignedBuilderBid", jsonCase: "eth2"} +); + +export const ExecutionPayloadAndBlobsBundle = new ContainerType( + { + ...denebSsz.ExecutionPayloadAndBlobsBundle.fields, + }, + {typeName: "ExecutionPayloadAndBlobsBundle", jsonCase: "eth2"} +); + +export const BeaconState = new ContainerType( + { + ...denebSsz.BeaconState.fields, + }, + {typeName: "BeaconState", jsonCase: "eth2"} +); + +export const LightClientHeader = new ContainerType( + { + ...denebSsz.LightClientHeader.fields, + }, + {typeName: "LightClientHeader", jsonCase: "eth2"} +); + +export const LightClientBootstrap = new ContainerType( + { + ...denebSsz.LightClientBootstrap.fields, + }, + {typeName: "LightClientBootstrap", jsonCase: "eth2"} +); + +export const LightClientUpdate = new ContainerType( + { + ...denebSsz.LightClientUpdate.fields, + }, + {typeName: "LightClientUpdate", jsonCase: "eth2"} +); + +export const LightClientFinalityUpdate = new ContainerType( + { + ...denebSsz.LightClientFinalityUpdate.fields, + }, + {typeName: "LightClientFinalityUpdate", jsonCase: "eth2"} +); + +export const LightClientOptimisticUpdate = new ContainerType( + { + ...denebSsz.LightClientOptimisticUpdate.fields, + }, + {typeName: "LightClientOptimisticUpdate", jsonCase: "eth2"} +); + +export const LightClientStore = new ContainerType( + { + ...denebSsz.LightClientStore.fields, + }, + {typeName: "LightClientStore", jsonCase: "eth2"} +); + +export const SSEPayloadAttributes = new ContainerType( + { + ...denebSsz.SSEPayloadAttributes.fields, + }, + {typeName: "SSEPayloadAttributes", jsonCase: "eth2"} +); diff --git a/packages/types/src/electra/types.ts b/packages/types/src/electra/types.ts new file mode 100644 index 000000000000..198259eed1dd --- /dev/null +++ b/packages/types/src/electra/types.ts @@ -0,0 +1,29 @@ +import {ValueOf} from "@chainsafe/ssz"; +import * as ssz from "./sszTypes.js"; + +export type BlobSidecar = ValueOf; +export type ExecutionPayloadAndBlobsBundle = ValueOf; + +export type ExecutionPayload = ValueOf; +export type ExecutionPayloadHeader = ValueOf; + +export type BeaconBlockBody = ValueOf; +export type BeaconBlock = ValueOf; +export type SignedBeaconBlock = ValueOf; + +export type BeaconState = ValueOf; + +export type BlindedBeaconBlockBody = ValueOf; +export type BlindedBeaconBlock = ValueOf; +export type SignedBlindedBeaconBlock = ValueOf; + +export type BuilderBid = ValueOf; +export type SignedBuilderBid = ValueOf; +export type SSEPayloadAttributes = ValueOf; + +export type LightClientHeader = ValueOf; +export type LightClientBootstrap = ValueOf; +export type LightClientUpdate = ValueOf; +export type LightClientFinalityUpdate = ValueOf; +export type LightClientOptimisticUpdate = ValueOf; +export type LightClientStore = ValueOf; diff --git a/packages/types/src/sszTypes.ts b/packages/types/src/sszTypes.ts index 2a7df948a447..ff3e77ab6947 100644 --- a/packages/types/src/sszTypes.ts +++ b/packages/types/src/sszTypes.ts @@ -4,6 +4,7 @@ export {ssz as altair} from "./altair/index.js"; export {ssz as bellatrix} from "./bellatrix/index.js"; export {ssz as capella} from "./capella/index.js"; export {ssz as deneb} from "./deneb/index.js"; +export {ssz as electra} from "./electra/index.js"; import {ssz as allForksSsz} from "./allForks/index.js"; export const allForks = allForksSsz.allForks; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index e2e416fa3667..fbd7d5621da0 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -6,6 +6,7 @@ export {ts as altair} from "./altair/index.js"; export {ts as bellatrix} from "./bellatrix/index.js"; export {ts as capella} from "./capella/index.js"; export {ts as deneb} from "./deneb/index.js"; +export {ts as electra} from "./electra/index.js"; export {ts as allForks} from "./allForks/index.js"; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 8ccaf9fe75ba..0afede39b951 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -73,6 +73,7 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record Date: Tue, 20 Feb 2024 02:51:21 +0800 Subject: [PATCH 2/9] feat: implement EIP-6110 (#6042) * Add immutable in the dependencies * Initial change to pubkeyCache * Added todos * Moved unfinalized cache to epochCache * Move populating finalized cache to afterProcessEpoch * Specify unfinalized cache during state cloning * Move from unfinalized to finalized cache in afterProcessEpoch * Confused myself * Clean up * Change logic * Fix cloning issue * Clean up redundant code * Add CarryoverData in epochCtx.createFromState * Fix typo * Update usage of pubkeyCache * Update pubkeyCache usage * Fix lint * Fix lint * Add 6110 to ChainConfig * Add 6110 to BeaconPreset * Define 6110 fork and container * Add V6110 api to execution engine * Update test * Add depositReceiptsRoot to process_execution_payload * State transitioning to EIP6110 * State transitioning to EIP6110 * Light client change in EIP-6110 * Update tests * produceBlock * Refactor processDeposit to match the spec * Implement processDepositReceipt * Implement 6110 fork guard for pubkeyCache * Handle changes in eth1 deposit * Update eth1 deposit test * Fix typo * Lint * Remove embarassing comments * Address comments * Modify applyDeposit signature * Update packages/state-transition/src/cache/epochCache.ts Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> * Update packages/state-transition/src/cache/epochCache.ts Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> * Update packages/state-transition/src/cache/pubkeyCache.ts Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> * Remove old code * Rename fields in epochCache and immutableData * Remove CarryoverData * Move isAfter6110 from var to method * Fix cyclic import * Fix operations spec runner * Fix for spec test * Fix spec test * state.depositReceiptsStartIndex to BigInt * getDeposit requires cached state * default depositReceiptsStartIndex value in genesis * Fix pubkeyCache bug * newUnfinalizedPubkeyIndexMap in createCachedBeaconState * Lint * Pass epochCache instead of pubkey2IndexFn in apis * Address comments * Add unit test on pubkey cache cloning * Add unfinalizedPubkeyCacheSize to metrics * Add unfinalizedPubkeyCacheSize to metrics * Clean up code * Add besu to el-interop * Add 6110 genesis file * Template for sim test * Add unit test for getEth1DepositCount * Update sim test * Update besudocker * Finish beacon api calls in sim test * Update epochCache.createFromState() * Fix bug unfinalized validators are not finalized * Add sim test to run a few blocks * Lint * Merge branch 'unstable' into 611 * Add more check to sim test * Update besu docker image instruction * Update sim test with correct tx * Address comment + cleanup * Clean up code * Properly handle promise rejection * Lint * Update packages/beacon-node/src/execution/engine/types.ts Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> * Update comments * Accept type undefined in ExecutionPayloadBodyRpc * Update comment and semantic * Remove if statement when adding finalized validator * Comment on repeated insert on finalized cache * rename createFromState * Add comment on getPubkey() * Stash change to reduce diffs * Stash change to reduce diffs * Lint * addFinalizedPubkey on finalized checkpoint * Update comment * Use OrderedMap for unfinalized cache * Pull out logic of deleting pubkeys for batch op * Add updateUnfinalizedPubkeys in regen * Update updateUnfinalizedPubkeys logic * Add comment * Add metrics for state context caches * Address comment * Address comment * Deprecate eth1Data polling when condition is reached * Fix conflicts * Fix sim test * Lint * Fix type * Fix test * Fix test * Lint * Update packages/light-client/src/spec/utils.ts Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> * Fix spec test * Address comments * Improve cache logic on checkpoint finalized * Update sim test according to new cache logic * Update comment * Lint * Finalized pubkey cache only update once per checkpoint * Add perf test for updateUnfinalizedPubkeys * Add perf test for updateUnfinalizedPubkeys * Tweak params for perf test * Freeze besu docker image version for 6110 * Add benchmark result * Use Map instead of OrderedMap. Update benchmark * Minor optimization * Minor optimization * Add memory test for immutable.js * Update test * Reduce code duplication * Lint * Remove try/catch in updateUnfinalizedPubkeys * Introduce EpochCache metric * Add historicalValidatorLengths * Polish code * Migrate state-transition unit tests to vitest * Fix calculation of pivot index * `historicalValidatorLengths` only activate post 6110 * Update sim test * Lint * Update packages/state-transition/src/cache/epochCache.ts Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> * Improve readability on historicalValidatorLengths * Update types * Fix calculation * Add eth1data poll todo * Add epochCache.getValidatorCountAtEpoch * Add todo * Add getStateIterator for state cache * Partial commit * Update perf test * updateUnfinalizedPubkeys directly modify states from regen * Update sim test. Lint * Add todo * some improvements and a fix for effectiveBalanceIncrements fork safeness * rename eip6110 to elctra * fix electra-interop.test.ts --------- Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Co-authored-by: gajinder lint and tsc small cleanup fix rebase issue --- packages/beacon-node/src/chain/chain.ts | 37 ++ .../beacon-node/src/chain/regen/queued.ts | 50 +- .../chain/stateCache/fifoBlockStateCache.ts | 4 + .../stateCache/inMemoryCheckpointsCache.ts | 4 + .../stateCache/persistentCheckpointsCache.ts | 4 + .../src/chain/stateCache/stateContextCache.ts | 6 +- .../beacon-node/src/chain/stateCache/types.ts | 2 + .../src/eth1/eth1DepositDataTracker.ts | 32 +- packages/beacon-node/src/eth1/index.ts | 8 + packages/beacon-node/src/eth1/interface.ts | 5 + .../beacon-node/src/eth1/utils/deposits.ts | 14 +- .../beacon-node/src/execution/engine/http.ts | 10 +- .../src/execution/engine/interface.ts | 4 +- .../beacon-node/src/execution/engine/mock.ts | 4 + .../src/execution/engine/payloadIdCache.ts | 8 + .../beacon-node/src/execution/engine/types.ts | 68 ++- .../beacon-node/src/metrics/metrics/beacon.ts | 7 + .../src/metrics/metrics/lodestar.ts | 31 ++ .../test/memory/unfinalizedPubkey2Index.ts | 54 +++ .../opPools/aggregatedAttestationPool.test.ts | 2 +- .../test/perf/chain/opPools/opPool.test.ts | 2 +- .../produceBlock/produceBlockBody.test.ts | 2 +- .../updateUnfinalizedPubkeys.test.ts | 110 +++++ .../scripts/el-interop/besu/common-setup.sh | 19 + .../test/scripts/el-interop/besu/electra.tmpl | 77 +++ .../scripts/el-interop/besu/post-merge.sh | 8 + .../el-interop/besudocker/common-setup.sh | 22 + .../el-interop/besudocker/electra.tmpl | 77 +++ .../el-interop/besudocker/post-merge.sh | 8 + .../test/sim/electra-interop.test.ts | 457 ++++++++++++++++++ .../test/spec/presets/fork.test.ts | 3 +- .../test/spec/presets/genesis.test.ts | 4 +- .../test/spec/presets/operations.test.ts | 9 +- .../test/spec/presets/ssz_static.test.ts | 1 + .../test/spec/utils/specTestIterator.ts | 1 - .../upgradeLightClientHeader.test.ts | 34 +- .../opPools/aggregatedAttestationPool.test.ts | 4 +- .../test/unit/chain/shufflingCache.test.ts | 8 +- .../stateCache/fifoBlockStateCache.test.ts | 3 +- .../test/unit/eth1/utils/deposits.test.ts | 52 +- .../test/unit/executionEngine/http.test.ts | 4 + .../beaconBlocksMaybeBlobsByRange.test.ts | 1 + packages/beacon-node/test/utils/state.ts | 23 +- .../test/utils/validationData/attestation.ts | 7 +- .../config/src/chainConfig/configs/mainnet.ts | 2 +- .../config/src/chainConfig/configs/minimal.ts | 3 +- packages/light-client/src/spec/utils.ts | 14 +- packages/params/src/index.ts | 5 + packages/params/src/presets/mainnet.ts | 3 + packages/params/src/presets/minimal.ts | 3 + packages/params/src/types.ts | 6 + packages/state-transition/package.json | 3 +- .../src/block/processDeposit.ts | 112 +++-- .../src/block/processDepositReceipt.ts | 17 + .../src/block/processOperations.ts | 17 +- .../state-transition/src/cache/epochCache.ts | 184 ++++++- .../state-transition/src/cache/pubkeyCache.ts | 21 +- .../state-transition/src/cache/stateCache.ts | 5 +- packages/state-transition/src/cache/types.ts | 6 +- packages/state-transition/src/index.ts | 9 +- packages/state-transition/src/metrics.ts | 5 + .../src/signatureSets/attesterSlashings.ts | 3 +- packages/state-transition/src/slot/index.ts | 1 + .../src/slot/upgradeStateToElectra.ts | 33 ++ .../state-transition/src/stateTransition.ts | 5 + packages/state-transition/src/types.ts | 2 + packages/state-transition/src/util/deposit.ts | 24 + .../state-transition/src/util/execution.ts | 7 +- packages/state-transition/src/util/genesis.ts | 12 + packages/state-transition/src/util/index.ts | 1 + .../test/unit/cachedBeaconState.test.ts | 62 ++- .../test/unit/upgradeState.test.ts | 16 + .../test/unit/util/deposit.test.ts | 99 ++++ packages/types/package.json | 3 + packages/types/src/allForks/sszTypes.ts | 2 +- packages/types/src/allForks/types.ts | 170 +++++-- packages/types/src/electra/sszTypes.ts | 147 +++++- packages/types/src/electra/types.ts | 8 +- packages/types/src/primitive/sszTypes.ts | 1 + packages/validator/src/util/params.ts | 4 +- yarn.lock | 5 + 81 files changed, 2097 insertions(+), 213 deletions(-) create mode 100644 packages/beacon-node/test/memory/unfinalizedPubkey2Index.ts create mode 100644 packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts create mode 100755 packages/beacon-node/test/scripts/el-interop/besu/common-setup.sh create mode 100644 packages/beacon-node/test/scripts/el-interop/besu/electra.tmpl create mode 100755 packages/beacon-node/test/scripts/el-interop/besu/post-merge.sh create mode 100644 packages/beacon-node/test/scripts/el-interop/besudocker/common-setup.sh create mode 100644 packages/beacon-node/test/scripts/el-interop/besudocker/electra.tmpl create mode 100755 packages/beacon-node/test/scripts/el-interop/besudocker/post-merge.sh create mode 100644 packages/beacon-node/test/sim/electra-interop.test.ts create mode 100644 packages/state-transition/src/block/processDepositReceipt.ts create mode 100644 packages/state-transition/src/slot/upgradeStateToElectra.ts create mode 100644 packages/state-transition/src/util/deposit.ts create mode 100644 packages/state-transition/test/unit/util/deposit.test.ts diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 22a205a797a6..7c55c25bc4ef 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -927,6 +927,9 @@ export class BeaconChain implements IBeaconChain { metrics.forkChoice.balancesLength.set(forkChoiceMetrics.balancesLength); metrics.forkChoice.nodes.set(forkChoiceMetrics.nodes); metrics.forkChoice.indices.set(forkChoiceMetrics.indices); + + const headState = this.getHeadState(); + metrics.headState.unfinalizedPubkeyCacheSize.set(headState.epochCtx.unfinalizedPubkey2index.size); } private onClockSlot(slot: Slot): void { @@ -1015,6 +1018,40 @@ export class BeaconChain implements IBeaconChain { if (headState) { this.opPool.pruneAll(headBlock, headState); } + + const cpEpoch = cp.epoch; + const electraEpoch = headState?.config.ELECTRA_FORK_EPOCH ?? Infinity; + + if (headState === null) { + this.logger.verbose("Head state is null"); + } else if (cpEpoch >= electraEpoch) { + // Get the validator.length from the state at cpEpoch + // We are confident the last element in the list is from headEpoch + // Thus we query from the end of the list. (cpEpoch - headEpoch - 1) is negative number + const pivotValidatorIndex = headState.epochCtx.getValidatorCountAtEpoch(cpEpoch); + + if (pivotValidatorIndex !== undefined) { + // Note EIP-6914 will break this logic + const newFinalizedValidators = headState.epochCtx.unfinalizedPubkey2index.filter( + (index, _pubkey) => index < pivotValidatorIndex + ); + + // Populate finalized pubkey cache and remove unfinalized pubkey cache + if (!newFinalizedValidators.isEmpty()) { + this.regen.updateUnfinalizedPubkeys(newFinalizedValidators); + } + } + } + + // TODO-Electra: Deprecating eth1Data poll requires a check on a finalized checkpoint state. + // Will resolve this later + // if (cpEpoch >= (this.config.ELECTRA_FORK_EPOCH ?? Infinity)) { + // // finalizedState can be safely casted to Electra state since cp is already post-Electra + // if (finalizedState.eth1DepositIndex >= (finalizedState as CachedBeaconStateElectra).depositReceiptsStartIndex) { + // // Signal eth1 to stop polling eth1Data + // this.eth1.stopPollingEth1Data(); + // } + // } } async updateBeaconProposerData(epoch: Epoch, proposers: ProposerPreparationData[]): Promise { diff --git a/packages/beacon-node/src/chain/regen/queued.ts b/packages/beacon-node/src/chain/regen/queued.ts index 1b2456d2b325..5f3554aba34b 100644 --- a/packages/beacon-node/src/chain/regen/queued.ts +++ b/packages/beacon-node/src/chain/regen/queued.ts @@ -1,7 +1,7 @@ import {toHexString} from "@chainsafe/ssz"; import {phase0, Slot, allForks, RootHex, Epoch} from "@lodestar/types"; import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice"; -import {CachedBeaconStateAllForks, computeEpochAtSlot} from "@lodestar/state-transition"; +import {CachedBeaconStateAllForks, UnfinalizedPubkeyIndexMap, computeEpochAtSlot} from "@lodestar/state-transition"; import {Logger} from "@lodestar/utils"; import {routes} from "@lodestar/api"; import {CheckpointHex, toCheckpointHex} from "../stateCache/index.js"; @@ -194,6 +194,54 @@ export class QueuedStateRegenerator implements IStateRegenerator { return this.checkpointStateCache.updatePreComputedCheckpoint(rootHex, epoch); } + /** + * Remove `validators` from all unfinalized cache's epochCtx.UnfinalizedPubkey2Index, + * and add them to epochCtx.pubkey2index and epochCtx.index2pubkey + */ + updateUnfinalizedPubkeys(validators: UnfinalizedPubkeyIndexMap): void { + let numStatesUpdated = 0; + const states = this.stateCache.getStates(); + const cpStates = this.checkpointStateCache.getStates(); + + // Add finalized pubkeys to all states. + const addTimer = this.metrics?.regenFnAddPubkeyTime.startTimer(); + + // We only need to add pubkeys to any one of the states since the finalized caches is shared globally across all states + const firstState = (states.next().value ?? cpStates.next().value) as CachedBeaconStateAllForks | undefined; + + if (firstState !== undefined) { + firstState.epochCtx.addFinalizedPubkeys(validators, this.metrics?.epochCache ?? undefined); + } else { + this.logger.warn("Attempt to delete finalized pubkey from unfinalized pubkey cache. But no state is available"); + } + + addTimer?.(); + + // Delete finalized pubkeys from unfinalized pubkey cache for all states + const deleteTimer = this.metrics?.regenFnDeletePubkeyTime.startTimer(); + const pubkeysToDelete = Array.from(validators.keys()); + + for (const s of states) { + s.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete); + numStatesUpdated++; + } + + for (const s of cpStates) { + s.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete); + numStatesUpdated++; + } + + // Since first state is consumed from the iterator. Will need to perform delete explicitly + if (firstState !== undefined) { + firstState?.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete); + numStatesUpdated++; + } + + deleteTimer?.(); + + this.metrics?.regenFnNumStatesUpdated.observe(numStatesUpdated); + } + /** * Get the state to run with `block`. * - State after `block.parentRoot` dialed forward to block.slot diff --git a/packages/beacon-node/src/chain/stateCache/fifoBlockStateCache.ts b/packages/beacon-node/src/chain/stateCache/fifoBlockStateCache.ts index 942825581c4a..7829c86cd88a 100644 --- a/packages/beacon-node/src/chain/stateCache/fifoBlockStateCache.ts +++ b/packages/beacon-node/src/chain/stateCache/fifoBlockStateCache.ts @@ -173,6 +173,10 @@ export class FIFOBlockStateCache implements BlockStateCache { })); } + getStates(): IterableIterator { + throw new Error("Method not implemented."); + } + /** * For unit test only. */ diff --git a/packages/beacon-node/src/chain/stateCache/inMemoryCheckpointsCache.ts b/packages/beacon-node/src/chain/stateCache/inMemoryCheckpointsCache.ts index 37c67c0d86b4..1bb45dfaa1a6 100644 --- a/packages/beacon-node/src/chain/stateCache/inMemoryCheckpointsCache.ts +++ b/packages/beacon-node/src/chain/stateCache/inMemoryCheckpointsCache.ts @@ -177,6 +177,10 @@ export class InMemoryCheckpointStateCache implements CheckpointStateCache { })); } + getStates(): IterableIterator { + return this.cache.values(); + } + /** ONLY FOR DEBUGGING PURPOSES. For spec tests on error */ dumpCheckpointKeys(): string[] { return Array.from(this.cache.keys()); diff --git a/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts b/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts index 46f0b59334e8..fe96d1a7923e 100644 --- a/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts +++ b/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts @@ -597,6 +597,10 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache { }); } + getStates(): IterableIterator { + throw new Error("Method not implemented."); + } + /** ONLY FOR DEBUGGING PURPOSES. For spec tests on error */ dumpCheckpointKeys(): string[] { return Array.from(this.cache.keys()); diff --git a/packages/beacon-node/src/chain/stateCache/stateContextCache.ts b/packages/beacon-node/src/chain/stateCache/stateContextCache.ts index 1b1067a3cec7..56970e11b6ce 100644 --- a/packages/beacon-node/src/chain/stateCache/stateContextCache.ts +++ b/packages/beacon-node/src/chain/stateCache/stateContextCache.ts @@ -34,7 +34,7 @@ export class StateContextCache implements BlockStateCache { this.maxStates = maxStates; this.cache = new MapTracker(metrics?.stateCache); if (metrics) { - this.metrics = metrics.stateCache; + this.metrics = {...metrics.stateCache, ...metrics.epochCache}; metrics.stateCache.size.addCollect(() => metrics.stateCache.size.set(this.cache.size)); } } @@ -128,6 +128,10 @@ export class StateContextCache implements BlockStateCache { })); } + getStates(): IterableIterator { + return this.cache.values(); + } + private deleteAllEpochItems(epoch: Epoch): void { for (const rootHex of this.epochIndex.get(epoch) || []) { this.cache.delete(rootHex); diff --git a/packages/beacon-node/src/chain/stateCache/types.ts b/packages/beacon-node/src/chain/stateCache/types.ts index 4a86a0527889..0a92689d1e9b 100644 --- a/packages/beacon-node/src/chain/stateCache/types.ts +++ b/packages/beacon-node/src/chain/stateCache/types.ts @@ -29,6 +29,7 @@ export interface BlockStateCache { prune(headStateRootHex: RootHex): void; deleteAllBeforeEpoch(finalizedEpoch: Epoch): void; dumpSummary(): routes.lodestar.StateCacheItem[]; + getStates(): IterableIterator; // Expose beacon states stored in cache. Use with caution } /** @@ -70,6 +71,7 @@ export interface CheckpointStateCache { processState(blockRootHex: RootHex, state: CachedBeaconStateAllForks): Promise; clear(): void; dumpSummary(): routes.lodestar.StateCacheItem[]; + getStates(): IterableIterator; // Expose beacon states stored in cache. Use with caution } export enum CacheItemType { diff --git a/packages/beacon-node/src/eth1/eth1DepositDataTracker.ts b/packages/beacon-node/src/eth1/eth1DepositDataTracker.ts index f36f70abbbc4..c0b3ab35a73a 100644 --- a/packages/beacon-node/src/eth1/eth1DepositDataTracker.ts +++ b/packages/beacon-node/src/eth1/eth1DepositDataTracker.ts @@ -1,6 +1,11 @@ import {phase0, ssz} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; -import {BeaconStateAllForks, becomesNewEth1Data} from "@lodestar/state-transition"; +import { + BeaconStateAllForks, + CachedBeaconStateAllForks, + CachedBeaconStateElectra, + becomesNewEth1Data, +} from "@lodestar/state-transition"; import {ErrorAborted, TimeoutError, fromHex, Logger, isErrorAborted, sleep} from "@lodestar/utils"; import {IBeaconDb} from "../db/index.js"; @@ -67,6 +72,8 @@ export class Eth1DepositDataTracker { /** Dynamically adjusted batch size to fetch deposit logs */ private eth1GetLogsBatchSizeDynamic = MAX_BLOCKS_PER_LOG_QUERY; private readonly forcedEth1DataVote: phase0.Eth1Data | null; + /** To stop `runAutoUpdate()` in addition to AbortSignal */ + private stopPolling: boolean; constructor( opts: Eth1Options, @@ -81,6 +88,8 @@ export class Eth1DepositDataTracker { this.depositsCache = new Eth1DepositsCache(opts, config, db); this.eth1DataCache = new Eth1DataCache(config, db); this.eth1FollowDistance = config.ETH1_FOLLOW_DISTANCE; + // TODO Electra: fix scenario where node starts post-Electra and `stopPolling` will always be false + this.stopPolling = false; this.forcedEth1DataVote = opts.forcedEth1DataVote ? ssz.phase0.Eth1Data.deserialize(fromHex(opts.forcedEth1DataVote)) @@ -109,10 +118,22 @@ export class Eth1DepositDataTracker { } } + // TODO Electra: Figure out how an elegant way to stop eth1data polling + stopPollingEth1Data(): void { + this.stopPolling = true; + } + /** * Return eth1Data and deposits ready for block production for a given state */ - async getEth1DataAndDeposits(state: BeaconStateAllForks): Promise { + async getEth1DataAndDeposits(state: CachedBeaconStateAllForks): Promise { + if ( + state.epochCtx.isAfterElectra() && + state.eth1DepositIndex >= (state as CachedBeaconStateElectra).depositReceiptsStartIndex + ) { + // No need to poll eth1Data since Electra deprecates the mechanism after depositReceiptsStartIndex is reached + return {eth1Data: state.eth1Data, deposits: []}; + } const eth1Data = this.forcedEth1DataVote ?? (await this.getEth1Data(state)); const deposits = await this.getDeposits(state, eth1Data); return {eth1Data, deposits}; @@ -141,7 +162,10 @@ export class Eth1DepositDataTracker { * Returns deposits to be included for a given state and eth1Data vote. * Requires internal caches to be updated regularly to return good results */ - private async getDeposits(state: BeaconStateAllForks, eth1DataVote: phase0.Eth1Data): Promise { + private async getDeposits( + state: CachedBeaconStateAllForks, + eth1DataVote: phase0.Eth1Data + ): Promise { // No new deposits have to be included, continue if (eth1DataVote.depositCount === state.eth1DepositIndex) { return []; @@ -162,7 +186,7 @@ export class Eth1DepositDataTracker { private async runAutoUpdate(): Promise { let lastRunMs = 0; - while (!this.signal.aborted) { + while (!this.signal.aborted && !this.stopPolling) { lastRunMs = Date.now(); try { diff --git a/packages/beacon-node/src/eth1/index.ts b/packages/beacon-node/src/eth1/index.ts index 9fdba90258a2..a8ba55c54141 100644 --- a/packages/beacon-node/src/eth1/index.ts +++ b/packages/beacon-node/src/eth1/index.ts @@ -106,6 +106,10 @@ export class Eth1ForBlockProduction implements IEth1ForBlockProduction { startPollingMergeBlock(): void { return this.eth1MergeBlockTracker.startPollingMergeBlock(); } + + stopPollingEth1Data(): void { + return this.eth1DepositDataTracker?.stopPollingEth1Data(); + } } /** @@ -140,4 +144,8 @@ export class Eth1ForBlockProductionDisabled implements IEth1ForBlockProduction { startPollingMergeBlock(): void { // Ignore } + + stopPollingEth1Data(): void { + // Ignore + } } diff --git a/packages/beacon-node/src/eth1/interface.ts b/packages/beacon-node/src/eth1/interface.ts index fc9626eb5b8a..898041ac8947 100644 --- a/packages/beacon-node/src/eth1/interface.ts +++ b/packages/beacon-node/src/eth1/interface.ts @@ -62,6 +62,11 @@ export interface IEth1ForBlockProduction { * - head state not isMergeTransitionComplete */ startPollingMergeBlock(): void; + + /** + * Should stop polling eth1Data after a Electra block is finalized AND deposit_receipts_start_index is reached + */ + stopPollingEth1Data(): void; } /** Different Eth1Block from phase0.Eth1Block with blockHash */ diff --git a/packages/beacon-node/src/eth1/utils/deposits.ts b/packages/beacon-node/src/eth1/utils/deposits.ts index 19544917ffdc..7cf64d4455d2 100644 --- a/packages/beacon-node/src/eth1/utils/deposits.ts +++ b/packages/beacon-node/src/eth1/utils/deposits.ts @@ -1,9 +1,9 @@ import {toGindex, Tree} from "@chainsafe/persistent-merkle-tree"; import {toHexString} from "@chainsafe/ssz"; -import {MAX_DEPOSITS} from "@lodestar/params"; -import {BeaconStateAllForks} from "@lodestar/state-transition"; +import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; import {phase0, ssz} from "@lodestar/types"; import {FilterOptions} from "@lodestar/db"; +import {getEth1DepositCount} from "@lodestar/state-transition"; import {Eth1Error, Eth1ErrorCode} from "../errors.js"; import {DepositTree} from "../../db/repositories/depositDataRoot.js"; @@ -11,7 +11,7 @@ export type DepositGetter = (indexRange: FilterOptions, eth1Data: pha export async function getDeposits( // eth1_deposit_index represents the next deposit index to be added - state: BeaconStateAllForks, + state: CachedBeaconStateAllForks, eth1Data: phase0.Eth1Data, depositsGetter: DepositGetter ): Promise { @@ -22,9 +22,11 @@ export async function getDeposits( throw new Eth1Error({code: Eth1ErrorCode.DEPOSIT_INDEX_TOO_HIGH, depositIndex, depositCount}); } - // Spec v0.12.2 - // assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index) - const depositsLen = Math.min(MAX_DEPOSITS, depositCount - depositIndex); + const depositsLen = getEth1DepositCount(state, eth1Data); + + if (depositsLen === 0) { + return []; // If depositsLen === 0, we can return early since no deposit with be returned from depositsGetter + } const indexRange = {gte: depositIndex, lt: depositIndex + depositsLen}; const deposits = await depositsGetter(indexRange, eth1Data); diff --git a/packages/beacon-node/src/execution/engine/http.ts b/packages/beacon-node/src/execution/engine/http.ts index f5ec03f41626..274e2164c7be 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -176,7 +176,9 @@ export class ExecutionEngineHttp implements IExecutionEngine { parentBlockRoot?: Root ): Promise { const method = - ForkSeq[fork] >= ForkSeq.deneb + ForkSeq[fork] >= ForkSeq.electra + ? "engine_newPayloadV6110" + : ForkSeq[fork] >= ForkSeq.deneb ? "engine_newPayloadV3" : ForkSeq[fork] >= ForkSeq.capella ? "engine_newPayloadV2" @@ -196,7 +198,7 @@ export class ExecutionEngineHttp implements IExecutionEngine { const serializedVersionedHashes = serializeVersionedHashes(versionedHashes); const parentBeaconBlockRoot = serializeBeaconBlockRoot(parentBlockRoot); - const method = "engine_newPayloadV3"; + const method = ForkSeq[fork] >= ForkSeq.electra ? "engine_newPayloadV6110" : "engine_newPayloadV3"; engineRequest = { method, params: [serializedExecutionPayload, serializedVersionedHashes, parentBeaconBlockRoot], @@ -370,7 +372,9 @@ export class ExecutionEngineHttp implements IExecutionEngine { shouldOverrideBuilder?: boolean; }> { const method = - ForkSeq[fork] >= ForkSeq.deneb + ForkSeq[fork] >= ForkSeq.electra + ? "engine_getPayloadV6110" + : ForkSeq[fork] >= ForkSeq.deneb ? "engine_getPayloadV3" : ForkSeq[fork] >= ForkSeq.capella ? "engine_getPayloadV2" diff --git a/packages/beacon-node/src/execution/engine/interface.ts b/packages/beacon-node/src/execution/engine/interface.ts index e5f612fc0965..c303ee6d6bf3 100644 --- a/packages/beacon-node/src/execution/engine/interface.ts +++ b/packages/beacon-node/src/execution/engine/interface.ts @@ -3,10 +3,10 @@ import {KZGCommitment, Blob, KZGProof} from "@lodestar/types/deneb"; import {Root, RootHex, allForks, capella, Wei} from "@lodestar/types"; import {DATA} from "../../eth1/provider/utils.js"; -import {PayloadIdCache, PayloadId, WithdrawalV1} from "./payloadIdCache.js"; +import {PayloadIdCache, PayloadId, WithdrawalV1, DepositReceiptV1} from "./payloadIdCache.js"; import {ExecutionPayloadBody} from "./types.js"; -export {PayloadIdCache, type PayloadId, type WithdrawalV1}; +export {PayloadIdCache, type PayloadId, type WithdrawalV1, type DepositReceiptV1}; export enum ExecutionPayloadStatus { /** given payload is valid */ diff --git a/packages/beacon-node/src/execution/engine/mock.ts b/packages/beacon-node/src/execution/engine/mock.ts index 5779713435a5..83b63bc01c7c 100644 --- a/packages/beacon-node/src/execution/engine/mock.ts +++ b/packages/beacon-node/src/execution/engine/mock.ts @@ -35,6 +35,7 @@ export type ExecutionEngineMockOpts = { onlyPredefinedResponses?: boolean; capellaForkTimestamp?: number; denebForkTimestamp?: number; + electraForkTimestamp?: number; }; type ExecutionBlock = { @@ -88,12 +89,14 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend { engine_newPayloadV1: this.notifyNewPayload.bind(this), engine_newPayloadV2: this.notifyNewPayload.bind(this), engine_newPayloadV3: this.notifyNewPayload.bind(this), + engine_newPayloadV6110: this.notifyNewPayload.bind(this), engine_forkchoiceUpdatedV1: this.notifyForkchoiceUpdate.bind(this), engine_forkchoiceUpdatedV2: this.notifyForkchoiceUpdate.bind(this), engine_forkchoiceUpdatedV3: this.notifyForkchoiceUpdate.bind(this), engine_getPayloadV1: this.getPayload.bind(this), engine_getPayloadV2: this.getPayload.bind(this), engine_getPayloadV3: this.getPayload.bind(this), + engine_getPayloadV6110: this.getPayload.bind(this), engine_getPayloadBodiesByHashV1: this.getPayloadBodiesByHash.bind(this), engine_getPayloadBodiesByRangeV1: this.getPayloadBodiesByRange.bind(this), }; @@ -387,6 +390,7 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend { } private timestampToFork(timestamp: number): ForkExecution { + if (timestamp > (this.opts.electraForkTimestamp ?? Infinity)) return ForkName.electra; if (timestamp > (this.opts.denebForkTimestamp ?? Infinity)) return ForkName.deneb; if (timestamp > (this.opts.capellaForkTimestamp ?? Infinity)) return ForkName.capella; return ForkName.bellatrix; diff --git a/packages/beacon-node/src/execution/engine/payloadIdCache.ts b/packages/beacon-node/src/execution/engine/payloadIdCache.ts index ea37e0922e9c..e5baa9fba92d 100644 --- a/packages/beacon-node/src/execution/engine/payloadIdCache.ts +++ b/packages/beacon-node/src/execution/engine/payloadIdCache.ts @@ -18,6 +18,14 @@ export type WithdrawalV1 = { amount: QUANTITY; }; +export type DepositReceiptV1 = { + pubkey: DATA; + withdrawalCredentials: DATA; + amount: QUANTITY; + signature: DATA; + index: QUANTITY; +}; + type FcuAttributes = {headBlockHash: DATA; finalizedBlockHash: DATA} & Omit; export class PayloadIdCache { diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index 72a0100f7a51..ca64b20afaed 100644 --- a/packages/beacon-node/src/execution/engine/types.ts +++ b/packages/beacon-node/src/execution/engine/types.ts @@ -1,4 +1,4 @@ -import {allForks, capella, deneb, Wei, bellatrix, Root} from "@lodestar/types"; +import {allForks, capella, deneb, Wei, bellatrix, Root, electra} from "@lodestar/types"; import { BYTES_PER_LOGS_BLOOM, FIELD_ELEMENTS_PER_BLOB, @@ -17,7 +17,7 @@ import { quantityToBigint, } from "../../eth1/provider/utils.js"; import {ExecutionPayloadStatus, BlobsBundle, PayloadAttributes, VersionedHashes} from "./interface.js"; -import {WithdrawalV1} from "./payloadIdCache.js"; +import {WithdrawalV1, DepositReceiptV1} from "./payloadIdCache.js"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -28,6 +28,7 @@ export type EngineApiRpcParamTypes = { engine_newPayloadV1: [ExecutionPayloadRpc]; engine_newPayloadV2: [ExecutionPayloadRpc]; engine_newPayloadV3: [ExecutionPayloadRpc, VersionedHashesRpc, DATA]; + engine_newPayloadV6110: [ExecutionPayloadRpc, VersionedHashesRpc, DATA]; /** * 1. Object - Payload validity status with respect to the consensus rules: * - blockHash: DATA, 32 Bytes - block hash value of the payload @@ -51,6 +52,7 @@ export type EngineApiRpcParamTypes = { engine_getPayloadV1: [QUANTITY]; engine_getPayloadV2: [QUANTITY]; engine_getPayloadV3: [QUANTITY]; + engine_getPayloadV6110: [QUANTITY]; /** * 1. Array of DATA - Array of block_hash field values of the ExecutionPayload structure @@ -78,6 +80,7 @@ export type EngineApiRpcReturnTypes = { engine_newPayloadV1: PayloadStatus; engine_newPayloadV2: PayloadStatus; engine_newPayloadV3: PayloadStatus; + engine_newPayloadV6110: PayloadStatus; engine_forkchoiceUpdatedV1: { payloadStatus: PayloadStatus; payloadId: QUANTITY | null; @@ -96,6 +99,7 @@ export type EngineApiRpcReturnTypes = { engine_getPayloadV1: ExecutionPayloadRpc; engine_getPayloadV2: ExecutionPayloadResponse; engine_getPayloadV3: ExecutionPayloadResponse; + engine_getPayloadV6110: ExecutionPayloadResponse; engine_getPayloadBodiesByHashV1: (ExecutionPayloadBodyRpc | null)[]; @@ -111,9 +115,17 @@ type ExecutionPayloadRpcWithValue = { }; type ExecutionPayloadResponse = ExecutionPayloadRpc | ExecutionPayloadRpcWithValue; -export type ExecutionPayloadBodyRpc = {transactions: DATA[]; withdrawals: WithdrawalV1[] | null}; +export type ExecutionPayloadBodyRpc = { + transactions: DATA[]; + withdrawals: WithdrawalV1[] | null | undefined; + depositReceipts: DepositReceiptV1[] | null | undefined; +}; -export type ExecutionPayloadBody = {transactions: bellatrix.Transaction[]; withdrawals: capella.Withdrawals | null}; +export type ExecutionPayloadBody = { + transactions: bellatrix.Transaction[]; + withdrawals: capella.Withdrawals | null; + depositReceipts: electra.DepositReceipts | null; +}; export type ExecutionPayloadRpc = { parentHash: DATA; // 32 bytes @@ -134,6 +146,7 @@ export type ExecutionPayloadRpc = { blobGasUsed?: QUANTITY; // DENEB excessBlobGas?: QUANTITY; // DENEB parentBeaconBlockRoot?: QUANTITY; // DENEB + depositReceipts?: DepositReceiptRpc[]; // ELECTRA }; export type WithdrawalRpc = { @@ -143,6 +156,14 @@ export type WithdrawalRpc = { amount: QUANTITY; }; +export type DepositReceiptRpc = { + pubkey: DATA; + withdrawalCredentials: DATA; + amount: QUANTITY; + signature: DATA; + index: QUANTITY; +}; + export type VersionedHashesRpc = DATA[]; export type PayloadAttributesRpc = { @@ -194,6 +215,12 @@ export function serializeExecutionPayload(fork: ForkName, data: allForks.Executi payload.excessBlobGas = numToQuantity(excessBlobGas); } + // ELECTRA adds depositReceipts to the ExecutionPayload + if (ForkSeq[fork] >= ForkSeq.electra) { + const {depositReceipts} = data as electra.ExecutionPayload; + payload.depositReceipts = depositReceipts.map(serializeDepositReceipt); + } + return payload; } @@ -279,6 +306,17 @@ export function parseExecutionPayload( (executionPayload as deneb.ExecutionPayload).excessBlobGas = quantityToBigint(excessBlobGas); } + if (ForkSeq[fork] >= ForkSeq.electra) { + const {depositReceipts} = data; + // Geth can also reply with null + if (depositReceipts == null) { + throw Error( + `depositReceipts missing for ${fork} >= electra executionPayload number=${executionPayload.blockNumber} hash=${data.blockHash}` + ); + } + (executionPayload as electra.ExecutionPayload).depositReceipts = depositReceipts.map(deserializeDepositReceipts); + } + return {executionPayload, executionPayloadValue, blobsBundle, shouldOverrideBuilder}; } @@ -345,11 +383,32 @@ export function deserializeWithdrawal(serialized: WithdrawalRpc): capella.Withdr } as capella.Withdrawal; } +export function serializeDepositReceipt(depositReceipt: electra.DepositReceipt): DepositReceiptRpc { + return { + pubkey: bytesToData(depositReceipt.pubkey), + withdrawalCredentials: bytesToData(depositReceipt.withdrawalCredentials), + amount: numToQuantity(depositReceipt.amount), + signature: bytesToData(depositReceipt.signature), + index: numToQuantity(depositReceipt.index), + }; +} + +export function deserializeDepositReceipts(serialized: DepositReceiptRpc): electra.DepositReceipt { + return { + pubkey: dataToBytes(serialized.pubkey, 48), + withdrawalCredentials: dataToBytes(serialized.withdrawalCredentials, 32), + amount: quantityToNum(serialized.amount), + signature: dataToBytes(serialized.signature, 96), + index: quantityToNum(serialized.index), + } as electra.DepositReceipt; +} + export function deserializeExecutionPayloadBody(data: ExecutionPayloadBodyRpc | null): ExecutionPayloadBody | null { return data ? { transactions: data.transactions.map((tran) => dataToBytes(tran, null)), withdrawals: data.withdrawals ? data.withdrawals.map(deserializeWithdrawal) : null, + depositReceipts: data.depositReceipts ? data.depositReceipts.map(deserializeDepositReceipts) : null, } : null; } @@ -359,6 +418,7 @@ export function serializeExecutionPayloadBody(data: ExecutionPayloadBody | null) ? { transactions: data.transactions.map((tran) => bytesToData(tran)), withdrawals: data.withdrawals ? data.withdrawals.map(serializeWithdrawal) : null, + depositReceipts: data.depositReceipts ? data.depositReceipts.map(serializeDepositReceipt) : null, } : null; } diff --git a/packages/beacon-node/src/metrics/metrics/beacon.ts b/packages/beacon-node/src/metrics/metrics/beacon.ts index 9366174ef6c6..d658d88b87c8 100644 --- a/packages/beacon-node/src/metrics/metrics/beacon.ts +++ b/packages/beacon-node/src/metrics/metrics/beacon.ts @@ -111,6 +111,13 @@ export function createBeaconMetrics(register: RegistryMetricCreator) { }), }, + headState: { + unfinalizedPubkeyCacheSize: register.gauge({ + name: "head_state_unfinalized_pubkey_cache_size", + help: "Current size of the unfinalizedPubkey2Index cache in the head state", + }), + }, + parentBlockDistance: register.histogram({ name: "beacon_imported_block_parent_distance", help: "Histogram of distance to parent block of valid imported blocks", diff --git a/packages/beacon-node/src/metrics/metrics/lodestar.ts b/packages/beacon-node/src/metrics/metrics/lodestar.ts index 20dc0333989a..8b3b774b22e4 100644 --- a/packages/beacon-node/src/metrics/metrics/lodestar.ts +++ b/packages/beacon-node/src/metrics/metrics/lodestar.ts @@ -374,6 +374,17 @@ export function createLodestarMetrics( help: "Total count state.validators nodesPopulated is false on stfn for post state", }), + epochCache: { + finalizedPubkeyDuplicateInsert: register.gauge({ + name: "lodestar_epoch_cache_finalized_pubkey_duplicate_insert", + help: "Total count of duplicate insert of finalized pubkeys", + }), + newUnFinalizedPubkey: register.gauge({ + name: "lodestar_epoch_cache_new_unfinalized_pubkey", + help: "Total count of unfinalized pubkeys added", + }), + }, + // BLS verifier thread pool and queue bls: { @@ -1200,6 +1211,11 @@ export function createLodestarMetrics( help: "Histogram of time to serialize state to db", buckets: [0.1, 0.5, 1, 2, 3, 4], }), + numStatesUpdated: register.histogram({ + name: "lodestar_cp_state_cache_state_updated_count", + help: "Histogram of number of state cache items updated every time removing and adding pubkeys to pubkey cache", + buckets: [1, 2, 5, 10, 50, 250], + }), statePruneFromMemoryCount: register.gauge({ name: "lodestar_cp_state_cache_state_prune_from_memory_count", help: "Total number of states pruned from memory", @@ -1368,6 +1384,21 @@ export function createLodestarMetrics( help: "regen function total errors", labelNames: ["entrypoint", "caller"], }), + regenFnAddPubkeyTime: register.histogram({ + name: "lodestar_regen_fn_add_pubkey_time_seconds", + help: "Historgram of time spent on adding pubkeys to all state cache items in seconds", + buckets: [0.01, 0.1, 0.5, 1, 2, 5], + }), + regenFnDeletePubkeyTime: register.histogram({ + name: "lodestar_regen_fn_delete_pubkey_time_seconds", + help: "Histrogram of time spent on deleting pubkeys from all state cache items in seconds", + buckets: [0.01, 0.1, 0.5, 1, 2, 5], + }), + regenFnNumStatesUpdated: register.histogram({ + name: "lodestar_regen_state_cache_state_updated_count", + help: "Histogram of number of state cache items updated every time removing pubkeys from unfinalized cache", + buckets: [1, 2, 5, 10, 50, 250], + }), unhandledPromiseRejections: register.gauge({ name: "lodestar_unhandled_promise_rejections_total", help: "UnhandledPromiseRejection total count", diff --git a/packages/beacon-node/test/memory/unfinalizedPubkey2Index.ts b/packages/beacon-node/test/memory/unfinalizedPubkey2Index.ts new file mode 100644 index 000000000000..b37967d16ca4 --- /dev/null +++ b/packages/beacon-node/test/memory/unfinalizedPubkey2Index.ts @@ -0,0 +1,54 @@ +import crypto from "node:crypto"; +import {Map} from "immutable"; +import {ValidatorIndex} from "@lodestar/types"; +import {toMemoryEfficientHexStr} from "@lodestar/state-transition/src/cache/pubkeyCache.js"; +import {testRunnerMemory} from "./testRunnerMemory.js"; + +// Results in MacOS Nov 2023 +// +// UnfinalizedPubkey2Index 1000 keys - 274956.5 bytes / instance +// UnfinalizedPubkey2Index 10000 keys - 2591129.3 bytes / instance +// UnfinalizedPubkey2Index 100000 keys - 27261443.4 bytes / instance + +testRunnerMemoryBpi([ + { + id: "UnfinalizedPubkey2Index 1000 keys", + getInstance: () => getRandomMap(1000, () => toMemoryEfficientHexStr(crypto.randomBytes(48))), + }, + { + id: "UnfinalizedPubkey2Index 10000 keys", + getInstance: () => getRandomMap(10000, () => toMemoryEfficientHexStr(crypto.randomBytes(48))), + }, + { + id: "UnfinalizedPubkey2Index 100000 keys", + getInstance: () => getRandomMap(100000, () => toMemoryEfficientHexStr(crypto.randomBytes(48))), + }, +]); + +function getRandomMap(n: number, getKey: (i: number) => string): Map { + const map = Map(); + + return map.withMutations((m) => { + for (let i = 0; i < n; i++) { + m.set(getKey(i), i); + } + }); +} + +/** + * Test bytes per instance in different representations of raw binary data + */ +function testRunnerMemoryBpi(testCases: {getInstance: (bytes: number) => unknown; id: string}[]): void { + const longestId = Math.max(...testCases.map(({id}) => id.length)); + + for (const {id, getInstance} of testCases) { + const bpi = testRunnerMemory({ + getInstance, + convergeFactor: 1 / 100, + sampleEvery: 5, + }); + + // eslint-disable-next-line no-console + console.log(`${id.padEnd(longestId)} - ${bpi.toFixed(1)} bytes / instance`); + } +} diff --git a/packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts b/packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts index 2e0c3500bea5..0bea2bab0e72 100644 --- a/packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts +++ b/packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts @@ -32,7 +32,7 @@ describe(`getAttestationsForBlock vc=${vc}`, () => { before(function () { this.timeout(5 * 60 * 1000); // Generating the states for the first time is very slow - originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true, vc}); + originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true, vc}) as unknown as CachedBeaconStateAltair; const {blockHeader, checkpoint} = computeAnchorCheckpoint(originalState.config, originalState); // TODO figure out why getBlockRootAtSlot(originalState, justifiedSlot) is not the same to justifiedCheckpoint.root diff --git a/packages/beacon-node/test/perf/chain/opPools/opPool.test.ts b/packages/beacon-node/test/perf/chain/opPools/opPool.test.ts index 6e420f0e1011..7998a204f09d 100644 --- a/packages/beacon-node/test/perf/chain/opPools/opPool.test.ts +++ b/packages/beacon-node/test/perf/chain/opPools/opPool.test.ts @@ -24,7 +24,7 @@ describe("opPool", () => { before(function () { this.timeout(2 * 60 * 1000); // Generating the states for the first time is very slow - originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true}); + originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true}) as unknown as CachedBeaconStateAltair; }); itBench({ diff --git a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts index 96dda3acaece..62b5466bf891 100644 --- a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts +++ b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts @@ -25,7 +25,7 @@ describe("produceBlockBody", () => { before(async () => { db = new BeaconDb(config, await LevelDbController.create({name: ".tmpdb"}, {logger})); - state = stateOg.clone(); + state = stateOg.clone() as unknown as CachedBeaconStateAltair; chain = new BeaconChain( { proposerBoostEnabled: true, diff --git a/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts b/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts new file mode 100644 index 000000000000..900f6a6fb873 --- /dev/null +++ b/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts @@ -0,0 +1,110 @@ +import {itBench, setBenchOpts} from "@dapplion/benchmark"; +import {Map} from "immutable"; +import {toBufferBE} from "bigint-buffer"; +import {digest} from "@chainsafe/as-sha256"; +import type {SecretKey} from "@chainsafe/bls/types"; +import bls from "@chainsafe/bls"; +import {ssz} from "@lodestar/types"; +import {type CachedBeaconStateAllForks, PubkeyIndexMap} from "@lodestar/state-transition"; +import {bytesToBigInt, intToBytes} from "@lodestar/utils"; +import {CheckpointStateCache, StateContextCache} from "../../../../src/chain/stateCache/index.js"; +import {generateCachedElectraState} from "../../../utils/state.js"; + +// Benchmark date from Mon Nov 21 2023 - Intel Core i7-9750H @ 2.60Ghz +// ✔ updateUnfinalizedPubkeys - updating 10 pubkeys 1444.173 ops/s 692.4380 us/op - 1057 runs 6.03 s +// ✔ updateUnfinalizedPubkeys - updating 100 pubkeys 189.5965 ops/s 5.274358 ms/op - 57 runs 1.15 s +// ✔ updateUnfinalizedPubkeys - updating 1000 pubkeys 12.90495 ops/s 77.48967 ms/op - 13 runs 1.62 s +describe("updateUnfinalizedPubkeys perf tests", function () { + setBenchOpts({noThreshold: true}); + + const numPubkeysToBeFinalizedCases = [10, 100, 1000]; + const numCheckpointStateCache = 8; + const numStateCache = 3 * 32; + + let checkpointStateCache: CheckpointStateCache; + let stateCache: StateContextCache; + + const unfinalizedPubkey2Index = generatePubkey2Index(0, Math.max.apply(null, numPubkeysToBeFinalizedCases)); + const baseState = generateCachedElectraState(); + + for (const numPubkeysToBeFinalized of numPubkeysToBeFinalizedCases) { + itBench({ + id: `updateUnfinalizedPubkeys - updating ${numPubkeysToBeFinalized} pubkeys`, + beforeEach: async () => { + baseState.epochCtx.unfinalizedPubkey2index = Map(unfinalizedPubkey2Index.map); + baseState.epochCtx.pubkey2index = new PubkeyIndexMap(); + baseState.epochCtx.index2pubkey = []; + + checkpointStateCache = new CheckpointStateCache({}); + stateCache = new StateContextCache({}); + + for (let i = 0; i < numCheckpointStateCache; i++) { + const clonedState = baseState.clone(); + const checkpoint = ssz.phase0.Checkpoint.defaultValue(); + + clonedState.slot = i; + checkpoint.epoch = i; // Assigning arbitrary non-duplicate values to ensure checkpointStateCache correctly saves all the states + + checkpointStateCache.add(checkpoint, clonedState); + } + + for (let i = 0; i < numStateCache; i++) { + const clonedState = baseState.clone(); + clonedState.slot = i; + stateCache.add(clonedState); + } + }, + fn: async () => { + const newFinalizedValidators = baseState.epochCtx.unfinalizedPubkey2index.filter( + (index, _pubkey) => index < numPubkeysToBeFinalized + ); + + const states = stateCache.getStates(); + const cpStates = checkpointStateCache.getStates(); + + const firstState = states.next().value as CachedBeaconStateAllForks; + firstState.epochCtx.addFinalizedPubkeys(newFinalizedValidators); + + const pubkeysToDelete = Array.from(newFinalizedValidators.keys()); + + firstState.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete); + + for (const s of states) { + s.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete); + } + + for (const s of cpStates) { + s.epochCtx.deleteUnfinalizedPubkeys(pubkeysToDelete); + } + }, + }); + } + + function generatePubkey2Index(startIndex: number, endIndex: number): PubkeyIndexMap { + const pubkey2Index = new PubkeyIndexMap(); + const pubkeys = generatePubkeys(endIndex - startIndex); + + for (let i = startIndex; i < endIndex; i++) { + pubkey2Index.set(pubkeys[i], i); + } + + return pubkey2Index; + } + + function generatePubkeys(validatorCount: number): Uint8Array[] { + const keys = []; + + for (let i = 0; i < validatorCount; i++) { + const sk = generatePrivateKey(i); + const pk = sk.toPublicKey().toBytes(); + keys.push(pk); + } + + return keys; + } + + function generatePrivateKey(index: number): SecretKey { + const secretKeyBytes = toBufferBE(bytesToBigInt(digest(intToBytes(index, 32))) % BigInt("38581184513"), 32); + return bls.SecretKey.fromBytes(secretKeyBytes); + } +}); diff --git a/packages/beacon-node/test/scripts/el-interop/besu/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/besu/common-setup.sh new file mode 100755 index 000000000000..1444f6d3a479 --- /dev/null +++ b/packages/beacon-node/test/scripts/el-interop/besu/common-setup.sh @@ -0,0 +1,19 @@ +#!/bin/bash -x + +echo $TTD +echo $DATA_DIR +echo $EL_BINARY_DIR +echo $JWT_SECRET_HEX +echo $TEMPLATE_FILE + +echo $scriptDir +echo $currentDir + + +env TTD=$TTD envsubst < $scriptDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json +echo "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" > $DATA_DIR/sk.json +echo "12345678" > $DATA_DIR/password.txt +pubKey="0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + +# echo a hex encoded 256 bit secret into a file +echo $JWT_SECRET_HEX> $DATA_DIR/jwtsecret \ No newline at end of file diff --git a/packages/beacon-node/test/scripts/el-interop/besu/electra.tmpl b/packages/beacon-node/test/scripts/el-interop/besu/electra.tmpl new file mode 100644 index 000000000000..7a63bfbe36d6 --- /dev/null +++ b/packages/beacon-node/test/scripts/el-interop/besu/electra.tmpl @@ -0,0 +1,77 @@ +{ + "config": { + "chainId":6110, + "homesteadBlock":0, + "eip150Block":0, + "eip155Block":0, + "eip158Block":0, + "byzantiumBlock":0, + "constantinopleBlock":0, + "petersburgBlock":0, + "istanbulBlock":0, + "muirGlacierBlock":0, + "berlinBlock":0, + "londonBlock":0, + "terminalTotalDifficulty":0, + "cancunTime":0, + "experimentalEipsTime":10, + "clique": { + "period": 5, + "epoch": 30000 + }, + "depositContractAddress": "0x4242424242424242424242424242424242424242" + }, + "nonce":"0x42", + "timestamp":"0x0", + "extraData":"0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit":"0x1C9C380", + "difficulty":"0x400000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase":"0x0000000000000000000000000000000000000000", + "alloc":{ + "0xa4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb": { + "balance": "1000000000000000000000000000" + }, + "0x4242424242424242424242424242424242424242": { + "balance": "0", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212201dd26f37a621703009abf16e77e69c93dc50c79db7f6cc37543e3e0e3decdc9764736f6c634300060b0033", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x0000000000000000000000000000000000000000000000000000000000000024": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x0000000000000000000000000000000000000000000000000000000000000025": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x0000000000000000000000000000000000000000000000000000000000000026": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x0000000000000000000000000000000000000000000000000000000000000027": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x0000000000000000000000000000000000000000000000000000000000000028": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x0000000000000000000000000000000000000000000000000000000000000029": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x000000000000000000000000000000000000000000000000000000000000002a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x000000000000000000000000000000000000000000000000000000000000002b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x000000000000000000000000000000000000000000000000000000000000002c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x000000000000000000000000000000000000000000000000000000000000002d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x000000000000000000000000000000000000000000000000000000000000002e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x000000000000000000000000000000000000000000000000000000000000002f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x0000000000000000000000000000000000000000000000000000000000000030": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x0000000000000000000000000000000000000000000000000000000000000031": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x0000000000000000000000000000000000000000000000000000000000000032": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x0000000000000000000000000000000000000000000000000000000000000033": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x0000000000000000000000000000000000000000000000000000000000000034": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x0000000000000000000000000000000000000000000000000000000000000035": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x0000000000000000000000000000000000000000000000000000000000000036": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x0000000000000000000000000000000000000000000000000000000000000037": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x0000000000000000000000000000000000000000000000000000000000000038": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x0000000000000000000000000000000000000000000000000000000000000039": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x000000000000000000000000000000000000000000000000000000000000003a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x000000000000000000000000000000000000000000000000000000000000003b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x000000000000000000000000000000000000000000000000000000000000003c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x000000000000000000000000000000000000000000000000000000000000003d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x000000000000000000000000000000000000000000000000000000000000003e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x000000000000000000000000000000000000000000000000000000000000003f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + } + }, + "number":"0x0", + "gasUsed":"0x0", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas":"0x7" +} diff --git a/packages/beacon-node/test/scripts/el-interop/besu/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/besu/post-merge.sh new file mode 100755 index 000000000000..47bec71cf8bb --- /dev/null +++ b/packages/beacon-node/test/scripts/el-interop/besu/post-merge.sh @@ -0,0 +1,8 @@ +#!/bin/bash -x + +scriptDir=$(dirname $0) +currentDir=$(pwd) + +. $scriptDir/common-setup.sh + +$EL_BINARY_DIR/besu --engine-rpc-enabled --rpc-http-enabled --rpc-http-api ADMIN,ETH,MINER,NET --rpc-http-port $ETH_PORT --engine-rpc-port $ENGINE_PORT --engine-jwt-secret $currentDir/$DATA_DIR/jwtsecret --data-path $DATA_DIR --data-storage-format BONSAI --genesis-file $DATA_DIR/genesis.json \ No newline at end of file diff --git a/packages/beacon-node/test/scripts/el-interop/besudocker/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/besudocker/common-setup.sh new file mode 100644 index 000000000000..b3d93190ef2d --- /dev/null +++ b/packages/beacon-node/test/scripts/el-interop/besudocker/common-setup.sh @@ -0,0 +1,22 @@ +#!/bin/bash -x + +echo $TTD +echo $DATA_DIR +echo $EL_BINARY_DIR +echo $JWT_SECRET_HEX +echo $TEMPLATE_FILE + +echo $scriptDir +echo $currentDir + + +env TTD=$TTD envsubst < $scriptDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json +echo "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" > $DATA_DIR/sk.json +echo "12345678" > $DATA_DIR/password.txt +pubKey="0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + +# echo a hex encoded 256 bit secret into a file +echo $JWT_SECRET_HEX> $DATA_DIR/jwtsecret +# clear any previous docker dangling docker run +docker rm -f custom-execution +rm -rf $DATA_DIR/besu diff --git a/packages/beacon-node/test/scripts/el-interop/besudocker/electra.tmpl b/packages/beacon-node/test/scripts/el-interop/besudocker/electra.tmpl new file mode 100644 index 000000000000..7a63bfbe36d6 --- /dev/null +++ b/packages/beacon-node/test/scripts/el-interop/besudocker/electra.tmpl @@ -0,0 +1,77 @@ +{ + "config": { + "chainId":6110, + "homesteadBlock":0, + "eip150Block":0, + "eip155Block":0, + "eip158Block":0, + "byzantiumBlock":0, + "constantinopleBlock":0, + "petersburgBlock":0, + "istanbulBlock":0, + "muirGlacierBlock":0, + "berlinBlock":0, + "londonBlock":0, + "terminalTotalDifficulty":0, + "cancunTime":0, + "experimentalEipsTime":10, + "clique": { + "period": 5, + "epoch": 30000 + }, + "depositContractAddress": "0x4242424242424242424242424242424242424242" + }, + "nonce":"0x42", + "timestamp":"0x0", + "extraData":"0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit":"0x1C9C380", + "difficulty":"0x400000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase":"0x0000000000000000000000000000000000000000", + "alloc":{ + "0xa4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb": { + "balance": "1000000000000000000000000000" + }, + "0x4242424242424242424242424242424242424242": { + "balance": "0", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212201dd26f37a621703009abf16e77e69c93dc50c79db7f6cc37543e3e0e3decdc9764736f6c634300060b0033", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x0000000000000000000000000000000000000000000000000000000000000024": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x0000000000000000000000000000000000000000000000000000000000000025": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x0000000000000000000000000000000000000000000000000000000000000026": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x0000000000000000000000000000000000000000000000000000000000000027": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x0000000000000000000000000000000000000000000000000000000000000028": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x0000000000000000000000000000000000000000000000000000000000000029": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x000000000000000000000000000000000000000000000000000000000000002a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x000000000000000000000000000000000000000000000000000000000000002b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x000000000000000000000000000000000000000000000000000000000000002c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x000000000000000000000000000000000000000000000000000000000000002d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x000000000000000000000000000000000000000000000000000000000000002e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x000000000000000000000000000000000000000000000000000000000000002f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x0000000000000000000000000000000000000000000000000000000000000030": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x0000000000000000000000000000000000000000000000000000000000000031": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x0000000000000000000000000000000000000000000000000000000000000032": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x0000000000000000000000000000000000000000000000000000000000000033": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x0000000000000000000000000000000000000000000000000000000000000034": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x0000000000000000000000000000000000000000000000000000000000000035": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x0000000000000000000000000000000000000000000000000000000000000036": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x0000000000000000000000000000000000000000000000000000000000000037": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x0000000000000000000000000000000000000000000000000000000000000038": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x0000000000000000000000000000000000000000000000000000000000000039": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x000000000000000000000000000000000000000000000000000000000000003a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x000000000000000000000000000000000000000000000000000000000000003b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x000000000000000000000000000000000000000000000000000000000000003c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x000000000000000000000000000000000000000000000000000000000000003d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x000000000000000000000000000000000000000000000000000000000000003e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x000000000000000000000000000000000000000000000000000000000000003f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + } + }, + "number":"0x0", + "gasUsed":"0x0", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas":"0x7" +} diff --git a/packages/beacon-node/test/scripts/el-interop/besudocker/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/besudocker/post-merge.sh new file mode 100755 index 000000000000..fb091a7d838b --- /dev/null +++ b/packages/beacon-node/test/scripts/el-interop/besudocker/post-merge.sh @@ -0,0 +1,8 @@ +#!/bin/bash -x + +scriptDir=$(dirname $0) +currentDir=$(pwd) + +. $scriptDir/common-setup.sh + +docker run --rm -u $(id -u ${USER}):$(id -g ${USER}) --name custom-execution -p $ETH_PORT:$ETH_PORT -p $ENGINE_PORT:$ENGINE_PORT -v $currentDir/$DATA_DIR:/data $EL_BINARY_DIR --engine-rpc-enabled --rpc-http-enabled --rpc-http-api ADMIN,ETH,MINER,NET --rpc-http-port $ETH_PORT --engine-rpc-port $ENGINE_PORT --engine-jwt-secret /data/jwtsecret --data-path /data/besu --data-storage-format BONSAI --genesis-file /data/genesis.json \ No newline at end of file diff --git a/packages/beacon-node/test/sim/electra-interop.test.ts b/packages/beacon-node/test/sim/electra-interop.test.ts new file mode 100644 index 000000000000..c1663211cbd9 --- /dev/null +++ b/packages/beacon-node/test/sim/electra-interop.test.ts @@ -0,0 +1,457 @@ +import fs from "node:fs"; +import {describe, it, vi, afterAll, afterEach} from "vitest"; +/* eslint-disable @typescript-eslint/naming-convention */ +import _ from "lodash"; +import {LogLevel, sleep} from "@lodestar/utils"; +import {ForkName, SLOTS_PER_EPOCH, UNSET_DEPOSIT_RECEIPTS_START_INDEX} from "@lodestar/params"; +import {electra, Epoch, Slot} from "@lodestar/types"; +import {ValidatorProposerConfig} from "@lodestar/validator"; + +import {ChainConfig} from "@lodestar/config"; +import {TimestampFormatCode} from "@lodestar/logger"; +import {CachedBeaconStateElectra} from "@lodestar/state-transition"; +import {initializeExecutionEngine} from "../../src/execution/index.js"; +import {ExecutionPayloadStatus, PayloadAttributes} from "../../src/execution/engine/interface.js"; + +import {testLogger, TestLoggerOpts} from "../utils/logger.js"; +import {runEL, ELStartMode, ELClient, sendRawTransactionBig} from "../utils/runEl.js"; +import {defaultExecutionEngineHttpOpts} from "../../src/execution/engine/http.js"; +import {getDevBeaconNode} from "../utils/node/beacon.js"; +import {BeaconRestApiServerOpts} from "../../src/api/index.js"; +import {simTestInfoTracker} from "../utils/node/simTest.js"; +import {getAndInitDevValidators} from "../utils/node/validator.js"; +import {ClockEvent} from "../../src/util/clock.js"; +import {dataToBytes} from "../../src/eth1/provider/utils.js"; +import {bytesToData} from "../../lib/eth1/provider/utils.js"; +import {BeaconNode} from "../../src/index.js"; +import {logFilesDir} from "./params.js"; +import {shell} from "./shell.js"; + +// NOTE: How to run +// DEV_RUN=true EL_BINARY_DIR=naviechan/besu:v6110 EL_SCRIPT_DIR=besudocker yarn vitest --run test/sim/electra-interop.test.ts +// or +// DEV_RUN=true EL_BINARY_DIR=/Volumes/fast_boi/navie/Documents/workspace/besu/build/install/besu/bin EL_SCRIPT_DIR=besu yarn vitest --run test/sim/electra-interop.test.ts +// ``` + +/* eslint-disable no-console, @typescript-eslint/naming-convention */ + +const jwtSecretHex = "0xdc6457099f127cf0bac78de8b297df04951281909db4f58b43def7c7151e765d"; +const retries = defaultExecutionEngineHttpOpts.retries; +const retryDelay = defaultExecutionEngineHttpOpts.retryDelay; +describe("executionEngine / ExecutionEngineHttp", function () { + if (!process.env.EL_BINARY_DIR || !process.env.EL_SCRIPT_DIR) { + throw Error( + `EL ENV must be provided, EL_BINARY_DIR: ${process.env.EL_BINARY_DIR}, EL_SCRIPT_DIR: ${process.env.EL_SCRIPT_DIR}` + ); + } + vi.setConfig({testTimeout: 1000 * 60 * 10, hookTimeout: 1000 * 60 * 10}); + + const dataPath = fs.mkdtempSync("lodestar-test-electra"); + const elSetupConfig = { + elScriptDir: process.env.EL_SCRIPT_DIR, + elBinaryDir: process.env.EL_BINARY_DIR, + }; + const elRunOptions = { + dataPath, + jwtSecretHex, + enginePort: parseInt(process.env.ENGINE_PORT ?? "8551"), + ethPort: parseInt(process.env.ETH_PORT ?? "8545"), + }; + + const controller = new AbortController(); + afterAll(async () => { + controller?.abort(); + await shell(`sudo rm -rf ${dataPath}`); + }); + + const afterEachCallbacks: (() => Promise | void)[] = []; + afterEach(async () => { + while (afterEachCallbacks.length > 0) { + const callback = afterEachCallbacks.pop(); + if (callback) await callback(); + } + }); + + it("Send and get payloads with depositReceipts to/from EL", async () => { + const {elClient, tearDownCallBack} = await runEL( + {...elSetupConfig, mode: ELStartMode.PostMerge, genesisTemplate: "electra.tmpl"}, + {...elRunOptions, ttd: BigInt(0)}, + controller.signal + ); + afterEachCallbacks.push(() => tearDownCallBack()); + const {genesisBlockHash, engineRpcUrl, ethRpcUrl} = elClient; + console.log({genesisBlockHash}); + + const loggerExecutionEngine = testLogger("executionEngine"); + + const executionEngine = initializeExecutionEngine( + {mode: "http", urls: [engineRpcUrl], jwtSecretHex, retries, retryDelay}, + {signal: controller.signal, logger: loggerExecutionEngine} + ); + + // 1. Prepare payload + const preparePayloadParams: PayloadAttributes = { + // Note: this is created with a pre-defined genesis.json + timestamp: 10, + prevRandao: dataToBytes("0x0000000000000000000000000000000000000000000000000000000000000000", 32), + suggestedFeeRecipient: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + withdrawals: [], + parentBeaconBlockRoot: dataToBytes("0x0000000000000000000000000000000000000000000000000000000000000000", 32), + }; + const payloadId = await executionEngine.notifyForkchoiceUpdate( + ForkName.electra, + genesisBlockHash, + //use finalizedBlockHash as safeBlockHash + genesisBlockHash, + genesisBlockHash, + preparePayloadParams + ); + if (!payloadId) throw Error("InvalidPayloadId"); + + // 2. Send raw deposit transaction A and B. tx A is to be imported via newPayload, tx B is to be included in payload via getPayload + const depositTransactionA = + "0x02f9021c8217de808459682f008459682f0e830271009442424242424242424242424242424242424242428901bc16d674ec800000b901a422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120749715de5d1226545c6b3790f515d551a5cc5bf1d49c87a696860554d2fc4f14000000000000000000000000000000000000000000000000000000000000003096a96086cff07df17668f35f7418ef8798079167e3f4f9b72ecde17b28226137cf454ab1dd20ef5d924786ab3483c2f9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020003f5102dabe0a27b1746098d1dc17a5d3fbd478759fea9287e4e419b3c3cef20000000000000000000000000000000000000000000000000000000000000060b1acdb2c4d3df3f1b8d3bfd33421660df358d84d78d16c4603551935f4b67643373e7eb63dcb16ec359be0ec41fee33b03a16e80745f2374ff1d3c352508ac5d857c6476d3c3bcf7e6ca37427c9209f17be3af5264c0e2132b3dd1156c28b4e9c080a09f597089338d7f44f5c59f8230bb38f243849228a8d4e9d2e2956e6050f5b2c7a076486996c7e62802b8f95eee114783e4b403fd11093ba96286ff42c595f24452"; + const depositReceiptA = { + amount: 32000000000, + index: 0, + pubkey: dataToBytes( + "0x96a96086cff07df17668f35f7418ef8798079167e3f4f9b72ecde17b28226137cf454ab1dd20ef5d924786ab3483c2f9", + 48 + ), + signature: dataToBytes( + "0xb1acdb2c4d3df3f1b8d3bfd33421660df358d84d78d16c4603551935f4b67643373e7eb63dcb16ec359be0ec41fee33b03a16e80745f2374ff1d3c352508ac5d857c6476d3c3bcf7e6ca37427c9209f17be3af5264c0e2132b3dd1156c28b4e9", + 96 + ), + withdrawalCredentials: dataToBytes("0x003f5102dabe0a27b1746098d1dc17a5d3fbd478759fea9287e4e419b3c3cef2", 32), + }; + + const depositTransactionB = + "0x02f9021c8217de018459682f008459682f0e830271009442424242424242424242424242424242424242428901bc16d674ec800000b901a422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120a18b4c7cab0afa273ea9504904521ea8421a4e32740b7611bd3d5095ca99f0cb0000000000000000000000000000000000000000000000000000000000000030a5c85a60ba2905c215f6a12872e62b1ee037051364244043a5f639aa81b04a204c55e7cc851f29c7c183be253ea1510b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020001db70c485b6264692f26b8aeaab5b0c384180df8e2184a21a808a3ec8e86ca00000000000000000000000000000000000000000000000000000000000000609561731785b48cf1886412234531e4940064584463e96ac63a1a154320227e333fb51addc4a89b7e0d3f862d7c1fd4ea03bd8eb3d8806f1e7daf591cbbbb92b0beb74d13c01617f22c5026b4f9f9f294a8a7c32db895de3b01bee0132c9209e1c001a0644e0a763a34b4bfb9f56a677857b57fcf15e3db57e2f57060e92084f75f3d82a018ba8eaacbd8e6f6917675b1d0362b12ca82850ca8ef9c010430760c2b2e0cb5"; + const depositReceiptB = { + amount: 32000000000, + index: 1, + pubkey: dataToBytes( + "0xa5c85a60ba2905c215f6a12872e62b1ee037051364244043a5f639aa81b04a204c55e7cc851f29c7c183be253ea1510b", + 48 + ), + signature: dataToBytes( + "0x9561731785b48cf1886412234531e4940064584463e96ac63a1a154320227e333fb51addc4a89b7e0d3f862d7c1fd4ea03bd8eb3d8806f1e7daf591cbbbb92b0beb74d13c01617f22c5026b4f9f9f294a8a7c32db895de3b01bee0132c9209e1", + 96 + ), + withdrawalCredentials: dataToBytes("0x001db70c485b6264692f26b8aeaab5b0c384180df8e2184a21a808a3ec8e86ca", 32), + }; + + sendRawTransactionBig(ethRpcUrl, depositTransactionA, `${dataPath}/deposit.json`).catch((e: Error) => { + loggerExecutionEngine.error("Fail to send raw deposit transaction A", undefined, e); + }); + + sendRawTransactionBig(ethRpcUrl, depositTransactionB, `${dataPath}/deposit.json`).catch((e: Error) => { + loggerExecutionEngine.error("Fail to send raw deposit transaction B", undefined, e); + }); + + // 3. Import new payload with tx A and deposit receipt A + const newPayloadBlockHash = "0xfd1189e6ea0814b7d40d4e50b31ae5feabbb2acff39399457bbdda7cb5ccd490"; + const newPayload = { + parentHash: dataToBytes("0x26118cf71453320edcebbc4ebb34af5b578087a32385b80108bf691fa23efc42", 32), + feeRecipient: dataToBytes("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", 20), + stateRoot: dataToBytes("0x14208ac0e218167936e220b72d5d5887a963cb858ea2f2d268518f014a3da3fa", 32), + logsBloom: dataToBytes( + "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000", + 256 + ), + prevRandao: dataToBytes("0x0000000000000000000000000000000000000000000000000000000000000000", 32), + gasLimit: 30000000, + gasUsed: 84846, + timestamp: 16, + extraData: dataToBytes("0x", 0), + baseFeePerGas: 7n, + excessBlobGas: 0n, + transactions: [dataToBytes(depositTransactionA, null)], + withdrawals: [], + depositReceipts: [depositReceiptA], + blockNumber: 1, + blockHash: dataToBytes(newPayloadBlockHash, 32), + receiptsRoot: dataToBytes("0x79ee3424eb720a3ad4b1c5a372bb8160580cbe4d893778660f34213c685627a9", 32), + blobGasUsed: 0n, + }; + const parentBeaconBlockRoot = dataToBytes("0x0000000000000000000000000000000000000000000000000000000000000000", 32); + const payloadResult = await executionEngine.notifyNewPayload( + ForkName.electra, + newPayload, + [], + parentBeaconBlockRoot + ); + if (payloadResult.status !== ExecutionPayloadStatus.VALID) { + throw Error("getPayload returned payload that notifyNewPayload deems invalid"); + } + + // 4. Update fork choice + const preparePayloadParams2: PayloadAttributes = { + timestamp: 48, + prevRandao: dataToBytes("0x0000000000000000000000000000000000000000000000000000000000000000", 32), + suggestedFeeRecipient: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + withdrawals: [], + parentBeaconBlockRoot: dataToBytes("0x0000000000000000000000000000000000000000000000000000000000000000", 32), + }; + + const payloadId2 = await executionEngine.notifyForkchoiceUpdate( + ForkName.electra, + newPayloadBlockHash, + //use finalizedBlockHash as safeBlockHash + newPayloadBlockHash, + newPayloadBlockHash, + preparePayloadParams2 + ); + if (!payloadId2) throw Error("InvalidPayloadId"); + + // 5. Get the payload. Check depositReceipts field contains deposit + // Wait a bit first for besu to pick up tx from the tx pool. + await sleep(1000); + const payloadAndBlockValue = await executionEngine.getPayload(ForkName.electra, payloadId2); + const payload = payloadAndBlockValue.executionPayload as electra.ExecutionPayload; + + if (payload.transactions.length !== 1) { + throw Error(`Number of transactions mismatched. Expected: 1, actual: ${payload.transactions.length}`); + } else { + const actualTransaction = bytesToData(payload.transactions[0]); + + if (actualTransaction !== depositTransactionB) { + throw Error(`Transaction mismatched. Expected: ${depositTransactionB}, actual: ${actualTransaction}`); + } + } + + if (payload.depositReceipts.length !== 1) { + throw Error(`Number of depositReceipts mismatched. Expected: 1, actual: ${payload.depositReceipts.length}`); + } + + const actualDepositReceipt = payload.depositReceipts[0]; + if (!_.isEqual(actualDepositReceipt, depositReceiptB)) { + throw Error( + `Deposit receipts mismatched. Expected: ${JSON.stringify(depositReceiptB)}, actual: ${JSON.stringify( + actualDepositReceipt + )}` + ); + } + }); + + it("Post-merge, run for a few blocks", async function () { + console.log("\n\nPost-merge, run for a few blocks\n\n"); + const {elClient, tearDownCallBack} = await runEL( + {...elSetupConfig, mode: ELStartMode.PostMerge, genesisTemplate: "electra.tmpl"}, + {...elRunOptions, ttd: BigInt(0)}, + controller.signal + ); + afterEachCallbacks.push(() => tearDownCallBack()); + + await runNodeWithEL({ + elClient, + electraEpoch: 0, + testName: "post-merge", + }); + }); + + /** + * Want to test two things: + * 1) Send two raw deposit transactions, and see if two new validators with corrent balances show up in the state.validators and unfinalized cache + * 2) Upon state-transition, see if the two new validators move from unfinalized cache to finalized cache + */ + async function runNodeWithEL({ + elClient, + electraEpoch, + testName, + }: { + elClient: ELClient; + electraEpoch: Epoch; + testName: string; + }): Promise { + const {genesisBlockHash, ttd, engineRpcUrl, ethRpcUrl} = elClient; + const validatorClientCount = 1; + const validatorsPerClient = 32; + + const testParams: Pick = { + SECONDS_PER_SLOT: 2, + }; + + // Just enough to have a checkpoint finalized + const expectedEpochsToFinish = 4; + // 1 epoch of margin of error + const epochsOfMargin = 1; + const timeoutSetupMargin = 30 * 1000; // Give extra 30 seconds of margin + + // delay a bit so regular sync sees it's up to date and sync is completed from the beginning + const genesisSlotsDelay = 8; + + const timeout = + ((epochsOfMargin + expectedEpochsToFinish) * SLOTS_PER_EPOCH + genesisSlotsDelay) * + testParams.SECONDS_PER_SLOT * + 1000; + + vi.setConfig({testTimeout: timeout + 2 * timeoutSetupMargin}); + + const genesisTime = Math.floor(Date.now() / 1000) + genesisSlotsDelay * testParams.SECONDS_PER_SLOT; + + const testLoggerOpts: TestLoggerOpts = { + level: LogLevel.info, + file: { + filepath: `${logFilesDir}/mergemock-${testName}.log`, + level: LogLevel.debug, + }, + timestampFormat: { + format: TimestampFormatCode.EpochSlot, + genesisTime, + slotsPerEpoch: SLOTS_PER_EPOCH, + secondsPerSlot: testParams.SECONDS_PER_SLOT, + }, + }; + const loggerNodeA = testLogger("Node-A", testLoggerOpts); + + const bn = await getDevBeaconNode({ + params: { + ...testParams, + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: electraEpoch, + TERMINAL_TOTAL_DIFFICULTY: ttd, + }, + options: { + api: {rest: {enabled: true} as BeaconRestApiServerOpts}, + sync: {isSingleNode: true}, + network: {allowPublishToZeroPeers: true, discv5: null}, + // Now eth deposit/merge tracker methods directly available on engine endpoints + eth1: {enabled: false, providerUrls: [engineRpcUrl], jwtSecretHex}, + executionEngine: {urls: [engineRpcUrl], jwtSecretHex}, + chain: {suggestedFeeRecipient: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}, + }, + validatorCount: validatorClientCount * validatorsPerClient, + logger: loggerNodeA, + genesisTime, + eth1BlockHash: dataToBytes(genesisBlockHash, 32), + withEth1Credentials: true, + }); + + afterEachCallbacks.push(async function () { + await bn.close(); + await sleep(1000); + }); + + const stopInfoTracker = simTestInfoTracker(bn, loggerNodeA); + const valProposerConfig = { + defaultConfig: { + feeRecipient: "0xcccccccccccccccccccccccccccccccccccccccc", + }, + } as ValidatorProposerConfig; + + const {validators} = await getAndInitDevValidators({ + node: bn, + logPrefix: "Node-A", + validatorsPerClient, + validatorClientCount, + startIndex: 0, + // At least one sim test must use the REST API for beacon <-> validator comms + useRestApi: true, + testLoggerOpts, + valProposerConfig, + }); + + afterEachCallbacks.push(async function () { + await Promise.all(validators.map((v) => v.close())); + }); + + await waitForSlot(bn, 1); + + // send raw tx at slot 1 + const depositTransaction = + "0x02f9021e8217de8085012a05f20085019254d380830271009442424242424242424242424242424242424242428901bc16d674ec800000b901a422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120ef950826b191ebea0bbafa92a2c6bffa8239c6f456d92891ce2852b8360f0d30000000000000000000000000000000000000000000000000000000000000003095e4f91aea91a9e00387fad9d60997cff6cbf68d42d1b6629a7b248cdef255f94a2a2381e5d4125273fe42da5f7aa0e1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020003f5102dabe0a27b1746098d1dc17a5d3fbd478759fea9287e4e419b3c3cef20000000000000000000000000000000000000000000000000000000000000060b6c06e65228046268aa918baf78e072c25e65aa0bcf258cefcac3371c47df81bc4d43ca942f5fc28f9a563e925fd9c5010bc8c300add3faf3af0d61fabaaf03694020feaafb03e47c1bc4fcf082684c7ed3f7d5839d1722214b24f95ad2b226cc080a0be1161617492e4ca2fcb89edcadf5e71e8cac0d6447d18cfde9b55e5a8412417a07ec8c47dd484036c745049bb2e2980d44e38d4dacac50dc4a14a2f23c52f2e5f"; + sendRawTransactionBig(ethRpcUrl, depositTransaction, `${dataPath}/deposit.json`).catch((e: Error) => { + loggerNodeA.error("Fail to send raw deposit transaction", undefined, e); + }); + + await waitForSlot(bn, 5); + // Expect new validator to be in unfinalized cache, in state.validators and not in finalized cache + let headState = bn.chain.getHeadState(); + let epochCtx = headState.epochCtx; + if (headState.validators.length !== 33 || headState.balances.length !== 33) { + throw Error("New validator is not reflected in the beacon state at slot 5"); + } + if (epochCtx.index2pubkey.length !== 32 || epochCtx.pubkey2index.size !== 32) { + throw Error("Finalized cache is modified."); + } + if (epochCtx.unfinalizedPubkey2index.size !== 1) { + throw Error( + `Unfinalized cache is missing the expected validator. Size: ${epochCtx.unfinalizedPubkey2index.size}` + ); + } + // validator count at epoch 1 should be empty at this point since no epoch transition has happened. + if (epochCtx.getValidatorCountAtEpoch(1) !== undefined) { + throw Error("Historical validator lengths is modified"); + } + + await new Promise((resolve, _reject) => { + bn.chain.clock.on(ClockEvent.epoch, (epoch) => { + // Resolve only if the finalized checkpoint includes execution payload + if (epoch >= expectedEpochsToFinish) { + console.log("\nGot event epoch, stopping validators and nodes\n"); + resolve(); + } + }); + }); + + // Stop chain and un-subscribe events so the execution engine won't update it's head + // Allow some time to broadcast finalized events and complete the importBlock routine + await Promise.all(validators.map((v) => v.close())); + await bn.close(); + await sleep(500); + + // Check if new validator is in finalized cache + headState = bn.chain.getHeadState() as CachedBeaconStateElectra; + epochCtx = headState.epochCtx; + + if (headState.validators.length !== 33 || headState.balances.length !== 33) { + throw Error("New validator is not reflected in the beacon state."); + } + if (epochCtx.index2pubkey.length !== 33 || epochCtx.pubkey2index.size !== 33) { + throw Error("New validator is not in finalized cache"); + } + if (!epochCtx.unfinalizedPubkey2index.isEmpty()) { + throw Error("Unfinalized cache still contains new validator"); + } + // After 4 epochs, headState's finalized cp epoch should be 2 + // epochCtx should only have validator count for epoch 3 and 4. + if (epochCtx.getValidatorCountAtEpoch(4) === undefined || epochCtx.getValidatorCountAtEpoch(3) === undefined) { + throw Error("Missing historical validator length for epoch 3 or 4"); + } + + if (epochCtx.getValidatorCountAtEpoch(4) !== 33 || epochCtx.getValidatorCountAtEpoch(3) !== 33) { + throw Error("Incorrect historical validator length for epoch 3 or 4"); + } + + if (epochCtx.getValidatorCountAtEpoch(2) !== undefined || epochCtx.getValidatorCountAtEpoch(1) !== undefined) { + throw Error("Historical validator length for epoch 1 or 2 is not dropped properly"); + } + + if (headState.depositReceiptsStartIndex === UNSET_DEPOSIT_RECEIPTS_START_INDEX) { + throw Error("state.depositReceiptsStartIndex is not set upon processing new deposit receipt"); + } + + // wait for 1 slot to print current epoch stats + await sleep(1 * bn.config.SECONDS_PER_SLOT * 1000); + stopInfoTracker(); + console.log("\n\nDone\n\n"); + } +}); + +async function waitForSlot(bn: BeaconNode, targetSlot: Slot): Promise { + await new Promise((resolve, reject) => { + bn.chain.clock.on(ClockEvent.slot, (currentSlot) => { + if (currentSlot === targetSlot) { + resolve(); + return; + } + if (currentSlot > targetSlot) { + reject(Error(`Beacon node has passed target slot ${targetSlot}. Current slot ${currentSlot}`)); + } + }); + }); +} diff --git a/packages/beacon-node/test/spec/presets/fork.test.ts b/packages/beacon-node/test/spec/presets/fork.test.ts index c880d24bbbe3..c121e651fcea 100644 --- a/packages/beacon-node/test/spec/presets/fork.test.ts +++ b/packages/beacon-node/test/spec/presets/fork.test.ts @@ -5,6 +5,7 @@ import { CachedBeaconStateAltair, CachedBeaconStatePhase0, CachedBeaconStateCapella, + CachedBeaconStateDeneb, } from "@lodestar/state-transition"; import * as slotFns from "@lodestar/state-transition/slot"; import {phase0, ssz} from "@lodestar/types"; @@ -36,7 +37,7 @@ const fork: TestRunnerFn = (forkNext) => { case ForkName.deneb: return slotFns.upgradeStateToDeneb(preState as CachedBeaconStateCapella); case ForkName.electra: - throw Error("not Implemented"); + return slotFns.upgradeStateToElectra(preState as CachedBeaconStateDeneb); } }, options: { diff --git a/packages/beacon-node/test/spec/presets/genesis.test.ts b/packages/beacon-node/test/spec/presets/genesis.test.ts index ba3351a2103c..aa052c52a270 100644 --- a/packages/beacon-node/test/spec/presets/genesis.test.ts +++ b/packages/beacon-node/test/spec/presets/genesis.test.ts @@ -1,6 +1,6 @@ import path from "node:path"; import {expect} from "vitest"; -import {phase0, Root, ssz, TimeSeconds, allForks, deneb} from "@lodestar/types"; +import {phase0, Root, ssz, TimeSeconds, allForks, electra} from "@lodestar/types"; import {InputType} from "@lodestar/spec-test-util"; import { BeaconStateAllForks, @@ -60,7 +60,7 @@ const genesisInitialization: TestRunnerFn> = blockFns.processDeposit(fork, state, testCase.deposit); }, + deposit_receipt: (state, testCase: {deposit_receipt: electra.DepositReceipt}) => { + const fork = state.config.getForkSeq(state.slot); + blockFns.processDepositReceipt(fork, state as CachedBeaconStateElectra, testCase.deposit_receipt); + }, + proposer_slashing: (state, testCase: {proposer_slashing: phase0.ProposerSlashing}) => { const fork = state.config.getForkSeq(state.slot); blockFns.processProposerSlashing(fork, state, testCase.proposer_slashing); @@ -121,6 +127,7 @@ const operations: TestRunnerFn = (fork, block: ssz[fork].BeaconBlock, body: ssz[fork].BeaconBlockBody, deposit: ssz.phase0.Deposit, + deposit_receipt: ssz.electra.DepositReceipt, proposer_slashing: ssz.phase0.ProposerSlashing, voluntary_exit: ssz.phase0.SignedVoluntaryExit, // Altair diff --git a/packages/beacon-node/test/spec/presets/ssz_static.test.ts b/packages/beacon-node/test/spec/presets/ssz_static.test.ts index bcab25acde4c..c77a3377c433 100644 --- a/packages/beacon-node/test/spec/presets/ssz_static.test.ts +++ b/packages/beacon-node/test/spec/presets/ssz_static.test.ts @@ -48,6 +48,7 @@ const sszStatic = // will help us get the right type for lightclient objects ((ssz.allForksLightClient[fork as ForkLightClient] || {}) as Types)[typeName] || (ssz[fork] as Types)[typeName] || + (ssz.deneb as Types)[typeName] || (ssz.capella as Types)[typeName] || (ssz.bellatrix as Types)[typeName] || (ssz.altair as Types)[typeName] || diff --git a/packages/beacon-node/test/spec/utils/specTestIterator.ts b/packages/beacon-node/test/spec/utils/specTestIterator.ts index 6aa683cb6530..3201fb15dbf3 100644 --- a/packages/beacon-node/test/spec/utils/specTestIterator.ts +++ b/packages/beacon-node/test/spec/utils/specTestIterator.ts @@ -57,7 +57,6 @@ const coveredTestRunners = [ // ], // ``` export const defaultSkipOpts: SkipOpts = { - skippedForks: ["eip6110"], // TODO: capella // BeaconBlockBody proof in lightclient is the new addition in v1.3.0-rc.2-hotfix // Skip them for now to enable subsequently diff --git a/packages/beacon-node/test/unit/chain/lightclient/upgradeLightClientHeader.test.ts b/packages/beacon-node/test/unit/chain/lightclient/upgradeLightClientHeader.test.ts index 9df60a39af4d..8a33beffe21e 100644 --- a/packages/beacon-node/test/unit/chain/lightclient/upgradeLightClientHeader.test.ts +++ b/packages/beacon-node/test/unit/chain/lightclient/upgradeLightClientHeader.test.ts @@ -15,7 +15,7 @@ describe("UpgradeLightClientHeader", function () { BELLATRIX_FORK_EPOCH: 2, CAPELLA_FORK_EPOCH: 3, DENEB_FORK_EPOCH: 4, - ELECTRA_FORK_EPOCH: Infinity, + ELECTRA_FORK_EPOCH: 5, }); const genesisValidatorsRoot = Buffer.alloc(32, 0xaa); @@ -37,18 +37,12 @@ describe("UpgradeLightClientHeader", function () { bellatrix: 17, capella: 25, deneb: 33, - electra: 0, + electra: 41, }; }); - // Since electra is not implemented for loop is till deneb (Object.values(ForkName).length-1) - // Once electra is implemnted run for loop till Object.values(ForkName).length - - // for (let i = ForkSeq.altair; i < Object.values(ForkName).length; i++) { - // for (let j = i + 1; j < Object.values(ForkName).length; j++) { - - for (let i = ForkSeq.altair; i < Object.values(ForkName).length - 1; i++) { - for (let j = i + 1; j < Object.values(ForkName).length - 1; j++) { + for (let i = ForkSeq.altair; i < Object.values(ForkName).length; i++) { + for (let j = i + 1; j < Object.values(ForkName).length; j++) { const fromFork = ForkName[ForkSeq[i] as ForkName]; const toFork = ForkName[ForkSeq[j] as ForkName]; @@ -62,27 +56,7 @@ describe("UpgradeLightClientHeader", function () { } } - // for electra not implemented for (let i = ForkSeq.altair; i < Object.values(ForkName).length; i++) { - const fromFork = ForkName[ForkSeq[i] as ForkName]; - const toFork = ForkName["electra"]; - - it(`Throw error ${fromFork}=>${toFork}`, function () { - lcHeaderByFork[fromFork].beacon.slot = testSlots[fromFork]; - lcHeaderByFork[toFork].beacon.slot = testSlots[fromFork]; - - expect(() => { - upgradeLightClientHeader(config, toFork, lcHeaderByFork[fromFork]); - }).toThrow("Not Implemented"); - }); - } - - // Since electra is not implemented for loop is till deneb (Object.values(ForkName).length-1) - // Once electra is implemnted run for loop till Object.values(ForkName).length - - // for (let i = ForkSeq.altair; i < Object.values(ForkName).length; i++) { - - for (let i = ForkSeq.altair; i < Object.values(ForkName).length - 1; i++) { for (let j = i; j > 0; j--) { const fromFork = ForkName[ForkSeq[i] as ForkName]; const toFork = ForkName[ForkSeq[j] as ForkName]; diff --git a/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts b/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts index 3c248ad4d194..71dfd63830c1 100644 --- a/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts +++ b/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts @@ -62,9 +62,9 @@ describe("AggregatedAttestationPool", function () { epochParticipation[committee[i]] = 0b000; } } - (originalState as CachedBeaconStateAltair).previousEpochParticipation = + (originalState as unknown as CachedBeaconStateAltair).previousEpochParticipation = ssz.altair.EpochParticipation.toViewDU(epochParticipation); - (originalState as CachedBeaconStateAltair).currentEpochParticipation = + (originalState as unknown as CachedBeaconStateAltair).currentEpochParticipation = ssz.altair.EpochParticipation.toViewDU(epochParticipation); originalState.commit(); let altairState: CachedBeaconStateAllForks; diff --git a/packages/beacon-node/test/unit/chain/shufflingCache.test.ts b/packages/beacon-node/test/unit/chain/shufflingCache.test.ts index 6295a993c072..e6a7e8706bbe 100644 --- a/packages/beacon-node/test/unit/chain/shufflingCache.test.ts +++ b/packages/beacon-node/test/unit/chain/shufflingCache.test.ts @@ -1,6 +1,6 @@ import {describe, it, expect, beforeEach} from "vitest"; -import {getShufflingDecisionBlock} from "@lodestar/state-transition"; +import {getShufflingDecisionBlock, CachedBeaconStateAllForks} from "@lodestar/state-transition"; // eslint-disable-next-line import/no-relative-packages import {generateTestCachedBeaconStateOnlyValidators} from "../../../../state-transition/test/perf/util.js"; import {ShufflingCache} from "../../../src/chain/shufflingCache.js"; @@ -14,7 +14,7 @@ describe("ShufflingCache", function () { beforeEach(() => { shufflingCache = new ShufflingCache(null, {maxShufflingCacheEpochs: 1}); - shufflingCache.processState(state, currentEpoch); + shufflingCache.processState(state as unknown as CachedBeaconStateAllForks, currentEpoch); }); it("should get shuffling from cache", async function () { @@ -29,7 +29,7 @@ describe("ShufflingCache", function () { shufflingCache.insertPromise(currentEpoch, "0x00"); expect(await shufflingCache.get(currentEpoch, decisionRoot)).toEqual(state.epochCtx.currentShuffling); // insert shufflings at other epochs does prune the cache - shufflingCache.processState(state, currentEpoch + 1); + shufflingCache.processState(state as unknown as CachedBeaconStateAllForks, currentEpoch + 1); // the current shuffling is not available anymore expect(await shufflingCache.get(currentEpoch, decisionRoot)).toBeNull(); }); @@ -39,7 +39,7 @@ describe("ShufflingCache", function () { shufflingCache.insertPromise(currentEpoch + 1, nextDecisionRoot); const shufflingRequest0 = shufflingCache.get(currentEpoch + 1, nextDecisionRoot); const shufflingRequest1 = shufflingCache.get(currentEpoch + 1, nextDecisionRoot); - shufflingCache.processState(state, currentEpoch + 1); + shufflingCache.processState(state as unknown as CachedBeaconStateAllForks, currentEpoch + 1); expect(await shufflingRequest0).toEqual(state.epochCtx.nextShuffling); expect(await shufflingRequest1).toEqual(state.epochCtx.nextShuffling); }); diff --git a/packages/beacon-node/test/unit/chain/stateCache/fifoBlockStateCache.test.ts b/packages/beacon-node/test/unit/chain/stateCache/fifoBlockStateCache.test.ts index 994cf3f7c085..3881f2813dbf 100644 --- a/packages/beacon-node/test/unit/chain/stateCache/fifoBlockStateCache.test.ts +++ b/packages/beacon-node/test/unit/chain/stateCache/fifoBlockStateCache.test.ts @@ -1,8 +1,7 @@ import {describe, it, expect, beforeEach} from "vitest"; import {toHexString} from "@chainsafe/ssz"; -import {EpochShuffling} from "@lodestar/state-transition"; +import {EpochShuffling, CachedBeaconStateAllForks} from "@lodestar/state-transition"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; -import {CachedBeaconStateAllForks} from "@lodestar/state-transition/src/types.js"; import {FIFOBlockStateCache} from "../../../../src/chain/stateCache/index.js"; import {generateCachedState} from "../../../utils/state.js"; diff --git a/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts b/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts index ce0d7fae1fad..6cc3ae8f1ce0 100644 --- a/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts +++ b/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts @@ -1,13 +1,15 @@ import {describe, it, expect} from "vitest"; import {phase0, ssz} from "@lodestar/types"; -import {MAX_DEPOSITS} from "@lodestar/params"; +import {MAX_DEPOSITS, SLOTS_PER_EPOCH} from "@lodestar/params"; import {verifyMerkleBranch} from "@lodestar/utils"; +import {createChainForkConfig} from "@lodestar/config"; import {filterBy} from "../../../utils/db.js"; import {Eth1ErrorCode} from "../../../../src/eth1/errors.js"; import {generateState} from "../../../utils/state.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; import {getDeposits, getDepositsWithProofs, DepositGetter} from "../../../../src/eth1/utils/deposits.js"; import {DepositTree} from "../../../../src/db/repositories/depositDataRoot.js"; +import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; describe("eth1 / util / deposits", function () { describe("getDeposits", () => { @@ -18,6 +20,7 @@ describe("eth1 / util / deposits", function () { depositIndexes: number[]; expectedReturnedIndexes?: number[]; error?: Eth1ErrorCode; + postElectra?: boolean; }; const testCases: TestCase[] = [ @@ -70,18 +73,59 @@ describe("eth1 / util / deposits", function () { depositIndexes: [], expectedReturnedIndexes: [], }, + { + id: "No deposits to be included post Electra after deposit_receipts_start_index", + depositCount: 2030, + eth1DepositIndex: 2025, + depositIndexes: Array.from({length: 2030}, (_, i) => i), + expectedReturnedIndexes: [], + postElectra: true, + }, + { + id: "Should return deposits post Electra before deposit_receipts_start_index", + depositCount: 2022, + eth1DepositIndex: 2018, + depositIndexes: Array.from({length: 2022}, (_, i) => i), + expectedReturnedIndexes: [2018, 2019, 2020, 2021], + postElectra: true, + }, + { + id: "Should return deposits less than MAX_DEPOSITS post Electra before deposit_receipts_start_index", + depositCount: 10 * MAX_DEPOSITS, + eth1DepositIndex: 0, + depositIndexes: Array.from({length: 10 * MAX_DEPOSITS}, (_, i) => i), + expectedReturnedIndexes: Array.from({length: MAX_DEPOSITS}, (_, i) => i), + postElectra: true, + }, ]; + /* eslint-disable @typescript-eslint/naming-convention */ + const postElectraConfig = createChainForkConfig({ + ALTAIR_FORK_EPOCH: 1, + BELLATRIX_FORK_EPOCH: 2, + CAPELLA_FORK_EPOCH: 3, + DENEB_FORK_EPOCH: 4, + ELECTRA_FORK_EPOCH: 5, + }); + const postElectraSlot = postElectraConfig.ELECTRA_FORK_EPOCH * SLOTS_PER_EPOCH + 1; + for (const testCase of testCases) { - const {id, depositIndexes, eth1DepositIndex, depositCount, expectedReturnedIndexes, error} = testCase; + const {id, depositIndexes, eth1DepositIndex, depositCount, expectedReturnedIndexes, error, postElectra} = + testCase; it(id, async function () { - const state = generateState({eth1DepositIndex}); + const state = postElectra + ? generateState({slot: postElectraSlot, eth1DepositIndex}, postElectraConfig) + : generateState({eth1DepositIndex}); + const cachedState = createCachedBeaconStateTest( + state, + postElectra ? postElectraConfig : createChainForkConfig({}) + ); const eth1Data = generateEth1Data(depositCount); const deposits = depositIndexes.map((index) => generateDepositEvent(index)); const depositsGetter: DepositGetter = async (indexRange) => filterBy(deposits, indexRange, (deposit) => deposit.index); - const resultPromise = getDeposits(state, eth1Data, depositsGetter); + const resultPromise = getDeposits(cachedState, eth1Data, depositsGetter); if (expectedReturnedIndexes) { const result = await resultPromise; diff --git a/packages/beacon-node/test/unit/executionEngine/http.test.ts b/packages/beacon-node/test/unit/executionEngine/http.test.ts index 27e9b86887ee..d4a7f3a476e9 100644 --- a/packages/beacon-node/test/unit/executionEngine/http.test.ts +++ b/packages/beacon-node/test/unit/executionEngine/http.test.ts @@ -188,6 +188,7 @@ describe("ExecutionEngine / http", () => { amount: "0x7b", }, ], + depositReceipts: null, // depositReceipts is null pre-electra }, null, // null returned for missing blocks { @@ -196,6 +197,7 @@ describe("ExecutionEngine / http", () => { "0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174", ], withdrawals: null, // withdrawals is null pre-capella + depositReceipts: null, // depositReceipts is null pre-electra }, ], }; @@ -243,6 +245,7 @@ describe("ExecutionEngine / http", () => { amount: "0x7b", }, ], + depositReceipts: null, // depositReceipts is null pre-electra }, null, // null returned for missing blocks { @@ -251,6 +254,7 @@ describe("ExecutionEngine / http", () => { "0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174", ], withdrawals: null, // withdrawals is null pre-capella + depositReceipts: null, // depositReceipts is null pre-electra }, ], }; diff --git a/packages/beacon-node/test/unit/network/beaconBlocksMaybeBlobsByRange.test.ts b/packages/beacon-node/test/unit/network/beaconBlocksMaybeBlobsByRange.test.ts index 56fb64104744..444f9063a8eb 100644 --- a/packages/beacon-node/test/unit/network/beaconBlocksMaybeBlobsByRange.test.ts +++ b/packages/beacon-node/test/unit/network/beaconBlocksMaybeBlobsByRange.test.ts @@ -23,6 +23,7 @@ describe("beaconBlocksMaybeBlobsByRange", () => { BELLATRIX_FORK_EPOCH: 0, CAPELLA_FORK_EPOCH: 0, DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: 0, }); const genesisValidatorsRoot = Buffer.alloc(32, 0xaa); const config = createBeaconConfig(chainConfig, genesisValidatorsRoot); diff --git a/packages/beacon-node/test/utils/state.ts b/packages/beacon-node/test/utils/state.ts index c9dc79d34ed0..3d7d8ee5abfe 100644 --- a/packages/beacon-node/test/utils/state.ts +++ b/packages/beacon-node/test/utils/state.ts @@ -7,8 +7,10 @@ import { PubkeyIndexMap, CachedBeaconStateBellatrix, BeaconStateBellatrix, + CachedBeaconStateElectra, + BeaconStateElectra, } from "@lodestar/state-transition"; -import {allForks, altair, bellatrix, ssz} from "@lodestar/types"; +import {allForks, altair, bellatrix, electra, ssz} from "@lodestar/types"; import {createBeaconConfig, ChainForkConfig} from "@lodestar/config"; import {FAR_FUTURE_EPOCH, ForkName, ForkSeq, MAX_EFFECTIVE_BALANCE, SYNC_COMMITTEE_SIZE} from "@lodestar/params"; @@ -64,6 +66,7 @@ export function generateState( : generateValidators(numValidators, validatorOpts)); state.genesisTime = Math.floor(Date.now() / 1000); + state.slot = stateSlot; state.fork.previousVersion = config.GENESIS_FORK_VERSION; state.fork.currentVersion = config.GENESIS_FORK_VERSION; state.latestBlockHeader.bodyRoot = ssz.phase0.BeaconBlockBody.hashTreeRoot(ssz.phase0.BeaconBlockBody.defaultValue()); @@ -92,6 +95,12 @@ export function generateState( }; } + if (forkSeq >= ForkSeq.electra) { + const stateElectra = state as electra.BeaconState; + stateElectra.depositReceiptsStartIndex = 2023n; + stateElectra.latestExecutionPayloadHeader = ssz.electra.ExecutionPayloadHeader.defaultValue(); + } + return config.getForkTypes(stateSlot).BeaconState.toViewDU(state); } @@ -137,6 +146,18 @@ export function generateCachedBellatrixState(opts?: TestBeaconState): CachedBeac }); } +/** + * This generates state with default pubkey + */ +export function generateCachedElectraState(opts?: TestBeaconState): CachedBeaconStateElectra { + const config = getConfig(ForkName.electra); + const state = generateState(opts, config); + return createCachedBeaconState(state as BeaconStateElectra, { + config: createBeaconConfig(config, state.genesisValidatorsRoot), + pubkey2index: new PubkeyIndexMap(), + index2pubkey: [], + }); +} export const zeroProtoBlock: ProtoBlock = { slot: 0, blockRoot: ZERO_HASH_HEX, diff --git a/packages/beacon-node/test/utils/validationData/attestation.ts b/packages/beacon-node/test/utils/validationData/attestation.ts index 24e599852d86..02d20fd8e5bb 100644 --- a/packages/beacon-node/test/utils/validationData/attestation.ts +++ b/packages/beacon-node/test/utils/validationData/attestation.ts @@ -4,6 +4,7 @@ import { computeSigningRoot, computeStartSlotAtEpoch, getShufflingDecisionBlock, + CachedBeaconStateAllForks, } from "@lodestar/state-transition"; import {ProtoBlock, IForkChoice, ExecutionStatus} from "@lodestar/fork-choice"; import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params"; @@ -81,8 +82,8 @@ export function getAttestationValidData(opts: AttestationValidDataOpts): { }; const shufflingCache = new ShufflingCache(); - shufflingCache.processState(state, state.epochCtx.currentShuffling.epoch); - shufflingCache.processState(state, state.epochCtx.nextShuffling.epoch); + shufflingCache.processState(state as unknown as CachedBeaconStateAllForks, state.epochCtx.currentShuffling.epoch); + shufflingCache.processState(state as unknown as CachedBeaconStateAllForks, state.epochCtx.nextShuffling.epoch); const dependentRoot = getShufflingDecisionBlock(state, state.epochCtx.currentShuffling.epoch); const forkChoice = { @@ -132,7 +133,7 @@ export function getAttestationValidData(opts: AttestationValidDataOpts): { getState: async () => state, // TODO: remove this once we have a better way to get state getStateSync: () => state, - } as Partial as IStateRegenerator; + } as unknown as Partial as IStateRegenerator; const chain = { clock, diff --git a/packages/config/src/chainConfig/configs/mainnet.ts b/packages/config/src/chainConfig/configs/mainnet.ts index 0de1bee666ec..6d1fd9b75952 100644 --- a/packages/config/src/chainConfig/configs/mainnet.ts +++ b/packages/config/src/chainConfig/configs/mainnet.ts @@ -49,7 +49,7 @@ export const chainConfig: ChainConfig = { DENEB_FORK_VERSION: b("0x04000000"), DENEB_FORK_EPOCH: 269568, // March 13, 2024, 01:55:35pm UTC - // Electra + // ELECTRA ELECTRA_FORK_VERSION: b("0x05000000"), ELECTRA_FORK_EPOCH: Infinity, diff --git a/packages/config/src/chainConfig/configs/minimal.ts b/packages/config/src/chainConfig/configs/minimal.ts index c99a76d1ee40..44a28ca36ec7 100644 --- a/packages/config/src/chainConfig/configs/minimal.ts +++ b/packages/config/src/chainConfig/configs/minimal.ts @@ -45,8 +45,7 @@ export const chainConfig: ChainConfig = { // Deneb DENEB_FORK_VERSION: b("0x04000001"), DENEB_FORK_EPOCH: Infinity, - - // Electra + // ELECTRA ELECTRA_FORK_VERSION: b("0x05000001"), ELECTRA_FORK_EPOCH: Infinity, diff --git a/packages/light-client/src/spec/utils.ts b/packages/light-client/src/spec/utils.ts index ed4b9e6e961d..8502cb6f5656 100644 --- a/packages/light-client/src/spec/utils.ts +++ b/packages/light-client/src/spec/utils.ts @@ -8,7 +8,7 @@ import { BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH, BLOCK_BODY_EXECUTION_PAYLOAD_INDEX as EXECUTION_PAYLOAD_INDEX, } from "@lodestar/params"; -import {altair, phase0, ssz, allForks, capella, deneb, Slot} from "@lodestar/types"; +import {altair, phase0, ssz, allForks, capella, deneb, Slot, electra} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {isValidMerkleBranch, computeEpochAtSlot, computeSyncPeriodAtSlot} from "../utils/index.js"; @@ -106,7 +106,11 @@ export function upgradeLightClientHeader( // eslint-disable-next-line no-fallthrough case ForkName.electra: - throw Error("Not Implemented"); + (upgradedHeader as electra.LightClientHeader).execution.depositReceiptsRoot = + ssz.electra.LightClientHeader.fields.execution.fields.depositReceiptsRoot.defaultValue(); + + // Break if no further upgrades is required else fall through + if (ForkSeq[targetFork] <= ForkSeq.electra) break; } return upgradedHeader; } @@ -140,6 +144,12 @@ export function isValidLightClientHeader(config: ChainForkConfig, header: allFor } } + if (epoch < config.ELECTRA_FORK_EPOCH) { + if ((header as electra.LightClientHeader).execution.depositReceiptsRoot !== undefined) { + return false; + } + } + return isValidMerkleBranch( config .getExecutionForkTypes(header.beacon.slot) diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 6a95e3ca632e..482e591123b6 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -93,6 +93,8 @@ export const { MAX_BLOB_COMMITMENTS_PER_BLOCK, MAX_BLOBS_PER_BLOCK, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, + + MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD, } = activePreset; //////////// @@ -244,3 +246,6 @@ export const KZG_COMMITMENT_SUBTREE_INDEX0 = KZG_COMMITMENT_GINDEX0 - 2 ** KZG_C // ssz.deneb.BlobSidecars.elementType.fixedSize export const BLOBSIDECAR_FIXED_SIZE = ACTIVE_PRESET === PresetName.minimal ? 131672 : 131928; + +// Electra Misc +export const UNSET_DEPOSIT_RECEIPTS_START_INDEX = 2n ** 64n - 1n; diff --git a/packages/params/src/presets/mainnet.ts b/packages/params/src/presets/mainnet.ts index 42a705a07f03..86f5c39c539e 100644 --- a/packages/params/src/presets/mainnet.ts +++ b/packages/params/src/presets/mainnet.ts @@ -118,4 +118,7 @@ export const mainnetPreset: BeaconPreset = { MAX_BLOB_COMMITMENTS_PER_BLOCK: 4096, MAX_BLOBS_PER_BLOCK: 6, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 17, + + // ELECTRA + MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192, }; diff --git a/packages/params/src/presets/minimal.ts b/packages/params/src/presets/minimal.ts index b940841a0429..6239a8c1a3eb 100644 --- a/packages/params/src/presets/minimal.ts +++ b/packages/params/src/presets/minimal.ts @@ -119,4 +119,7 @@ export const minimalPreset: BeaconPreset = { MAX_BLOB_COMMITMENTS_PER_BLOCK: 16, MAX_BLOBS_PER_BLOCK: 6, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: 9, + + // ELECTRA + MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4, }; diff --git a/packages/params/src/types.ts b/packages/params/src/types.ts index 3c5ba6381131..57ee8230a77d 100644 --- a/packages/params/src/types.ts +++ b/packages/params/src/types.ts @@ -82,6 +82,9 @@ export type BeaconPreset = { MAX_BLOB_COMMITMENTS_PER_BLOCK: number; MAX_BLOBS_PER_BLOCK: number; KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: number; + + // ELECTRA + MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: number; }; /** @@ -167,6 +170,9 @@ export const beaconPresetTypes: BeaconPresetTypes = { MAX_BLOB_COMMITMENTS_PER_BLOCK: "number", MAX_BLOBS_PER_BLOCK: "number", KZG_COMMITMENT_INCLUSION_PROOF_DEPTH: "number", + + // ELECTRA + MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: "number", }; type BeaconPresetTypes = { diff --git a/packages/state-transition/package.json b/packages/state-transition/package.json index b9e74baa1435..f05a3c021a9f 100644 --- a/packages/state-transition/package.json +++ b/packages/state-transition/package.json @@ -69,7 +69,8 @@ "@lodestar/types": "^1.17.0", "@lodestar/utils": "^1.17.0", "bigint-buffer": "^1.1.5", - "buffer-xor": "^2.0.2" + "buffer-xor": "^2.0.2", + "immutable": "^4.3.2" }, "devDependencies": { "@types/buffer-xor": "^2.0.0" diff --git a/packages/state-transition/src/block/processDeposit.ts b/packages/state-transition/src/block/processDeposit.ts index dae37d5d0afe..fbefafdc7fbf 100644 --- a/packages/state-transition/src/block/processDeposit.ts +++ b/packages/state-transition/src/block/processDeposit.ts @@ -1,6 +1,6 @@ import bls from "@chainsafe/bls"; import {CoordType} from "@chainsafe/bls/types"; -import {phase0, ssz} from "@lodestar/types"; +import {BLSPubkey, Bytes32, UintNum64, phase0, ssz} from "@lodestar/types"; import {verifyMerkleBranch} from "@lodestar/utils"; import { @@ -12,6 +12,8 @@ import { MAX_EFFECTIVE_BALANCE, } from "@lodestar/params"; +import {DepositData} from "@lodestar/types/lib/phase0/types.js"; +import {DepositReceipt} from "@lodestar/types/lib/electra/types.js"; import {ZERO_HASH} from "../constants/index.js"; import {computeDomain, computeSigningRoot, increaseBalance} from "../util/index.js"; import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "../types.js"; @@ -23,8 +25,6 @@ import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "../types.js"; * PERF: Work depends on number of Deposit per block. On regular networks the average is 0 / block. */ export function processDeposit(fork: ForkSeq, state: CachedBeaconStateAllForks, deposit: phase0.Deposit): void { - const {config, validators, epochCtx} = state; - // verify the merkle branch if ( !verifyMerkleBranch( @@ -41,15 +41,29 @@ export function processDeposit(fork: ForkSeq, state: CachedBeaconStateAllForks, // deposits must be processed in order state.eth1DepositIndex += 1; - const pubkey = deposit.data.pubkey; // Drop tree - const amount = deposit.data.amount; - const cachedIndex = epochCtx.pubkey2index.get(pubkey); + applyDeposit(fork, state, deposit.data); +} + +/** + * Adds a new validator into the registry. Or increase balance if already exist. + * Follows applyDeposit() in consensus spec. Will be used by processDeposit() and processDepositReceipt() + * + */ +export function applyDeposit( + fork: ForkSeq, + state: CachedBeaconStateAllForks, + deposit: DepositData | DepositReceipt +): void { + const {config, validators, epochCtx} = state; + const {pubkey, withdrawalCredentials, amount} = deposit; + + const cachedIndex = epochCtx.getValidatorIndex(pubkey); if (cachedIndex === undefined || !Number.isSafeInteger(cachedIndex) || cachedIndex >= validators.length) { // verify the deposit signature (proof of posession) which is not checked by the deposit contract const depositMessage = { - pubkey: deposit.data.pubkey, // Retain tree for hashing - withdrawalCredentials: deposit.data.withdrawalCredentials, // Retain tree for hashing - amount: deposit.data.amount, + pubkey, + withdrawalCredentials, + amount, }; // fork-agnostic domain since deposits are valid across forks const domain = computeDomain(DOMAIN_DEPOSIT, config.GENESIS_FORK_VERSION, ZERO_HASH); @@ -57,52 +71,62 @@ export function processDeposit(fork: ForkSeq, state: CachedBeaconStateAllForks, try { // Pubkeys must be checked for group + inf. This must be done only once when the validator deposit is processed const publicKey = bls.PublicKey.fromBytes(pubkey, CoordType.affine, true); - const signature = bls.Signature.fromBytes(deposit.data.signature, CoordType.affine, true); + const signature = bls.Signature.fromBytes(deposit.signature, CoordType.affine, true); if (!signature.verify(publicKey, signingRoot)) { return; } } catch (e) { return; // Catch all BLS errors: failed key validation, failed signature validation, invalid signature } + addValidatorToRegistry(fork, state, pubkey, withdrawalCredentials, amount); + } else { + // increase balance by deposit amount + increaseBalance(state, cachedIndex, amount); + } +} - // add validator and balance entries - const effectiveBalance = Math.min(amount - (amount % EFFECTIVE_BALANCE_INCREMENT), MAX_EFFECTIVE_BALANCE); - validators.push( - ssz.phase0.Validator.toViewDU({ - pubkey, - withdrawalCredentials: deposit.data.withdrawalCredentials, - activationEligibilityEpoch: FAR_FUTURE_EPOCH, - activationEpoch: FAR_FUTURE_EPOCH, - exitEpoch: FAR_FUTURE_EPOCH, - withdrawableEpoch: FAR_FUTURE_EPOCH, - effectiveBalance, - slashed: false, - }) - ); - state.balances.push(amount); +function addValidatorToRegistry( + fork: ForkSeq, + state: CachedBeaconStateAllForks, + pubkey: BLSPubkey, + withdrawalCredentials: Bytes32, + amount: UintNum64 +): void { + const {validators, epochCtx} = state; + // add validator and balance entries + const effectiveBalance = Math.min(amount - (amount % EFFECTIVE_BALANCE_INCREMENT), MAX_EFFECTIVE_BALANCE); + validators.push( + ssz.phase0.Validator.toViewDU({ + pubkey, + withdrawalCredentials, + activationEligibilityEpoch: FAR_FUTURE_EPOCH, + activationEpoch: FAR_FUTURE_EPOCH, + exitEpoch: FAR_FUTURE_EPOCH, + withdrawableEpoch: FAR_FUTURE_EPOCH, + effectiveBalance, + slashed: false, + }) + ); + state.balances.push(amount); - const validatorIndex = validators.length - 1; - // Updating here is better than updating at once on epoch transition - // - Simplify genesis fn applyDeposits(): effectiveBalanceIncrements is populated immediately - // - Keep related code together to reduce risk of breaking this cache - // - Should have equal performance since it sets a value in a flat array - epochCtx.effectiveBalanceIncrementsSet(validatorIndex, effectiveBalance); + const validatorIndex = validators.length - 1; + // Updating here is better than updating at once on epoch transition + // - Simplify genesis fn applyDeposits(): effectiveBalanceIncrements is populated immediately + // - Keep related code together to reduce risk of breaking this cache + // - Should have equal performance since it sets a value in a flat array + epochCtx.effectiveBalanceIncrementsSet(validatorIndex, effectiveBalance); - // now that there is a new validator, update the epoch context with the new pubkey - epochCtx.addPubkey(validatorIndex, pubkey); + // now that there is a new validator, update the epoch context with the new pubkey + epochCtx.addPubkey(validatorIndex, pubkey); - // Only after altair: - if (fork >= ForkSeq.altair) { - const stateAltair = state as CachedBeaconStateAltair; + // Only after altair: + if (fork >= ForkSeq.altair) { + const stateAltair = state as CachedBeaconStateAltair; - stateAltair.inactivityScores.push(0); + stateAltair.inactivityScores.push(0); - // add participation caches - stateAltair.previousEpochParticipation.push(0); - stateAltair.currentEpochParticipation.push(0); - } - } else { - // increase balance by deposit amount - increaseBalance(state, cachedIndex, amount); + // add participation caches + stateAltair.previousEpochParticipation.push(0); + stateAltair.currentEpochParticipation.push(0); } } diff --git a/packages/state-transition/src/block/processDepositReceipt.ts b/packages/state-transition/src/block/processDepositReceipt.ts new file mode 100644 index 000000000000..140e38d634fd --- /dev/null +++ b/packages/state-transition/src/block/processDepositReceipt.ts @@ -0,0 +1,17 @@ +import {electra} from "@lodestar/types"; +import {ForkSeq, UNSET_DEPOSIT_RECEIPTS_START_INDEX} from "@lodestar/params"; + +import {CachedBeaconStateElectra} from "../types.js"; +import {applyDeposit} from "./processDeposit.js"; + +export function processDepositReceipt( + fork: ForkSeq, + state: CachedBeaconStateElectra, + depositReceipt: electra.DepositReceipt +): void { + if (state.depositReceiptsStartIndex === UNSET_DEPOSIT_RECEIPTS_START_INDEX) { + state.depositReceiptsStartIndex = BigInt(depositReceipt.index); + } + + applyDeposit(fork, state, depositReceipt); +} diff --git a/packages/state-transition/src/block/processOperations.ts b/packages/state-transition/src/block/processOperations.ts index c523f1dad246..968182fdf8c5 100644 --- a/packages/state-transition/src/block/processOperations.ts +++ b/packages/state-transition/src/block/processOperations.ts @@ -1,13 +1,15 @@ -import {allForks, capella} from "@lodestar/types"; -import {ForkSeq, MAX_DEPOSITS} from "@lodestar/params"; +import {allForks, capella, electra} from "@lodestar/types"; +import {ForkSeq} from "@lodestar/params"; -import {CachedBeaconStateAllForks, CachedBeaconStateCapella} from "../types.js"; +import {CachedBeaconStateAllForks, CachedBeaconStateCapella, CachedBeaconStateElectra} from "../types.js"; +import {getEth1DepositCount} from "../util/deposit.js"; import {processAttestations} from "./processAttestations.js"; import {processProposerSlashing} from "./processProposerSlashing.js"; import {processAttesterSlashing} from "./processAttesterSlashing.js"; import {processDeposit} from "./processDeposit.js"; import {processVoluntaryExit} from "./processVoluntaryExit.js"; import {processBlsToExecutionChange} from "./processBlsToExecutionChange.js"; +import {processDepositReceipt} from "./processDepositReceipt.js"; import {ProcessBlockOpts} from "./types.js"; export { @@ -17,6 +19,7 @@ export { processDeposit, processVoluntaryExit, processBlsToExecutionChange, + processDepositReceipt, }; export function processOperations( @@ -26,7 +29,7 @@ export function processOperations( opts: ProcessBlockOpts = {verifySignatures: true} ): void { // verify that outstanding deposits are processed up to the maximum number of deposits - const maxDeposits = Math.min(MAX_DEPOSITS, state.eth1Data.depositCount - state.eth1DepositIndex); + const maxDeposits = getEth1DepositCount(state); if (body.deposits.length !== maxDeposits) { throw new Error( `Block contains incorrect number of deposits: depositCount=${body.deposits.length} expected=${maxDeposits}` @@ -54,4 +57,10 @@ export function processOperations( processBlsToExecutionChange(state as CachedBeaconStateCapella, blsToExecutionChange); } } + + if (fork >= ForkSeq.electra) { + for (const depositReceipt of (body as electra.BeaconBlockBody).executionPayload.depositReceipts) { + processDepositReceipt(fork, state as CachedBeaconStateElectra, depositReceipt); + } + } } diff --git a/packages/state-transition/src/cache/epochCache.ts b/packages/state-transition/src/cache/epochCache.ts index 9565898eb09d..97834c2afb2c 100644 --- a/packages/state-transition/src/cache/epochCache.ts +++ b/packages/state-transition/src/cache/epochCache.ts @@ -1,5 +1,7 @@ -import {CoordType} from "@chainsafe/bls/types"; +import {CoordType, PublicKey} from "@chainsafe/bls/types"; import bls from "@chainsafe/bls"; +import * as immutable from "immutable"; +import {fromHexString} from "@chainsafe/ssz"; import {BLSSignature, CommitteeIndex, Epoch, Slot, ValidatorIndex, phase0, SyncPeriod} from "@lodestar/types"; import {createBeaconConfig, BeaconConfig, ChainConfig} from "@lodestar/config"; import { @@ -30,8 +32,17 @@ import {computeEpochShuffling, EpochShuffling, getShufflingDecisionBlock} from " import {computeBaseRewardPerIncrement, computeSyncParticipantReward} from "../util/syncCommittee.js"; import {sumTargetUnslashedBalanceIncrements} from "../util/targetUnslashedBalance.js"; import {getTotalSlashingsByIncrement} from "../epoch/processSlashings.js"; +import {EpochCacheMetrics} from "../metrics.js"; import {EffectiveBalanceIncrements, getEffectiveBalanceIncrementsWithLen} from "./effectiveBalanceIncrements.js"; -import {Index2PubkeyCache, PubkeyIndexMap, syncPubkeys} from "./pubkeyCache.js"; +import { + Index2PubkeyCache, + PubkeyIndexMap, + UnfinalizedPubkeyIndexMap, + syncPubkeys, + toMemoryEfficientHexStr, + PubkeyHex, + newUnfinalizedPubkeyIndexMap, +} from "./pubkeyCache.js"; import {BeaconStateAllForks, BeaconStateAltair, ShufflingGetter} from "./types.js"; import { computeSyncCommitteeCache, @@ -83,23 +94,31 @@ type ProposersDeferred = {computed: false; seed: Uint8Array} | {computed: true; export class EpochCache { config: BeaconConfig; /** - * Unique globally shared pubkey registry. There should only exist one for the entire application. + * Unique globally shared finalized pubkey registry. There should only exist one for the entire application. * * TODO: this is a hack, we need a safety mechanism in case a bad eth1 majority vote is in, * or handle non finalized data differently, or use an immutable.js structure for cheap copies - * Warning: may contain pubkeys that do not yet exist in the current state, but do in a later processed state. + * + * New: This would include only validators whose activation_eligibility_epoch != FAR_FUTURE_EPOCH and hence it is + * insert only. Validators could be 1) Active 2) In the activation queue 3) Initialized but pending queued * * $VALIDATOR_COUNT x 192 char String -> Number Map */ pubkey2index: PubkeyIndexMap; /** - * Unique globally shared pubkey registry. There should only exist one for the entire application. + * Unique globally shared finalized pubkey registry. There should only exist one for the entire application. * - * Warning: may contain indices that do not yet exist in the current state, but do in a later processed state. + * New: This would include only validators whose activation_eligibility_epoch != FAR_FUTURE_EPOCH and hence it is + * insert only. Validators could be 1) Active 2) In the activation queue 3) Initialized but pending queued * * $VALIDATOR_COUNT x BLST deserialized pubkey (Jacobian coordinates) */ index2pubkey: Index2PubkeyCache; + /** + * Unique pubkey registry shared in the same fork. There should only exist one for the fork. + */ + unfinalizedPubkey2index: UnfinalizedPubkeyIndexMap; + /** * Indexes of the block proposers for the current epoch. * @@ -199,11 +218,21 @@ export class EpochCache { // TODO: Helper stats epoch: Epoch; syncPeriod: SyncPeriod; + /** + * state.validators.length of every state at epoch boundary + * They are saved in increasing order of epoch. + * The first validator length in the list corresponds to the state AFTER the latest finalized checkpoint state. ie. state.finalizedCheckpoint.epoch - 1 + * The last validator length corresponds to the latest epoch state ie. this.epoch + * eg. latest epoch = 105, latest finalized cp state epoch = 102 + * then the list will be (in terms of epoch) [103, 104, 105] + */ + private historicalValidatorLengths: immutable.List; constructor(data: { config: BeaconConfig; pubkey2index: PubkeyIndexMap; index2pubkey: Index2PubkeyCache; + unfinalizedPubkey2index: UnfinalizedPubkeyIndexMap; proposers: number[]; proposersPrevEpoch: number[] | null; proposersNextEpoch: ProposersDeferred; @@ -226,10 +255,12 @@ export class EpochCache { nextSyncCommitteeIndexed: SyncCommitteeCache; epoch: Epoch; syncPeriod: SyncPeriod; + historialValidatorLengths: immutable.List; }) { this.config = data.config; this.pubkey2index = data.pubkey2index; this.index2pubkey = data.index2pubkey; + this.unfinalizedPubkey2index = data.unfinalizedPubkey2index; this.proposers = data.proposers; this.proposersPrevEpoch = data.proposersPrevEpoch; this.proposersNextEpoch = data.proposersNextEpoch; @@ -252,11 +283,12 @@ export class EpochCache { this.nextSyncCommitteeIndexed = data.nextSyncCommitteeIndexed; this.epoch = data.epoch; this.syncPeriod = data.syncPeriod; + this.historicalValidatorLengths = data.historialValidatorLengths; } /** * Create an epoch cache - * @param validators cached validators that matches `state.validators` + * @param state a finalized beacon state. Passing in unfinalized state may cause unexpected behaviour eg. empty unfinalized cache * * SLOW CODE - 🐢 */ @@ -427,6 +459,8 @@ export class EpochCache { config, pubkey2index, index2pubkey, + // `createFromFinalizedState()` creates cache with empty unfinalizedPubkey2index. Be cautious to only pass in finalized state + unfinalizedPubkey2index: newUnfinalizedPubkeyIndexMap(), proposers, // On first epoch, set to null to prevent unnecessary work since this is only used for metrics proposersPrevEpoch: null, @@ -450,6 +484,7 @@ export class EpochCache { nextSyncCommitteeIndexed, epoch: currentEpoch, syncPeriod: computeSyncPeriodAtEpoch(currentEpoch), + historialValidatorLengths: immutable.List(), }); } @@ -465,6 +500,8 @@ export class EpochCache { // Common append-only structures shared with all states, no need to clone pubkey2index: this.pubkey2index, index2pubkey: this.index2pubkey, + // No need to clone this reference. On each mutation the `unfinalizedPubkey2index` reference is replaced, @see `addPubkey` + unfinalizedPubkey2index: this.unfinalizedPubkey2index, // Immutable data proposers: this.proposers, proposersPrevEpoch: this.proposersPrevEpoch, @@ -491,6 +528,7 @@ export class EpochCache { nextSyncCommitteeIndexed: this.nextSyncCommitteeIndexed, epoch: this.epoch, syncPeriod: this.syncPeriod, + historialValidatorLengths: this.historicalValidatorLengths, }); } @@ -501,6 +539,7 @@ export class EpochCache { afterProcessEpoch( state: BeaconStateAllForks, epochTransitionCache: { + indicesEligibleForActivationQueue: ValidatorIndex[]; nextEpochShufflingActiveValidatorIndices: ValidatorIndex[]; nextEpochTotalActiveBalanceByIncrement: number; } @@ -573,6 +612,25 @@ export class EpochCache { // ``` this.epoch = computeEpochAtSlot(state.slot); this.syncPeriod = computeSyncPeriodAtEpoch(this.epoch); + // ELECTRA Only: Add current cpState.validators.length + // Only keep validatorLength for epochs after finalized cpState.epoch + // eg. [100(epoch 1), 102(epoch 2)].push(104(epoch 3)), this.epoch = 3, finalized cp epoch = 1 + // We keep the last (3 - 1) items = [102, 104] + if (currEpoch >= this.config.ELECTRA_FORK_EPOCH) { + this.historicalValidatorLengths = this.historicalValidatorLengths.push(state.validators.length); + + // If number of validatorLengths we want to keep exceeds the current list size, it implies + // finalized checkpoint hasn't advanced, and no need to slice + const hasFinalizedCpAdvanced = + this.epoch - state.finalizedCheckpoint.epoch < this.historicalValidatorLengths.size; + + if (hasFinalizedCpAdvanced) { + // We use finalized cp epoch - this.epoch which is a negative number to keep the last n entries and discard the rest + this.historicalValidatorLengths = this.historicalValidatorLengths.slice( + state.finalizedCheckpoint.epoch - this.epoch + ); + } + } } beforeEpochTransition(): void { @@ -760,9 +818,75 @@ export class EpochCache { return isAggregatorFromCommitteeLength(committee.length, slotSignature); } + /** + * Return finalized pubkey given the validator index. + * Only finalized pubkey as we do not store unfinalized pubkey because no where in the spec has a + * need to make such enquiry + */ + getPubkey(index: ValidatorIndex): PublicKey | undefined { + return this.index2pubkey[index]; + } + + getValidatorIndex(pubkey: Uint8Array | PubkeyHex): ValidatorIndex | undefined { + if (this.isAfterElectra()) { + return this.pubkey2index.get(pubkey) ?? this.unfinalizedPubkey2index.get(toMemoryEfficientHexStr(pubkey)); + } else { + return this.pubkey2index.get(pubkey); + } + } + + /** + * + * Add unfinalized pubkeys + * + */ addPubkey(index: ValidatorIndex, pubkey: Uint8Array): void { + if (this.isAfterElectra()) { + this.addUnFinalizedPubkey(index, pubkey); + } else { + // deposit mechanism pre ELECTRA follows a safe distance with assumption + // that they are already canonical + this.addFinalizedPubkey(index, pubkey); + } + } + + addUnFinalizedPubkey(index: ValidatorIndex, pubkey: PubkeyHex | Uint8Array, metrics?: EpochCacheMetrics): void { + this.unfinalizedPubkey2index = this.unfinalizedPubkey2index.set(toMemoryEfficientHexStr(pubkey), index); + metrics?.newUnFinalizedPubkey.inc(); + } + + addFinalizedPubkeys(pubkeyMap: UnfinalizedPubkeyIndexMap, metrics?: EpochCacheMetrics): void { + pubkeyMap.forEach((index, pubkey) => this.addFinalizedPubkey(index, pubkey, metrics)); + } + + /** + * Add finalized validator index and pubkey into finalized cache. + * Since addFinalizedPubkey() primarily takes pubkeys from unfinalized cache, it can take pubkey hex string directly + */ + addFinalizedPubkey(index: ValidatorIndex, pubkey: PubkeyHex | Uint8Array, metrics?: EpochCacheMetrics): void { + const existingIndex = this.pubkey2index.get(pubkey); + + if (existingIndex !== undefined) { + if (existingIndex === index) { + // Repeated insert. + metrics?.finalizedPubkeyDuplicateInsert.inc(); + return; + } else { + // attempt to insert the same pubkey with different index, should never happen. + throw Error("inserted existing pubkey into finalizedPubkey2index cache with a different index"); + } + } + this.pubkey2index.set(pubkey, index); - this.index2pubkey[index] = bls.PublicKey.fromBytes(pubkey, CoordType.jacobian); // Optimize for aggregation + const pubkeyBytes = pubkey instanceof Uint8Array ? pubkey : fromHexString(pubkey); + this.index2pubkey[index] = bls.PublicKey.fromBytes(pubkeyBytes, CoordType.jacobian); + } + + /** + * Delete pubkeys from unfinalized cache + */ + deleteUnfinalizedPubkeys(pubkeys: Iterable): void { + this.unfinalizedPubkey2index = this.unfinalizedPubkey2index.deleteAll(pubkeys); } getShufflingAtSlot(slot: Slot): EpochShuffling { @@ -839,15 +963,53 @@ export class EpochCache { } effectiveBalanceIncrementsSet(index: number, effectiveBalance: number): void { - if (index >= this.effectiveBalanceIncrements.length) { - // Clone and extend effectiveBalanceIncrements + if (this.isAfterElectra()) { + // TODO: electra + // getting length and setting getEffectiveBalanceIncrementsByteLen is not fork safe + // so each time we add an index, we should new the Uint8Array to keep it forksafe + // one simple optimization could be to increment the length once per block rather + // on each add/set + // + // there could still be some unused length remaining from the prev ELECTRA padding + const newLength = + index >= this.effectiveBalanceIncrements.length ? index + 1 : this.effectiveBalanceIncrements.length; const effectiveBalanceIncrements = this.effectiveBalanceIncrements; - this.effectiveBalanceIncrements = new Uint8Array(getEffectiveBalanceIncrementsByteLen(index + 1)); + this.effectiveBalanceIncrements = new Uint8Array(newLength); this.effectiveBalanceIncrements.set(effectiveBalanceIncrements, 0); + } else { + if (index >= this.effectiveBalanceIncrements.length) { + // Clone and extend effectiveBalanceIncrements + const effectiveBalanceIncrements = this.effectiveBalanceIncrements; + this.effectiveBalanceIncrements = new Uint8Array(getEffectiveBalanceIncrementsByteLen(index + 1)); + this.effectiveBalanceIncrements.set(effectiveBalanceIncrements, 0); + } } this.effectiveBalanceIncrements[index] = Math.floor(effectiveBalance / EFFECTIVE_BALANCE_INCREMENT); } + + isAfterElectra(): boolean { + return this.epoch >= (this.config.ELECTRA_FORK_EPOCH ?? Infinity); + } + + getValidatorCountAtEpoch(targetEpoch: Epoch): number | undefined { + const currentEpoch = this.epoch; + + if (targetEpoch === currentEpoch) { + return this.historicalValidatorLengths.get(-1); + } + + // Attempt to get validator count from future epoch + if (targetEpoch > currentEpoch) { + return undefined; + } + + // targetEpoch is so far back that historicalValidatorLengths doesnt contain such info + if (targetEpoch < currentEpoch - this.historicalValidatorLengths.size + 1) { + return undefined; + } + return this.historicalValidatorLengths.get(targetEpoch - currentEpoch - 1); + } } function getEffectiveBalanceIncrementsByteLen(validatorCount: number): number { diff --git a/packages/state-transition/src/cache/pubkeyCache.ts b/packages/state-transition/src/cache/pubkeyCache.ts index 64a3f4f23c8f..6059bde53683 100644 --- a/packages/state-transition/src/cache/pubkeyCache.ts +++ b/packages/state-transition/src/cache/pubkeyCache.ts @@ -1,11 +1,19 @@ import {CoordType, PublicKey} from "@chainsafe/bls/types"; import bls from "@chainsafe/bls"; +import * as immutable from "immutable"; import {ValidatorIndex} from "@lodestar/types"; import {BeaconStateAllForks} from "./types.js"; export type Index2PubkeyCache = PublicKey[]; +/** + * OrderedMap preserves the order of entries in which they are `set()`. + * We assume `values()` yields validator indices in strictly increasing order + * as new validator indices are assigned in increasing order. + * EIP-6914 will break this assumption. + */ +export type UnfinalizedPubkeyIndexMap = immutable.Map; -type PubkeyHex = string; +export type PubkeyHex = string; /** * toHexString() creates hex strings via string concatenation, which are very memory inefficient. @@ -15,7 +23,7 @@ type PubkeyHex = string; * * See https://github.com/ChainSafe/lodestar/issues/3446 */ -function toMemoryEfficientHexStr(hex: Uint8Array | string): string { +export function toMemoryEfficientHexStr(hex: Uint8Array | string): string { if (typeof hex === "string") { if (hex.startsWith("0x")) { hex = hex.slice(2); @@ -26,6 +34,13 @@ function toMemoryEfficientHexStr(hex: Uint8Array | string): string { return Buffer.from(hex).toString("hex"); } +/** + * A wrapper for calling immutable.js. To abstract the initialization of UnfinalizedPubkeyIndexMap + */ +export function newUnfinalizedPubkeyIndexMap(): UnfinalizedPubkeyIndexMap { + return immutable.Map(); +} + export class PubkeyIndexMap { // We don't really need the full pubkey. We could just use the first 20 bytes like an Ethereum address readonly map = new Map(); @@ -41,7 +56,7 @@ export class PubkeyIndexMap { return this.map.get(toMemoryEfficientHexStr(key)); } - set(key: Uint8Array, value: ValidatorIndex): void { + set(key: Uint8Array | PubkeyHex, value: ValidatorIndex): void { this.map.set(toMemoryEfficientHexStr(key), value); } } diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index 8b45152a3646..aaaaf62a48df 100644 --- a/packages/state-transition/src/cache/stateCache.ts +++ b/packages/state-transition/src/cache/stateCache.ts @@ -11,6 +11,7 @@ import { BeaconStateBellatrix, BeaconStateCapella, BeaconStateDeneb, + BeaconStateElectra, } from "./types.js"; import {RewardCache, createEmptyRewardCache} from "./rewardCache.js"; @@ -131,11 +132,13 @@ export type CachedBeaconStateAltair = CachedBeaconState; export type CachedBeaconStateBellatrix = CachedBeaconState; export type CachedBeaconStateCapella = CachedBeaconState; export type CachedBeaconStateDeneb = CachedBeaconState; +export type CachedBeaconStateElectra = CachedBeaconState; export type CachedBeaconStateAllForks = CachedBeaconState; export type CachedBeaconStateExecutions = CachedBeaconState; /** * Create CachedBeaconState computing a new EpochCache instance + * TODO ELECTRA: rename this to createFinalizedCachedBeaconState() as it's intended for finalized state only */ export function createCachedBeaconState( state: T, @@ -159,7 +162,7 @@ export function createCachedBeaconState( * Create a CachedBeaconState given a cached seed state and state bytes * This guarantees that the returned state shares the same tree with the seed state * Check loadState() api for more details - * // TODO: rename to loadUnfinalizedCachedBeaconState() due to EIP-6110 + * // TODO: rename to loadUnfinalizedCachedBeaconState() due to ELECTRA */ export function loadCachedBeaconState( cachedSeedState: T, diff --git a/packages/state-transition/src/cache/types.ts b/packages/state-transition/src/cache/types.ts index 39b1dbb4b45b..a865aa4f4183 100644 --- a/packages/state-transition/src/cache/types.ts +++ b/packages/state-transition/src/cache/types.ts @@ -7,6 +7,7 @@ export type BeaconStateAltair = CompositeViewDU; export type BeaconStateBellatrix = CompositeViewDU; export type BeaconStateCapella = CompositeViewDU; export type BeaconStateDeneb = CompositeViewDU; +export type BeaconStateElectra = CompositeViewDU; // Union at the TreeViewDU level // - Works well as function argument and as generic type for allForks functions @@ -18,8 +19,9 @@ export type BeaconStateAllForks = | BeaconStateAltair | BeaconStateBellatrix | BeaconStateCapella - | BeaconStateDeneb; + | BeaconStateDeneb + | BeaconStateElectra; -export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateDeneb; +export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateDeneb | BeaconStateElectra; export type ShufflingGetter = (shufflingEpoch: Epoch, dependentRoot: RootHex) => EpochShuffling | null; diff --git a/packages/state-transition/src/index.ts b/packages/state-transition/src/index.ts index 0ef460e784af..4ed801e3c490 100644 --- a/packages/state-transition/src/index.ts +++ b/packages/state-transition/src/index.ts @@ -11,6 +11,7 @@ export type { CachedBeaconStateBellatrix, CachedBeaconStateCapella, CachedBeaconStateDeneb, + CachedBeaconStateElectra, CachedBeaconStateAllForks, CachedBeaconStateExecutions, // Non-cached states @@ -19,6 +20,7 @@ export type { BeaconStateBellatrix, BeaconStateCapella, BeaconStateDeneb, + BeaconStateElectra, BeaconStateAllForks, BeaconStateExecutions, } from "./types.js"; @@ -42,7 +44,12 @@ export { export {type EpochTransitionCache, beforeProcessEpoch} from "./cache/epochTransitionCache.js"; // Aux data-structures -export {PubkeyIndexMap, type Index2PubkeyCache} from "./cache/pubkeyCache.js"; +export { + PubkeyIndexMap, + type Index2PubkeyCache, + type UnfinalizedPubkeyIndexMap, + newUnfinalizedPubkeyIndexMap, +} from "./cache/pubkeyCache.js"; export { type EffectiveBalanceIncrements, diff --git a/packages/state-transition/src/metrics.ts b/packages/state-transition/src/metrics.ts index 62062bbfc539..9478087df83f 100644 --- a/packages/state-transition/src/metrics.ts +++ b/packages/state-transition/src/metrics.ts @@ -24,6 +24,11 @@ export type BeaconStateTransitionMetrics = { registerValidatorStatuses: (currentEpoch: Epoch, statuses: AttesterStatus[], balances?: number[]) => void; }; +export type EpochCacheMetrics = { + finalizedPubkeyDuplicateInsert: Gauge; + newUnFinalizedPubkey: Gauge; +}; + export function onStateCloneMetrics( state: CachedBeaconStateAllForks, metrics: BeaconStateTransitionMetrics, diff --git a/packages/state-transition/src/signatureSets/attesterSlashings.ts b/packages/state-transition/src/signatureSets/attesterSlashings.ts index 370d48a7d2f0..6a985f84f64e 100644 --- a/packages/state-transition/src/signatureSets/attesterSlashings.ts +++ b/packages/state-transition/src/signatureSets/attesterSlashings.ts @@ -27,13 +27,12 @@ export function getIndexedAttestationBigintSignatureSet( state: CachedBeaconStateAllForks, indexedAttestation: phase0.IndexedAttestationBigint ): ISignatureSet { - const {index2pubkey} = state.epochCtx; const slot = computeStartSlotAtEpoch(Number(indexedAttestation.data.target.epoch as bigint)); const domain = state.config.getDomain(state.slot, DOMAIN_BEACON_ATTESTER, slot); return { type: SignatureSetType.aggregate, - pubkeys: indexedAttestation.attestingIndices.map((i) => index2pubkey[i]), + pubkeys: indexedAttestation.attestingIndices.map((i) => state.epochCtx.index2pubkey[i]), signingRoot: computeSigningRoot(ssz.phase0.AttestationDataBigint, indexedAttestation.data, domain), signature: indexedAttestation.signature, }; diff --git a/packages/state-transition/src/slot/index.ts b/packages/state-transition/src/slot/index.ts index 6c4add1d1230..b05bd7ac93f2 100644 --- a/packages/state-transition/src/slot/index.ts +++ b/packages/state-transition/src/slot/index.ts @@ -7,6 +7,7 @@ export {upgradeStateToAltair} from "./upgradeStateToAltair.js"; export {upgradeStateToBellatrix} from "./upgradeStateToBellatrix.js"; export {upgradeStateToCapella} from "./upgradeStateToCapella.js"; export {upgradeStateToDeneb} from "./upgradeStateToDeneb.js"; +export {upgradeStateToElectra} from "./upgradeStateToElectra.js"; /** * Dial state to next slot. Common for all forks diff --git a/packages/state-transition/src/slot/upgradeStateToElectra.ts b/packages/state-transition/src/slot/upgradeStateToElectra.ts new file mode 100644 index 000000000000..19cac8811f77 --- /dev/null +++ b/packages/state-transition/src/slot/upgradeStateToElectra.ts @@ -0,0 +1,33 @@ +import {ssz} from "@lodestar/types"; +import {UNSET_DEPOSIT_RECEIPTS_START_INDEX} from "@lodestar/params"; +import {CachedBeaconStateDeneb} from "../types.js"; +import {CachedBeaconStateElectra, getCachedBeaconState} from "../cache/stateCache.js"; + +/** + * Upgrade a state from Capella to Deneb. + */ +export function upgradeStateToElectra(stateDeneb: CachedBeaconStateDeneb): CachedBeaconStateElectra { + const {config} = stateDeneb; + + const stateElectraNode = ssz.deneb.BeaconState.commitViewDU(stateDeneb); + const stateElectraView = ssz.electra.BeaconState.getViewDU(stateElectraNode); + + const stateElectra = getCachedBeaconState(stateElectraView, stateDeneb); + + stateElectra.fork = ssz.phase0.Fork.toViewDU({ + previousVersion: stateDeneb.fork.currentVersion, + currentVersion: config.ELECTRA_FORK_VERSION, + epoch: stateDeneb.epochCtx.epoch, + }); + + // latestExecutionPayloadHeader's depositReceiptsRoot set to zeros by default + // default value of depositReceiptsStartIndex is UNSET_DEPOSIT_RECEIPTS_START_INDEX + stateElectra.depositReceiptsStartIndex = UNSET_DEPOSIT_RECEIPTS_START_INDEX; + + // Commit new added fields ViewDU to the root node + stateElectra.commit(); + // Clear cache to ensure the cache of deneb fields is not used by new ELECTRA fields + stateElectra["clearCache"](); + + return stateElectra; +} diff --git a/packages/state-transition/src/stateTransition.ts b/packages/state-transition/src/stateTransition.ts index b3f3b41eb865..049f04dce287 100644 --- a/packages/state-transition/src/stateTransition.ts +++ b/packages/state-transition/src/stateTransition.ts @@ -9,6 +9,7 @@ import { CachedBeaconStateAltair, CachedBeaconStateBellatrix, CachedBeaconStateCapella, + CachedBeaconStateDeneb, } from "./types.js"; import {computeEpochAtSlot} from "./util/index.js"; import {verifyProposerSignature} from "./signatureSets/index.js"; @@ -18,6 +19,7 @@ import { upgradeStateToBellatrix, upgradeStateToCapella, upgradeStateToDeneb, + upgradeStateToElectra, } from "./slot/index.js"; import {processBlock} from "./block/index.js"; import {EpochTransitionStep, processEpoch} from "./epoch/index.js"; @@ -230,6 +232,9 @@ function processSlotsWithTransientCache( if (stateSlot === config.DENEB_FORK_EPOCH) { postState = upgradeStateToDeneb(postState as CachedBeaconStateCapella) as CachedBeaconStateAllForks; } + if (stateSlot === config.ELECTRA_FORK_EPOCH) { + postState = upgradeStateToElectra(postState as CachedBeaconStateDeneb) as CachedBeaconStateAllForks; + } } else { postState.slot++; } diff --git a/packages/state-transition/src/types.ts b/packages/state-transition/src/types.ts index 6b6b1f6260b2..d3a1ed69a7a9 100644 --- a/packages/state-transition/src/types.ts +++ b/packages/state-transition/src/types.ts @@ -9,6 +9,7 @@ export type { CachedBeaconStateBellatrix, CachedBeaconStateCapella, CachedBeaconStateDeneb, + CachedBeaconStateElectra, } from "./cache/stateCache.js"; export type { @@ -19,4 +20,5 @@ export type { BeaconStateBellatrix, BeaconStateCapella, BeaconStateDeneb, + BeaconStateElectra, } from "./cache/types.js"; diff --git a/packages/state-transition/src/util/deposit.ts b/packages/state-transition/src/util/deposit.ts new file mode 100644 index 000000000000..493099fdd982 --- /dev/null +++ b/packages/state-transition/src/util/deposit.ts @@ -0,0 +1,24 @@ +import {ForkSeq, MAX_DEPOSITS} from "@lodestar/params"; +import {UintNum64, phase0} from "@lodestar/types"; +import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js"; + +export function getEth1DepositCount(state: CachedBeaconStateAllForks, eth1Data?: phase0.Eth1Data): UintNum64 { + const eth1DataToUse = eth1Data ?? state.eth1Data; + if (state.config.getForkSeq(state.slot) >= ForkSeq.electra) { + const electraState = state as CachedBeaconStateElectra; + // eth1DataIndexLimit = min(UintNum64, UintBn64) can be safely casted as UintNum64 + // since the result lies within upper and lower bound of UintNum64 + const eth1DataIndexLimit: UintNum64 = + eth1DataToUse.depositCount < electraState.depositReceiptsStartIndex + ? eth1DataToUse.depositCount + : Number(electraState.depositReceiptsStartIndex); + + if (state.eth1DepositIndex < eth1DataIndexLimit) { + return Math.min(MAX_DEPOSITS, eth1DataIndexLimit - state.eth1DepositIndex); + } else { + return 0; + } + } else { + return Math.min(MAX_DEPOSITS, eth1DataToUse.depositCount - state.eth1DepositIndex); + } +} diff --git a/packages/state-transition/src/util/execution.ts b/packages/state-transition/src/util/execution.ts index 7ac4da4aeecb..b7923fd9f807 100644 --- a/packages/state-transition/src/util/execution.ts +++ b/packages/state-transition/src/util/execution.ts @@ -1,4 +1,4 @@ -import {allForks, bellatrix, capella, deneb, isBlindedBeaconBlockBody, ssz} from "@lodestar/types"; +import {allForks, bellatrix, capella, deneb, electra, isBlindedBeaconBlockBody, ssz} from "@lodestar/types"; import {ForkSeq} from "@lodestar/params"; import { @@ -170,5 +170,10 @@ export function executionPayloadToPayloadHeader( ).excessBlobGas; } + if (fork >= ForkSeq.electra) { + (bellatrixPayloadFields as electra.ExecutionPayloadHeader).depositReceiptsRoot = + ssz.electra.DepositReceipts.hashTreeRoot((payload as electra.ExecutionPayload).depositReceipts); + } + return bellatrixPayloadFields; } diff --git a/packages/state-transition/src/util/genesis.ts b/packages/state-transition/src/util/genesis.ts index 1041c33d0eb3..6c700436d7f6 100644 --- a/packages/state-transition/src/util/genesis.ts +++ b/packages/state-transition/src/util/genesis.ts @@ -7,6 +7,7 @@ import { GENESIS_EPOCH, GENESIS_SLOT, MAX_EFFECTIVE_BALANCE, + UNSET_DEPOSIT_RECEIPTS_START_INDEX, } from "@lodestar/params"; import {Bytes32, phase0, Root, ssz, TimeSeconds} from "@lodestar/types"; @@ -214,6 +215,7 @@ export function initializeBeaconStateFromEth1( | typeof ssz.bellatrix.ExecutionPayloadHeader | typeof ssz.capella.ExecutionPayloadHeader | typeof ssz.deneb.ExecutionPayloadHeader + | typeof ssz.electra.ExecutionPayloadHeader > ): CachedBeaconStateAllForks { const stateView = getGenesisBeaconState( @@ -284,6 +286,16 @@ export function initializeBeaconStateFromEth1( ssz.deneb.ExecutionPayloadHeader.defaultViewDU(); } + if (GENESIS_SLOT >= config.ELECTRA_FORK_EPOCH) { + const stateElectra = state as CompositeViewDU; + stateElectra.fork.previousVersion = config.ELECTRA_FORK_VERSION; + stateElectra.fork.currentVersion = config.ELECTRA_FORK_VERSION; + stateElectra.latestExecutionPayloadHeader = + (executionPayloadHeader as CompositeViewDU) ?? + ssz.electra.ExecutionPayloadHeader.defaultViewDU(); + stateElectra.depositReceiptsStartIndex = UNSET_DEPOSIT_RECEIPTS_START_INDEX; + } + state.commit(); return state; diff --git a/packages/state-transition/src/util/index.ts b/packages/state-transition/src/util/index.ts index 3f2e91da9a77..ba998f65b254 100644 --- a/packages/state-transition/src/util/index.ts +++ b/packages/state-transition/src/util/index.ts @@ -23,3 +23,4 @@ export * from "./slot.js"; export * from "./syncCommittee.js"; export * from "./validator.js"; export * from "./weakSubjectivity.js"; +export * from "./deposit.js"; diff --git a/packages/state-transition/test/unit/cachedBeaconState.test.ts b/packages/state-transition/test/unit/cachedBeaconState.test.ts index 2891cd3e6216..9b05b8947f73 100644 --- a/packages/state-transition/test/unit/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/cachedBeaconState.test.ts @@ -1,8 +1,9 @@ +import {fromHexString} from "@chainsafe/ssz"; import {describe, it, expect} from "vitest"; import {Epoch, ssz, RootHex} from "@lodestar/types"; import {toHexString} from "@lodestar/utils"; import {config as defaultConfig} from "@lodestar/config/default"; -import {createBeaconConfig} from "@lodestar/config"; +import {createBeaconConfig, createChainForkConfig} from "@lodestar/config"; import {createCachedBeaconStateTest} from "../utils/state.js"; import {PubkeyIndexMap} from "../../src/cache/pubkeyCache.js"; import {createCachedBeaconState, loadCachedBeaconState} from "../../src/cache/stateCache.js"; @@ -28,6 +29,65 @@ describe("CachedBeaconState", () => { expect(state2.epochCtx.epoch).toBe(0); }); + it("Clone and mutate cache pre-Electra", () => { + const stateView = ssz.altair.BeaconState.defaultViewDU(); + const state1 = createCachedBeaconStateTest(stateView); + + const pubkey1 = fromHexString( + "0x84105a985058fc8740a48bf1ede9d223ef09e8c6b1735ba0a55cf4a9ff2ff92376b778798365e488dab07a652eb04576" + ); + const index1 = 123; + const pubkey2 = fromHexString( + "0xa41726266b1d83ef609d759ba7796d54cfe549154e01e4730a3378309bc81a7638140d7e184b33593c072595f23f032d" + ); + const index2 = 456; + + state1.epochCtx.addPubkey(index1, pubkey1); + + const state2 = state1.clone(); + state2.epochCtx.addPubkey(index2, pubkey2); + + expect(state1.epochCtx.getValidatorIndex(pubkey1)).toBe(index1); + expect(state2.epochCtx.getValidatorIndex(pubkey1)).toBe(index1); + expect(state1.epochCtx.getValidatorIndex(pubkey2)).toBe(index2); + expect(state2.epochCtx.getValidatorIndex(pubkey2)).toBe(index2); + }); + + /* eslint-disable @typescript-eslint/naming-convention */ + it("Clone and mutate cache post-Electra", () => { + const stateView = ssz.electra.BeaconState.defaultViewDU(); + const state1 = createCachedBeaconStateTest( + stateView, + createChainForkConfig({ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: 0, + }), + {skipSyncCommitteeCache: true, skipSyncPubkeys: true} + ); + + const pubkey1 = fromHexString( + "0x84105a985058fc8740a48bf1ede9d223ef09e8c6b1735ba0a55cf4a9ff2ff92376b778798365e488dab07a652eb04576" + ); + const index1 = 123; + const pubkey2 = fromHexString( + "0xa41726266b1d83ef609d759ba7796d54cfe549154e01e4730a3378309bc81a7638140d7e184b33593c072595f23f032d" + ); + const index2 = 456; + + state1.epochCtx.addPubkey(index1, pubkey1); + + const state2 = state1.clone(); + state2.epochCtx.addPubkey(index2, pubkey2); + + expect(state1.epochCtx.getValidatorIndex(pubkey1)).toBe(index1); + expect(state2.epochCtx.getValidatorIndex(pubkey1)).toBe(index1); + expect(state1.epochCtx.getValidatorIndex(pubkey2)).toBe(undefined); + expect(state2.epochCtx.getValidatorIndex(pubkey2)).toBe(index2); + }); + it("Auto-commit on hashTreeRoot", () => { // Use Checkpoint instead of BeaconState to speed up the test const cp1 = ssz.phase0.Checkpoint.defaultViewDU(); diff --git a/packages/state-transition/test/unit/upgradeState.test.ts b/packages/state-transition/test/unit/upgradeState.test.ts index 75ba415c1bea..df9b052542f9 100644 --- a/packages/state-transition/test/unit/upgradeState.test.ts +++ b/packages/state-transition/test/unit/upgradeState.test.ts @@ -5,6 +5,7 @@ import {createBeaconConfig, ChainForkConfig, createChainForkConfig} from "@lodes import {config as chainConfig} from "@lodestar/config/default"; import {upgradeStateToDeneb} from "../../src/slot/upgradeStateToDeneb.js"; +import {upgradeStateToElectra} from "../../src/slot/upgradeStateToElectra.js"; import {createCachedBeaconState} from "../../src/cache/stateCache.js"; import {PubkeyIndexMap} from "../../src/cache/pubkeyCache.js"; @@ -24,6 +25,21 @@ describe("upgradeState", () => { const newState = upgradeStateToDeneb(stateView); expect(() => newState.toValue()).not.toThrow(); }); + it("upgradeStateToElectra", () => { + const denebState = ssz.deneb.BeaconState.defaultViewDU(); + const config = getConfig(ForkName.deneb); + const stateView = createCachedBeaconState( + denebState, + { + config: createBeaconConfig(config, denebState.genesisValidatorsRoot), + pubkey2index: new PubkeyIndexMap(), + index2pubkey: [], + }, + {skipSyncCommitteeCache: true} + ); + const newState = upgradeStateToElectra(stateView); + expect(() => newState.toValue()).not.toThrow(); + }); }); const ZERO_HASH = Buffer.alloc(32, 0); diff --git a/packages/state-transition/test/unit/util/deposit.test.ts b/packages/state-transition/test/unit/util/deposit.test.ts new file mode 100644 index 000000000000..e8f7f7a86af8 --- /dev/null +++ b/packages/state-transition/test/unit/util/deposit.test.ts @@ -0,0 +1,99 @@ +import {describe, it, expect} from "vitest"; +import {ssz} from "@lodestar/types"; +import {createChainForkConfig} from "@lodestar/config"; +import {MAX_DEPOSITS} from "@lodestar/params"; +import {CachedBeaconStateElectra, getEth1DepositCount} from "../../../src/index.js"; +import {createCachedBeaconStateTest} from "../../utils/state.js"; + +describe("getEth1DepositCount", () => { + it("Pre Electra", () => { + const stateView = ssz.altair.BeaconState.defaultViewDU(); + const preElectraState = createCachedBeaconStateTest(stateView); + + if (preElectraState.epochCtx.isAfterElectra()) { + throw Error("Not a pre-Electra state"); + } + + preElectraState.eth1Data.depositCount = 123; + + // 1. Should get less than MAX_DEPOSIT + preElectraState.eth1DepositIndex = 120; + expect(getEth1DepositCount(preElectraState)).toBe(3); + + // 2. Should get MAX_DEPOSIT + preElectraState.eth1DepositIndex = 100; + expect(getEth1DepositCount(preElectraState)).toBe(MAX_DEPOSITS); + }); + it("Post Electra with eth1 deposit", () => { + const stateView = ssz.electra.BeaconState.defaultViewDU(); + const postElectraState = createCachedBeaconStateTest( + stateView, + createChainForkConfig({ + /* eslint-disable @typescript-eslint/naming-convention */ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: 0, + }), + {skipSyncCommitteeCache: true, skipSyncPubkeys: true} + ) as CachedBeaconStateElectra; + + if (!postElectraState.epochCtx.isAfterElectra()) { + throw Error("Not a post-Electra state"); + } + + postElectraState.depositReceiptsStartIndex = 1000n; + postElectraState.eth1Data.depositCount = 995; + + // 1. Should get less than MAX_DEPOSIT + postElectraState.eth1DepositIndex = 990; + expect(getEth1DepositCount(postElectraState)).toBe(5); + + // 2. Should get MAX_DEPOSIT + postElectraState.eth1DepositIndex = 100; + expect(getEth1DepositCount(postElectraState)).toBe(MAX_DEPOSITS); + + // 3. Should be 0 + postElectraState.eth1DepositIndex = 1000; + expect(getEth1DepositCount(postElectraState)).toBe(0); + }); + it("Post Electra without eth1 deposit", () => { + const stateView = ssz.electra.BeaconState.defaultViewDU(); + const postElectraState = createCachedBeaconStateTest( + stateView, + createChainForkConfig({ + /* eslint-disable @typescript-eslint/naming-convention */ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + ELECTRA_FORK_EPOCH: 0, + }), + {skipSyncCommitteeCache: true, skipSyncPubkeys: true} + ) as CachedBeaconStateElectra; + + if (!postElectraState.epochCtx.isAfterElectra()) { + throw Error("Not a post-Electra state"); + } + + postElectraState.depositReceiptsStartIndex = 1000n; + postElectraState.eth1Data.depositCount = 1005; + + // Before eth1DepositIndex reaching the start index + // 1. Should get less than MAX_DEPOSIT + postElectraState.eth1DepositIndex = 990; + expect(getEth1DepositCount(postElectraState)).toBe(10); + + // 2. Should get MAX_DEPOSIT + postElectraState.eth1DepositIndex = 983; + expect(getEth1DepositCount(postElectraState)).toBe(MAX_DEPOSITS); + + // After eth1DepositIndex reaching the start index + // 1. Should be 0 + postElectraState.eth1DepositIndex = 1000; + expect(getEth1DepositCount(postElectraState)).toBe(0); + postElectraState.eth1DepositIndex = 1003; + expect(getEth1DepositCount(postElectraState)).toBe(0); + }); +}); diff --git a/packages/types/package.json b/packages/types/package.json index debff7534aca..ddedf8331ea7 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -32,6 +32,9 @@ "./deneb": { "import": "./lib/deneb/index.js" }, + "./electra": { + "import": "./lib/electra/index.js" + }, "./phase0": { "import": "./lib/phase0/index.js" } diff --git a/packages/types/src/allForks/sszTypes.ts b/packages/types/src/allForks/sszTypes.ts index 6030215ac8ca..84c6bb86ce5f 100644 --- a/packages/types/src/allForks/sszTypes.ts +++ b/packages/types/src/allForks/sszTypes.ts @@ -192,7 +192,7 @@ export const allForksBlobs = { ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, }, electra: { - BlobSidecar: electra.BlobSidecar, + BlobSidecar: deneb.BlobSidecar, ExecutionPayloadAndBlobsBundle: electra.ExecutionPayloadAndBlobsBundle, }, }; diff --git a/packages/types/src/allForks/types.ts b/packages/types/src/allForks/types.ts index 59768a5a3308..416bb051065a 100644 --- a/packages/types/src/allForks/types.ts +++ b/packages/types/src/allForks/types.ts @@ -4,12 +4,14 @@ import {ts as altair} from "../altair/index.js"; import {ts as bellatrix} from "../bellatrix/index.js"; import {ts as capella} from "../capella/index.js"; import {ts as deneb} from "../deneb/index.js"; +import {ts as electra} from "../electra/index.js"; import {ssz as phase0Ssz} from "../phase0/index.js"; import {ssz as altairSsz} from "../altair/index.js"; import {ssz as bellatrixSsz} from "../bellatrix/index.js"; import {ssz as capellaSsz} from "../capella/index.js"; import {ssz as denebSsz} from "../deneb/index.js"; +import {ssz as electraSsz} from "../electra/index.js"; // Re-export union types for types that are _known_ to differ @@ -18,47 +20,66 @@ export type BeaconBlockBody = | altair.BeaconBlockBody | bellatrix.BeaconBlockBody | capella.BeaconBlockBody - | deneb.BeaconBlockBody; + | deneb.BeaconBlockBody + | electra.BeaconBlockBody; export type BeaconBlock = | phase0.BeaconBlock | altair.BeaconBlock | bellatrix.BeaconBlock | capella.BeaconBlock - | deneb.BeaconBlock; + | deneb.BeaconBlock + | electra.BeaconBlock; export type SignedBeaconBlock = | phase0.SignedBeaconBlock | altair.SignedBeaconBlock | bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock - | deneb.SignedBeaconBlock; + | deneb.SignedBeaconBlock + | electra.SignedBeaconBlock; export type BeaconState = | phase0.BeaconState | altair.BeaconState | bellatrix.BeaconState | capella.BeaconState - | deneb.BeaconState; + | deneb.BeaconState + | electra.BeaconState; export type Metadata = phase0.Metadata | altair.Metadata; // For easy reference in the assemble block for building payloads -export type ExecutionBlockBody = bellatrix.BeaconBlockBody | capella.BeaconBlockBody | deneb.BeaconBlockBody; +export type ExecutionBlockBody = + | bellatrix.BeaconBlockBody + | capella.BeaconBlockBody + | deneb.BeaconBlockBody + | electra.BeaconBlockBody; // These two additional types will also change bellatrix forward -export type ExecutionPayload = bellatrix.ExecutionPayload | capella.ExecutionPayload | deneb.ExecutionPayload; +export type ExecutionPayload = + | bellatrix.ExecutionPayload + | capella.ExecutionPayload + | deneb.ExecutionPayload + | electra.ExecutionPayload; export type ExecutionPayloadHeader = | bellatrix.ExecutionPayloadHeader | capella.ExecutionPayloadHeader - | deneb.ExecutionPayloadHeader; + | deneb.ExecutionPayloadHeader + | electra.ExecutionPayloadHeader; // Blinded types that will change across forks export type BlindedBeaconBlockBody = | bellatrix.BlindedBeaconBlockBody | capella.BlindedBeaconBlockBody - | deneb.BlindedBeaconBlockBody; -export type BlindedBeaconBlock = bellatrix.BlindedBeaconBlock | capella.BlindedBeaconBlock | deneb.BlindedBeaconBlock; + | deneb.BlindedBeaconBlockBody + | electra.BlindedBeaconBlockBody; +export type BlindedBeaconBlock = + | bellatrix.BlindedBeaconBlock + | capella.BlindedBeaconBlock + | deneb.BlindedBeaconBlock + | electra.BlindedBeaconBlock; export type SignedBlindedBeaconBlock = | bellatrix.SignedBlindedBeaconBlock | capella.SignedBlindedBeaconBlock - | deneb.SignedBlindedBeaconBlock; + | deneb.SignedBlindedBeaconBlock + | electra.SignedBlindedBeaconBlock; // Full or blinded types export type FullOrBlindedExecutionPayload = @@ -80,30 +101,52 @@ export type SignedBeaconBlockOrContents = SignedBeaconBlock | SignedBlockContent export type FullOrBlindedBeaconBlockOrContents = BeaconBlockOrContents | BlindedBeaconBlock; -export type BuilderBid = bellatrix.BuilderBid | capella.BuilderBid | deneb.BuilderBid; -export type SignedBuilderBid = bellatrix.SignedBuilderBid | capella.SignedBuilderBid | deneb.SignedBuilderBid; -export type ExecutionPayloadAndBlobsBundle = deneb.ExecutionPayloadAndBlobsBundle; +export type BuilderBid = bellatrix.BuilderBid | capella.BuilderBid | deneb.BuilderBid | electra.BuilderBid; +export type SignedBuilderBid = + | bellatrix.SignedBuilderBid + | capella.SignedBuilderBid + | deneb.SignedBuilderBid + | electra.SignedBuilderBid; +export type ExecutionPayloadAndBlobsBundle = + | deneb.ExecutionPayloadAndBlobsBundle + | electra.ExecutionPayloadAndBlobsBundle; -export type LightClientHeader = altair.LightClientHeader | capella.LightClientHeader | deneb.LightClientHeader; +export type LightClientHeader = + | altair.LightClientHeader + | capella.LightClientHeader + | deneb.LightClientHeader + | electra.LightClientHeader; export type LightClientBootstrap = | altair.LightClientBootstrap | capella.LightClientBootstrap - | deneb.LightClientBootstrap; -export type LightClientUpdate = altair.LightClientUpdate | capella.LightClientUpdate | deneb.LightClientUpdate; + | deneb.LightClientBootstrap + | electra.LightClientBootstrap; +export type LightClientUpdate = + | altair.LightClientUpdate + | capella.LightClientUpdate + | deneb.LightClientUpdate + | electra.LightClientUpdate; export type LightClientFinalityUpdate = | altair.LightClientFinalityUpdate | capella.LightClientFinalityUpdate - | deneb.LightClientFinalityUpdate; + | deneb.LightClientFinalityUpdate + | electra.LightClientFinalityUpdate; export type LightClientOptimisticUpdate = | altair.LightClientOptimisticUpdate | capella.LightClientOptimisticUpdate - | deneb.LightClientOptimisticUpdate; -export type LightClientStore = altair.LightClientStore | capella.LightClientStore | deneb.LightClientStore; + | deneb.LightClientOptimisticUpdate + | electra.LightClientOptimisticUpdate; +export type LightClientStore = + | altair.LightClientStore + | capella.LightClientStore + | deneb.LightClientStore + | electra.LightClientStore; export type SSEPayloadAttributes = | bellatrix.SSEPayloadAttributes | capella.SSEPayloadAttributes - | deneb.SSEPayloadAttributes; + | deneb.SSEPayloadAttributes + | electra.SSEPayloadAttributes; /** * Types known to change between forks @@ -128,7 +171,12 @@ export type AllForksBlindedTypes = { }; export type AllForksLightClient = { - BeaconBlock: altair.BeaconBlock | bellatrix.BeaconBlock | capella.BeaconBlock | deneb.BeaconBlock; + BeaconBlock: + | altair.BeaconBlock + | bellatrix.BeaconBlock + | capella.BeaconBlock + | deneb.BeaconBlock + | electra.BeaconBlock; LightClientHeader: LightClientHeader; LightClientBootstrap: LightClientBootstrap; LightClientUpdate: LightClientUpdate; @@ -138,8 +186,12 @@ export type AllForksLightClient = { }; export type AllForksExecution = { - BeaconBlock: bellatrix.BeaconBlock | capella.BeaconBlock | deneb.BeaconBlock; - BeaconBlockBody: bellatrix.BeaconBlockBody | capella.BeaconBlockBody | deneb.BeaconBlockBody; + BeaconBlock: bellatrix.BeaconBlock | capella.BeaconBlock | deneb.BeaconBlock | electra.BeaconBlock; + BeaconBlockBody: + | bellatrix.BeaconBlockBody + | capella.BeaconBlockBody + | deneb.BeaconBlockBody + | electra.BeaconBlockBody; }; /** @@ -178,6 +230,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof denebSsz.BeaconBlockBody + | typeof electraSsz.BeaconBlockBody >; BeaconBlock: AllForksTypeOf< | typeof phase0Ssz.BeaconBlock @@ -185,6 +238,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock + | typeof electraSsz.BeaconBlock >; SignedBeaconBlock: AllForksTypeOf< | typeof phase0Ssz.SignedBeaconBlock @@ -192,6 +246,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock | typeof denebSsz.SignedBeaconBlock + | typeof electraSsz.SignedBeaconBlock >; BeaconState: AllForksTypeOf< | typeof phase0Ssz.BeaconState @@ -199,41 +254,65 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState | typeof denebSsz.BeaconState + | typeof electraSsz.BeaconState >; Metadata: AllForksTypeOf; }; export type AllForksExecutionSSZTypes = { BeaconBlockBody: AllForksTypeOf< - typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof denebSsz.BeaconBlockBody + | typeof bellatrixSsz.BeaconBlockBody + | typeof capellaSsz.BeaconBlockBody + | typeof denebSsz.BeaconBlockBody + | typeof electraSsz.BeaconBlockBody >; BeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock + | typeof bellatrixSsz.BeaconBlock + | typeof capellaSsz.BeaconBlock + | typeof denebSsz.BeaconBlock + | typeof electraSsz.BeaconBlock >; SignedBeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock | typeof denebSsz.SignedBeaconBlock + | typeof bellatrixSsz.SignedBeaconBlock + | typeof capellaSsz.SignedBeaconBlock + | typeof denebSsz.SignedBeaconBlock + | typeof electraSsz.SignedBeaconBlock >; BeaconState: AllForksTypeOf< - typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState | typeof denebSsz.BeaconState + | typeof bellatrixSsz.BeaconState + | typeof capellaSsz.BeaconState + | typeof denebSsz.BeaconState + | typeof electraSsz.BeaconState >; ExecutionPayload: AllForksTypeOf< - typeof bellatrixSsz.ExecutionPayload | typeof capellaSsz.ExecutionPayload | typeof denebSsz.ExecutionPayload + | typeof bellatrixSsz.ExecutionPayload + | typeof capellaSsz.ExecutionPayload + | typeof denebSsz.ExecutionPayload + | typeof electraSsz.ExecutionPayload >; ExecutionPayloadHeader: AllForksTypeOf< | typeof bellatrixSsz.ExecutionPayloadHeader | typeof capellaSsz.ExecutionPayloadHeader | typeof denebSsz.ExecutionPayloadHeader + | typeof electraSsz.ExecutionPayloadHeader >; BuilderBid: AllForksTypeOf< - typeof bellatrixSsz.BuilderBid | typeof capellaSsz.BuilderBid | typeof denebSsz.BuilderBid + | typeof bellatrixSsz.BuilderBid + | typeof capellaSsz.BuilderBid + | typeof denebSsz.BuilderBid + | typeof electraSsz.BuilderBid >; SignedBuilderBid: AllForksTypeOf< - typeof bellatrixSsz.SignedBuilderBid | typeof capellaSsz.SignedBuilderBid | typeof denebSsz.SignedBuilderBid + | typeof bellatrixSsz.SignedBuilderBid + | typeof capellaSsz.SignedBuilderBid + | typeof denebSsz.SignedBuilderBid + | typeof electraSsz.SignedBuilderBid >; SSEPayloadAttributes: AllForksTypeOf< | typeof bellatrixSsz.SSEPayloadAttributes | typeof capellaSsz.SSEPayloadAttributes | typeof denebSsz.SSEPayloadAttributes + | typeof electraSsz.SSEPayloadAttributes >; }; @@ -242,14 +321,19 @@ export type AllForksBlindedSSZTypes = { | typeof bellatrixSsz.BlindedBeaconBlockBody | typeof capellaSsz.BlindedBeaconBlock | typeof denebSsz.BlindedBeaconBlock + | typeof electraSsz.BlindedBeaconBlock >; BeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.BlindedBeaconBlock | typeof capellaSsz.BlindedBeaconBlock | typeof denebSsz.BlindedBeaconBlock + | typeof bellatrixSsz.BlindedBeaconBlock + | typeof capellaSsz.BlindedBeaconBlock + | typeof denebSsz.BlindedBeaconBlock + | typeof electraSsz.BlindedBeaconBlock >; SignedBeaconBlock: AllForksTypeOf< | typeof bellatrixSsz.SignedBlindedBeaconBlock | typeof capellaSsz.SignedBlindedBeaconBlock | typeof denebSsz.SignedBlindedBeaconBlock + | typeof electraSsz.SignedBlindedBeaconBlock >; }; @@ -259,40 +343,56 @@ export type AllForksLightClientSSZTypes = { | typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock + | typeof electraSsz.BeaconBlock >; BeaconBlockBody: AllForksTypeOf< | typeof altairSsz.BeaconBlockBody | typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof denebSsz.BeaconBlockBody + | typeof electraSsz.BeaconBlockBody >; LightClientHeader: AllForksTypeOf< - typeof altairSsz.LightClientHeader | typeof capellaSsz.LightClientHeader | typeof denebSsz.LightClientHeader + | typeof altairSsz.LightClientHeader + | typeof capellaSsz.LightClientHeader + | typeof denebSsz.LightClientHeader + | typeof electraSsz.LightClientHeader >; LightClientBootstrap: AllForksTypeOf< | typeof altairSsz.LightClientBootstrap | typeof capellaSsz.LightClientBootstrap | typeof denebSsz.LightClientBootstrap + | typeof electraSsz.LightClientBootstrap >; LightClientUpdate: AllForksTypeOf< - typeof altairSsz.LightClientUpdate | typeof capellaSsz.LightClientUpdate | typeof denebSsz.LightClientUpdate + | typeof altairSsz.LightClientUpdate + | typeof capellaSsz.LightClientUpdate + | typeof denebSsz.LightClientUpdate + | typeof electraSsz.LightClientUpdate >; LightClientFinalityUpdate: AllForksTypeOf< | typeof altairSsz.LightClientFinalityUpdate | typeof capellaSsz.LightClientFinalityUpdate | typeof denebSsz.LightClientFinalityUpdate + | typeof electraSsz.LightClientFinalityUpdate >; LightClientOptimisticUpdate: AllForksTypeOf< | typeof altairSsz.LightClientOptimisticUpdate | typeof capellaSsz.LightClientOptimisticUpdate | typeof denebSsz.LightClientOptimisticUpdate + | typeof electraSsz.LightClientOptimisticUpdate >; LightClientStore: AllForksTypeOf< - typeof altairSsz.LightClientStore | typeof capellaSsz.LightClientStore | typeof denebSsz.LightClientStore + | typeof altairSsz.LightClientStore + | typeof capellaSsz.LightClientStore + | typeof denebSsz.LightClientStore + | typeof electraSsz.LightClientStore >; }; export type AllForksBlobsSSZTypes = { BlobSidecar: AllForksTypeOf; - ExecutionPayloadAndBlobsBundle: AllForksTypeOf; + ExecutionPayloadAndBlobsBundle: AllForksTypeOf< + typeof denebSsz.ExecutionPayloadAndBlobsBundle | typeof electraSsz.ExecutionPayloadAndBlobsBundle + >; }; diff --git a/packages/types/src/electra/sszTypes.ts b/packages/types/src/electra/sszTypes.ts index 30690a499845..b347b20df9c7 100644 --- a/packages/types/src/electra/sszTypes.ts +++ b/packages/types/src/electra/sszTypes.ts @@ -1,12 +1,37 @@ -import {ContainerType} from "@chainsafe/ssz"; +import {ContainerType, ListCompositeType, VectorCompositeType} from "@chainsafe/ssz"; +import { + HISTORICAL_ROOTS_LIMIT, + BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH, + EPOCHS_PER_SYNC_COMMITTEE_PERIOD, + SLOTS_PER_EPOCH, + MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD, +} from "@lodestar/params"; import {ssz as primitiveSsz} from "../primitive/index.js"; +import {ssz as phase0Ssz} from "../phase0/index.js"; +import {ssz as altairSsz} from "../altair/index.js"; +import {ssz as bellatrixSsz} from "../bellatrix/index.js"; +import {ssz as capellaSsz} from "../capella/index.js"; import {ssz as denebSsz} from "../deneb/index.js"; -const {BLSSignature} = primitiveSsz; +const {UintNum64, Slot, Root, BLSSignature, UintBn256, Bytes32, BLSPubkey, DepositIndex, UintBn64} = primitiveSsz; + +export const DepositReceipt = new ContainerType( + { + pubkey: BLSPubkey, + withdrawalCredentials: Bytes32, + amount: UintNum64, + signature: BLSSignature, + index: DepositIndex, + }, + {typeName: "DepositReceipt", jsonCase: "eth2"} +); + +export const DepositReceipts = new ListCompositeType(DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD); export const ExecutionPayload = new ContainerType( { ...denebSsz.ExecutionPayload.fields, + depositReceipts: DepositReceipts, // New in ELECTRA }, {typeName: "ExecutionPayload", jsonCase: "eth2"} ); @@ -14,13 +39,18 @@ export const ExecutionPayload = new ContainerType( export const ExecutionPayloadHeader = new ContainerType( { ...denebSsz.ExecutionPayloadHeader.fields, + depositReceiptsRoot: Root, // New in ELECTRA }, {typeName: "ExecutionPayloadHeader", jsonCase: "eth2"} ); +// We have to preserve Fields ordering while changing the type of ExecutionPayload export const BeaconBlockBody = new ContainerType( { - ...denebSsz.BeaconBlockBody.fields, + ...altairSsz.BeaconBlockBody.fields, + executionPayload: ExecutionPayload, // Modified in ELECTRA + blsToExecutionChanges: capellaSsz.BeaconBlockBody.fields.blsToExecutionChanges, + blobKzgCommitments: denebSsz.BeaconBlockBody.fields.blobKzgCommitments, }, {typeName: "BeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} ); @@ -28,28 +58,25 @@ export const BeaconBlockBody = new ContainerType( export const BeaconBlock = new ContainerType( { ...denebSsz.BeaconBlock.fields, + body: BeaconBlockBody, // Modified in ELECTRA }, {typeName: "BeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} ); export const SignedBeaconBlock = new ContainerType( { - message: BeaconBlock, + message: BeaconBlock, // Modified in ELECTRA signature: BLSSignature, }, {typeName: "SignedBeaconBlock", jsonCase: "eth2"} ); -export const BlobSidecar = new ContainerType( - { - ...denebSsz.BlobSidecar.fields, - }, - {typeName: "BlobSidecar", jsonCase: "eth2"} -); - export const BlindedBeaconBlockBody = new ContainerType( { - ...denebSsz.BlindedBeaconBlockBody.fields, + ...altairSsz.BeaconBlockBody.fields, + executionPayloadHeader: ExecutionPayloadHeader, // Modified in ELECTRA + blsToExecutionChanges: capellaSsz.BeaconBlockBody.fields.blsToExecutionChanges, + blobKzgCommitments: denebSsz.BeaconBlockBody.fields.blobKzgCommitments, }, {typeName: "BlindedBeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} ); @@ -57,13 +84,14 @@ export const BlindedBeaconBlockBody = new ContainerType( export const BlindedBeaconBlock = new ContainerType( { ...denebSsz.BlindedBeaconBlock.fields, + body: BlindedBeaconBlockBody, // Modified in ELECTRA }, {typeName: "BlindedBeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} ); export const SignedBlindedBeaconBlock = new ContainerType( { - message: BlindedBeaconBlock, + message: BlindedBeaconBlock, // Modified in ELECTRA signature: BLSSignature, }, {typeName: "SignedBlindedBeaconBlock", jsonCase: "eth2"} @@ -71,7 +99,10 @@ export const SignedBlindedBeaconBlock = new ContainerType( export const BuilderBid = new ContainerType( { - ...denebSsz.BuilderBid.fields, + header: ExecutionPayloadHeader, // Modified in ELECTRA + blindedBlobsBundle: denebSsz.BlobKzgCommitments, + value: UintBn256, + pubkey: BLSPubkey, }, {typeName: "BuilderBid", jsonCase: "eth2"} ); @@ -86,63 +117,133 @@ export const SignedBuilderBid = new ContainerType( export const ExecutionPayloadAndBlobsBundle = new ContainerType( { - ...denebSsz.ExecutionPayloadAndBlobsBundle.fields, + executionPayload: ExecutionPayload, // Modified in ELECTRA + blobsBundle: denebSsz.BlobsBundle, }, {typeName: "ExecutionPayloadAndBlobsBundle", jsonCase: "eth2"} ); +// We don't spread deneb.BeaconState fields since we need to replace +// latestExecutionPayloadHeader and we cannot keep order doing that export const BeaconState = new ContainerType( { - ...denebSsz.BeaconState.fields, + genesisTime: UintNum64, + genesisValidatorsRoot: Root, + slot: primitiveSsz.Slot, + fork: phase0Ssz.Fork, + // History + latestBlockHeader: phase0Ssz.BeaconBlockHeader, + blockRoots: phase0Ssz.HistoricalBlockRoots, + stateRoots: phase0Ssz.HistoricalStateRoots, + // historical_roots Frozen in Capella, replaced by historical_summaries + historicalRoots: new ListCompositeType(Root, HISTORICAL_ROOTS_LIMIT), + // Eth1 + eth1Data: phase0Ssz.Eth1Data, + eth1DataVotes: phase0Ssz.Eth1DataVotes, + eth1DepositIndex: UintNum64, + // Registry + validators: phase0Ssz.Validators, + balances: phase0Ssz.Balances, + randaoMixes: phase0Ssz.RandaoMixes, + // Slashings + slashings: phase0Ssz.Slashings, + // Participation + previousEpochParticipation: altairSsz.EpochParticipation, + currentEpochParticipation: altairSsz.EpochParticipation, + // Finality + justificationBits: phase0Ssz.JustificationBits, + previousJustifiedCheckpoint: phase0Ssz.Checkpoint, + currentJustifiedCheckpoint: phase0Ssz.Checkpoint, + finalizedCheckpoint: phase0Ssz.Checkpoint, + // Inactivity + inactivityScores: altairSsz.InactivityScores, + // Sync + currentSyncCommittee: altairSsz.SyncCommittee, + nextSyncCommittee: altairSsz.SyncCommittee, + // Execution + latestExecutionPayloadHeader: ExecutionPayloadHeader, // Modified in ELECTRA + // Withdrawals + nextWithdrawalIndex: capellaSsz.BeaconState.fields.nextWithdrawalIndex, + nextWithdrawalValidatorIndex: capellaSsz.BeaconState.fields.nextWithdrawalValidatorIndex, + // Deep history valid from Capella onwards + historicalSummaries: capellaSsz.BeaconState.fields.historicalSummaries, + depositReceiptsStartIndex: UintBn64, // New in ELECTRA }, {typeName: "BeaconState", jsonCase: "eth2"} ); export const LightClientHeader = new ContainerType( { - ...denebSsz.LightClientHeader.fields, + beacon: phase0Ssz.BeaconBlockHeader, + execution: ExecutionPayloadHeader, // Modified in ELECTRA + executionBranch: new VectorCompositeType(Bytes32, EXECUTION_PAYLOAD_DEPTH), }, {typeName: "LightClientHeader", jsonCase: "eth2"} ); export const LightClientBootstrap = new ContainerType( { - ...denebSsz.LightClientBootstrap.fields, + header: LightClientHeader, + currentSyncCommittee: altairSsz.SyncCommittee, + currentSyncCommitteeBranch: altairSsz.LightClientBootstrap.fields.currentSyncCommitteeBranch, }, {typeName: "LightClientBootstrap", jsonCase: "eth2"} ); export const LightClientUpdate = new ContainerType( { - ...denebSsz.LightClientUpdate.fields, + attestedHeader: LightClientHeader, + nextSyncCommittee: altairSsz.SyncCommittee, + nextSyncCommitteeBranch: altairSsz.LightClientUpdate.fields.nextSyncCommitteeBranch, + finalizedHeader: LightClientHeader, + finalityBranch: altairSsz.LightClientUpdate.fields.finalityBranch, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, }, {typeName: "LightClientUpdate", jsonCase: "eth2"} ); export const LightClientFinalityUpdate = new ContainerType( { - ...denebSsz.LightClientFinalityUpdate.fields, + attestedHeader: LightClientHeader, + finalizedHeader: LightClientHeader, + finalityBranch: altairSsz.LightClientFinalityUpdate.fields.finalityBranch, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, }, {typeName: "LightClientFinalityUpdate", jsonCase: "eth2"} ); export const LightClientOptimisticUpdate = new ContainerType( { - ...denebSsz.LightClientOptimisticUpdate.fields, + attestedHeader: LightClientHeader, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, }, {typeName: "LightClientOptimisticUpdate", jsonCase: "eth2"} ); export const LightClientStore = new ContainerType( { - ...denebSsz.LightClientStore.fields, + snapshot: LightClientBootstrap, + validUpdates: new ListCompositeType(LightClientUpdate, EPOCHS_PER_SYNC_COMMITTEE_PERIOD * SLOTS_PER_EPOCH), }, {typeName: "LightClientStore", jsonCase: "eth2"} ); +// PayloadAttributes primarily for SSE event +export const PayloadAttributes = new ContainerType( + { + ...capellaSsz.PayloadAttributes.fields, + parentBeaconBlockRoot: Root, + }, + {typeName: "PayloadAttributes", jsonCase: "eth2"} +); + export const SSEPayloadAttributes = new ContainerType( { - ...denebSsz.SSEPayloadAttributes.fields, + ...bellatrixSsz.SSEPayloadAttributesCommon.fields, + payloadAttributes: PayloadAttributes, }, {typeName: "SSEPayloadAttributes", jsonCase: "eth2"} ); diff --git a/packages/types/src/electra/types.ts b/packages/types/src/electra/types.ts index 198259eed1dd..3286c10a0334 100644 --- a/packages/types/src/electra/types.ts +++ b/packages/types/src/electra/types.ts @@ -1,12 +1,14 @@ import {ValueOf} from "@chainsafe/ssz"; import * as ssz from "./sszTypes.js"; -export type BlobSidecar = ValueOf; -export type ExecutionPayloadAndBlobsBundle = ValueOf; +export type DepositReceipt = ValueOf; +export type DepositReceipts = ValueOf; export type ExecutionPayload = ValueOf; export type ExecutionPayloadHeader = ValueOf; +export type ExecutionPayloadAndBlobsBundle = ValueOf; + export type BeaconBlockBody = ValueOf; export type BeaconBlock = ValueOf; export type SignedBeaconBlock = ValueOf; @@ -17,6 +19,8 @@ export type BlindedBeaconBlockBody = ValueOf; export type BlindedBeaconBlock = ValueOf; export type SignedBlindedBeaconBlock = ValueOf; +export type FullOrBlindedExecutionPayload = ExecutionPayload | ExecutionPayloadHeader; + export type BuilderBid = ValueOf; export type SignedBuilderBid = ValueOf; export type SSEPayloadAttributes = ValueOf; diff --git a/packages/types/src/primitive/sszTypes.ts b/packages/types/src/primitive/sszTypes.ts index 068a32e2cc17..376e17c3f1b6 100644 --- a/packages/types/src/primitive/sszTypes.ts +++ b/packages/types/src/primitive/sszTypes.ts @@ -50,6 +50,7 @@ export const SubcommitteeIndex = UintNum64; */ export const ValidatorIndex = UintNum64; export const WithdrawalIndex = UintNum64; +export const DepositIndex = UintNum64; export const Gwei = UintBn64; export const Wei = UintBn256; export const Root = new ByteVectorType(32); diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 0afede39b951..298707c07303 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -106,7 +106,6 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record Date: Fri, 12 Apr 2024 23:38:07 +0800 Subject: [PATCH 3/9] chore: fix CI failure due to recent merge from `unstable` (#6646) --- .../beacon-node/src/execution/engine/http.ts | 16 ++++++++-------- .../updateUnfinalizedPubkeys.test.ts | 6 +++--- .../test/sim/electra-interop.test.ts | 18 +++++++++--------- .../test/spec/utils/specTestIterator.ts | 1 + .../state-transition/src/cache/epochCache.ts | 2 +- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/beacon-node/src/execution/engine/http.ts b/packages/beacon-node/src/execution/engine/http.ts index 274e2164c7be..140663aef6ac 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -179,10 +179,10 @@ export class ExecutionEngineHttp implements IExecutionEngine { ForkSeq[fork] >= ForkSeq.electra ? "engine_newPayloadV6110" : ForkSeq[fork] >= ForkSeq.deneb - ? "engine_newPayloadV3" - : ForkSeq[fork] >= ForkSeq.capella - ? "engine_newPayloadV2" - : "engine_newPayloadV1"; + ? "engine_newPayloadV3" + : ForkSeq[fork] >= ForkSeq.capella + ? "engine_newPayloadV2" + : "engine_newPayloadV1"; const serializedExecutionPayload = serializeExecutionPayload(fork, executionPayload); @@ -375,10 +375,10 @@ export class ExecutionEngineHttp implements IExecutionEngine { ForkSeq[fork] >= ForkSeq.electra ? "engine_getPayloadV6110" : ForkSeq[fork] >= ForkSeq.deneb - ? "engine_getPayloadV3" - : ForkSeq[fork] >= ForkSeq.capella - ? "engine_getPayloadV2" - : "engine_getPayloadV1"; + ? "engine_getPayloadV3" + : ForkSeq[fork] >= ForkSeq.capella + ? "engine_getPayloadV2" + : "engine_getPayloadV1"; const payloadResponse = await this.rpc.fetchWithRetries< EngineApiRpcReturnTypes[typeof method], EngineApiRpcParamTypes[typeof method] diff --git a/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts b/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts index 900f6a6fb873..39bf1a1551c9 100644 --- a/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts +++ b/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts @@ -7,7 +7,7 @@ import bls from "@chainsafe/bls"; import {ssz} from "@lodestar/types"; import {type CachedBeaconStateAllForks, PubkeyIndexMap} from "@lodestar/state-transition"; import {bytesToBigInt, intToBytes} from "@lodestar/utils"; -import {CheckpointStateCache, StateContextCache} from "../../../../src/chain/stateCache/index.js"; +import {InMemoryCheckpointStateCache, StateContextCache} from "../../../../src/chain/stateCache/index.js"; import {generateCachedElectraState} from "../../../utils/state.js"; // Benchmark date from Mon Nov 21 2023 - Intel Core i7-9750H @ 2.60Ghz @@ -21,7 +21,7 @@ describe("updateUnfinalizedPubkeys perf tests", function () { const numCheckpointStateCache = 8; const numStateCache = 3 * 32; - let checkpointStateCache: CheckpointStateCache; + let checkpointStateCache: InMemoryCheckpointStateCache; let stateCache: StateContextCache; const unfinalizedPubkey2Index = generatePubkey2Index(0, Math.max.apply(null, numPubkeysToBeFinalizedCases)); @@ -35,7 +35,7 @@ describe("updateUnfinalizedPubkeys perf tests", function () { baseState.epochCtx.pubkey2index = new PubkeyIndexMap(); baseState.epochCtx.index2pubkey = []; - checkpointStateCache = new CheckpointStateCache({}); + checkpointStateCache = new InMemoryCheckpointStateCache({}); stateCache = new StateContextCache({}); for (let i = 0; i < numCheckpointStateCache; i++) { diff --git a/packages/beacon-node/test/sim/electra-interop.test.ts b/packages/beacon-node/test/sim/electra-interop.test.ts index c1663211cbd9..428bba3c63ad 100644 --- a/packages/beacon-node/test/sim/electra-interop.test.ts +++ b/packages/beacon-node/test/sim/electra-interop.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; +import assert from "node:assert"; import {describe, it, vi, afterAll, afterEach} from "vitest"; -/* eslint-disable @typescript-eslint/naming-convention */ -import _ from "lodash"; + import {LogLevel, sleep} from "@lodestar/utils"; import {ForkName, SLOTS_PER_EPOCH, UNSET_DEPOSIT_RECEIPTS_START_INDEX} from "@lodestar/params"; import {electra, Epoch, Slot} from "@lodestar/types"; @@ -225,13 +225,13 @@ describe("executionEngine / ExecutionEngineHttp", function () { } const actualDepositReceipt = payload.depositReceipts[0]; - if (!_.isEqual(actualDepositReceipt, depositReceiptB)) { - throw Error( - `Deposit receipts mismatched. Expected: ${JSON.stringify(depositReceiptB)}, actual: ${JSON.stringify( - actualDepositReceipt - )}` - ); - } + assert.deepStrictEqual( + actualDepositReceipt, + depositReceiptB, + `Deposit receipts mismatched. Expected: ${JSON.stringify(depositReceiptB)}, actual: ${JSON.stringify( + actualDepositReceipt + )}` + ); }); it("Post-merge, run for a few blocks", async function () { diff --git a/packages/beacon-node/test/spec/utils/specTestIterator.ts b/packages/beacon-node/test/spec/utils/specTestIterator.ts index 3201fb15dbf3..6aa683cb6530 100644 --- a/packages/beacon-node/test/spec/utils/specTestIterator.ts +++ b/packages/beacon-node/test/spec/utils/specTestIterator.ts @@ -57,6 +57,7 @@ const coveredTestRunners = [ // ], // ``` export const defaultSkipOpts: SkipOpts = { + skippedForks: ["eip6110"], // TODO: capella // BeaconBlockBody proof in lightclient is the new addition in v1.3.0-rc.2-hotfix // Skip them for now to enable subsequently diff --git a/packages/state-transition/src/cache/epochCache.ts b/packages/state-transition/src/cache/epochCache.ts index 97834c2afb2c..ade519719fc0 100644 --- a/packages/state-transition/src/cache/epochCache.ts +++ b/packages/state-transition/src/cache/epochCache.ts @@ -226,7 +226,7 @@ export class EpochCache { * eg. latest epoch = 105, latest finalized cp state epoch = 102 * then the list will be (in terms of epoch) [103, 104, 105] */ - private historicalValidatorLengths: immutable.List; + historicalValidatorLengths: immutable.List; constructor(data: { config: BeaconConfig; From d458e5c8976d9480605f60023624f8d239a556a7 Mon Sep 17 00:00:00 2001 From: NC Date: Tue, 12 Mar 2024 15:31:40 +0800 Subject: [PATCH 4/9] Rename pubkey caches to finalized pubkey caches --- .../src/api/impl/beacon/state/index.ts | 17 +++-- .../src/api/impl/validator/index.ts | 2 +- packages/beacon-node/src/chain/chain.ts | 14 ++--- packages/beacon-node/src/chain/interface.ts | 4 +- .../beacon-node/src/chain/regen/queued.ts | 2 +- .../src/chain/validation/aggregateAndProof.ts | 4 +- .../src/chain/validation/attestation.ts | 4 +- .../signatureSets/contributionAndProof.ts | 2 +- .../validation/signatureSets/syncCommittee.ts | 2 +- .../syncCommitteeSelectionProof.ts | 2 +- .../syncCommitteeContributionAndProof.ts | 4 +- packages/beacon-node/test/fixtures/capella.ts | 2 +- .../test/memory/seenAttestationData.ts | 2 +- .../perf/api/impl/validator/attester.test.ts | 10 +-- .../produceBlock/produceBlockBody.test.ts | 2 +- .../updateUnfinalizedPubkeys.test.ts | 4 +- .../test/sim/electra-interop.test.ts | 4 +- .../unit/api/impl/beacon/state/utils.test.ts | 16 ++--- packages/beacon-node/test/utils/state.ts | 16 ++--- .../test/utils/validationData/attestation.ts | 2 +- .../src/block/processSyncCommittee.ts | 2 +- .../state-transition/src/cache/epochCache.ts | 62 +++++++++++-------- .../state-transition/src/cache/pubkeyCache.ts | 18 +++--- .../state-transition/src/cache/stateCache.ts | 12 ++-- .../src/cache/syncCommitteeCache.ts | 8 +-- .../src/epoch/processSyncCommitteeUpdates.ts | 2 +- .../src/signatureSets/attesterSlashings.ts | 2 +- .../src/signatureSets/indexedAttestation.ts | 2 +- .../src/signatureSets/proposer.ts | 4 +- .../src/signatureSets/proposerSlashings.ts | 2 +- .../src/signatureSets/randao.ts | 2 +- .../src/signatureSets/voluntaryExits.ts | 2 +- packages/state-transition/src/util/genesis.ts | 4 +- .../src/util/syncCommittee.ts | 2 +- packages/state-transition/test/perf/util.ts | 42 ++++++------- .../perf/util/loadState/loadState.test.ts | 16 ++--- .../test/unit/cachedBeaconState.test.ts | 20 +++--- .../test/unit/upgradeState.test.ts | 14 ++--- .../test/unit/util/cachedBeaconState.test.ts | 4 +- packages/state-transition/test/utils/state.ts | 8 +-- 40 files changed, 181 insertions(+), 162 deletions(-) diff --git a/packages/beacon-node/src/api/impl/beacon/state/index.ts b/packages/beacon-node/src/api/impl/beacon/state/index.ts index 275b0614af0f..1d760ecaed6e 100644 --- a/packages/beacon-node/src/api/impl/beacon/state/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/state/index.ts @@ -84,12 +84,12 @@ export function getBeaconStateApi({ const {state, executionOptimistic, finalized} = await resolveStateId(chain, stateId); const currentEpoch = getCurrentEpoch(state); const {validators, balances} = state; // Get the validators sub tree once for all the loop - const {pubkey2index} = chain.getHeadState().epochCtx; + const {finalizedPubkey2index} = chain.getHeadState().epochCtx; const validatorResponses: routes.beacon.ValidatorResponse[] = []; if (filters?.id) { for (const id of filters.id) { - const resp = getStateValidatorIndex(id, state, pubkey2index); + const resp = getStateValidatorIndex(id, state, finalizedPubkey2index); if (resp.valid) { const validatorIndex = resp.validatorIndex; const validator = validators.getReadonly(validatorIndex); @@ -111,7 +111,12 @@ export function getBeaconStateApi({ data: validatorResponses, }; } else if (filters?.status) { - const validatorsByStatus = filterStateValidatorsByStatus(filters.status, state, pubkey2index, currentEpoch); + const validatorsByStatus = filterStateValidatorsByStatus( + filters.status, + state, + finalizedPubkey2index, + currentEpoch + ); return { executionOptimistic, finalized, @@ -136,9 +141,9 @@ export function getBeaconStateApi({ async getStateValidator(stateId, validatorId) { const {state, executionOptimistic, finalized} = await resolveStateId(chain, stateId); - const {pubkey2index} = chain.getHeadState().epochCtx; + const {finalizedPubkey2index} = chain.getHeadState().epochCtx; - const resp = getStateValidatorIndex(validatorId, state, pubkey2index); + const resp = getStateValidatorIndex(validatorId, state, finalizedPubkey2index); if (!resp.valid) { throw new ApiError(resp.code, resp.reason); } @@ -169,7 +174,7 @@ export function getBeaconStateApi({ } balances.push({index: id, balance: state.balances.get(id)}); } else { - const index = headState.epochCtx.pubkey2index.get(id); + const index = headState.epochCtx.finalizedPubkey2index.get(id); if (index != null && index <= state.validators.length) { balances.push({index, balance: state.balances.get(index)}); } diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 3b21b4434f1d..62339de84693 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -1303,7 +1303,7 @@ export function getValidatorApi({ const filteredRegistrations = registrations.filter((registration) => { const {pubkey} = registration.message; - const validatorIndex = headState.epochCtx.pubkey2index.get(pubkey); + const validatorIndex = headState.epochCtx.finalizedPubkey2index.get(pubkey); if (validatorIndex === undefined) return false; const validator = headState.validators.getReadonly(validatorIndex); diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 7c55c25bc4ef..23603a914322 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -138,8 +138,8 @@ export class BeaconChain implements IBeaconChain { readonly seenBlockAttesters = new SeenBlockAttesters(); // Global state caches - readonly pubkey2index: PubkeyIndexMap; - readonly index2pubkey: Index2PubkeyCache; + readonly finalizedPubkey2index: PubkeyIndexMap; + readonly finalizedIndex2pubkey: Index2PubkeyCache; readonly beaconProposerCache: BeaconProposerCache; readonly checkpointBalancesCache: CheckpointBalancesCache; @@ -235,16 +235,16 @@ export class BeaconChain implements IBeaconChain { ? anchorState : createCachedBeaconState(anchorState, { config, - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }); this.shufflingCache.processState(cachedState, cachedState.epochCtx.previousShuffling.epoch); this.shufflingCache.processState(cachedState, cachedState.epochCtx.currentShuffling.epoch); this.shufflingCache.processState(cachedState, cachedState.epochCtx.nextShuffling.epoch); // Persist single global instance of state caches - this.pubkey2index = cachedState.epochCtx.pubkey2index; - this.index2pubkey = cachedState.epochCtx.index2pubkey; + this.finalizedPubkey2index = cachedState.epochCtx.finalizedPubkey2index; + this.finalizedIndex2pubkey = cachedState.epochCtx.finalizedIndex2pubkey; const fileDataStore = opts.nHistoricalStatesFileDataStore ?? false; const stateCache = this.opts.nHistoricalStates @@ -585,7 +585,7 @@ export class BeaconChain implements IBeaconChain { RegenCaller.produceBlock ); const proposerIndex = state.epochCtx.getBeaconProposer(slot); - const proposerPubKey = state.epochCtx.index2pubkey[proposerIndex].toBytes(); + const proposerPubKey = state.epochCtx.finalizedIndex2pubkey[proposerIndex].toBytes(); const {body, blobs, executionPayloadValue, shouldOverrideBuilder} = await produceBlockBody.call( this, diff --git a/packages/beacon-node/src/chain/interface.ts b/packages/beacon-node/src/chain/interface.ts index a277834d76b7..4e327f4142eb 100644 --- a/packages/beacon-node/src/chain/interface.ts +++ b/packages/beacon-node/src/chain/interface.ts @@ -89,8 +89,8 @@ export interface IBeaconChain { readonly regen: IStateRegenerator; readonly lightClientServer: LightClientServer; readonly reprocessController: ReprocessController; - readonly pubkey2index: PubkeyIndexMap; - readonly index2pubkey: Index2PubkeyCache; + readonly finalizedPubkey2index: PubkeyIndexMap; + readonly finalizedIndex2pubkey: Index2PubkeyCache; // Ops pool readonly attestationPool: AttestationPool; diff --git a/packages/beacon-node/src/chain/regen/queued.ts b/packages/beacon-node/src/chain/regen/queued.ts index 5f3554aba34b..cf90d16832e2 100644 --- a/packages/beacon-node/src/chain/regen/queued.ts +++ b/packages/beacon-node/src/chain/regen/queued.ts @@ -196,7 +196,7 @@ export class QueuedStateRegenerator implements IStateRegenerator { /** * Remove `validators` from all unfinalized cache's epochCtx.UnfinalizedPubkey2Index, - * and add them to epochCtx.pubkey2index and epochCtx.index2pubkey + * and add them to epochCtx.finalizedPubkey2index and epochCtx.finalizedIndex2pubkey */ updateUnfinalizedPubkeys(validators: UnfinalizedPubkeyIndexMap): void { let numStatesUpdated = 0; diff --git a/packages/beacon-node/src/chain/validation/aggregateAndProof.ts b/packages/beacon-node/src/chain/validation/aggregateAndProof.ts index 170e7421024e..b02a7ab24028 100644 --- a/packages/beacon-node/src/chain/validation/aggregateAndProof.ts +++ b/packages/beacon-node/src/chain/validation/aggregateAndProof.ts @@ -183,10 +183,10 @@ async function validateAggregateAndProof( // by the validator with index aggregate_and_proof.aggregator_index. // [REJECT] The aggregator signature, signed_aggregate_and_proof.signature, is valid. // [REJECT] The signature of aggregate is valid. - const aggregator = chain.index2pubkey[aggregateAndProof.aggregatorIndex]; + const aggregator = chain.finalizedIndex2pubkey[aggregateAndProof.aggregatorIndex]; const signingRoot = cachedAttData ? cachedAttData.signingRoot : getAttestationDataSigningRoot(chain.config, attData); const indexedAttestationSignatureSet = createAggregateSignatureSetFromComponents( - indexedAttestation.attestingIndices.map((i) => chain.index2pubkey[i]), + indexedAttestation.attestingIndices.map((i) => chain.finalizedIndex2pubkey[i]), signingRoot, indexedAttestation.signature ); diff --git a/packages/beacon-node/src/chain/validation/attestation.ts b/packages/beacon-node/src/chain/validation/attestation.ts index fc39534b45e6..c6e10903e8a8 100644 --- a/packages/beacon-node/src/chain/validation/attestation.ts +++ b/packages/beacon-node/src/chain/validation/attestation.ts @@ -424,14 +424,14 @@ async function validateGossipAttestationNoSignatureCheck( if (attestationOrCache.cache) { // there could be up to 6% of cpu time to compute signing root if we don't clone the signature set signatureSet = createSingleSignatureSetFromComponents( - chain.index2pubkey[validatorIndex], + chain.finalizedIndex2pubkey[validatorIndex], attestationOrCache.cache.signingRoot, signature ); attDataRootHex = attestationOrCache.cache.attDataRootHex; } else { signatureSet = createSingleSignatureSetFromComponents( - chain.index2pubkey[validatorIndex], + chain.finalizedIndex2pubkey[validatorIndex], getSigningRoot(), signature ); diff --git a/packages/beacon-node/src/chain/validation/signatureSets/contributionAndProof.ts b/packages/beacon-node/src/chain/validation/signatureSets/contributionAndProof.ts index 21e447b83677..79c8c950a8c6 100644 --- a/packages/beacon-node/src/chain/validation/signatureSets/contributionAndProof.ts +++ b/packages/beacon-node/src/chain/validation/signatureSets/contributionAndProof.ts @@ -20,7 +20,7 @@ export function getContributionAndProofSignatureSet( const signingData = signedContributionAndProof.message; return { type: SignatureSetType.single, - pubkey: epochCtx.index2pubkey[signedContributionAndProof.message.aggregatorIndex], + pubkey: epochCtx.finalizedIndex2pubkey[signedContributionAndProof.message.aggregatorIndex], signingRoot: computeSigningRoot(ssz.altair.ContributionAndProof, signingData, domain), signature: signedContributionAndProof.signature, }; diff --git a/packages/beacon-node/src/chain/validation/signatureSets/syncCommittee.ts b/packages/beacon-node/src/chain/validation/signatureSets/syncCommittee.ts index ce2b2e2484ab..1a2bc2454f3f 100644 --- a/packages/beacon-node/src/chain/validation/signatureSets/syncCommittee.ts +++ b/packages/beacon-node/src/chain/validation/signatureSets/syncCommittee.ts @@ -15,7 +15,7 @@ export function getSyncCommitteeSignatureSet( return { type: SignatureSetType.single, - pubkey: state.epochCtx.index2pubkey[syncCommittee.validatorIndex], + pubkey: state.epochCtx.finalizedIndex2pubkey[syncCommittee.validatorIndex], signingRoot: computeSigningRoot(ssz.Root, syncCommittee.beaconBlockRoot, domain), signature: syncCommittee.signature, }; diff --git a/packages/beacon-node/src/chain/validation/signatureSets/syncCommitteeSelectionProof.ts b/packages/beacon-node/src/chain/validation/signatureSets/syncCommitteeSelectionProof.ts index 14fcfad2b92e..f5f1e00980d7 100644 --- a/packages/beacon-node/src/chain/validation/signatureSets/syncCommitteeSelectionProof.ts +++ b/packages/beacon-node/src/chain/validation/signatureSets/syncCommitteeSelectionProof.ts @@ -20,7 +20,7 @@ export function getSyncCommitteeSelectionProofSignatureSet( }; return { type: SignatureSetType.single, - pubkey: epochCtx.index2pubkey[contributionAndProof.aggregatorIndex], + pubkey: epochCtx.finalizedIndex2pubkey[contributionAndProof.aggregatorIndex], signingRoot: computeSigningRoot(ssz.altair.SyncAggregatorSelectionData, signingData, domain), signature: contributionAndProof.selectionProof, }; diff --git a/packages/beacon-node/src/chain/validation/syncCommitteeContributionAndProof.ts b/packages/beacon-node/src/chain/validation/syncCommitteeContributionAndProof.ts index 3f9351f2d0d3..88bc0dae109d 100644 --- a/packages/beacon-node/src/chain/validation/syncCommitteeContributionAndProof.ts +++ b/packages/beacon-node/src/chain/validation/syncCommitteeContributionAndProof.ts @@ -74,7 +74,7 @@ export async function validateSyncCommitteeGossipContributionAndProof( // > Checked in validateGossipSyncCommitteeExceptSig() const participantPubkeys = syncCommitteeParticipantIndices.map( - (validatorIndex) => headState.epochCtx.index2pubkey[validatorIndex] + (validatorIndex) => headState.epochCtx.finalizedIndex2pubkey[validatorIndex] ); const signatureSets = [ // [REJECT] The contribution_and_proof.selection_proof is a valid signature of the SyncAggregatorSelectionData @@ -104,7 +104,7 @@ export async function validateSyncCommitteeGossipContributionAndProof( /** * Retrieve pubkeys in contribution aggregate using epochCtx: * - currSyncCommitteeIndexes cache - * - index2pubkey cache + * - finalizedIndex2pubkey cache */ function getContributionIndices( state: CachedBeaconStateAltair, diff --git a/packages/beacon-node/test/fixtures/capella.ts b/packages/beacon-node/test/fixtures/capella.ts index fe9b0206efb1..0196f9492459 100644 --- a/packages/beacon-node/test/fixtures/capella.ts +++ b/packages/beacon-node/test/fixtures/capella.ts @@ -10,7 +10,7 @@ export function generateBlsToExecutionChanges( for (const validatorIndex of state.epochCtx.proposers) { result.push({ message: { - fromBlsPubkey: state.epochCtx.index2pubkey[validatorIndex].toBytes(), + fromBlsPubkey: state.epochCtx.finalizedIndex2pubkey[validatorIndex].toBytes(), toExecutionAddress: Buffer.alloc(20), validatorIndex, }, diff --git a/packages/beacon-node/test/memory/seenAttestationData.ts b/packages/beacon-node/test/memory/seenAttestationData.ts index c6735bd861e9..ac52a3badaf5 100644 --- a/packages/beacon-node/test/memory/seenAttestationData.ts +++ b/packages/beacon-node/test/memory/seenAttestationData.ts @@ -29,7 +29,7 @@ function getRandomSeenAttestationDatas(n: number): SeenAttestationDatas { for (let i = 0; i < n; i++) { const attDataBytes = crypto.randomBytes(128); const key = Buffer.from(attDataBytes).toString("base64"); - // skip index2pubkey and committeeIndices as they are shared + // skip finalizedIndex2pubkey and committeeIndices as they are shared const attDataCacheEntry = { signingRoot: crypto.randomBytes(32), attDataRootHex: toHexString(crypto.randomBytes(32)), diff --git a/packages/beacon-node/test/perf/api/impl/validator/attester.test.ts b/packages/beacon-node/test/perf/api/impl/validator/attester.test.ts index 10527fdf94b5..f026303a0250 100644 --- a/packages/beacon-node/test/perf/api/impl/validator/attester.test.ts +++ b/packages/beacon-node/test/perf/api/impl/validator/attester.test.ts @@ -8,9 +8,9 @@ import {linspace} from "../../../../../src/util/numpy.js"; // Using state.validators.persistent is the fastest way of retrieving pubkeys by far // Benchmark data from Wed Jun 30 2021 - Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz // -// ✓ getPubkeys - index2pubkey - req 1 vs - 200000 vc 836120.4 ops/s 1.196000 us/op - 1224786 runs 2.07 s -// ✓ getPubkeys - index2pubkey - req 100 vs - 200000 vc 10347.15 ops/s 96.64500 us/op - 20602 runs 2.00 s -// ✓ getPubkeys - index2pubkey - req 1000 vs - 200000 vc 1248.985 ops/s 800.6500 us/op - 2518 runs 2.02 s +// ✓ getPubkeys - finalizedIndex2pubkey - req 1 vs - 200000 vc 836120.4 ops/s 1.196000 us/op - 1224786 runs 2.07 s +// ✓ getPubkeys - finalizedIndex2pubkey - req 100 vs - 200000 vc 10347.15 ops/s 96.64500 us/op - 20602 runs 2.00 s +// ✓ getPubkeys - finalizedIndex2pubkey - req 1000 vs - 200000 vc 1248.985 ops/s 800.6500 us/op - 2518 runs 2.02 s // ✓ getPubkeys - validatorsArr - req 1 vs - 200000 vc 1109878 ops/s 901.0000 ns/op - 1714328 runs 2.09 s // ✓ getPubkeys - validatorsArr - req 100 vs - 200000 vc 19937.00 ops/s 50.15800 us/op - 39548 runs 2.00 s // ✓ getPubkeys - validatorsArr - req 1000 vs - 200000 vc 2475.542 ops/s 403.9520 us/op - 4947 runs 2.00 s @@ -31,11 +31,11 @@ describe("api / impl / validator", () => { for (const reqCount of reqCounts) { itBench({ - id: `getPubkeys - index2pubkey - req ${reqCount} vs - ${numValidators} vc`, + id: `getPubkeys - finalizedIndex2pubkey - req ${reqCount} vs - ${numValidators} vc`, noThreshold: true, fn: () => { for (let i = 0; i < reqCount; i++) { - const pubkey = state.epochCtx.index2pubkey[i]; + const pubkey = state.epochCtx.finalizedIndex2pubkey[i]; pubkey.toBytes(PointFormat.compressed); } }, diff --git a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts index 62b5466bf891..f64a1b215c17 100644 --- a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts +++ b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts @@ -64,7 +64,7 @@ describe("produceBlockBody", () => { beforeEach: async () => { const head = chain.forkChoice.getHead(); const proposerIndex = state.epochCtx.getBeaconProposer(state.slot); - const proposerPubKey = state.epochCtx.index2pubkey[proposerIndex].toBytes(); + const proposerPubKey = state.epochCtx.finalizedIndex2pubkey[proposerIndex].toBytes(); return {chain, state, head, proposerIndex, proposerPubKey}; }, diff --git a/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts b/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts index 39bf1a1551c9..8a224c1493bc 100644 --- a/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts +++ b/packages/beacon-node/test/perf/chain/stateCache/updateUnfinalizedPubkeys.test.ts @@ -32,8 +32,8 @@ describe("updateUnfinalizedPubkeys perf tests", function () { id: `updateUnfinalizedPubkeys - updating ${numPubkeysToBeFinalized} pubkeys`, beforeEach: async () => { baseState.epochCtx.unfinalizedPubkey2index = Map(unfinalizedPubkey2Index.map); - baseState.epochCtx.pubkey2index = new PubkeyIndexMap(); - baseState.epochCtx.index2pubkey = []; + baseState.epochCtx.finalizedPubkey2index = new PubkeyIndexMap(); + baseState.epochCtx.finalizedIndex2pubkey = []; checkpointStateCache = new InMemoryCheckpointStateCache({}); stateCache = new StateContextCache({}); diff --git a/packages/beacon-node/test/sim/electra-interop.test.ts b/packages/beacon-node/test/sim/electra-interop.test.ts index 428bba3c63ad..64a1174557c5 100644 --- a/packages/beacon-node/test/sim/electra-interop.test.ts +++ b/packages/beacon-node/test/sim/electra-interop.test.ts @@ -375,7 +375,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { if (headState.validators.length !== 33 || headState.balances.length !== 33) { throw Error("New validator is not reflected in the beacon state at slot 5"); } - if (epochCtx.index2pubkey.length !== 32 || epochCtx.pubkey2index.size !== 32) { + if (epochCtx.finalizedIndex2pubkey.length !== 32 || epochCtx.finalizedPubkey2index.size !== 32) { throw Error("Finalized cache is modified."); } if (epochCtx.unfinalizedPubkey2index.size !== 1) { @@ -411,7 +411,7 @@ describe("executionEngine / ExecutionEngineHttp", function () { if (headState.validators.length !== 33 || headState.balances.length !== 33) { throw Error("New validator is not reflected in the beacon state."); } - if (epochCtx.index2pubkey.length !== 33 || epochCtx.pubkey2index.size !== 33) { + if (epochCtx.finalizedIndex2pubkey.length !== 33 || epochCtx.finalizedPubkey2index.size !== 33) { throw Error("New validator is not in finalized cache"); } if (!epochCtx.unfinalizedPubkey2index.isEmpty()) { diff --git a/packages/beacon-node/test/unit/api/impl/beacon/state/utils.test.ts b/packages/beacon-node/test/unit/api/impl/beacon/state/utils.test.ts index 6986df406bf0..d3c5c0add513 100644 --- a/packages/beacon-node/test/unit/api/impl/beacon/state/utils.test.ts +++ b/packages/beacon-node/test/unit/api/impl/beacon/state/utils.test.ts @@ -104,38 +104,38 @@ describe("beacon state api utils", function () { describe("getStateValidatorIndex", () => { const state = generateCachedAltairState(); - const pubkey2index = state.epochCtx.pubkey2index; + const finalizedPubkey2index = state.epochCtx.finalizedPubkey2index; it("should return valid: false on invalid input", () => { // "invalid validator id number" - expect(getStateValidatorIndex("foo", state, pubkey2index).valid).toBe(false); + expect(getStateValidatorIndex("foo", state, finalizedPubkey2index).valid).toBe(false); // "invalid hex" - expect(getStateValidatorIndex("0xfoo", state, pubkey2index).valid).toBe(false); + expect(getStateValidatorIndex("0xfoo", state, finalizedPubkey2index).valid).toBe(false); }); it("should return valid: false on validator indices / pubkeys not in the state", () => { // "validator id not in state" - expect(getStateValidatorIndex(String(state.validators.length), state, pubkey2index).valid).toBe(false); + expect(getStateValidatorIndex(String(state.validators.length), state, finalizedPubkey2index).valid).toBe(false); // "validator pubkey not in state" - expect(getStateValidatorIndex("0xabcd", state, pubkey2index).valid).toBe(false); + expect(getStateValidatorIndex("0xabcd", state, finalizedPubkey2index).valid).toBe(false); }); it("should return valid: true on validator indices / pubkeys in the state", () => { const index = state.validators.length - 1; - const resp1 = getStateValidatorIndex(String(index), state, pubkey2index); + const resp1 = getStateValidatorIndex(String(index), state, finalizedPubkey2index); if (resp1.valid) { expect(resp1.validatorIndex).toBe(index); } else { expect.fail("validator index should be found - validator index input"); } const pubkey = state.validators.get(index).pubkey; - const resp2 = getStateValidatorIndex(pubkey, state, pubkey2index); + const resp2 = getStateValidatorIndex(pubkey, state, finalizedPubkey2index); if (resp2.valid) { expect(resp2.validatorIndex).toBe(index); } else { expect.fail("validator index should be found - Uint8Array input"); } - const resp3 = getStateValidatorIndex(toHexString(pubkey), state, pubkey2index); + const resp3 = getStateValidatorIndex(toHexString(pubkey), state, finalizedPubkey2index); if (resp3.valid) { expect(resp3.validatorIndex).toBe(index); } else { diff --git a/packages/beacon-node/test/utils/state.ts b/packages/beacon-node/test/utils/state.ts index 3d7d8ee5abfe..23b5ec577af3 100644 --- a/packages/beacon-node/test/utils/state.ts +++ b/packages/beacon-node/test/utils/state.ts @@ -113,8 +113,8 @@ export function generateCachedState(opts?: TestBeaconState): CachedBeaconStateAl return createCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }); } @@ -127,8 +127,8 @@ export function generateCachedAltairState(opts?: TestBeaconState, altairForkEpoc return createCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }); } @@ -141,8 +141,8 @@ export function generateCachedBellatrixState(opts?: TestBeaconState): CachedBeac return createCachedBeaconState(state as BeaconStateBellatrix, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }); } @@ -154,8 +154,8 @@ export function generateCachedElectraState(opts?: TestBeaconState): CachedBeacon const state = generateState(opts, config); return createCachedBeaconState(state as BeaconStateElectra, { config: createBeaconConfig(config, state.genesisValidatorsRoot), - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }); } export const zeroProtoBlock: ProtoBlock = { diff --git a/packages/beacon-node/test/utils/validationData/attestation.ts b/packages/beacon-node/test/utils/validationData/attestation.ts index 02d20fd8e5bb..798d74cc2f46 100644 --- a/packages/beacon-node/test/utils/validationData/attestation.ts +++ b/packages/beacon-node/test/utils/validationData/attestation.ts @@ -147,7 +147,7 @@ export function getAttestationValidData(opts: AttestationValidDataOpts): { ? new BlsSingleThreadVerifier({metrics: null}) : new BlsMultiThreadWorkerPool({}, {logger: testLogger(), metrics: null}), waitForBlock: () => Promise.resolve(false), - index2pubkey: state.epochCtx.index2pubkey, + finalizedIndex2pubkey: state.epochCtx.finalizedIndex2pubkey, shufflingCache, opts: defaultChainOptions, } as Partial as IBeaconChain; diff --git a/packages/state-transition/src/block/processSyncCommittee.ts b/packages/state-transition/src/block/processSyncCommittee.ts index 70c918ed60bf..656fcb06c729 100644 --- a/packages/state-transition/src/block/processSyncCommittee.ts +++ b/packages/state-transition/src/block/processSyncCommittee.ts @@ -105,7 +105,7 @@ export function getSyncCommitteeSignatureSet( return { type: SignatureSetType.aggregate, - pubkeys: participantIndices.map((i) => epochCtx.index2pubkey[i]), + pubkeys: participantIndices.map((i) => epochCtx.finalizedIndex2pubkey[i]), signingRoot: computeSigningRoot(ssz.Root, rootSigned, domain), signature, }; diff --git a/packages/state-transition/src/cache/epochCache.ts b/packages/state-transition/src/cache/epochCache.ts index ade519719fc0..d4f1112082eb 100644 --- a/packages/state-transition/src/cache/epochCache.ts +++ b/packages/state-transition/src/cache/epochCache.ts @@ -56,8 +56,8 @@ export const PROPOSER_WEIGHT_FACTOR = PROPOSER_WEIGHT / (WEIGHT_DENOMINATOR - PR export type EpochCacheImmutableData = { config: BeaconConfig; - pubkey2index: PubkeyIndexMap; - index2pubkey: Index2PubkeyCache; + finalizedPubkey2index: PubkeyIndexMap; + finalizedIndex2pubkey: Index2PubkeyCache; }; export type EpochCacheOpts = { @@ -103,8 +103,11 @@ export class EpochCache { * insert only. Validators could be 1) Active 2) In the activation queue 3) Initialized but pending queued * * $VALIDATOR_COUNT x 192 char String -> Number Map + * + * Only stores pubkey and index of finalized validators + * */ - pubkey2index: PubkeyIndexMap; + finalizedPubkey2index: PubkeyIndexMap; /** * Unique globally shared finalized pubkey registry. There should only exist one for the entire application. * @@ -112,8 +115,11 @@ export class EpochCache { * insert only. Validators could be 1) Active 2) In the activation queue 3) Initialized but pending queued * * $VALIDATOR_COUNT x BLST deserialized pubkey (Jacobian coordinates) + * + * Only stores pubkey and index of finalized validators + * */ - index2pubkey: Index2PubkeyCache; + finalizedIndex2pubkey: Index2PubkeyCache; /** * Unique pubkey registry shared in the same fork. There should only exist one for the fork. */ @@ -230,8 +236,8 @@ export class EpochCache { constructor(data: { config: BeaconConfig; - pubkey2index: PubkeyIndexMap; - index2pubkey: Index2PubkeyCache; + finalizedPubkey2index: PubkeyIndexMap; + finalizedIndex2pubkey: Index2PubkeyCache; unfinalizedPubkey2index: UnfinalizedPubkeyIndexMap; proposers: number[]; proposersPrevEpoch: number[] | null; @@ -258,8 +264,8 @@ export class EpochCache { historialValidatorLengths: immutable.List; }) { this.config = data.config; - this.pubkey2index = data.pubkey2index; - this.index2pubkey = data.index2pubkey; + this.finalizedPubkey2index = data.finalizedPubkey2index; + this.finalizedIndex2pubkey = data.finalizedIndex2pubkey; this.unfinalizedPubkey2index = data.unfinalizedPubkey2index; this.proposers = data.proposers; this.proposersPrevEpoch = data.proposersPrevEpoch; @@ -294,13 +300,17 @@ export class EpochCache { */ static createFromState( state: BeaconStateAllForks, - {config, pubkey2index, index2pubkey}: EpochCacheImmutableData, + { + config, + finalizedPubkey2index, + finalizedIndex2pubkey, + }: EpochCacheImmutableData, opts?: EpochCacheOpts ): EpochCache { // syncPubkeys here to ensure EpochCacheImmutableData is popualted before computing the rest of caches - // - computeSyncCommitteeCache() needs a fully populated pubkey2index cache + // - computeSyncCommitteeCache() needs a fully populated finalizedPubkey2index cache if (!opts?.skipSyncPubkeys) { - syncPubkeys(state, pubkey2index, index2pubkey); + syncPubkeys(state, finalizedPubkey2index, finalizedIndex2pubkey); } const currentEpoch = computeEpochAtSlot(state.slot); @@ -404,8 +414,8 @@ export class EpochCache { // Allow to skip populating sync committee for initializeBeaconStateFromEth1() if (afterAltairFork && !opts?.skipSyncCommitteeCache) { const altairState = state as BeaconStateAltair; - currentSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.currentSyncCommittee, pubkey2index); - nextSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.nextSyncCommittee, pubkey2index); + currentSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.currentSyncCommittee, finalizedPubkey2index); + nextSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.nextSyncCommittee, finalizedPubkey2index); } else { currentSyncCommitteeIndexed = new SyncCommitteeCacheEmpty(); nextSyncCommitteeIndexed = new SyncCommitteeCacheEmpty(); @@ -457,8 +467,8 @@ export class EpochCache { return new EpochCache({ config, - pubkey2index, - index2pubkey, + finalizedPubkey2index, + finalizedIndex2pubkey, // `createFromFinalizedState()` creates cache with empty unfinalizedPubkey2index. Be cautious to only pass in finalized state unfinalizedPubkey2index: newUnfinalizedPubkeyIndexMap(), proposers, @@ -498,8 +508,8 @@ export class EpochCache { return new EpochCache({ config: this.config, // Common append-only structures shared with all states, no need to clone - pubkey2index: this.pubkey2index, - index2pubkey: this.index2pubkey, + finalizedPubkey2index: this.finalizedPubkey2index, + finalizedIndex2pubkey: this.finalizedIndex2pubkey, // No need to clone this reference. On each mutation the `unfinalizedPubkey2index` reference is replaced, @see `addPubkey` unfinalizedPubkey2index: this.unfinalizedPubkey2index, // Immutable data @@ -824,14 +834,16 @@ export class EpochCache { * need to make such enquiry */ getPubkey(index: ValidatorIndex): PublicKey | undefined { - return this.index2pubkey[index]; + return this.finalizedIndex2pubkey[index]; } getValidatorIndex(pubkey: Uint8Array | PubkeyHex): ValidatorIndex | undefined { if (this.isAfterElectra()) { - return this.pubkey2index.get(pubkey) ?? this.unfinalizedPubkey2index.get(toMemoryEfficientHexStr(pubkey)); + return ( + this.finalizedPubkey2index.get(pubkey) ?? this.unfinalizedPubkey2index.get(toMemoryEfficientHexStr(pubkey)) + ); } else { - return this.pubkey2index.get(pubkey); + return this.finalizedPubkey2index.get(pubkey); } } @@ -864,7 +876,7 @@ export class EpochCache { * Since addFinalizedPubkey() primarily takes pubkeys from unfinalized cache, it can take pubkey hex string directly */ addFinalizedPubkey(index: ValidatorIndex, pubkey: PubkeyHex | Uint8Array, metrics?: EpochCacheMetrics): void { - const existingIndex = this.pubkey2index.get(pubkey); + const existingIndex = this.finalizedPubkey2index.get(pubkey); if (existingIndex !== undefined) { if (existingIndex === index) { @@ -877,9 +889,9 @@ export class EpochCache { } } - this.pubkey2index.set(pubkey, index); + this.finalizedPubkey2index.set(pubkey, index); const pubkeyBytes = pubkey instanceof Uint8Array ? pubkey : fromHexString(pubkey); - this.index2pubkey[index] = bls.PublicKey.fromBytes(pubkeyBytes, CoordType.jacobian); + this.finalizedIndex2pubkey[index] = bls.PublicKey.fromBytes(pubkeyBytes, CoordType.jacobian); } /** @@ -1064,7 +1076,7 @@ export function createEmptyEpochCacheImmutableData( return { config: createBeaconConfig(chainConfig, state.genesisValidatorsRoot), // This is a test state, there's no need to have a global shared cache of keys - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }; } diff --git a/packages/state-transition/src/cache/pubkeyCache.ts b/packages/state-transition/src/cache/pubkeyCache.ts index 6059bde53683..906aeb87a96f 100644 --- a/packages/state-transition/src/cache/pubkeyCache.ts +++ b/packages/state-transition/src/cache/pubkeyCache.ts @@ -64,29 +64,31 @@ export class PubkeyIndexMap { /** * Checks the pubkey indices against a state and adds missing pubkeys * - * Mutates `pubkey2index` and `index2pubkey` + * Mutates `finalizedPubkey2index` and `finalizedIndex2pubkey` * * If pubkey caches are empty: SLOW CODE - 🐢 */ export function syncPubkeys( state: BeaconStateAllForks, - pubkey2index: PubkeyIndexMap, - index2pubkey: Index2PubkeyCache + finalizedPubkey2index: PubkeyIndexMap, + finalizedIndex2pubkey: Index2PubkeyCache ): void { - if (pubkey2index.size !== index2pubkey.length) { - throw new Error(`Pubkey indices have fallen out of sync: ${pubkey2index.size} != ${index2pubkey.length}`); + if (finalizedPubkey2index.size !== finalizedIndex2pubkey.length) { + throw new Error( + `Pubkey indices have fallen out of sync: ${finalizedPubkey2index.size} != ${finalizedIndex2pubkey.length}` + ); } // Get the validators sub tree once for all the loop const validators = state.validators; const newCount = state.validators.length; - for (let i = pubkey2index.size; i < newCount; i++) { + for (let i = finalizedPubkey2index.size; i < newCount; i++) { const pubkey = validators.getReadonly(i).pubkey; - pubkey2index.set(pubkey, i); + finalizedPubkey2index.set(pubkey, i); // Pubkeys must be checked for group + inf. This must be done only once when the validator deposit is processed. // Afterwards any public key is the state consider validated. // > Do not do any validation here - index2pubkey.push(bls.PublicKey.fromBytes(pubkey, CoordType.jacobian)); // Optimize for aggregation + finalizedIndex2pubkey.push(bls.PublicKey.fromBytes(pubkey, CoordType.jacobian)); // Optimize for aggregation } } diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index aaaaf62a48df..bc110cd8de92 100644 --- a/packages/state-transition/src/cache/stateCache.ts +++ b/packages/state-transition/src/cache/stateCache.ts @@ -74,7 +74,7 @@ type BeaconStateCacheMutable = Mutable; * - Micro-optimizations (TODO): * - Keep also the root of the node above pubkey and withdrawal creds. Will never change * - Keep pubkey + withdrawal creds in the same Uint8Array - * - Have a global pubkey + withdrawal creds Uint8Array global cache, like with the index2pubkey cache + * - Have a global pubkey + withdrawal creds Uint8Array global cache, like with the finalizedIndex2pubkey cache * * ------------------ * @@ -176,22 +176,22 @@ export function loadCachedBeaconState, - pubkey2index: PubkeyIndexMap + finalizedPubkey2index: PubkeyIndexMap ): SyncCommitteeCache { - const validatorIndices = computeSyncCommitteeIndices(syncCommittee, pubkey2index); + const validatorIndices = computeSyncCommitteeIndices(syncCommittee, finalizedPubkey2index); const validatorIndexMap = computeSyncComitteeMap(validatorIndices); return { validatorIndices, @@ -75,12 +75,12 @@ export function computeSyncComitteeMap(syncCommitteeIndexes: ValidatorIndex[]): */ function computeSyncCommitteeIndices( syncCommittee: CompositeViewDU, - pubkey2index: PubkeyIndexMap + finalizedPubkey2index: PubkeyIndexMap ): ValidatorIndex[] { const validatorIndices: ValidatorIndex[] = []; const pubkeys = syncCommittee.pubkeys.getAllReadonly(); for (const pubkey of pubkeys) { - const validatorIndex = pubkey2index.get(pubkey); + const validatorIndex = finalizedPubkey2index.get(pubkey); if (validatorIndex === undefined) { throw Error(`SyncCommittee pubkey is unknown ${toHexString(pubkey)}`); } diff --git a/packages/state-transition/src/epoch/processSyncCommitteeUpdates.ts b/packages/state-transition/src/epoch/processSyncCommitteeUpdates.ts index dc1f39274399..14f9a4e29211 100644 --- a/packages/state-transition/src/epoch/processSyncCommitteeUpdates.ts +++ b/packages/state-transition/src/epoch/processSyncCommitteeUpdates.ts @@ -23,7 +23,7 @@ export function processSyncCommitteeUpdates(state: CachedBeaconStateAltair): voi effectiveBalanceIncrements ); - // Using the index2pubkey cache is slower because it needs the serialized pubkey. + // Using the finalizedIndex2pubkey cache is slower because it needs the serialized pubkey. const nextSyncCommitteePubkeys = nextSyncCommitteeIndices.map( (index) => state.validators.getReadonly(index).pubkey ); diff --git a/packages/state-transition/src/signatureSets/attesterSlashings.ts b/packages/state-transition/src/signatureSets/attesterSlashings.ts index 6a985f84f64e..4c0c275e16a8 100644 --- a/packages/state-transition/src/signatureSets/attesterSlashings.ts +++ b/packages/state-transition/src/signatureSets/attesterSlashings.ts @@ -32,7 +32,7 @@ export function getIndexedAttestationBigintSignatureSet( return { type: SignatureSetType.aggregate, - pubkeys: indexedAttestation.attestingIndices.map((i) => state.epochCtx.index2pubkey[i]), + pubkeys: indexedAttestation.attestingIndices.map((i) => state.epochCtx.finalizedIndex2pubkey[i]), signingRoot: computeSigningRoot(ssz.phase0.AttestationDataBigint, indexedAttestation.data, domain), signature: indexedAttestation.signature, }; diff --git a/packages/state-transition/src/signatureSets/indexedAttestation.ts b/packages/state-transition/src/signatureSets/indexedAttestation.ts index b5c48a20c9d4..b7765e86a78d 100644 --- a/packages/state-transition/src/signatureSets/indexedAttestation.ts +++ b/packages/state-transition/src/signatureSets/indexedAttestation.ts @@ -24,7 +24,7 @@ export function getAttestationWithIndicesSignatureSet( attestingIndices: number[] ): ISignatureSet { return createAggregateSignatureSetFromComponents( - attestingIndices.map((i) => state.epochCtx.index2pubkey[i]), + attestingIndices.map((i) => state.epochCtx.finalizedIndex2pubkey[i]), getAttestationDataSigningRoot(state, attestation.data), attestation.signature ); diff --git a/packages/state-transition/src/signatureSets/proposer.ts b/packages/state-transition/src/signatureSets/proposer.ts index 135ac7ed5c7a..b159e9824ac6 100644 --- a/packages/state-transition/src/signatureSets/proposer.ts +++ b/packages/state-transition/src/signatureSets/proposer.ts @@ -25,7 +25,7 @@ export function getBlockProposerSignatureSet( return { type: SignatureSetType.single, - pubkey: epochCtx.index2pubkey[signedBlock.message.proposerIndex], + pubkey: epochCtx.finalizedIndex2pubkey[signedBlock.message.proposerIndex], signingRoot: computeSigningRoot(blockType, signedBlock.message, domain), signature: signedBlock.signature, }; @@ -40,7 +40,7 @@ export function getBlockHeaderProposerSignatureSet( return { type: SignatureSetType.single, - pubkey: epochCtx.index2pubkey[signedBlockHeader.message.proposerIndex], + pubkey: epochCtx.finalizedIndex2pubkey[signedBlockHeader.message.proposerIndex], signingRoot: computeSigningRoot(ssz.phase0.BeaconBlockHeader, signedBlockHeader.message, domain), signature: signedBlockHeader.signature, }; diff --git a/packages/state-transition/src/signatureSets/proposerSlashings.ts b/packages/state-transition/src/signatureSets/proposerSlashings.ts index 46be30c0636e..d3ee2dd75ce8 100644 --- a/packages/state-transition/src/signatureSets/proposerSlashings.ts +++ b/packages/state-transition/src/signatureSets/proposerSlashings.ts @@ -11,7 +11,7 @@ export function getProposerSlashingSignatureSets( proposerSlashing: phase0.ProposerSlashing ): ISignatureSet[] { const {epochCtx} = state; - const pubkey = epochCtx.index2pubkey[proposerSlashing.signedHeader1.message.proposerIndex]; + const pubkey = epochCtx.finalizedIndex2pubkey[proposerSlashing.signedHeader1.message.proposerIndex]; // In state transition, ProposerSlashing headers are only partially validated. Their slot could be higher than the // clock and the slashing would still be valid. Must use bigint variants to hash correctly to all possible values diff --git a/packages/state-transition/src/signatureSets/randao.ts b/packages/state-transition/src/signatureSets/randao.ts index 4fc8b77e4e38..5ba757a73473 100644 --- a/packages/state-transition/src/signatureSets/randao.ts +++ b/packages/state-transition/src/signatureSets/randao.ts @@ -27,7 +27,7 @@ export function getRandaoRevealSignatureSet( return { type: SignatureSetType.single, - pubkey: epochCtx.index2pubkey[block.proposerIndex], + pubkey: epochCtx.finalizedIndex2pubkey[block.proposerIndex], signingRoot: computeSigningRoot(ssz.Epoch, epoch, domain), signature: block.body.randaoReveal, }; diff --git a/packages/state-transition/src/signatureSets/voluntaryExits.ts b/packages/state-transition/src/signatureSets/voluntaryExits.ts index bb86ef41777a..b1dedc0d36a7 100644 --- a/packages/state-transition/src/signatureSets/voluntaryExits.ts +++ b/packages/state-transition/src/signatureSets/voluntaryExits.ts @@ -28,7 +28,7 @@ export function getVoluntaryExitSignatureSet( return { type: SignatureSetType.single, - pubkey: epochCtx.index2pubkey[signedVoluntaryExit.message.validatorIndex], + pubkey: epochCtx.finalizedIndex2pubkey[signedVoluntaryExit.message.validatorIndex], signingRoot: computeSigningRoot(ssz.phase0.VoluntaryExit, signedVoluntaryExit.message, domain), signature: signedVoluntaryExit.signature, }; diff --git a/packages/state-transition/src/util/genesis.ts b/packages/state-transition/src/util/genesis.ts index 6c700436d7f6..020ea638e54b 100644 --- a/packages/state-transition/src/util/genesis.ts +++ b/packages/state-transition/src/util/genesis.ts @@ -12,7 +12,7 @@ import { import {Bytes32, phase0, Root, ssz, TimeSeconds} from "@lodestar/types"; import {CachedBeaconStateAllForks, BeaconStateAllForks} from "../types.js"; -import {createCachedBeaconState} from "../cache/stateCache.js"; +import {createFinalizedCachedBeaconState} from "../cache/stateCache.js"; import {EpochCacheImmutableData} from "../cache/epochCache.js"; import {processDeposit} from "../block/processDeposit.js"; import {computeEpochAtSlot} from "./epoch.js"; @@ -234,7 +234,7 @@ export function initializeBeaconStateFromEth1( // - 3. interop state: Only supports starting from genesis at phase0 fork // So it's okay to skip syncing the sync committee cache here and expect it to be // populated latter when the altair fork happens for cases 2, 3. - const state = createCachedBeaconState(stateView, immutableData, {skipSyncCommitteeCache: true}); + const state = createFinalizedCachedBeaconState(stateView, immutableData, {skipSyncCommitteeCache: true}); applyTimestamp(config, state, eth1Timestamp); applyEth1BlockHash(state, eth1BlockHash); diff --git a/packages/state-transition/src/util/syncCommittee.ts b/packages/state-transition/src/util/syncCommittee.ts index 89c476b69c04..dcb279d46637 100644 --- a/packages/state-transition/src/util/syncCommittee.ts +++ b/packages/state-transition/src/util/syncCommittee.ts @@ -25,7 +25,7 @@ export function getNextSyncCommittee( ): {indices: ValidatorIndex[]; syncCommittee: altair.SyncCommittee} { const indices = getNextSyncCommitteeIndices(state, activeValidatorIndices, effectiveBalanceIncrements); - // Using the index2pubkey cache is slower because it needs the serialized pubkey. + // Using the finalizedIndex2pubkey cache is slower because it needs the serialized pubkey. const pubkeys = indices.map((index) => state.validators.getReadonly(index).pubkey); return { diff --git a/packages/state-transition/test/perf/util.ts b/packages/state-transition/test/perf/util.ts index 4df6746ea938..ded10c47bf15 100644 --- a/packages/state-transition/test/perf/util.ts +++ b/packages/state-transition/test/perf/util.ts @@ -85,32 +85,32 @@ export function getSecretKeyFromIndexCached(validatorIndex: number): SecretKey { } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -function getPubkeyCaches({pubkeysMod, pubkeysModObj}: ReturnType) { +function getFinalizedPubkeyCaches({pubkeysMod, pubkeysModObj}: ReturnType) { // Manually sync pubkeys to prevent doing BLS opts 110_000 times - const pubkey2index = new PubkeyIndexMap(); - const index2pubkey = [] as PublicKey[]; + const finalizedPubkey2index = new PubkeyIndexMap(); + const finalizedIndex2pubkey = [] as PublicKey[]; for (let i = 0; i < numValidators; i++) { const pubkey = pubkeysMod[i % keypairsMod]; const pubkeyObj = pubkeysModObj[i % keypairsMod]; - pubkey2index.set(pubkey, i); - index2pubkey.push(pubkeyObj); + finalizedPubkey2index.set(pubkey, i); + finalizedIndex2pubkey.push(pubkeyObj); } - // Since most pubkeys are equal the size of pubkey2index is not numValidators. + // Since most pubkeys are equal the size of finalizedPubkey2index is not numValidators. // Fill with junk up to numValidators - for (let i = pubkey2index.size; i < numValidators; i++) { + for (let i = finalizedPubkey2index.size; i < numValidators; i++) { const buf = Buffer.alloc(48, 0); buf.writeInt32LE(i); - pubkey2index.set(buf, i); + finalizedPubkey2index.set(buf, i); } - return {pubkey2index, index2pubkey}; + return {finalizedPubkey2index, finalizedIndex2pubkey}; } export function generatePerfTestCachedStatePhase0(opts?: {goBackOneSlot: boolean}): CachedBeaconStatePhase0 { // Generate only some publicKeys const {pubkeys, pubkeysMod, pubkeysModObj} = getPubkeys(); - const {pubkey2index, index2pubkey} = getPubkeyCaches({pubkeys, pubkeysMod, pubkeysModObj}); + const {finalizedPubkey2index, finalizedIndex2pubkey} = getFinalizedPubkeyCaches({pubkeys, pubkeysMod, pubkeysModObj}); if (!phase0State) { const state = buildPerformanceStatePhase0(); @@ -127,8 +127,8 @@ export function generatePerfTestCachedStatePhase0(opts?: {goBackOneSlot: boolean state.slot -= 1; phase0CachedState23637 = createCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), - pubkey2index, - index2pubkey, + finalizedPubkey2index, + finalizedIndex2pubkey, }); const currentEpoch = computeEpochAtSlot(state.slot - 1); @@ -220,7 +220,7 @@ export function generatePerfTestCachedStateAltair(opts?: { vc?: number; }): CachedBeaconStateAltair { const {pubkeys, pubkeysMod, pubkeysModObj} = getPubkeys(opts?.vc); - const {pubkey2index, index2pubkey} = getPubkeyCaches({pubkeys, pubkeysMod, pubkeysModObj}); + const {finalizedPubkey2index, finalizedIndex2pubkey} = getFinalizedPubkeyCaches({pubkeys, pubkeysMod, pubkeysModObj}); // eslint-disable-next-line @typescript-eslint/naming-convention const altairConfig = createChainForkConfig({ALTAIR_FORK_EPOCH: 0}); @@ -232,8 +232,8 @@ export function generatePerfTestCachedStateAltair(opts?: { state.slot -= 1; altairCachedState23637 = createCachedBeaconState(state, { config: createBeaconConfig(altairConfig, state.genesisValidatorsRoot), - pubkey2index, - index2pubkey, + finalizedPubkey2index, + finalizedIndex2pubkey, }); } if (!altairCachedState23638) { @@ -389,13 +389,13 @@ export function generateTestCachedBeaconStateOnlyValidators({ const {pubkeys, pubkeysMod, pubkeysModObj} = getPubkeys(vc); // Manually sync pubkeys to prevent doing BLS opts 110_000 times - const pubkey2index = new PubkeyIndexMap(); - const index2pubkey = [] as PublicKey[]; + const finalizedPubkey2index = new PubkeyIndexMap(); + const finalizedIndex2pubkey = [] as PublicKey[]; for (let i = 0; i < vc; i++) { const pubkey = pubkeysMod[i % keypairsMod]; const pubkeyObj = pubkeysModObj[i % keypairsMod]; - pubkey2index.set(pubkey, i); - index2pubkey.push(pubkeyObj); + finalizedPubkey2index.set(pubkey, i); + finalizedIndex2pubkey.push(pubkeyObj); } const state = ssz.phase0.BeaconState.defaultViewDU(); @@ -435,8 +435,8 @@ export function generateTestCachedBeaconStateOnlyValidators({ state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), - pubkey2index, - index2pubkey, + finalizedPubkey2index, + finalizedIndex2pubkey, }, {skipSyncPubkeys: true} ); diff --git a/packages/state-transition/test/perf/util/loadState/loadState.test.ts b/packages/state-transition/test/perf/util/loadState/loadState.test.ts index 5d40c64f6ab4..af8697d88fc8 100644 --- a/packages/state-transition/test/perf/util/loadState/loadState.test.ts +++ b/packages/state-transition/test/perf/util/loadState/loadState.test.ts @@ -2,7 +2,7 @@ import bls from "@chainsafe/bls"; import {CoordType} from "@chainsafe/bls/types"; import {itBench, setBenchOpts} from "@dapplion/benchmark"; import {loadState} from "../../../../src/util/loadState/loadState.js"; -import {createCachedBeaconState} from "../../../../src/cache/stateCache.js"; +import {createFinalizedCachedBeaconState} from "../../../../src/cache/stateCache.js"; import {Index2PubkeyCache, PubkeyIndexMap} from "../../../../src/cache/pubkeyCache.js"; import {generatePerfTestCachedStateAltair} from "../../util.js"; @@ -72,23 +72,23 @@ describe("loadState", function () { migratedState.hashTreeRoot(); // Get the validators sub tree once for all the loop const validators = migratedState.validators; - const pubkey2index = new PubkeyIndexMap(); - const index2pubkey: Index2PubkeyCache = []; + const finalizedPubkey2index = new PubkeyIndexMap(); + const finalizedIndex2pubkey: Index2PubkeyCache = []; for (const validatorIndex of modifiedValidators) { const validator = validators.getReadonly(validatorIndex); const pubkey = validator.pubkey; - pubkey2index.set(pubkey, validatorIndex); - index2pubkey[validatorIndex] = bls.PublicKey.fromBytes(pubkey, CoordType.jacobian); + finalizedPubkey2index.set(pubkey, validatorIndex); + finalizedIndex2pubkey[validatorIndex] = bls.PublicKey.fromBytes(pubkey, CoordType.jacobian); } // skip computimg shuffling in performance test because in reality we have a ShufflingCache // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const shufflingGetter = () => seedState.epochCtx.currentShuffling; - createCachedBeaconState( + createFinalizedCachedBeaconState( migratedState, { config: seedState.config, - pubkey2index, - index2pubkey, + finalizedPubkey2index, + finalizedIndex2pubkey, }, {skipSyncPubkeys: true, skipSyncCommitteeCache: true, shufflingGetter} ); diff --git a/packages/state-transition/test/unit/cachedBeaconState.test.ts b/packages/state-transition/test/unit/cachedBeaconState.test.ts index 9b05b8947f73..994701800bb6 100644 --- a/packages/state-transition/test/unit/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/cachedBeaconState.test.ts @@ -6,7 +6,7 @@ import {config as defaultConfig} from "@lodestar/config/default"; import {createBeaconConfig, createChainForkConfig} from "@lodestar/config"; import {createCachedBeaconStateTest} from "../utils/state.js"; import {PubkeyIndexMap} from "../../src/cache/pubkeyCache.js"; -import {createCachedBeaconState, loadCachedBeaconState} from "../../src/cache/stateCache.js"; +import {createFinalizedCachedBeaconState, loadUnfinalizedCachedBeaconState} from "../../src/cache/stateCache.js"; import {interopPubkeysCached} from "../utils/interop.js"; import {modifyStateSameValidator, newStateWithValidators} from "../utils/capella.js"; import {EpochShuffling, getShufflingDecisionBlock} from "../../src/util/epochShuffling.js"; @@ -119,12 +119,12 @@ describe("CachedBeaconState", () => { const stateView = newStateWithValidators(numValidator); const config = createBeaconConfig(defaultConfig, stateView.genesisValidatorsRoot); - const seedState = createCachedBeaconState( + const seedState = createFinalizedCachedBeaconState( stateView, { config, - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }, {skipSyncCommitteeCache: true} ); @@ -189,7 +189,7 @@ describe("CachedBeaconState", () => { // confirm loadState() result const stateBytes = state.serialize(); - const newCachedState = loadCachedBeaconState(seedState, stateBytes, {skipSyncCommitteeCache: true}); + const newCachedState = loadUnfinalizedCachedBeaconState(seedState, stateBytes, {skipSyncCommitteeCache: true}); const newStateBytes = newCachedState.serialize(); expect(newStateBytes).toEqual(stateBytes); expect(newCachedState.hashTreeRoot()).toEqual(state.hashTreeRoot()); @@ -217,12 +217,12 @@ describe("CachedBeaconState", () => { return null; }; - const cachedState = createCachedBeaconState( + const cachedState = createFinalizedCachedBeaconState( state, { config, - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }, {skipSyncCommitteeCache: true, shufflingGetter} ); @@ -233,8 +233,8 @@ describe("CachedBeaconState", () => { // confirm loadCachedBeaconState() result for (let i = 0; i < newCachedState.validators.length; i++) { - expect(newCachedState.epochCtx.pubkey2index.get(newCachedState.validators.get(i).pubkey)).toBe(i); - expect(newCachedState.epochCtx.index2pubkey[i].toBytes()).toEqual(pubkeys[i]); + expect(newCachedState.epochCtx.finalizedPubkey2index.get(newCachedState.validators.get(i).pubkey)).toBe(i); + expect(newCachedState.epochCtx.finalizedIndex2pubkey[i].toBytes()).toEqual(pubkeys[i]); } }); } diff --git a/packages/state-transition/test/unit/upgradeState.test.ts b/packages/state-transition/test/unit/upgradeState.test.ts index df9b052542f9..19af491ce7e6 100644 --- a/packages/state-transition/test/unit/upgradeState.test.ts +++ b/packages/state-transition/test/unit/upgradeState.test.ts @@ -6,19 +6,19 @@ import {config as chainConfig} from "@lodestar/config/default"; import {upgradeStateToDeneb} from "../../src/slot/upgradeStateToDeneb.js"; import {upgradeStateToElectra} from "../../src/slot/upgradeStateToElectra.js"; -import {createCachedBeaconState} from "../../src/cache/stateCache.js"; +import {createFinalizedCachedBeaconState} from "../../src/cache/stateCache.js"; import {PubkeyIndexMap} from "../../src/cache/pubkeyCache.js"; describe("upgradeState", () => { it("upgradeStateToDeneb", () => { const capellaState = ssz.capella.BeaconState.defaultViewDU(); const config = getConfig(ForkName.capella); - const stateView = createCachedBeaconState( + const stateView = createFinalizedCachedBeaconState( capellaState, { config: createBeaconConfig(config, capellaState.genesisValidatorsRoot), - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }, {skipSyncCommitteeCache: true} ); @@ -28,12 +28,12 @@ describe("upgradeState", () => { it("upgradeStateToElectra", () => { const denebState = ssz.deneb.BeaconState.defaultViewDU(); const config = getConfig(ForkName.deneb); - const stateView = createCachedBeaconState( + const stateView = createFinalizedCachedBeaconState( denebState, { config: createBeaconConfig(config, denebState.genesisValidatorsRoot), - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }, {skipSyncCommitteeCache: true} ); diff --git a/packages/state-transition/test/unit/util/cachedBeaconState.test.ts b/packages/state-transition/test/unit/util/cachedBeaconState.test.ts index 654e0752adb8..057107b25427 100644 --- a/packages/state-transition/test/unit/util/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/util/cachedBeaconState.test.ts @@ -10,8 +10,8 @@ describe("CachedBeaconState", () => { createCachedBeaconState(emptyState, { config: createBeaconConfig(config, emptyState.genesisValidatorsRoot), - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }); }); }); diff --git a/packages/state-transition/test/utils/state.ts b/packages/state-transition/test/utils/state.ts index 29a1f98b5562..b466d03fd2af 100644 --- a/packages/state-transition/test/utils/state.ts +++ b/packages/state-transition/test/utils/state.ts @@ -92,8 +92,8 @@ export function generateCachedState( return createCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a test state, there's no need to have a global shared cache of keys - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }); } @@ -107,8 +107,8 @@ export function createCachedBeaconStateTest( { config: createBeaconConfig(configCustom, state.genesisValidatorsRoot), // This is a test state, there's no need to have a global shared cache of keys - pubkey2index: new PubkeyIndexMap(), - index2pubkey: [], + finalizedPubkey2index: new PubkeyIndexMap(), + finalizedIndex2pubkey: [], }, opts ); From 5098e7c462eb6d67a6e931b08b63453116d1f649 Mon Sep 17 00:00:00 2001 From: NC Date: Tue, 12 Mar 2024 15:46:20 +0800 Subject: [PATCH 5/9] Rename state cache functions --- packages/beacon-node/src/chain/chain.ts | 4 ++-- packages/beacon-node/src/chain/genesis/genesis.ts | 4 ++-- .../chain/stateCache/persistentCheckpointsCache.ts | 4 ++-- .../test/e2e/eth1/eth1ForBlockProduction.test.ts | 4 ++-- .../test/spec/presets/epoch_processing.test.ts | 4 ++-- .../beacon-node/test/spec/presets/finality.test.ts | 4 ++-- .../beacon-node/test/spec/presets/fork.test.ts | 4 ++-- .../test/spec/presets/fork_choice.test.ts | 4 ++-- .../test/spec/presets/operations.test.ts | 4 ++-- .../beacon-node/test/spec/presets/rewards.test.ts | 4 ++-- .../beacon-node/test/spec/presets/sanity.test.ts | 6 +++--- .../test/spec/presets/transition.test.ts | 4 ++-- .../api/impl/validator/duties/proposer.test.ts | 4 ++-- .../test/unit/chain/forkChoice/forkChoice.test.ts | 6 +++--- .../chain/validation/blsToExecutionChange.test.ts | 4 ++-- .../unit/chain/validation/voluntaryExit.test.ts | 4 ++-- .../test/unit/eth1/utils/deposits.test.ts | 4 ++-- .../beacon-node/test/utils/cachedBeaconState.ts | 6 +++--- .../beacon-node/test/utils/networkWithMockDb.ts | 4 ++-- packages/beacon-node/test/utils/state.ts | 10 +++++----- packages/state-transition/src/cache/stateCache.ts | 10 +++++----- packages/state-transition/src/index.ts | 4 ++-- .../state-transition/test/perf/analyzeEpochs.ts | 4 ++-- .../epoch/processEffectiveBalanceUpdates.test.ts | 4 ++-- packages/state-transition/test/perf/util.ts | 8 ++++---- .../test/unit/cachedBeaconState.test.ts | 14 +++++++------- .../test/unit/util/cachedBeaconState.test.ts | 4 ++-- .../test/unit/util/deposit.test.ts | 8 ++++---- packages/state-transition/test/utils/capella.ts | 6 +++--- packages/state-transition/test/utils/state.ts | 8 ++++---- .../state-transition/test/utils/testFileCache.ts | 6 +++--- 31 files changed, 84 insertions(+), 84 deletions(-) diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 23603a914322..6f135bb60a2f 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -5,7 +5,7 @@ import { CachedBeaconStateAllForks, computeEpochAtSlot, computeStartSlotAtEpoch, - createCachedBeaconState, + createFinalizedCachedBeaconState, EffectiveBalanceIncrements, getEffectiveBalanceIncrementsZeroInactive, isCachedBeaconState, @@ -233,7 +233,7 @@ export class BeaconChain implements IBeaconChain { const cachedState = isCachedBeaconState(anchorState) && opts.skipCreateStateCacheIfAvailable ? anchorState - : createCachedBeaconState(anchorState, { + : createFinalizedCachedBeaconState(anchorState, { config, finalizedPubkey2index: new PubkeyIndexMap(), finalizedIndex2pubkey: [], diff --git a/packages/beacon-node/src/chain/genesis/genesis.ts b/packages/beacon-node/src/chain/genesis/genesis.ts index 979476c69530..4b172fd8a9a2 100644 --- a/packages/beacon-node/src/chain/genesis/genesis.ts +++ b/packages/beacon-node/src/chain/genesis/genesis.ts @@ -9,7 +9,7 @@ import { applyTimestamp, applyEth1BlockHash, CachedBeaconStateAllForks, - createCachedBeaconState, + createFinalizedCachedBeaconState, BeaconStateAllForks, createEmptyEpochCacheImmutableData, getActiveValidatorIndices, @@ -86,7 +86,7 @@ export class GenesisBuilder implements IGenesisBuilder { } // TODO - PENDING: Ensure EpochCacheImmutableData is created only once - this.state = createCachedBeaconState(stateView, createEmptyEpochCacheImmutableData(config, stateView)); + this.state = createFinalizedCachedBeaconState(stateView, createEmptyEpochCacheImmutableData(config, stateView)); this.config = this.state.config; this.activatedValidatorCount = getActiveValidatorIndices(stateView, GENESIS_EPOCH).length; } diff --git a/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts b/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts index fe96d1a7923e..ddc9a16a9613 100644 --- a/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts +++ b/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts @@ -3,7 +3,7 @@ import {phase0, Epoch, RootHex} from "@lodestar/types"; import {CachedBeaconStateAllForks, computeStartSlotAtEpoch, getBlockRootAtSlot} from "@lodestar/state-transition"; import {Logger, MapDef, sleep} from "@lodestar/utils"; import {routes} from "@lodestar/api"; -import {loadCachedBeaconState} from "@lodestar/state-transition"; +import {loadUnfinalizedCachedBeaconState} from "@lodestar/state-transition"; import {INTERVALS_PER_SLOT} from "@lodestar/params"; import {Metrics} from "../../metrics/index.js"; import {IClock} from "../../util/clock.js"; @@ -217,7 +217,7 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache { } sszTimer?.(); const timer = this.metrics?.stateReloadDuration.startTimer(); - const newCachedState = loadCachedBeaconState( + const newCachedState = loadUnfinalizedCachedBeaconState( seedState, stateBytes, { diff --git a/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts b/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts index 1a80d95c1fdc..34ac4ca46824 100644 --- a/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts +++ b/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts @@ -14,7 +14,7 @@ import {BeaconDb} from "../../../src/db/index.js"; import {generateState} from "../../utils/state.js"; import {Eth1Provider} from "../../../src/eth1/provider/eth1Provider.js"; import {getGoerliRpcUrl} from "../../testParams.js"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; const dbLocation = "./.__testdb"; @@ -112,7 +112,7 @@ describe.skip("eth1 / Eth1Provider", function () { config ); - const state = createCachedBeaconStateTest(tbState, config); + const state = createFinalizedCachedBeaconStateTest(tbState, config); const result = await eth1ForBlockProduction.getEth1DataAndDeposits(state); expect(result.eth1Data).toEqual(latestEth1Data); diff --git a/packages/beacon-node/test/spec/presets/epoch_processing.test.ts b/packages/beacon-node/test/spec/presets/epoch_processing.test.ts index a244762143f3..c61ee22d1698 100644 --- a/packages/beacon-node/test/spec/presets/epoch_processing.test.ts +++ b/packages/beacon-node/test/spec/presets/epoch_processing.test.ts @@ -9,7 +9,7 @@ import { import * as epochFns from "@lodestar/state-transition/epoch"; import {ssz} from "@lodestar/types"; import {ACTIVE_PRESET} from "@lodestar/params"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {getConfig} from "../../utils/config.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; @@ -64,7 +64,7 @@ const epochProcessing = return { testFunction: (testcase) => { const stateTB = testcase.pre.clone(); - const state = createCachedBeaconStateTest(stateTB, config); + const state = createFinalizedCachedBeaconStateTest(stateTB, config); const epochTransitionCache = beforeProcessEpoch(state, {assertCorrectProgressiveBalances}); diff --git a/packages/beacon-node/test/spec/presets/finality.test.ts b/packages/beacon-node/test/spec/presets/finality.test.ts index 0ec4017064f4..c1a22740785f 100644 --- a/packages/beacon-node/test/spec/presets/finality.test.ts +++ b/packages/beacon-node/test/spec/presets/finality.test.ts @@ -7,7 +7,7 @@ import { } from "@lodestar/state-transition"; import {altair, bellatrix, ssz} from "@lodestar/types"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {RunnerType, shouldVerify, TestRunnerFn} from "../utils/types.js"; import {getConfig} from "../../utils/config.js"; @@ -20,7 +20,7 @@ import {specTestIterator} from "../utils/specTestIterator.js"; const finality: TestRunnerFn = (fork) => { return { testFunction: (testcase) => { - let state = createCachedBeaconStateTest(testcase.pre, getConfig(fork)); + let state = createFinalizedCachedBeaconStateTest(testcase.pre, getConfig(fork)); const verify = shouldVerify(testcase); for (let i = 0; i < testcase.meta.blocks_count; i++) { const signedBlock = testcase[`blocks_${i}`] as bellatrix.SignedBeaconBlock; diff --git a/packages/beacon-node/test/spec/presets/fork.test.ts b/packages/beacon-node/test/spec/presets/fork.test.ts index c121e651fcea..1a2634680e8a 100644 --- a/packages/beacon-node/test/spec/presets/fork.test.ts +++ b/packages/beacon-node/test/spec/presets/fork.test.ts @@ -12,7 +12,7 @@ import {phase0, ssz} from "@lodestar/types"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; import {createChainForkConfig, ChainForkConfig} from "@lodestar/config"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; import {ethereumConsensusSpecsTests} from "../specTestVersioning.js"; import {specTestIterator} from "../utils/specTestIterator.js"; @@ -23,7 +23,7 @@ const fork: TestRunnerFn = (forkNext) => { return { testFunction: (testcase) => { - const preState = createCachedBeaconStateTest(testcase.pre, config); + const preState = createFinalizedCachedBeaconStateTest(testcase.pre, config); switch (forkNext) { case ForkName.phase0: diff --git a/packages/beacon-node/test/spec/presets/fork_choice.test.ts b/packages/beacon-node/test/spec/presets/fork_choice.test.ts index 49d78cc42f6a..cd439ea2fbc0 100644 --- a/packages/beacon-node/test/spec/presets/fork_choice.test.ts +++ b/packages/beacon-node/test/spec/presets/fork_choice.test.ts @@ -11,7 +11,7 @@ import {ACTIVE_PRESET, ForkSeq, isForkBlobs} from "@lodestar/params"; import {BeaconChain, ChainEvent} from "../../../src/chain/index.js"; import {ClockEvent} from "../../../src/util/clock.js"; import {computeInclusionProof} from "../../../src/util/blobs.js"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {testLogger} from "../../utils/logger.js"; import {getConfig} from "../../utils/config.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; @@ -60,7 +60,7 @@ const forkChoiceTest = const {steps, anchorState} = testcase; const currentSlot = anchorState.slot; const config = getConfig(fork); - const state = createCachedBeaconStateTest(anchorState, config); + const state = createFinalizedCachedBeaconStateTest(anchorState, config); /** This is to track test's tickTime to be used in proposer boost */ let tickTime = 0; diff --git a/packages/beacon-node/test/spec/presets/operations.test.ts b/packages/beacon-node/test/spec/presets/operations.test.ts index 2c4ae22475a5..3a91b91ed904 100644 --- a/packages/beacon-node/test/spec/presets/operations.test.ts +++ b/packages/beacon-node/test/spec/presets/operations.test.ts @@ -13,7 +13,7 @@ import {ssz, phase0, altair, bellatrix, capella, electra} from "@lodestar/types" import {InputType} from "@lodestar/spec-test-util"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {getConfig} from "../../utils/config.js"; import {BaseSpecTest, RunnerType, shouldVerify, TestRunnerFn} from "../utils/types.js"; @@ -111,7 +111,7 @@ const operations: TestRunnerFn = (fork, testFunction: (testcase) => { const state = testcase.pre.clone(); const epoch = (state.fork as phase0.Fork).epoch; - const cachedState = createCachedBeaconStateTest(state, getConfig(fork, epoch)); + const cachedState = createFinalizedCachedBeaconStateTest(state, getConfig(fork, epoch)); operationFn(cachedState, testcase); state.commit(); diff --git a/packages/beacon-node/test/spec/presets/rewards.test.ts b/packages/beacon-node/test/spec/presets/rewards.test.ts index 426245242d84..061ed2ff3cde 100644 --- a/packages/beacon-node/test/spec/presets/rewards.test.ts +++ b/packages/beacon-node/test/spec/presets/rewards.test.ts @@ -5,7 +5,7 @@ import {BeaconStateAllForks, beforeProcessEpoch} from "@lodestar/state-transitio import {getRewardsAndPenalties} from "@lodestar/state-transition/epoch"; import {ssz} from "@lodestar/types"; import {ACTIVE_PRESET} from "@lodestar/params"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {getConfig} from "../../utils/config.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; @@ -21,7 +21,7 @@ const rewards: TestRunnerFn = (fork) => { return { testFunction: (testcase) => { const config = getConfig(fork); - const wrappedState = createCachedBeaconStateTest(testcase.pre, config); + const wrappedState = createFinalizedCachedBeaconStateTest(testcase.pre, config); const epochTransitionCache = beforeProcessEpoch(wrappedState, {assertCorrectProgressiveBalances}); // To debug this test and get granular results you can tweak inputs to get more granular results diff --git a/packages/beacon-node/test/spec/presets/sanity.test.ts b/packages/beacon-node/test/spec/presets/sanity.test.ts index 57afb8cf3d28..223fab0ded08 100644 --- a/packages/beacon-node/test/spec/presets/sanity.test.ts +++ b/packages/beacon-node/test/spec/presets/sanity.test.ts @@ -10,7 +10,7 @@ import { import {allForks, deneb, ssz} from "@lodestar/types"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; import {bnToNum} from "@lodestar/utils"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {RunnerType, shouldVerify, TestRunnerFn} from "../utils/types.js"; import {getConfig} from "../../utils/config.js"; @@ -35,7 +35,7 @@ const sanitySlots: TestRunnerFn = (for return { testFunction: (testcase) => { const stateTB = testcase.pre.clone(); - const state = createCachedBeaconStateTest(stateTB, getConfig(fork)); + const state = createFinalizedCachedBeaconStateTest(stateTB, getConfig(fork)); const postState = processSlots(state, state.slot + bnToNum(testcase.slots), {assertCorrectProgressiveBalances}); // TODO: May be part of runStateTranstion, necessary to commit again? postState.commit(); @@ -62,7 +62,7 @@ const sanityBlocks: TestRunnerFn = (f return { testFunction: (testcase) => { const stateTB = testcase.pre; - let wrappedState = createCachedBeaconStateTest(stateTB, getConfig(fork)); + let wrappedState = createFinalizedCachedBeaconStateTest(stateTB, getConfig(fork)); const verify = shouldVerify(testcase); for (let i = 0; i < testcase.meta.blocks_count; i++) { const signedBlock = testcase[`blocks_${i}`] as deneb.SignedBeaconBlock; diff --git a/packages/beacon-node/test/spec/presets/transition.test.ts b/packages/beacon-node/test/spec/presets/transition.test.ts index 1f98dbb41f58..f5cb0decdea1 100644 --- a/packages/beacon-node/test/spec/presets/transition.test.ts +++ b/packages/beacon-node/test/spec/presets/transition.test.ts @@ -11,7 +11,7 @@ import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; import {bnToNum} from "@lodestar/utils"; import {config} from "@lodestar/config/default"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; -import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; import {assertCorrectProgressiveBalances} from "../config.js"; import {ethereumConsensusSpecsTests} from "../specTestVersioning.js"; @@ -51,7 +51,7 @@ const transition = const forkEpoch = bnToNum(meta.fork_epoch); const testConfig = createChainForkConfig(getTransitionConfig(forkNext, forkEpoch)); - let state = createCachedBeaconStateTest(testcase.pre, testConfig); + let state = createFinalizedCachedBeaconStateTest(testcase.pre, testConfig); for (let i = 0; i < meta.blocks_count; i++) { const signedBlock = testcase[`blocks_${i}`] as allForks.SignedBeaconBlock; state = stateTransition(state, signedBlock, { diff --git a/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts b/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts index bf9544ad683e..43acc0c9f379 100644 --- a/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts +++ b/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts @@ -7,7 +7,7 @@ import {FAR_FUTURE_EPOCH} from "../../../../../../src/constants/index.js"; import {SYNC_TOLERANCE_EPOCHS, getValidatorApi} from "../../../../../../src/api/impl/validator/index.js"; import {generateState, zeroProtoBlock} from "../../../../../utils/state.js"; import {generateValidators} from "../../../../../utils/validator.js"; -import {createCachedBeaconStateTest} from "../../../../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../../../../utils/cachedBeaconState.js"; import {SyncState} from "../../../../../../src/sync/interface.js"; describe("get proposers api impl", function () { @@ -33,7 +33,7 @@ describe("get proposers api impl", function () { }, config ); - cachedState = createCachedBeaconStateTest(state, config); + cachedState = createFinalizedCachedBeaconStateTest(state, config); modules.chain.getHeadStateAtCurrentEpoch.mockResolvedValue(cachedState); modules.forkChoice.getHead.mockReturnValue(zeroProtoBlock); diff --git a/packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts b/packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts index 20b6fc8d7a00..78b7ea2310b7 100644 --- a/packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts +++ b/packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts @@ -12,7 +12,7 @@ import {phase0, Slot, ssz, ValidatorIndex} from "@lodestar/types"; import {getTemporaryBlockHeader, processSlots} from "@lodestar/state-transition"; import {ChainEventEmitter, computeAnchorCheckpoint, initializeForkChoice} from "../../../../src/chain/index.js"; import {generateSignedBlockAtSlot} from "../../../utils/typeGenerator.js"; -import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; import {generateState} from "../../../utils/state.js"; import {generateValidators} from "../../../utils/validator.js"; @@ -21,7 +21,7 @@ vi.unmock("@lodestar/fork-choice"); describe("LodestarForkChoice", function () { let forkChoice: ForkChoice; - const anchorState = createCachedBeaconStateTest( + const anchorState = createFinalizedCachedBeaconStateTest( generateState( { slot: 0, @@ -53,7 +53,7 @@ describe("LodestarForkChoice", function () { let state: CachedBeaconStateAllForks; beforeAll(() => { - state = createCachedBeaconStateTest(anchorState, config); + state = createFinalizedCachedBeaconStateTest(anchorState, config); }); beforeEach(() => { diff --git a/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts b/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts index f83b900beb69..3791a4a25878 100644 --- a/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts @@ -19,7 +19,7 @@ import {generateState} from "../../../utils/state.js"; import {validateGossipBlsToExecutionChange} from "../../../../src/chain/validation/blsToExecutionChange.js"; import {BlsToExecutionChangeErrorCode} from "../../../../src/chain/errors/blsToExecutionChangeError.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; -import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; describe("validate bls to execution change", () => { let chainStub: MockedBeaconChain; @@ -70,7 +70,7 @@ describe("validate bls to execution change", () => { // Generate the state const _state = generateState(stateEmpty, defaultConfig); const config = createBeaconConfig(defaultConfig, _state.genesisValidatorsRoot); - const state = createCachedBeaconStateTest(_state, config); + const state = createFinalizedCachedBeaconStateTest(_state, config); // Gen a valid blsToExecutionChange for first val const blsToExecutionChange = { diff --git a/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts b/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts index 3966815c28ce..7e7b2d5cca74 100644 --- a/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts @@ -16,7 +16,7 @@ import {generateState} from "../../../utils/state.js"; import {validateGossipVoluntaryExit} from "../../../../src/chain/validation/voluntaryExit.js"; import {VoluntaryExitErrorCode} from "../../../../src/chain/errors/voluntaryExitError.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; -import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; describe("validate voluntary exit", () => { let chainStub: MockedBeaconChain; @@ -58,7 +58,7 @@ describe("validate voluntary exit", () => { signedVoluntaryExit = {message: voluntaryExit, signature: sk.sign(signingRoot).toBytes()}; const _state = generateState(stateEmpty, config); - state = createCachedBeaconStateTest(_state, createBeaconConfig(config, _state.genesisValidatorsRoot)); + state = createFinalizedCachedBeaconStateTest(_state, createBeaconConfig(config, _state.genesisValidatorsRoot)); }); beforeEach(() => { diff --git a/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts b/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts index 6cc3ae8f1ce0..6355eb5d6723 100644 --- a/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts +++ b/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts @@ -9,7 +9,7 @@ import {generateState} from "../../../utils/state.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; import {getDeposits, getDepositsWithProofs, DepositGetter} from "../../../../src/eth1/utils/deposits.js"; import {DepositTree} from "../../../../src/db/repositories/depositDataRoot.js"; -import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; describe("eth1 / util / deposits", function () { describe("getDeposits", () => { @@ -116,7 +116,7 @@ describe("eth1 / util / deposits", function () { const state = postElectra ? generateState({slot: postElectraSlot, eth1DepositIndex}, postElectraConfig) : generateState({eth1DepositIndex}); - const cachedState = createCachedBeaconStateTest( + const cachedState = createFinalizedCachedBeaconStateTest( state, postElectra ? postElectraConfig : createChainForkConfig({}) ); diff --git a/packages/beacon-node/test/utils/cachedBeaconState.ts b/packages/beacon-node/test/utils/cachedBeaconState.ts index 3efb16b6250f..f647ccbd1265 100644 --- a/packages/beacon-node/test/utils/cachedBeaconState.ts +++ b/packages/beacon-node/test/utils/cachedBeaconState.ts @@ -1,14 +1,14 @@ import { BeaconStateAllForks, BeaconStateCache, - createCachedBeaconState, + createFinalizedCachedBeaconState, createEmptyEpochCacheImmutableData, } from "@lodestar/state-transition"; import {ChainForkConfig} from "@lodestar/config"; -export function createCachedBeaconStateTest( +export function createFinalizedCachedBeaconStateTest( state: T, chainConfig: ChainForkConfig ): T & BeaconStateCache { - return createCachedBeaconState(state, createEmptyEpochCacheImmutableData(chainConfig, state)); + return createFinalizedCachedBeaconState(state, createEmptyEpochCacheImmutableData(chainConfig, state)); } diff --git a/packages/beacon-node/test/utils/networkWithMockDb.ts b/packages/beacon-node/test/utils/networkWithMockDb.ts index f8a4a6181d2f..e1c4fd8b1a1a 100644 --- a/packages/beacon-node/test/utils/networkWithMockDb.ts +++ b/packages/beacon-node/test/utils/networkWithMockDb.ts @@ -8,7 +8,7 @@ import {GossipHandlers, Network, NetworkInitModules, getReqRespHandlers} from ". import {NetworkOptions, defaultNetworkOptions} from "../../src/network/options.js"; import {GetReqRespHandlerFn} from "../../src/network/reqresp/types.js"; import {getMockedBeaconDb} from "../mocks/mockedBeaconDb.js"; -import {createCachedBeaconStateTest} from "./cachedBeaconState.js"; +import {createFinalizedCachedBeaconStateTest} from "./cachedBeaconState.js"; import {ClockStatic} from "./clock.js"; import {testLogger} from "./logger.js"; import {generateState} from "./state.js"; @@ -64,7 +64,7 @@ export async function getNetworkForTest( // mock timer does not work on worker thread clock: new ClockStatic(startSlot, Math.floor(Date.now() / 1000) - startSlot * beaconConfig.SECONDS_PER_SLOT), metrics: null, - anchorState: createCachedBeaconStateTest(state, beaconConfig), + anchorState: createFinalizedCachedBeaconStateTest(state, beaconConfig), eth1: new Eth1ForBlockProductionDisabled(), executionEngine: new ExecutionEngineDisabled(), } diff --git a/packages/beacon-node/test/utils/state.ts b/packages/beacon-node/test/utils/state.ts index 23b5ec577af3..2edc31c6dc36 100644 --- a/packages/beacon-node/test/utils/state.ts +++ b/packages/beacon-node/test/utils/state.ts @@ -3,7 +3,7 @@ import {config as minimalConfig} from "@lodestar/config/default"; import { BeaconStateAllForks, CachedBeaconStateAllForks, - createCachedBeaconState, + createFinalizedCachedBeaconState, PubkeyIndexMap, CachedBeaconStateBellatrix, BeaconStateBellatrix, @@ -110,7 +110,7 @@ export function generateState( export function generateCachedState(opts?: TestBeaconState): CachedBeaconStateAllForks { const config = getConfig(ForkName.phase0); const state = generateState(opts, config); - return createCachedBeaconState(state, { + return createFinalizedCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys finalizedPubkey2index: new PubkeyIndexMap(), @@ -124,7 +124,7 @@ export function generateCachedState(opts?: TestBeaconState): CachedBeaconStateAl export function generateCachedAltairState(opts?: TestBeaconState, altairForkEpoch = 0): CachedBeaconStateAllForks { const config = getConfig(ForkName.altair, altairForkEpoch); const state = generateState(opts, config); - return createCachedBeaconState(state, { + return createFinalizedCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys finalizedPubkey2index: new PubkeyIndexMap(), @@ -138,7 +138,7 @@ export function generateCachedAltairState(opts?: TestBeaconState, altairForkEpoc export function generateCachedBellatrixState(opts?: TestBeaconState): CachedBeaconStateBellatrix { const config = getConfig(ForkName.bellatrix); const state = generateState(opts, config); - return createCachedBeaconState(state as BeaconStateBellatrix, { + return createFinalizedCachedBeaconState(state as BeaconStateBellatrix, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys finalizedPubkey2index: new PubkeyIndexMap(), @@ -152,7 +152,7 @@ export function generateCachedBellatrixState(opts?: TestBeaconState): CachedBeac export function generateCachedElectraState(opts?: TestBeaconState): CachedBeaconStateElectra { const config = getConfig(ForkName.electra); const state = generateState(opts, config); - return createCachedBeaconState(state as BeaconStateElectra, { + return createFinalizedCachedBeaconState(state as BeaconStateElectra, { config: createBeaconConfig(config, state.genesisValidatorsRoot), finalizedPubkey2index: new PubkeyIndexMap(), finalizedIndex2pubkey: [], diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index bc110cd8de92..332940bd7cbc 100644 --- a/packages/state-transition/src/cache/stateCache.ts +++ b/packages/state-transition/src/cache/stateCache.ts @@ -138,9 +138,9 @@ export type CachedBeaconStateAllForks = CachedBeaconState; export type CachedBeaconStateExecutions = CachedBeaconState; /** * Create CachedBeaconState computing a new EpochCache instance - * TODO ELECTRA: rename this to createFinalizedCachedBeaconState() as it's intended for finalized state only + * Note: this is intended for finalized state only */ -export function createCachedBeaconState( +export function createFinalizedCachedBeaconState( state: T, immutableData: EpochCacheImmutableData, opts?: EpochCacheOpts @@ -162,9 +162,9 @@ export function createCachedBeaconState( * Create a CachedBeaconState given a cached seed state and state bytes * This guarantees that the returned state shares the same tree with the seed state * Check loadState() api for more details - * // TODO: rename to loadUnfinalizedCachedBeaconState() due to ELECTRA + * Note: This only loads unfinalized beacon state */ -export function loadCachedBeaconState( +export function loadUnfinalizedCachedBeaconState( cachedSeedState: T, stateBytes: Uint8Array, opts?: EpochCacheOpts, @@ -186,7 +186,7 @@ export function loadCachedBeaconState { it("Clone and mutate", () => { const stateView = ssz.altair.BeaconState.defaultViewDU(); - const state1 = createCachedBeaconStateTest(stateView); + const state1 = createFinalizedCachedBeaconStateTest(stateView); const state2 = state1.clone(); state1.slot = 1; @@ -31,7 +31,7 @@ describe("CachedBeaconState", () => { it("Clone and mutate cache pre-Electra", () => { const stateView = ssz.altair.BeaconState.defaultViewDU(); - const state1 = createCachedBeaconStateTest(stateView); + const state1 = createFinalizedCachedBeaconStateTest(stateView); const pubkey1 = fromHexString( "0x84105a985058fc8740a48bf1ede9d223ef09e8c6b1735ba0a55cf4a9ff2ff92376b778798365e488dab07a652eb04576" @@ -56,7 +56,7 @@ describe("CachedBeaconState", () => { /* eslint-disable @typescript-eslint/naming-convention */ it("Clone and mutate cache post-Electra", () => { const stateView = ssz.electra.BeaconState.defaultViewDU(); - const state1 = createCachedBeaconStateTest( + const state1 = createFinalizedCachedBeaconStateTest( stateView, createChainForkConfig({ ALTAIR_FORK_EPOCH: 0, @@ -113,7 +113,7 @@ describe("CachedBeaconState", () => { expect(toHexString(cp1.serialize())).toBe(toHexString(cp2.serialize())); }); - describe("loadCachedBeaconState", () => { + describe("loadUnfinalizedCachedBeaconState", () => { const numValidator = 16; const pubkeys = interopPubkeysCached(2 * numValidator); @@ -132,7 +132,7 @@ describe("CachedBeaconState", () => { const capellaStateType = ssz.capella.BeaconState; for (let validatorCountDelta = -numValidator; validatorCountDelta <= numValidator; validatorCountDelta++) { - const testName = `loadCachedBeaconState - ${validatorCountDelta > 0 ? "more" : "less"} ${Math.abs( + const testName = `loadUnfinalizedCachedBeaconState - ${validatorCountDelta > 0 ? "more" : "less"} ${Math.abs( validatorCountDelta )} validators`; it(testName, () => { @@ -231,7 +231,7 @@ describe("CachedBeaconState", () => { expect(newCachedState.epochCtx).toEqual(cachedState.epochCtx); } - // confirm loadCachedBeaconState() result + // confirm loadUnfinalizedCachedBeaconState() result for (let i = 0; i < newCachedState.validators.length; i++) { expect(newCachedState.epochCtx.finalizedPubkey2index.get(newCachedState.validators.get(i).pubkey)).toBe(i); expect(newCachedState.epochCtx.finalizedIndex2pubkey[i].toBytes()).toEqual(pubkeys[i]); diff --git a/packages/state-transition/test/unit/util/cachedBeaconState.test.ts b/packages/state-transition/test/unit/util/cachedBeaconState.test.ts index 057107b25427..6e8378694749 100644 --- a/packages/state-transition/test/unit/util/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/util/cachedBeaconState.test.ts @@ -2,13 +2,13 @@ import {describe, it} from "vitest"; import {createBeaconConfig} from "@lodestar/config"; import {config} from "@lodestar/config/default"; import {ssz} from "@lodestar/types"; -import {createCachedBeaconState, PubkeyIndexMap} from "../../../src/index.js"; +import {createFinalizedCachedBeaconState, PubkeyIndexMap} from "../../../src/index.js"; describe("CachedBeaconState", () => { it("Create empty CachedBeaconState", () => { const emptyState = ssz.phase0.BeaconState.defaultViewDU(); - createCachedBeaconState(emptyState, { + createFinalizedCachedBeaconState(emptyState, { config: createBeaconConfig(config, emptyState.genesisValidatorsRoot), finalizedPubkey2index: new PubkeyIndexMap(), finalizedIndex2pubkey: [], diff --git a/packages/state-transition/test/unit/util/deposit.test.ts b/packages/state-transition/test/unit/util/deposit.test.ts index e8f7f7a86af8..2eb889955eb4 100644 --- a/packages/state-transition/test/unit/util/deposit.test.ts +++ b/packages/state-transition/test/unit/util/deposit.test.ts @@ -3,12 +3,12 @@ import {ssz} from "@lodestar/types"; import {createChainForkConfig} from "@lodestar/config"; import {MAX_DEPOSITS} from "@lodestar/params"; import {CachedBeaconStateElectra, getEth1DepositCount} from "../../../src/index.js"; -import {createCachedBeaconStateTest} from "../../utils/state.js"; +import {createFinalizedCachedBeaconStateTest} from "../../utils/state.js"; describe("getEth1DepositCount", () => { it("Pre Electra", () => { const stateView = ssz.altair.BeaconState.defaultViewDU(); - const preElectraState = createCachedBeaconStateTest(stateView); + const preElectraState = createFinalizedCachedBeaconStateTest(stateView); if (preElectraState.epochCtx.isAfterElectra()) { throw Error("Not a pre-Electra state"); @@ -26,7 +26,7 @@ describe("getEth1DepositCount", () => { }); it("Post Electra with eth1 deposit", () => { const stateView = ssz.electra.BeaconState.defaultViewDU(); - const postElectraState = createCachedBeaconStateTest( + const postElectraState = createFinalizedCachedBeaconStateTest( stateView, createChainForkConfig({ /* eslint-disable @typescript-eslint/naming-convention */ @@ -60,7 +60,7 @@ describe("getEth1DepositCount", () => { }); it("Post Electra without eth1 deposit", () => { const stateView = ssz.electra.BeaconState.defaultViewDU(); - const postElectraState = createCachedBeaconStateTest( + const postElectraState = createFinalizedCachedBeaconStateTest( stateView, createChainForkConfig({ /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/packages/state-transition/test/utils/capella.ts b/packages/state-transition/test/utils/capella.ts index 7ef9248a5675..1a71e79f82eb 100644 --- a/packages/state-transition/test/utils/capella.ts +++ b/packages/state-transition/test/utils/capella.ts @@ -8,7 +8,7 @@ import { SLOTS_PER_HISTORICAL_ROOT, } from "@lodestar/params"; import {BeaconStateCapella, CachedBeaconStateCapella} from "../../src/index.js"; -import {createCachedBeaconStateTest} from "./state.js"; +import {createFinalizedCachedBeaconStateTest} from "./state.js"; import {mulberry32} from "./rand.js"; import {interopPubkeysCached} from "./interop.js"; @@ -63,11 +63,11 @@ export function getExpectedWithdrawalsTestData(vc: number, opts: WithdrawalOpts) state.commit(); - return createCachedBeaconStateTest(state, config, {skipSyncPubkeys: true}); + return createFinalizedCachedBeaconStateTest(state, config, {skipSyncPubkeys: true}); } export function newStateWithValidators(numValidator: number): BeaconStateCapella { - // use real pubkeys to test loadCachedBeaconState api + // use real pubkeys to test loadUnfinalizedCachedBeaconState api const pubkeys = interopPubkeysCached(numValidator); const capellaStateType = ssz.capella.BeaconState; const stateView = capellaStateType.defaultViewDU(); diff --git a/packages/state-transition/test/utils/state.ts b/packages/state-transition/test/utils/state.ts index b466d03fd2af..99933718721e 100644 --- a/packages/state-transition/test/utils/state.ts +++ b/packages/state-transition/test/utils/state.ts @@ -17,7 +17,7 @@ import { BeaconStatePhase0, CachedBeaconStateAllForks, BeaconStateAllForks, - createCachedBeaconState, + createFinalizedCachedBeaconState, PubkeyIndexMap, } from "../../src/index.js"; import {BeaconStateCache} from "../../src/cache/stateCache.js"; @@ -89,7 +89,7 @@ export function generateCachedState( opts: TestBeaconState = {} ): CachedBeaconStateAllForks { const state = generateState(opts); - return createCachedBeaconState(state, { + return createFinalizedCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a test state, there's no need to have a global shared cache of keys finalizedPubkey2index: new PubkeyIndexMap(), @@ -97,12 +97,12 @@ export function generateCachedState( }); } -export function createCachedBeaconStateTest( +export function createFinalizedCachedBeaconStateTest( state: T, configCustom: ChainForkConfig = config, opts?: EpochCacheOpts ): T & BeaconStateCache { - return createCachedBeaconState( + return createFinalizedCachedBeaconState( state, { config: createBeaconConfig(configCustom, state.genesisValidatorsRoot), diff --git a/packages/state-transition/test/utils/testFileCache.ts b/packages/state-transition/test/utils/testFileCache.ts index e752f3c36e68..3cc0f3b34701 100644 --- a/packages/state-transition/test/utils/testFileCache.ts +++ b/packages/state-transition/test/utils/testFileCache.ts @@ -7,7 +7,7 @@ import {createChainForkConfig, ChainForkConfig} from "@lodestar/config"; import {allForks} from "@lodestar/types"; import {CachedBeaconStateAllForks, computeEpochAtSlot} from "../../src/index.js"; import {testCachePath} from "../cache.js"; -import {createCachedBeaconStateTest} from "../utils/state.js"; +import {createFinalizedCachedBeaconStateTest} from "../utils/state.js"; import {getInfuraBeaconUrl} from "./infura.js"; /** @@ -40,7 +40,7 @@ export async function getNetworkCachedState( if (fs.existsSync(filepath)) { const stateSsz = fs.readFileSync(filepath); - return createCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config); + return createFinalizedCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config); } else { const stateSsz = await tryEach([ () => downloadTestFile(fileId), @@ -59,7 +59,7 @@ export async function getNetworkCachedState( ]); fs.writeFileSync(filepath, stateSsz); - return createCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config); + return createFinalizedCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config); } } From 67977d0a30906f354d846d9783e15bf0e811cddd Mon Sep 17 00:00:00 2001 From: NC Date: Mon, 18 Mar 2024 21:48:55 +0800 Subject: [PATCH 6/9] Lint --- .../src/chain/rewards/syncCommitteeRewards.ts | 5 +++-- .../unit/api/impl/validator/duties/proposer.test.ts | 2 +- packages/state-transition/src/cache/epochCache.ts | 6 +----- packages/state-transition/test/utils/testFileCache.ts | 10 ++++++++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/beacon-node/src/chain/rewards/syncCommitteeRewards.ts b/packages/beacon-node/src/chain/rewards/syncCommitteeRewards.ts index ba45d03adbab..38036be6f9e4 100644 --- a/packages/beacon-node/src/chain/rewards/syncCommitteeRewards.ts +++ b/packages/beacon-node/src/chain/rewards/syncCommitteeRewards.ts @@ -18,7 +18,7 @@ export async function computeSyncCommitteeRewards( const altairBlock = block as altair.BeaconBlock; const preStateAltair = preState as CachedBeaconStateAltair; - const {index2pubkey} = preStateAltair.epochCtx; + const {finalizedIndex2pubkey} = preStateAltair.epochCtx; // Bound committeeIndices in case it goes beyond SYNC_COMMITTEE_SIZE just to be safe const committeeIndices = preStateAltair.epochCtx.currentSyncCommitteeIndexed.validatorIndices.slice( @@ -49,7 +49,8 @@ export async function computeSyncCommitteeRewards( if (validatorIds !== undefined) { const filtersSet = new Set(validatorIds); return rewards.filter( - (reward) => filtersSet.has(reward.validatorIndex) || filtersSet.has(index2pubkey[reward.validatorIndex].toHex()) + (reward) => + filtersSet.has(reward.validatorIndex) || filtersSet.has(finalizedIndex2pubkey[reward.validatorIndex].toHex()) ); } else { return rewards; diff --git a/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts b/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts index 43acc0c9f379..0cd43e9231a7 100644 --- a/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts +++ b/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts @@ -14,7 +14,7 @@ describe("get proposers api impl", function () { let api: ReturnType; let modules: ApiTestModules; let state: BeaconStateAllForks; - let cachedState: ReturnType; + let cachedState: ReturnType; beforeEach(function () { vi.useFakeTimers({now: 0}); diff --git a/packages/state-transition/src/cache/epochCache.ts b/packages/state-transition/src/cache/epochCache.ts index d4f1112082eb..ae04c6d46aef 100644 --- a/packages/state-transition/src/cache/epochCache.ts +++ b/packages/state-transition/src/cache/epochCache.ts @@ -300,11 +300,7 @@ export class EpochCache { */ static createFromState( state: BeaconStateAllForks, - { - config, - finalizedPubkey2index, - finalizedIndex2pubkey, - }: EpochCacheImmutableData, + {config, finalizedPubkey2index, finalizedIndex2pubkey}: EpochCacheImmutableData, opts?: EpochCacheOpts ): EpochCache { // syncPubkeys here to ensure EpochCacheImmutableData is popualted before computing the rest of caches diff --git a/packages/state-transition/test/utils/testFileCache.ts b/packages/state-transition/test/utils/testFileCache.ts index 3cc0f3b34701..83e70040de46 100644 --- a/packages/state-transition/test/utils/testFileCache.ts +++ b/packages/state-transition/test/utils/testFileCache.ts @@ -40,7 +40,10 @@ export async function getNetworkCachedState( if (fs.existsSync(filepath)) { const stateSsz = fs.readFileSync(filepath); - return createFinalizedCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config); + return createFinalizedCachedBeaconStateTest( + config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), + config + ); } else { const stateSsz = await tryEach([ () => downloadTestFile(fileId), @@ -59,7 +62,10 @@ export async function getNetworkCachedState( ]); fs.writeFileSync(filepath, stateSsz); - return createFinalizedCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config); + return createFinalizedCachedBeaconStateTest( + config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), + config + ); } } From 505d13401b8e69dbbce3cf1cb64fac3eb8b21da7 Mon Sep 17 00:00:00 2001 From: NC Date: Thu, 4 Apr 2024 19:12:56 +0800 Subject: [PATCH 7/9] Undo unrelated renaming --- packages/beacon-node/src/chain/chain.ts | 4 ++-- .../beacon-node/src/chain/genesis/genesis.ts | 4 ++-- .../stateCache/persistentCheckpointsCache.ts | 4 ++-- .../e2e/eth1/eth1ForBlockProduction.test.ts | 4 ++-- .../spec/presets/epoch_processing.test.ts | 4 ++-- .../test/spec/presets/finality.test.ts | 4 ++-- .../test/spec/presets/fork.test.ts | 4 ++-- .../test/spec/presets/fork_choice.test.ts | 4 ++-- .../test/spec/presets/operations.test.ts | 4 ++-- .../test/spec/presets/rewards.test.ts | 4 ++-- .../test/spec/presets/sanity.test.ts | 6 ++--- .../test/spec/presets/transition.test.ts | 4 ++-- .../impl/validator/duties/proposer.test.ts | 6 ++--- .../unit/chain/forkChoice/forkChoice.test.ts | 6 ++--- .../validation/blsToExecutionChange.test.ts | 4 ++-- .../chain/validation/voluntaryExit.test.ts | 4 ++-- .../test/unit/eth1/utils/deposits.test.ts | 4 ++-- .../test/utils/cachedBeaconState.ts | 6 ++--- .../test/utils/networkWithMockDb.ts | 4 ++-- packages/beacon-node/test/utils/state.ts | 10 ++++----- .../state-transition/src/cache/stateCache.ts | 10 ++++----- packages/state-transition/src/index.ts | 4 ++-- packages/state-transition/src/util/genesis.ts | 4 ++-- .../test/perf/analyzeEpochs.ts | 4 ++-- .../processEffectiveBalanceUpdates.test.ts | 4 ++-- packages/state-transition/test/perf/util.ts | 8 +++---- .../perf/util/loadState/loadState.test.ts | 4 ++-- .../test/unit/cachedBeaconState.test.ts | 22 +++++++++---------- .../test/unit/upgradeState.test.ts | 6 ++--- .../test/unit/util/cachedBeaconState.test.ts | 4 ++-- .../test/unit/util/deposit.test.ts | 8 +++---- .../state-transition/test/utils/capella.ts | 6 ++--- packages/state-transition/test/utils/state.ts | 8 +++---- .../test/utils/testFileCache.ts | 12 +++------- 34 files changed, 96 insertions(+), 102 deletions(-) diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 6f135bb60a2f..23603a914322 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -5,7 +5,7 @@ import { CachedBeaconStateAllForks, computeEpochAtSlot, computeStartSlotAtEpoch, - createFinalizedCachedBeaconState, + createCachedBeaconState, EffectiveBalanceIncrements, getEffectiveBalanceIncrementsZeroInactive, isCachedBeaconState, @@ -233,7 +233,7 @@ export class BeaconChain implements IBeaconChain { const cachedState = isCachedBeaconState(anchorState) && opts.skipCreateStateCacheIfAvailable ? anchorState - : createFinalizedCachedBeaconState(anchorState, { + : createCachedBeaconState(anchorState, { config, finalizedPubkey2index: new PubkeyIndexMap(), finalizedIndex2pubkey: [], diff --git a/packages/beacon-node/src/chain/genesis/genesis.ts b/packages/beacon-node/src/chain/genesis/genesis.ts index 4b172fd8a9a2..979476c69530 100644 --- a/packages/beacon-node/src/chain/genesis/genesis.ts +++ b/packages/beacon-node/src/chain/genesis/genesis.ts @@ -9,7 +9,7 @@ import { applyTimestamp, applyEth1BlockHash, CachedBeaconStateAllForks, - createFinalizedCachedBeaconState, + createCachedBeaconState, BeaconStateAllForks, createEmptyEpochCacheImmutableData, getActiveValidatorIndices, @@ -86,7 +86,7 @@ export class GenesisBuilder implements IGenesisBuilder { } // TODO - PENDING: Ensure EpochCacheImmutableData is created only once - this.state = createFinalizedCachedBeaconState(stateView, createEmptyEpochCacheImmutableData(config, stateView)); + this.state = createCachedBeaconState(stateView, createEmptyEpochCacheImmutableData(config, stateView)); this.config = this.state.config; this.activatedValidatorCount = getActiveValidatorIndices(stateView, GENESIS_EPOCH).length; } diff --git a/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts b/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts index ddc9a16a9613..fe96d1a7923e 100644 --- a/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts +++ b/packages/beacon-node/src/chain/stateCache/persistentCheckpointsCache.ts @@ -3,7 +3,7 @@ import {phase0, Epoch, RootHex} from "@lodestar/types"; import {CachedBeaconStateAllForks, computeStartSlotAtEpoch, getBlockRootAtSlot} from "@lodestar/state-transition"; import {Logger, MapDef, sleep} from "@lodestar/utils"; import {routes} from "@lodestar/api"; -import {loadUnfinalizedCachedBeaconState} from "@lodestar/state-transition"; +import {loadCachedBeaconState} from "@lodestar/state-transition"; import {INTERVALS_PER_SLOT} from "@lodestar/params"; import {Metrics} from "../../metrics/index.js"; import {IClock} from "../../util/clock.js"; @@ -217,7 +217,7 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache { } sszTimer?.(); const timer = this.metrics?.stateReloadDuration.startTimer(); - const newCachedState = loadUnfinalizedCachedBeaconState( + const newCachedState = loadCachedBeaconState( seedState, stateBytes, { diff --git a/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts b/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts index 34ac4ca46824..1a80d95c1fdc 100644 --- a/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts +++ b/packages/beacon-node/test/e2e/eth1/eth1ForBlockProduction.test.ts @@ -14,7 +14,7 @@ import {BeaconDb} from "../../../src/db/index.js"; import {generateState} from "../../utils/state.js"; import {Eth1Provider} from "../../../src/eth1/provider/eth1Provider.js"; import {getGoerliRpcUrl} from "../../testParams.js"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; const dbLocation = "./.__testdb"; @@ -112,7 +112,7 @@ describe.skip("eth1 / Eth1Provider", function () { config ); - const state = createFinalizedCachedBeaconStateTest(tbState, config); + const state = createCachedBeaconStateTest(tbState, config); const result = await eth1ForBlockProduction.getEth1DataAndDeposits(state); expect(result.eth1Data).toEqual(latestEth1Data); diff --git a/packages/beacon-node/test/spec/presets/epoch_processing.test.ts b/packages/beacon-node/test/spec/presets/epoch_processing.test.ts index c61ee22d1698..a244762143f3 100644 --- a/packages/beacon-node/test/spec/presets/epoch_processing.test.ts +++ b/packages/beacon-node/test/spec/presets/epoch_processing.test.ts @@ -9,7 +9,7 @@ import { import * as epochFns from "@lodestar/state-transition/epoch"; import {ssz} from "@lodestar/types"; import {ACTIVE_PRESET} from "@lodestar/params"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {getConfig} from "../../utils/config.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; @@ -64,7 +64,7 @@ const epochProcessing = return { testFunction: (testcase) => { const stateTB = testcase.pre.clone(); - const state = createFinalizedCachedBeaconStateTest(stateTB, config); + const state = createCachedBeaconStateTest(stateTB, config); const epochTransitionCache = beforeProcessEpoch(state, {assertCorrectProgressiveBalances}); diff --git a/packages/beacon-node/test/spec/presets/finality.test.ts b/packages/beacon-node/test/spec/presets/finality.test.ts index c1a22740785f..0ec4017064f4 100644 --- a/packages/beacon-node/test/spec/presets/finality.test.ts +++ b/packages/beacon-node/test/spec/presets/finality.test.ts @@ -7,7 +7,7 @@ import { } from "@lodestar/state-transition"; import {altair, bellatrix, ssz} from "@lodestar/types"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {RunnerType, shouldVerify, TestRunnerFn} from "../utils/types.js"; import {getConfig} from "../../utils/config.js"; @@ -20,7 +20,7 @@ import {specTestIterator} from "../utils/specTestIterator.js"; const finality: TestRunnerFn = (fork) => { return { testFunction: (testcase) => { - let state = createFinalizedCachedBeaconStateTest(testcase.pre, getConfig(fork)); + let state = createCachedBeaconStateTest(testcase.pre, getConfig(fork)); const verify = shouldVerify(testcase); for (let i = 0; i < testcase.meta.blocks_count; i++) { const signedBlock = testcase[`blocks_${i}`] as bellatrix.SignedBeaconBlock; diff --git a/packages/beacon-node/test/spec/presets/fork.test.ts b/packages/beacon-node/test/spec/presets/fork.test.ts index 1a2634680e8a..c121e651fcea 100644 --- a/packages/beacon-node/test/spec/presets/fork.test.ts +++ b/packages/beacon-node/test/spec/presets/fork.test.ts @@ -12,7 +12,7 @@ import {phase0, ssz} from "@lodestar/types"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; import {createChainForkConfig, ChainForkConfig} from "@lodestar/config"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; import {ethereumConsensusSpecsTests} from "../specTestVersioning.js"; import {specTestIterator} from "../utils/specTestIterator.js"; @@ -23,7 +23,7 @@ const fork: TestRunnerFn = (forkNext) => { return { testFunction: (testcase) => { - const preState = createFinalizedCachedBeaconStateTest(testcase.pre, config); + const preState = createCachedBeaconStateTest(testcase.pre, config); switch (forkNext) { case ForkName.phase0: diff --git a/packages/beacon-node/test/spec/presets/fork_choice.test.ts b/packages/beacon-node/test/spec/presets/fork_choice.test.ts index cd439ea2fbc0..49d78cc42f6a 100644 --- a/packages/beacon-node/test/spec/presets/fork_choice.test.ts +++ b/packages/beacon-node/test/spec/presets/fork_choice.test.ts @@ -11,7 +11,7 @@ import {ACTIVE_PRESET, ForkSeq, isForkBlobs} from "@lodestar/params"; import {BeaconChain, ChainEvent} from "../../../src/chain/index.js"; import {ClockEvent} from "../../../src/util/clock.js"; import {computeInclusionProof} from "../../../src/util/blobs.js"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {testLogger} from "../../utils/logger.js"; import {getConfig} from "../../utils/config.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; @@ -60,7 +60,7 @@ const forkChoiceTest = const {steps, anchorState} = testcase; const currentSlot = anchorState.slot; const config = getConfig(fork); - const state = createFinalizedCachedBeaconStateTest(anchorState, config); + const state = createCachedBeaconStateTest(anchorState, config); /** This is to track test's tickTime to be used in proposer boost */ let tickTime = 0; diff --git a/packages/beacon-node/test/spec/presets/operations.test.ts b/packages/beacon-node/test/spec/presets/operations.test.ts index 3a91b91ed904..2c4ae22475a5 100644 --- a/packages/beacon-node/test/spec/presets/operations.test.ts +++ b/packages/beacon-node/test/spec/presets/operations.test.ts @@ -13,7 +13,7 @@ import {ssz, phase0, altair, bellatrix, capella, electra} from "@lodestar/types" import {InputType} from "@lodestar/spec-test-util"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {getConfig} from "../../utils/config.js"; import {BaseSpecTest, RunnerType, shouldVerify, TestRunnerFn} from "../utils/types.js"; @@ -111,7 +111,7 @@ const operations: TestRunnerFn = (fork, testFunction: (testcase) => { const state = testcase.pre.clone(); const epoch = (state.fork as phase0.Fork).epoch; - const cachedState = createFinalizedCachedBeaconStateTest(state, getConfig(fork, epoch)); + const cachedState = createCachedBeaconStateTest(state, getConfig(fork, epoch)); operationFn(cachedState, testcase); state.commit(); diff --git a/packages/beacon-node/test/spec/presets/rewards.test.ts b/packages/beacon-node/test/spec/presets/rewards.test.ts index 061ed2ff3cde..426245242d84 100644 --- a/packages/beacon-node/test/spec/presets/rewards.test.ts +++ b/packages/beacon-node/test/spec/presets/rewards.test.ts @@ -5,7 +5,7 @@ import {BeaconStateAllForks, beforeProcessEpoch} from "@lodestar/state-transitio import {getRewardsAndPenalties} from "@lodestar/state-transition/epoch"; import {ssz} from "@lodestar/types"; import {ACTIVE_PRESET} from "@lodestar/params"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {getConfig} from "../../utils/config.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; @@ -21,7 +21,7 @@ const rewards: TestRunnerFn = (fork) => { return { testFunction: (testcase) => { const config = getConfig(fork); - const wrappedState = createFinalizedCachedBeaconStateTest(testcase.pre, config); + const wrappedState = createCachedBeaconStateTest(testcase.pre, config); const epochTransitionCache = beforeProcessEpoch(wrappedState, {assertCorrectProgressiveBalances}); // To debug this test and get granular results you can tweak inputs to get more granular results diff --git a/packages/beacon-node/test/spec/presets/sanity.test.ts b/packages/beacon-node/test/spec/presets/sanity.test.ts index 223fab0ded08..57afb8cf3d28 100644 --- a/packages/beacon-node/test/spec/presets/sanity.test.ts +++ b/packages/beacon-node/test/spec/presets/sanity.test.ts @@ -10,7 +10,7 @@ import { import {allForks, deneb, ssz} from "@lodestar/types"; import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; import {bnToNum} from "@lodestar/utils"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; import {RunnerType, shouldVerify, TestRunnerFn} from "../utils/types.js"; import {getConfig} from "../../utils/config.js"; @@ -35,7 +35,7 @@ const sanitySlots: TestRunnerFn = (for return { testFunction: (testcase) => { const stateTB = testcase.pre.clone(); - const state = createFinalizedCachedBeaconStateTest(stateTB, getConfig(fork)); + const state = createCachedBeaconStateTest(stateTB, getConfig(fork)); const postState = processSlots(state, state.slot + bnToNum(testcase.slots), {assertCorrectProgressiveBalances}); // TODO: May be part of runStateTranstion, necessary to commit again? postState.commit(); @@ -62,7 +62,7 @@ const sanityBlocks: TestRunnerFn = (f return { testFunction: (testcase) => { const stateTB = testcase.pre; - let wrappedState = createFinalizedCachedBeaconStateTest(stateTB, getConfig(fork)); + let wrappedState = createCachedBeaconStateTest(stateTB, getConfig(fork)); const verify = shouldVerify(testcase); for (let i = 0; i < testcase.meta.blocks_count; i++) { const signedBlock = testcase[`blocks_${i}`] as deneb.SignedBeaconBlock; diff --git a/packages/beacon-node/test/spec/presets/transition.test.ts b/packages/beacon-node/test/spec/presets/transition.test.ts index f5cb0decdea1..1f98dbb41f58 100644 --- a/packages/beacon-node/test/spec/presets/transition.test.ts +++ b/packages/beacon-node/test/spec/presets/transition.test.ts @@ -11,7 +11,7 @@ import {ACTIVE_PRESET, ForkName} from "@lodestar/params"; import {bnToNum} from "@lodestar/utils"; import {config} from "@lodestar/config/default"; import {expectEqualBeaconState, inputTypeSszTreeViewDU} from "../utils/expectEqualBeaconState.js"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../utils/cachedBeaconState.js"; import {RunnerType, TestRunnerFn} from "../utils/types.js"; import {assertCorrectProgressiveBalances} from "../config.js"; import {ethereumConsensusSpecsTests} from "../specTestVersioning.js"; @@ -51,7 +51,7 @@ const transition = const forkEpoch = bnToNum(meta.fork_epoch); const testConfig = createChainForkConfig(getTransitionConfig(forkNext, forkEpoch)); - let state = createFinalizedCachedBeaconStateTest(testcase.pre, testConfig); + let state = createCachedBeaconStateTest(testcase.pre, testConfig); for (let i = 0; i < meta.blocks_count; i++) { const signedBlock = testcase[`blocks_${i}`] as allForks.SignedBeaconBlock; state = stateTransition(state, signedBlock, { diff --git a/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts b/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts index 0cd43e9231a7..bf9544ad683e 100644 --- a/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts +++ b/packages/beacon-node/test/unit/api/impl/validator/duties/proposer.test.ts @@ -7,14 +7,14 @@ import {FAR_FUTURE_EPOCH} from "../../../../../../src/constants/index.js"; import {SYNC_TOLERANCE_EPOCHS, getValidatorApi} from "../../../../../../src/api/impl/validator/index.js"; import {generateState, zeroProtoBlock} from "../../../../../utils/state.js"; import {generateValidators} from "../../../../../utils/validator.js"; -import {createFinalizedCachedBeaconStateTest} from "../../../../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../../../../utils/cachedBeaconState.js"; import {SyncState} from "../../../../../../src/sync/interface.js"; describe("get proposers api impl", function () { let api: ReturnType; let modules: ApiTestModules; let state: BeaconStateAllForks; - let cachedState: ReturnType; + let cachedState: ReturnType; beforeEach(function () { vi.useFakeTimers({now: 0}); @@ -33,7 +33,7 @@ describe("get proposers api impl", function () { }, config ); - cachedState = createFinalizedCachedBeaconStateTest(state, config); + cachedState = createCachedBeaconStateTest(state, config); modules.chain.getHeadStateAtCurrentEpoch.mockResolvedValue(cachedState); modules.forkChoice.getHead.mockReturnValue(zeroProtoBlock); diff --git a/packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts b/packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts index 78b7ea2310b7..20b6fc8d7a00 100644 --- a/packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts +++ b/packages/beacon-node/test/unit/chain/forkChoice/forkChoice.test.ts @@ -12,7 +12,7 @@ import {phase0, Slot, ssz, ValidatorIndex} from "@lodestar/types"; import {getTemporaryBlockHeader, processSlots} from "@lodestar/state-transition"; import {ChainEventEmitter, computeAnchorCheckpoint, initializeForkChoice} from "../../../../src/chain/index.js"; import {generateSignedBlockAtSlot} from "../../../utils/typeGenerator.js"; -import {createFinalizedCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; import {generateState} from "../../../utils/state.js"; import {generateValidators} from "../../../utils/validator.js"; @@ -21,7 +21,7 @@ vi.unmock("@lodestar/fork-choice"); describe("LodestarForkChoice", function () { let forkChoice: ForkChoice; - const anchorState = createFinalizedCachedBeaconStateTest( + const anchorState = createCachedBeaconStateTest( generateState( { slot: 0, @@ -53,7 +53,7 @@ describe("LodestarForkChoice", function () { let state: CachedBeaconStateAllForks; beforeAll(() => { - state = createFinalizedCachedBeaconStateTest(anchorState, config); + state = createCachedBeaconStateTest(anchorState, config); }); beforeEach(() => { diff --git a/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts b/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts index 3791a4a25878..f83b900beb69 100644 --- a/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts @@ -19,7 +19,7 @@ import {generateState} from "../../../utils/state.js"; import {validateGossipBlsToExecutionChange} from "../../../../src/chain/validation/blsToExecutionChange.js"; import {BlsToExecutionChangeErrorCode} from "../../../../src/chain/errors/blsToExecutionChangeError.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; -import {createFinalizedCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; describe("validate bls to execution change", () => { let chainStub: MockedBeaconChain; @@ -70,7 +70,7 @@ describe("validate bls to execution change", () => { // Generate the state const _state = generateState(stateEmpty, defaultConfig); const config = createBeaconConfig(defaultConfig, _state.genesisValidatorsRoot); - const state = createFinalizedCachedBeaconStateTest(_state, config); + const state = createCachedBeaconStateTest(_state, config); // Gen a valid blsToExecutionChange for first val const blsToExecutionChange = { diff --git a/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts b/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts index 7e7b2d5cca74..3966815c28ce 100644 --- a/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts @@ -16,7 +16,7 @@ import {generateState} from "../../../utils/state.js"; import {validateGossipVoluntaryExit} from "../../../../src/chain/validation/voluntaryExit.js"; import {VoluntaryExitErrorCode} from "../../../../src/chain/errors/voluntaryExitError.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; -import {createFinalizedCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; describe("validate voluntary exit", () => { let chainStub: MockedBeaconChain; @@ -58,7 +58,7 @@ describe("validate voluntary exit", () => { signedVoluntaryExit = {message: voluntaryExit, signature: sk.sign(signingRoot).toBytes()}; const _state = generateState(stateEmpty, config); - state = createFinalizedCachedBeaconStateTest(_state, createBeaconConfig(config, _state.genesisValidatorsRoot)); + state = createCachedBeaconStateTest(_state, createBeaconConfig(config, _state.genesisValidatorsRoot)); }); beforeEach(() => { diff --git a/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts b/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts index 6355eb5d6723..6cc3ae8f1ce0 100644 --- a/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts +++ b/packages/beacon-node/test/unit/eth1/utils/deposits.test.ts @@ -9,7 +9,7 @@ import {generateState} from "../../../utils/state.js"; import {expectRejectedWithLodestarError} from "../../../utils/errors.js"; import {getDeposits, getDepositsWithProofs, DepositGetter} from "../../../../src/eth1/utils/deposits.js"; import {DepositTree} from "../../../../src/db/repositories/depositDataRoot.js"; -import {createFinalizedCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "../../../utils/cachedBeaconState.js"; describe("eth1 / util / deposits", function () { describe("getDeposits", () => { @@ -116,7 +116,7 @@ describe("eth1 / util / deposits", function () { const state = postElectra ? generateState({slot: postElectraSlot, eth1DepositIndex}, postElectraConfig) : generateState({eth1DepositIndex}); - const cachedState = createFinalizedCachedBeaconStateTest( + const cachedState = createCachedBeaconStateTest( state, postElectra ? postElectraConfig : createChainForkConfig({}) ); diff --git a/packages/beacon-node/test/utils/cachedBeaconState.ts b/packages/beacon-node/test/utils/cachedBeaconState.ts index f647ccbd1265..3efb16b6250f 100644 --- a/packages/beacon-node/test/utils/cachedBeaconState.ts +++ b/packages/beacon-node/test/utils/cachedBeaconState.ts @@ -1,14 +1,14 @@ import { BeaconStateAllForks, BeaconStateCache, - createFinalizedCachedBeaconState, + createCachedBeaconState, createEmptyEpochCacheImmutableData, } from "@lodestar/state-transition"; import {ChainForkConfig} from "@lodestar/config"; -export function createFinalizedCachedBeaconStateTest( +export function createCachedBeaconStateTest( state: T, chainConfig: ChainForkConfig ): T & BeaconStateCache { - return createFinalizedCachedBeaconState(state, createEmptyEpochCacheImmutableData(chainConfig, state)); + return createCachedBeaconState(state, createEmptyEpochCacheImmutableData(chainConfig, state)); } diff --git a/packages/beacon-node/test/utils/networkWithMockDb.ts b/packages/beacon-node/test/utils/networkWithMockDb.ts index e1c4fd8b1a1a..f8a4a6181d2f 100644 --- a/packages/beacon-node/test/utils/networkWithMockDb.ts +++ b/packages/beacon-node/test/utils/networkWithMockDb.ts @@ -8,7 +8,7 @@ import {GossipHandlers, Network, NetworkInitModules, getReqRespHandlers} from ". import {NetworkOptions, defaultNetworkOptions} from "../../src/network/options.js"; import {GetReqRespHandlerFn} from "../../src/network/reqresp/types.js"; import {getMockedBeaconDb} from "../mocks/mockedBeaconDb.js"; -import {createFinalizedCachedBeaconStateTest} from "./cachedBeaconState.js"; +import {createCachedBeaconStateTest} from "./cachedBeaconState.js"; import {ClockStatic} from "./clock.js"; import {testLogger} from "./logger.js"; import {generateState} from "./state.js"; @@ -64,7 +64,7 @@ export async function getNetworkForTest( // mock timer does not work on worker thread clock: new ClockStatic(startSlot, Math.floor(Date.now() / 1000) - startSlot * beaconConfig.SECONDS_PER_SLOT), metrics: null, - anchorState: createFinalizedCachedBeaconStateTest(state, beaconConfig), + anchorState: createCachedBeaconStateTest(state, beaconConfig), eth1: new Eth1ForBlockProductionDisabled(), executionEngine: new ExecutionEngineDisabled(), } diff --git a/packages/beacon-node/test/utils/state.ts b/packages/beacon-node/test/utils/state.ts index 2edc31c6dc36..23b5ec577af3 100644 --- a/packages/beacon-node/test/utils/state.ts +++ b/packages/beacon-node/test/utils/state.ts @@ -3,7 +3,7 @@ import {config as minimalConfig} from "@lodestar/config/default"; import { BeaconStateAllForks, CachedBeaconStateAllForks, - createFinalizedCachedBeaconState, + createCachedBeaconState, PubkeyIndexMap, CachedBeaconStateBellatrix, BeaconStateBellatrix, @@ -110,7 +110,7 @@ export function generateState( export function generateCachedState(opts?: TestBeaconState): CachedBeaconStateAllForks { const config = getConfig(ForkName.phase0); const state = generateState(opts, config); - return createFinalizedCachedBeaconState(state, { + return createCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys finalizedPubkey2index: new PubkeyIndexMap(), @@ -124,7 +124,7 @@ export function generateCachedState(opts?: TestBeaconState): CachedBeaconStateAl export function generateCachedAltairState(opts?: TestBeaconState, altairForkEpoch = 0): CachedBeaconStateAllForks { const config = getConfig(ForkName.altair, altairForkEpoch); const state = generateState(opts, config); - return createFinalizedCachedBeaconState(state, { + return createCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys finalizedPubkey2index: new PubkeyIndexMap(), @@ -138,7 +138,7 @@ export function generateCachedAltairState(opts?: TestBeaconState, altairForkEpoc export function generateCachedBellatrixState(opts?: TestBeaconState): CachedBeaconStateBellatrix { const config = getConfig(ForkName.bellatrix); const state = generateState(opts, config); - return createFinalizedCachedBeaconState(state as BeaconStateBellatrix, { + return createCachedBeaconState(state as BeaconStateBellatrix, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a performance test, there's no need to have a global shared cache of keys finalizedPubkey2index: new PubkeyIndexMap(), @@ -152,7 +152,7 @@ export function generateCachedBellatrixState(opts?: TestBeaconState): CachedBeac export function generateCachedElectraState(opts?: TestBeaconState): CachedBeaconStateElectra { const config = getConfig(ForkName.electra); const state = generateState(opts, config); - return createFinalizedCachedBeaconState(state as BeaconStateElectra, { + return createCachedBeaconState(state as BeaconStateElectra, { config: createBeaconConfig(config, state.genesisValidatorsRoot), finalizedPubkey2index: new PubkeyIndexMap(), finalizedIndex2pubkey: [], diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index 332940bd7cbc..bc110cd8de92 100644 --- a/packages/state-transition/src/cache/stateCache.ts +++ b/packages/state-transition/src/cache/stateCache.ts @@ -138,9 +138,9 @@ export type CachedBeaconStateAllForks = CachedBeaconState; export type CachedBeaconStateExecutions = CachedBeaconState; /** * Create CachedBeaconState computing a new EpochCache instance - * Note: this is intended for finalized state only + * TODO ELECTRA: rename this to createFinalizedCachedBeaconState() as it's intended for finalized state only */ -export function createFinalizedCachedBeaconState( +export function createCachedBeaconState( state: T, immutableData: EpochCacheImmutableData, opts?: EpochCacheOpts @@ -162,9 +162,9 @@ export function createFinalizedCachedBeaconState( * Create a CachedBeaconState given a cached seed state and state bytes * This guarantees that the returned state shares the same tree with the seed state * Check loadState() api for more details - * Note: This only loads unfinalized beacon state + * // TODO: rename to loadUnfinalizedCachedBeaconState() due to ELECTRA */ -export function loadUnfinalizedCachedBeaconState( +export function loadCachedBeaconState( cachedSeedState: T, stateBytes: Uint8Array, opts?: EpochCacheOpts, @@ -186,7 +186,7 @@ export function loadUnfinalizedCachedBeaconState seedState.epochCtx.currentShuffling; - createFinalizedCachedBeaconState( + createCachedBeaconState( migratedState, { config: seedState.config, diff --git a/packages/state-transition/test/unit/cachedBeaconState.test.ts b/packages/state-transition/test/unit/cachedBeaconState.test.ts index 9db65f23d933..4b0c05f00d87 100644 --- a/packages/state-transition/test/unit/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/cachedBeaconState.test.ts @@ -4,9 +4,9 @@ import {Epoch, ssz, RootHex} from "@lodestar/types"; import {toHexString} from "@lodestar/utils"; import {config as defaultConfig} from "@lodestar/config/default"; import {createBeaconConfig, createChainForkConfig} from "@lodestar/config"; -import {createFinalizedCachedBeaconStateTest} from "../utils/state.js"; +import {createCachedBeaconStateTest} from "../utils/state.js"; import {PubkeyIndexMap} from "../../src/cache/pubkeyCache.js"; -import {createFinalizedCachedBeaconState, loadUnfinalizedCachedBeaconState} from "../../src/cache/stateCache.js"; +import {createCachedBeaconState, loadCachedBeaconState} from "../../src/cache/stateCache.js"; import {interopPubkeysCached} from "../utils/interop.js"; import {modifyStateSameValidator, newStateWithValidators} from "../utils/capella.js"; import {EpochShuffling, getShufflingDecisionBlock} from "../../src/util/epochShuffling.js"; @@ -14,7 +14,7 @@ import {EpochShuffling, getShufflingDecisionBlock} from "../../src/util/epochShu describe("CachedBeaconState", () => { it("Clone and mutate", () => { const stateView = ssz.altair.BeaconState.defaultViewDU(); - const state1 = createFinalizedCachedBeaconStateTest(stateView); + const state1 = createCachedBeaconStateTest(stateView); const state2 = state1.clone(); state1.slot = 1; @@ -31,7 +31,7 @@ describe("CachedBeaconState", () => { it("Clone and mutate cache pre-Electra", () => { const stateView = ssz.altair.BeaconState.defaultViewDU(); - const state1 = createFinalizedCachedBeaconStateTest(stateView); + const state1 = createCachedBeaconStateTest(stateView); const pubkey1 = fromHexString( "0x84105a985058fc8740a48bf1ede9d223ef09e8c6b1735ba0a55cf4a9ff2ff92376b778798365e488dab07a652eb04576" @@ -56,7 +56,7 @@ describe("CachedBeaconState", () => { /* eslint-disable @typescript-eslint/naming-convention */ it("Clone and mutate cache post-Electra", () => { const stateView = ssz.electra.BeaconState.defaultViewDU(); - const state1 = createFinalizedCachedBeaconStateTest( + const state1 = createCachedBeaconStateTest( stateView, createChainForkConfig({ ALTAIR_FORK_EPOCH: 0, @@ -113,13 +113,13 @@ describe("CachedBeaconState", () => { expect(toHexString(cp1.serialize())).toBe(toHexString(cp2.serialize())); }); - describe("loadUnfinalizedCachedBeaconState", () => { + describe("loadCachedBeaconState", () => { const numValidator = 16; const pubkeys = interopPubkeysCached(2 * numValidator); const stateView = newStateWithValidators(numValidator); const config = createBeaconConfig(defaultConfig, stateView.genesisValidatorsRoot); - const seedState = createFinalizedCachedBeaconState( + const seedState = createCachedBeaconState( stateView, { config, @@ -132,7 +132,7 @@ describe("CachedBeaconState", () => { const capellaStateType = ssz.capella.BeaconState; for (let validatorCountDelta = -numValidator; validatorCountDelta <= numValidator; validatorCountDelta++) { - const testName = `loadUnfinalizedCachedBeaconState - ${validatorCountDelta > 0 ? "more" : "less"} ${Math.abs( + const testName = `loadCachedBeaconState - ${validatorCountDelta > 0 ? "more" : "less"} ${Math.abs( validatorCountDelta )} validators`; it(testName, () => { @@ -189,7 +189,7 @@ describe("CachedBeaconState", () => { // confirm loadState() result const stateBytes = state.serialize(); - const newCachedState = loadUnfinalizedCachedBeaconState(seedState, stateBytes, {skipSyncCommitteeCache: true}); + const newCachedState = loadCachedBeaconState(seedState, stateBytes, {skipSyncCommitteeCache: true}); const newStateBytes = newCachedState.serialize(); expect(newStateBytes).toEqual(stateBytes); expect(newCachedState.hashTreeRoot()).toEqual(state.hashTreeRoot()); @@ -217,7 +217,7 @@ describe("CachedBeaconState", () => { return null; }; - const cachedState = createFinalizedCachedBeaconState( + const cachedState = createCachedBeaconState( state, { config, @@ -231,7 +231,7 @@ describe("CachedBeaconState", () => { expect(newCachedState.epochCtx).toEqual(cachedState.epochCtx); } - // confirm loadUnfinalizedCachedBeaconState() result + // confirm loadCachedBeaconState() result for (let i = 0; i < newCachedState.validators.length; i++) { expect(newCachedState.epochCtx.finalizedPubkey2index.get(newCachedState.validators.get(i).pubkey)).toBe(i); expect(newCachedState.epochCtx.finalizedIndex2pubkey[i].toBytes()).toEqual(pubkeys[i]); diff --git a/packages/state-transition/test/unit/upgradeState.test.ts b/packages/state-transition/test/unit/upgradeState.test.ts index 19af491ce7e6..a130fe21341c 100644 --- a/packages/state-transition/test/unit/upgradeState.test.ts +++ b/packages/state-transition/test/unit/upgradeState.test.ts @@ -6,14 +6,14 @@ import {config as chainConfig} from "@lodestar/config/default"; import {upgradeStateToDeneb} from "../../src/slot/upgradeStateToDeneb.js"; import {upgradeStateToElectra} from "../../src/slot/upgradeStateToElectra.js"; -import {createFinalizedCachedBeaconState} from "../../src/cache/stateCache.js"; +import {createCachedBeaconState} from "../../src/cache/stateCache.js"; import {PubkeyIndexMap} from "../../src/cache/pubkeyCache.js"; describe("upgradeState", () => { it("upgradeStateToDeneb", () => { const capellaState = ssz.capella.BeaconState.defaultViewDU(); const config = getConfig(ForkName.capella); - const stateView = createFinalizedCachedBeaconState( + const stateView = createCachedBeaconState( capellaState, { config: createBeaconConfig(config, capellaState.genesisValidatorsRoot), @@ -28,7 +28,7 @@ describe("upgradeState", () => { it("upgradeStateToElectra", () => { const denebState = ssz.deneb.BeaconState.defaultViewDU(); const config = getConfig(ForkName.deneb); - const stateView = createFinalizedCachedBeaconState( + const stateView = createCachedBeaconState( denebState, { config: createBeaconConfig(config, denebState.genesisValidatorsRoot), diff --git a/packages/state-transition/test/unit/util/cachedBeaconState.test.ts b/packages/state-transition/test/unit/util/cachedBeaconState.test.ts index 6e8378694749..057107b25427 100644 --- a/packages/state-transition/test/unit/util/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/util/cachedBeaconState.test.ts @@ -2,13 +2,13 @@ import {describe, it} from "vitest"; import {createBeaconConfig} from "@lodestar/config"; import {config} from "@lodestar/config/default"; import {ssz} from "@lodestar/types"; -import {createFinalizedCachedBeaconState, PubkeyIndexMap} from "../../../src/index.js"; +import {createCachedBeaconState, PubkeyIndexMap} from "../../../src/index.js"; describe("CachedBeaconState", () => { it("Create empty CachedBeaconState", () => { const emptyState = ssz.phase0.BeaconState.defaultViewDU(); - createFinalizedCachedBeaconState(emptyState, { + createCachedBeaconState(emptyState, { config: createBeaconConfig(config, emptyState.genesisValidatorsRoot), finalizedPubkey2index: new PubkeyIndexMap(), finalizedIndex2pubkey: [], diff --git a/packages/state-transition/test/unit/util/deposit.test.ts b/packages/state-transition/test/unit/util/deposit.test.ts index 2eb889955eb4..e8f7f7a86af8 100644 --- a/packages/state-transition/test/unit/util/deposit.test.ts +++ b/packages/state-transition/test/unit/util/deposit.test.ts @@ -3,12 +3,12 @@ import {ssz} from "@lodestar/types"; import {createChainForkConfig} from "@lodestar/config"; import {MAX_DEPOSITS} from "@lodestar/params"; import {CachedBeaconStateElectra, getEth1DepositCount} from "../../../src/index.js"; -import {createFinalizedCachedBeaconStateTest} from "../../utils/state.js"; +import {createCachedBeaconStateTest} from "../../utils/state.js"; describe("getEth1DepositCount", () => { it("Pre Electra", () => { const stateView = ssz.altair.BeaconState.defaultViewDU(); - const preElectraState = createFinalizedCachedBeaconStateTest(stateView); + const preElectraState = createCachedBeaconStateTest(stateView); if (preElectraState.epochCtx.isAfterElectra()) { throw Error("Not a pre-Electra state"); @@ -26,7 +26,7 @@ describe("getEth1DepositCount", () => { }); it("Post Electra with eth1 deposit", () => { const stateView = ssz.electra.BeaconState.defaultViewDU(); - const postElectraState = createFinalizedCachedBeaconStateTest( + const postElectraState = createCachedBeaconStateTest( stateView, createChainForkConfig({ /* eslint-disable @typescript-eslint/naming-convention */ @@ -60,7 +60,7 @@ describe("getEth1DepositCount", () => { }); it("Post Electra without eth1 deposit", () => { const stateView = ssz.electra.BeaconState.defaultViewDU(); - const postElectraState = createFinalizedCachedBeaconStateTest( + const postElectraState = createCachedBeaconStateTest( stateView, createChainForkConfig({ /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/packages/state-transition/test/utils/capella.ts b/packages/state-transition/test/utils/capella.ts index 1a71e79f82eb..7ef9248a5675 100644 --- a/packages/state-transition/test/utils/capella.ts +++ b/packages/state-transition/test/utils/capella.ts @@ -8,7 +8,7 @@ import { SLOTS_PER_HISTORICAL_ROOT, } from "@lodestar/params"; import {BeaconStateCapella, CachedBeaconStateCapella} from "../../src/index.js"; -import {createFinalizedCachedBeaconStateTest} from "./state.js"; +import {createCachedBeaconStateTest} from "./state.js"; import {mulberry32} from "./rand.js"; import {interopPubkeysCached} from "./interop.js"; @@ -63,11 +63,11 @@ export function getExpectedWithdrawalsTestData(vc: number, opts: WithdrawalOpts) state.commit(); - return createFinalizedCachedBeaconStateTest(state, config, {skipSyncPubkeys: true}); + return createCachedBeaconStateTest(state, config, {skipSyncPubkeys: true}); } export function newStateWithValidators(numValidator: number): BeaconStateCapella { - // use real pubkeys to test loadUnfinalizedCachedBeaconState api + // use real pubkeys to test loadCachedBeaconState api const pubkeys = interopPubkeysCached(numValidator); const capellaStateType = ssz.capella.BeaconState; const stateView = capellaStateType.defaultViewDU(); diff --git a/packages/state-transition/test/utils/state.ts b/packages/state-transition/test/utils/state.ts index 99933718721e..b466d03fd2af 100644 --- a/packages/state-transition/test/utils/state.ts +++ b/packages/state-transition/test/utils/state.ts @@ -17,7 +17,7 @@ import { BeaconStatePhase0, CachedBeaconStateAllForks, BeaconStateAllForks, - createFinalizedCachedBeaconState, + createCachedBeaconState, PubkeyIndexMap, } from "../../src/index.js"; import {BeaconStateCache} from "../../src/cache/stateCache.js"; @@ -89,7 +89,7 @@ export function generateCachedState( opts: TestBeaconState = {} ): CachedBeaconStateAllForks { const state = generateState(opts); - return createFinalizedCachedBeaconState(state, { + return createCachedBeaconState(state, { config: createBeaconConfig(config, state.genesisValidatorsRoot), // This is a test state, there's no need to have a global shared cache of keys finalizedPubkey2index: new PubkeyIndexMap(), @@ -97,12 +97,12 @@ export function generateCachedState( }); } -export function createFinalizedCachedBeaconStateTest( +export function createCachedBeaconStateTest( state: T, configCustom: ChainForkConfig = config, opts?: EpochCacheOpts ): T & BeaconStateCache { - return createFinalizedCachedBeaconState( + return createCachedBeaconState( state, { config: createBeaconConfig(configCustom, state.genesisValidatorsRoot), diff --git a/packages/state-transition/test/utils/testFileCache.ts b/packages/state-transition/test/utils/testFileCache.ts index 83e70040de46..e752f3c36e68 100644 --- a/packages/state-transition/test/utils/testFileCache.ts +++ b/packages/state-transition/test/utils/testFileCache.ts @@ -7,7 +7,7 @@ import {createChainForkConfig, ChainForkConfig} from "@lodestar/config"; import {allForks} from "@lodestar/types"; import {CachedBeaconStateAllForks, computeEpochAtSlot} from "../../src/index.js"; import {testCachePath} from "../cache.js"; -import {createFinalizedCachedBeaconStateTest} from "../utils/state.js"; +import {createCachedBeaconStateTest} from "../utils/state.js"; import {getInfuraBeaconUrl} from "./infura.js"; /** @@ -40,10 +40,7 @@ export async function getNetworkCachedState( if (fs.existsSync(filepath)) { const stateSsz = fs.readFileSync(filepath); - return createFinalizedCachedBeaconStateTest( - config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), - config - ); + return createCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config); } else { const stateSsz = await tryEach([ () => downloadTestFile(fileId), @@ -62,10 +59,7 @@ export async function getNetworkCachedState( ]); fs.writeFileSync(filepath, stateSsz); - return createFinalizedCachedBeaconStateTest( - config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), - config - ); + return createCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config); } } From f631fd9dfb35df813e2c08be7b677f607c2c7c40 Mon Sep 17 00:00:00 2001 From: NC Date: Sat, 13 Apr 2024 20:23:30 +0800 Subject: [PATCH 8/9] Fix conflict --- packages/beacon-node/src/chain/rewards/attestationsRewards.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/beacon-node/src/chain/rewards/attestationsRewards.ts b/packages/beacon-node/src/chain/rewards/attestationsRewards.ts index a59aa6a2b4d2..4aed92408713 100644 --- a/packages/beacon-node/src/chain/rewards/attestationsRewards.ts +++ b/packages/beacon-node/src/chain/rewards/attestationsRewards.ts @@ -143,7 +143,7 @@ function computeTotalAttestationsRewardsAltair( const {statuses} = transitionCache; const {epochCtx, config} = state; const validatorIndices = validatorIds - ?.map((id) => (typeof id === "number" ? id : epochCtx.pubkey2index.get(id))) + ?.map((id) => (typeof id === "number" ? id : epochCtx.finalizedPubkey2index.get(id))) .filter((index) => index !== undefined); // Validator indices to include in the result const inactivityPenaltyDenominator = config.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR; From 7dda166684738b199b272114998d74194e285b9f Mon Sep 17 00:00:00 2001 From: NC Date: Mon, 15 Apr 2024 19:52:34 +0800 Subject: [PATCH 9/9] address comment --- .../state-transition/src/cache/stateCache.ts | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index bc110cd8de92..3bc4e7f21ed8 100644 --- a/packages/state-transition/src/cache/stateCache.ts +++ b/packages/state-transition/src/cache/stateCache.ts @@ -1,5 +1,3 @@ -import bls from "@chainsafe/bls"; -import {CoordType} from "@chainsafe/bls/types"; import {BeaconConfig} from "@lodestar/config"; import {loadState} from "../util/loadState/loadState.js"; import {EpochCache, EpochCacheImmutableData, EpochCacheOpts} from "./epochCache.js"; @@ -176,25 +174,26 @@ export function loadCachedBeaconState