diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index 4f24480e0b96..442b336051e0 100644 --- a/packages/beacon-node/src/execution/engine/types.ts +++ b/packages/beacon-node/src/execution/engine/types.ts @@ -1,4 +1,5 @@ -import {allForks, capella, deneb, Wei, bellatrix, Root} from "@lodestar/types"; +import * as util from "node:util"; +import {allForks, capella, deneb, Wei, bellatrix, Root, verge, ssz} from "@lodestar/types"; import { BYTES_PER_LOGS_BLOOM, FIELD_ELEMENTS_PER_BLOB, @@ -133,6 +134,7 @@ export type ExecutionPayloadRpc = { blobGasUsed?: QUANTITY; // DENEB excessBlobGas?: QUANTITY; // DENEB parentBeaconBlockRoot?: QUANTITY; // DENEB + executionWitness?: Record; // DENEB }; export type WithdrawalRpc = { @@ -193,6 +195,20 @@ export function serializeExecutionPayload(fork: ForkName, data: allForks.Executi payload.excessBlobGas = numToQuantity(excessBlobGas); } + // VERGE adds executionWitness to the ExecutionPayload + if (ForkSeq[fork] >= ForkSeq.verge) { + const {executionWitness} = data as verge.ExecutionPayload; + // right now the caseMap of ssz ExecutionWitness is camel cased and can + // directly be used to serialize tojson + payload.executionWitness = ssz.verge.ExecutionWitness.toJson(executionWitness); + // serialization with ssz serialize suffix diff's suffix to a string while geth expects num + (payload.executionWitness as verge.ExecutionWitness).stateDiff.forEach((sDiff) => { + sDiff.suffixDiffs.forEach((sfDiff) => { + sfDiff.suffix = Number(sfDiff.suffix); + }); + }); + } + return payload; } @@ -269,6 +285,20 @@ export function parseExecutionPayload( (executionPayload as deneb.ExecutionPayload).excessBlobGas = quantityToBigint(excessBlobGas); } + // VERGE adds execution witness to the payload + if (ForkSeq[fork] >= ForkSeq.verge) { + // right now the casing of executionWitness is camel case in the ssz caseMap + // we can directly use fromJson to read the serialized data from payload + const {executionWitness} = data; + console.log( + "parse executionWitness from EL", + util.inspect(executionWitness, false, null, true /* enable colors */), + {blockNumber: data.blockNumber} + ); + (executionPayload as verge.ExecutionPayload).executionWitness = + ssz.verge.ExecutionWitness.fromJson(executionWitness); + } + return {executionPayload, executionPayloadValue, blobsBundle}; } diff --git a/packages/beacon-node/test/spec/presets/fork.test.ts b/packages/beacon-node/test/spec/presets/fork.test.ts index 228ab6a38935..64a00b63e220 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"; @@ -35,6 +36,8 @@ const fork: TestRunnerFn = (forkNext) => { return slotFns.upgradeStateToCapella(preState as CachedBeaconStateBellatrix); case ForkName.deneb: return slotFns.upgradeStateToDeneb(preState as CachedBeaconStateCapella); + case ForkName.verge: + return slotFns.upgradeStateToVerge(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 ef3006bd6221..5e6df3556504 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 "chai"; -import {phase0, Root, ssz, TimeSeconds, allForks, deneb} from "@lodestar/types"; +import {phase0, Root, ssz, TimeSeconds, allForks} from "@lodestar/types"; import {InputType} from "@lodestar/spec-test-util"; import { BeaconStateAllForks, @@ -47,7 +47,7 @@ const genesisInitialization: TestRunnerFn = { phase0: { @@ -57,6 +59,14 @@ function getForkConfig({ prevVersion: Buffer.from([0, 0, 0, 3]), prevForkName: ForkName.capella, }, + verge: { + name: ForkName.verge, + seq: ForkSeq.verge, + epoch: verge, + version: Buffer.from([0, 0, 0, 4]), + prevVersion: Buffer.from([0, 0, 0, 3]), + prevForkName: ForkName.capella, + }, }; 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 verge = 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, verge}); 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..727b0669517a 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.verge: + 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/presets/mainnet.ts b/packages/config/src/chainConfig/presets/mainnet.ts index 2c02643a032c..cb315608a9ae 100644 --- a/packages/config/src/chainConfig/presets/mainnet.ts +++ b/packages/config/src/chainConfig/presets/mainnet.ts @@ -45,6 +45,10 @@ export const chainConfig: ChainConfig = { DENEB_FORK_VERSION: b("0x04000000"), DENEB_FORK_EPOCH: Infinity, + // VERGE + ELECTRA_FORK_VERSION: b("0x05000000"), + ELECTRA_FORK_EPOCH: Infinity, + // Time parameters // --------------------------------------------------------------- // 12 seconds diff --git a/packages/config/src/chainConfig/presets/minimal.ts b/packages/config/src/chainConfig/presets/minimal.ts index d790032bcee1..ebc3a430da53 100644 --- a/packages/config/src/chainConfig/presets/minimal.ts +++ b/packages/config/src/chainConfig/presets/minimal.ts @@ -42,6 +42,9 @@ export const chainConfig: ChainConfig = { // Deneb DENEB_FORK_VERSION: b("0x04000001"), DENEB_FORK_EPOCH: Infinity, + // Verge + ELECTRA_FORK_VERSION: b("0x05000001"), + ELECTRA_FORK_EPOCH: Infinity, // Time parameters // --------------------------------------------------------------- diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 4818ef9ee0aa..ae1fd75c497b 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; + // VERGE + ELECTRA_FORK_VERSION: Uint8Array; + ELECTRA_FORK_EPOCH: number; // Time parameters SECONDS_PER_SLOT: number; @@ -93,6 +96,9 @@ export const chainConfigTypes: SpecTypes = { // DENEB DENEB_FORK_VERSION: "bytes", DENEB_FORK_EPOCH: "number", + // VERGE + 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..b8a3be3ced5a 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 verge: ForkInfo = { + name: ForkName.verge, + seq: ForkSeq.verge, + epoch: config.ELECTRA_FORK_EPOCH, + version: config.ELECTRA_FORK_VERSION, + prevVersion: config.CAPELLA_FORK_VERSION, + prevForkName: ForkName.capella, + }; /** 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, verge, deneb}; // Prevents allocating an array on every getForkInfo() call const forksAscendingEpochOrder = Object.values(forks); diff --git a/packages/params/src/forkName.ts b/packages/params/src/forkName.ts index 142684c313f4..ea8617257606 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", + verge = "verge", } /** @@ -17,7 +18,9 @@ export enum ForkSeq { altair = 1, bellatrix = 2, capella = 3, - deneb = 4, + // Verge is scheduled after capella for now + verge = 4, + deneb = 5, } export type ForkPreLightClient = ForkName.phase0; @@ -38,8 +41,14 @@ export function isForkWithdrawals(fork: ForkName): fork is ForkWithdrawals { return isForkExecution(fork) && fork !== ForkName.bellatrix; } -export type ForkPreBlobs = ForkPreWithdrawals | ForkName.capella; +export type ForkPreVerge = ForkPreWithdrawals | ForkName.capella; +export type ForkVerge = Exclude; +export function isForkVerge(fork: ForkName): fork is ForkVerge { + return isForkWithdrawals(fork) && fork !== ForkName.capella; +} + +export type ForkPreBlobs = ForkPreVerge | ForkName.verge; export type ForkBlobs = Exclude; export function isForkBlobs(fork: ForkName): fork is ForkBlobs { - return isForkWithdrawals(fork) && fork !== ForkName.capella; + return isForkVerge(fork) && fork !== ForkName.verge; } diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 3d784356ae0f..8b8a8a88d3a4 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -235,3 +235,10 @@ export const INTERVALS_PER_SLOT = 3; export const BYTES_PER_FIELD_ELEMENT = 32; export const BLOB_TX_TYPE = 0x03; export const VERSIONED_HASH_VERSION_KZG = 0x01; + +// TODO: Verge spec notes these as preset but there's only one value +// https://github.com/ethereum/consensus-specs/blob/db74090c1e8dc1fb2c052bae268e22dc63061e32/specs/verge/beacon-chain.md#preset +export const MAX_STEMS = 2 ** 16; +export const MAX_COMMITMENTS_PER_STEM = 33; +export const VERKLE_WIDTH = 256; +export const IPA_PROOF_DEPTH = 8; diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index 14a29b5f09c0..302bc9cc35e3 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, + BeaconStateVerge, } from "./types.js"; export type BeaconStateCache = { @@ -129,6 +130,7 @@ export type CachedBeaconStateAltair = CachedBeaconState; export type CachedBeaconStateBellatrix = CachedBeaconState; export type CachedBeaconStateCapella = CachedBeaconState; export type CachedBeaconStateDeneb = CachedBeaconState; +export type CachedBeaconStateVerge = CachedBeaconState; export type CachedBeaconStateAllForks = CachedBeaconState; export type CachedBeaconStateExecutions = CachedBeaconState; diff --git a/packages/state-transition/src/cache/types.ts b/packages/state-transition/src/cache/types.ts index 39b1dbb4b45b..cb85a5bcf64e 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 BeaconStateVerge = 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 + | BeaconStateVerge; -export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateDeneb; +export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateDeneb | BeaconStateVerge; export type ShufflingGetter = (shufflingEpoch: Epoch, dependentRoot: RootHex) => EpochShuffling | null; diff --git a/packages/state-transition/src/slot/index.ts b/packages/state-transition/src/slot/index.ts index 6c4add1d1230..8c47495bcccb 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 {upgradeStateToVerge} from "./upgradeStateToVerge.js"; /** * Dial state to next slot. Common for all forks diff --git a/packages/state-transition/src/slot/upgradeStateToVerge.ts b/packages/state-transition/src/slot/upgradeStateToVerge.ts new file mode 100644 index 000000000000..0a7e60c81d8d --- /dev/null +++ b/packages/state-transition/src/slot/upgradeStateToVerge.ts @@ -0,0 +1,26 @@ +import {ssz} from "@lodestar/types"; +import {CachedBeaconStateDeneb, CachedBeaconStateVerge} from "../types.js"; +import {getCachedBeaconState} from "../cache/stateCache.js"; + +/** + * Upgrade a state from Deneb to Verge. + */ +export function upgradeStateToVerge(stateDeneb: CachedBeaconStateDeneb): CachedBeaconStateVerge { + const {config} = stateDeneb; + + const stateDenebNode = ssz.deneb.BeaconState.commitViewDU(stateDeneb); + const stateVergeView = ssz.verge.BeaconState.getViewDU(stateDenebNode); + + const stateVerge = getCachedBeaconState(stateVergeView, stateDeneb); + + stateVerge.fork = ssz.phase0.Fork.toViewDU({ + previousVersion: stateDeneb.fork.currentVersion, + currentVersion: config.DENEB_FORK_VERSION, + epoch: stateDeneb.epochCtx.epoch, + }); + + // latestExecutionPayloadHeader's executionWitnessRoot will have default zero root + + stateVerge.commit(); + return stateVerge; +} diff --git a/packages/state-transition/src/stateTransition.ts b/packages/state-transition/src/stateTransition.ts index 8fd98f4df03e..57a1c16a93e6 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"; @@ -23,6 +24,7 @@ import {processBlock} from "./block/index.js"; import {processEpoch} from "./epoch/index.js"; import {BlockExternalData, DataAvailableStatus, ExecutionPayloadStatus} from "./block/externalData.js"; import {ProcessBlockOpts} from "./block/types.js"; +import {upgradeStateToVerge} from "./slot/upgradeStateToVerge.js"; // Multifork capable state transition @@ -196,6 +198,9 @@ function processSlotsWithTransientCache( if (stateSlot === config.DENEB_FORK_EPOCH) { postState = upgradeStateToDeneb(postState as CachedBeaconStateCapella) as CachedBeaconStateAllForks; } + if (stateSlot === config.ELECTRA_FORK_EPOCH) { + postState = upgradeStateToVerge(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..02f33fba0d39 100644 --- a/packages/state-transition/src/types.ts +++ b/packages/state-transition/src/types.ts @@ -9,6 +9,7 @@ export type { CachedBeaconStateBellatrix, CachedBeaconStateCapella, CachedBeaconStateDeneb, + CachedBeaconStateVerge, } from "./cache/stateCache.js"; export type { @@ -19,4 +20,5 @@ export type { BeaconStateBellatrix, BeaconStateCapella, BeaconStateDeneb, + BeaconStateVerge, } from "./cache/types.js"; diff --git a/packages/state-transition/src/util/execution.ts b/packages/state-transition/src/util/execution.ts index 7ac4da4aeecb..10e936f25784 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, verge, isBlindedBeaconBlockBody, ssz} from "@lodestar/types"; import {ForkSeq} from "@lodestar/params"; import { @@ -170,5 +170,11 @@ export function executionPayloadToPayloadHeader( ).excessBlobGas; } + if (fork >= ForkSeq.verge) { + // https://github.com/ethereum/consensus-specs/blob/db74090c1e8dc1fb2c052bae268e22dc63061e32/specs/verge/beacon-chain.md#process_execution_payload + (bellatrixPayloadFields as verge.ExecutionPayloadHeader).executionWitnessRoot = + ssz.verge.ExecutionWitness.hashTreeRoot((payload as verge.ExecutionPayload).executionWitness); + } + return bellatrixPayloadFields; } diff --git a/packages/state-transition/src/util/genesis.ts b/packages/state-transition/src/util/genesis.ts index 1041c33d0eb3..ce76cce5ef64 100644 --- a/packages/state-transition/src/util/genesis.ts +++ b/packages/state-transition/src/util/genesis.ts @@ -214,6 +214,7 @@ export function initializeBeaconStateFromEth1( | typeof ssz.bellatrix.ExecutionPayloadHeader | typeof ssz.capella.ExecutionPayloadHeader | typeof ssz.deneb.ExecutionPayloadHeader + | typeof ssz.verge.ExecutionPayloadHeader > ): CachedBeaconStateAllForks { const stateView = getGenesisBeaconState( @@ -284,6 +285,15 @@ export function initializeBeaconStateFromEth1( ssz.deneb.ExecutionPayloadHeader.defaultViewDU(); } + if (GENESIS_SLOT >= config.ELECTRA_FORK_EPOCH) { + const stateVerge = state as CompositeViewDU; + stateVerge.fork.previousVersion = config.ELECTRA_FORK_VERSION; + stateVerge.fork.currentVersion = config.ELECTRA_FORK_VERSION; + stateVerge.latestExecutionPayloadHeader = + (executionPayloadHeader as CompositeViewDU) ?? + ssz.verge.ExecutionPayloadHeader.defaultViewDU(); + } + state.commit(); return state; diff --git a/packages/types/src/allForks/sszTypes.ts b/packages/types/src/allForks/sszTypes.ts index 463e5c57bd0d..ef210bde40b7 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 verge} from "../verge/index.js"; /** * Index the ssz types that differ by fork @@ -44,6 +45,13 @@ export const allForks = { BeaconState: deneb.BeaconState, Metadata: altair.Metadata, }, + verge: { + BeaconBlockBody: verge.BeaconBlockBody, + BeaconBlock: verge.BeaconBlock, + SignedBeaconBlock: verge.SignedBeaconBlock, + BeaconState: verge.BeaconState, + Metadata: altair.Metadata, + }, }; /** @@ -85,6 +93,17 @@ export const allForksExecution = { SignedBuilderBid: deneb.SignedBuilderBid, SSEPayloadAttributes: deneb.SSEPayloadAttributes, }, + verge: { + BeaconBlockBody: verge.BeaconBlockBody, + BeaconBlock: verge.BeaconBlock, + SignedBeaconBlock: verge.SignedBeaconBlock, + BeaconState: verge.BeaconState, + ExecutionPayload: verge.ExecutionPayload, + ExecutionPayloadHeader: verge.ExecutionPayloadHeader, + BuilderBid: deneb.BuilderBid, + SignedBuilderBid: deneb.SignedBuilderBid, + SSEPayloadAttributes: deneb.SSEPayloadAttributes, + }, }; /** @@ -107,6 +126,11 @@ export const allForksBlinded = { BeaconBlock: deneb.BlindedBeaconBlock, SignedBeaconBlock: deneb.SignedBlindedBeaconBlock, }, + verge: { + BeaconBlockBody: verge.BlindedBeaconBlockBody, + BeaconBlock: verge.BlindedBeaconBlock, + SignedBeaconBlock: verge.SignedBlindedBeaconBlock, + }, }; export const allForksLightClient = { @@ -150,6 +174,16 @@ export const allForksLightClient = { LightClientOptimisticUpdate: deneb.LightClientOptimisticUpdate, LightClientStore: deneb.LightClientStore, }, + verge: { + BeaconBlock: verge.BeaconBlock, + BeaconBlockBody: verge.BeaconBlockBody, + LightClientHeader: verge.LightClientHeader, + LightClientBootstrap: verge.LightClientBootstrap, + LightClientUpdate: verge.LightClientUpdate, + LightClientFinalityUpdate: verge.LightClientFinalityUpdate, + LightClientOptimisticUpdate: verge.LightClientOptimisticUpdate, + LightClientStore: verge.LightClientStore, + }, }; export const allForksBlobs = { @@ -158,4 +192,8 @@ export const allForksBlobs = { BlindedBlobSidecar: deneb.BlindedBlobSidecar, ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, }, + verge: { + BlobSidecar: deneb.BlobSidecar, + BlindedBlobSidecar: deneb.BlindedBlobSidecar, + }, }; diff --git a/packages/types/src/allForks/types.ts b/packages/types/src/allForks/types.ts index 01c597b8a245..eda078c1c5d6 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 verge} from "../verge/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 vergeSsz} from "../verge/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 + | verge.BeaconBlockBody; export type BeaconBlock = | phase0.BeaconBlock | altair.BeaconBlock | bellatrix.BeaconBlock | capella.BeaconBlock - | deneb.BeaconBlock; + | deneb.BeaconBlock + | verge.BeaconBlock; export type SignedBeaconBlock = | phase0.SignedBeaconBlock | altair.SignedBeaconBlock | bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock - | deneb.SignedBeaconBlock; + | deneb.SignedBeaconBlock + | verge.SignedBeaconBlock; export type BeaconState = | phase0.BeaconState | altair.BeaconState | bellatrix.BeaconState | capella.BeaconState - | deneb.BeaconState; + | deneb.BeaconState + | verge.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 + | verge.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 + | verge.ExecutionPayload; export type ExecutionPayloadHeader = | bellatrix.ExecutionPayloadHeader | capella.ExecutionPayloadHeader - | deneb.ExecutionPayloadHeader; + | deneb.ExecutionPayloadHeader + | verge.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 + | verge.BlindedBeaconBlockBody; +export type BlindedBeaconBlock = + | bellatrix.BlindedBeaconBlock + | capella.BlindedBeaconBlock + | deneb.BlindedBeaconBlock + | verge.BlindedBeaconBlock; export type SignedBlindedBeaconBlock = | bellatrix.SignedBlindedBeaconBlock | capella.SignedBlindedBeaconBlock - | deneb.SignedBlindedBeaconBlock; + | deneb.SignedBlindedBeaconBlock + | verge.SignedBlindedBeaconBlock; // Full or blinded types export type FullOrBlindedExecutionPayload = @@ -192,6 +213,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof denebSsz.BeaconBlockBody + | typeof vergeSsz.BeaconBlockBody >; BeaconBlock: AllForksTypeOf< | typeof phase0Ssz.BeaconBlock @@ -199,6 +221,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock + | typeof vergeSsz.BeaconBlock >; SignedBeaconBlock: AllForksTypeOf< | typeof phase0Ssz.SignedBeaconBlock @@ -206,6 +229,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock | typeof denebSsz.SignedBeaconBlock + | typeof vergeSsz.SignedBeaconBlock >; BeaconState: AllForksTypeOf< | typeof phase0Ssz.BeaconState @@ -213,30 +237,47 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState | typeof denebSsz.BeaconState + | typeof vergeSsz.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 vergeSsz.BeaconBlockBody >; BeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock + | typeof bellatrixSsz.BeaconBlock + | typeof capellaSsz.BeaconBlock + | typeof denebSsz.BeaconBlock + | typeof vergeSsz.BeaconBlock >; SignedBeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock | typeof denebSsz.SignedBeaconBlock + | typeof bellatrixSsz.SignedBeaconBlock + | typeof capellaSsz.SignedBeaconBlock + | typeof denebSsz.SignedBeaconBlock + | typeof vergeSsz.SignedBeaconBlock >; BeaconState: AllForksTypeOf< - typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState | typeof denebSsz.BeaconState + | typeof bellatrixSsz.BeaconState + | typeof capellaSsz.BeaconState + | typeof denebSsz.BeaconState + | typeof vergeSsz.BeaconState >; ExecutionPayload: AllForksTypeOf< - typeof bellatrixSsz.ExecutionPayload | typeof capellaSsz.ExecutionPayload | typeof denebSsz.ExecutionPayload + | typeof bellatrixSsz.ExecutionPayload + | typeof capellaSsz.ExecutionPayload + | typeof denebSsz.ExecutionPayload + | typeof vergeSsz.ExecutionPayload >; ExecutionPayloadHeader: AllForksTypeOf< | typeof bellatrixSsz.ExecutionPayloadHeader | typeof capellaSsz.ExecutionPayloadHeader | typeof denebSsz.ExecutionPayloadHeader + | typeof vergeSsz.ExecutionPayloadHeader >; BuilderBid: AllForksTypeOf< typeof bellatrixSsz.BuilderBid | typeof capellaSsz.BuilderBid | typeof denebSsz.BuilderBid @@ -256,14 +297,19 @@ export type AllForksBlindedSSZTypes = { | typeof bellatrixSsz.BlindedBeaconBlockBody | typeof capellaSsz.BlindedBeaconBlock | typeof denebSsz.BlindedBeaconBlock + | typeof vergeSsz.BlindedBeaconBlock >; BeaconBlock: AllForksTypeOf< - typeof bellatrixSsz.BlindedBeaconBlock | typeof capellaSsz.BlindedBeaconBlock | typeof denebSsz.BlindedBeaconBlock + | typeof bellatrixSsz.BlindedBeaconBlock + | typeof capellaSsz.BlindedBeaconBlock + | typeof denebSsz.BlindedBeaconBlock + | typeof vergeSsz.BlindedBeaconBlock >; SignedBeaconBlock: AllForksTypeOf< | typeof bellatrixSsz.SignedBlindedBeaconBlock | typeof capellaSsz.SignedBlindedBeaconBlock | typeof denebSsz.SignedBlindedBeaconBlock + | typeof vergeSsz.SignedBlindedBeaconBlock >; }; @@ -273,12 +319,14 @@ export type AllForksLightClientSSZTypes = { | typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock + | typeof vergeSsz.BeaconBlock >; BeaconBlockBody: AllForksTypeOf< | typeof altairSsz.BeaconBlockBody | typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof denebSsz.BeaconBlockBody + | typeof vergeSsz.BeaconBlockBody >; LightClientHeader: AllForksTypeOf< typeof altairSsz.LightClientHeader | typeof capellaSsz.LightClientHeader | typeof denebSsz.LightClientHeader diff --git a/packages/types/src/sszTypes.ts b/packages/types/src/sszTypes.ts index 2a7df948a447..4e6c3eccfc39 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 verge} from "./verge/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..84f261af00b3 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 verge} from "./verge/index.js"; export {ts as allForks} from "./allForks/index.js"; diff --git a/packages/types/src/verge/index.ts b/packages/types/src/verge/index.ts new file mode 100644 index 000000000000..7856cd729620 --- /dev/null +++ b/packages/types/src/verge/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/verge/sszTypes.ts b/packages/types/src/verge/sszTypes.ts new file mode 100644 index 000000000000..34dcd403dba2 --- /dev/null +++ b/packages/types/src/verge/sszTypes.ts @@ -0,0 +1,283 @@ +import { + ContainerType, + ListCompositeType, + ByteVectorType, + VectorCompositeType, + ByteListType, + OptionalType, +} from "@chainsafe/ssz"; +import { + HISTORICAL_ROOTS_LIMIT, + VERKLE_WIDTH, + MAX_STEMS, + IPA_PROOF_DEPTH, + MAX_COMMITMENTS_PER_STEM, + BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH, + EPOCHS_PER_SYNC_COMMITTEE_PERIOD, + SLOTS_PER_EPOCH, +} 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 {UintNum64, Root, BLSSignature, Slot, Bytes32} = primitiveSsz; + +// Spec: https://github.com/ethereum/consensus-specs/blob/db74090c1e8dc1fb2c052bae268e22dc63061e32/specs/verge/beacon-chain.md + +// Custom types + +export const Bytes31 = new ByteVectorType(31); +export const BanderwagonGroupElement = new ByteVectorType(32); +export const BanderwagonFieldElement = new ByteVectorType(32); +export const Stem = new ByteVectorType(31); + +// Beacon chain + +export const SuffixStateDiff = new ContainerType( + { + suffix: primitiveSsz.Byte, + // Null means not currently present + currentValue: new OptionalType(primitiveSsz.Bytes32), + // Null means value not updated + newValue: new OptionalType(primitiveSsz.Bytes32), + }, + { + typeName: "SuffixStateDiff", + casingMap: { + suffix: "suffix", + currentValue: "currentValue", + newValue: "newValue", + }, + } +); + +export const StemStateDiff = new ContainerType( + { + stem: Stem, + // Valid only if list is sorted by suffixes + suffixDiffs: new ListCompositeType(SuffixStateDiff, VERKLE_WIDTH), + }, + {typeName: "StemStateDiff", casingMap: {stem: "stem", suffixDiffs: "suffixDiffs"}} +); + +// Valid only if list is sorted by stems +export const StateDiff = new ListCompositeType(StemStateDiff, MAX_STEMS); + +export const IpaProof = new ContainerType( + { + cl: new VectorCompositeType(BanderwagonGroupElement, IPA_PROOF_DEPTH), + cr: new VectorCompositeType(BanderwagonGroupElement, IPA_PROOF_DEPTH), + finalEvaluation: BanderwagonFieldElement, + }, + {typeName: "IpaProof", casingMap: {cl: "cl", cr: "cr", finalEvaluation: "finalEvaluation"}} +); + +export const VerkleProof = new ContainerType( + { + otherStems: new ListCompositeType(Bytes31, MAX_STEMS), + depthExtensionPresent: new ByteListType(MAX_STEMS), + commitmentsByPath: new ListCompositeType(BanderwagonGroupElement, MAX_STEMS * MAX_COMMITMENTS_PER_STEM), + d: BanderwagonGroupElement, + ipaProof: IpaProof, + }, + { + typeName: "VerkleProof", + casingMap: { + otherStems: "otherStems", + depthExtensionPresent: "depthExtensionPresent", + commitmentsByPath: "commitmentsByPath", + d: "d", + ipaProof: "ipaProof", + }, + } +); + +export const ExecutionWitness = new ContainerType( + { + stateDiff: StateDiff, + verkleProof: VerkleProof, + }, + {typeName: "ExecutionWitness", casingMap: {stateDiff: "stateDiff", verkleProof: "verkleProof"}} +); + +// Beacon Chain types +// https://github.com/ethereum/consensus-specs/blob/dev/specs/eip4844/beacon-chain.md#containers + +export const ExecutionPayload = new ContainerType( + { + ...capellaSsz.ExecutionPayload.fields, + executionWitness: ExecutionWitness, // New in verge + }, + {typeName: "ExecutionPayload", jsonCase: "eth2"} +); + +export const ExecutionPayloadHeader = new ContainerType( + { + ...capellaSsz.ExecutionPayloadHeader.fields, + executionWitnessRoot: Root, // New in verge + }, + {typeName: "ExecutionPayloadHeader", jsonCase: "eth2"} +); + +// We have to preserve Fields ordering while changing the type of ExecutionPayload +export const BeaconBlockBody = new ContainerType( + { + ...altairSsz.BeaconBlockBody.fields, + executionPayload: ExecutionPayload, // Modified in verge + blsToExecutionChanges: capellaSsz.BeaconBlockBody.fields.blsToExecutionChanges, + // blobKzgCommitments: denebSsz.BlobKzgCommitments, + }, + {typeName: "BeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const BeaconBlock = new ContainerType( + { + ...capellaSsz.BeaconBlock.fields, + body: BeaconBlockBody, // Modified in verge + }, + {typeName: "BeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const SignedBeaconBlock = new ContainerType( + { + message: BeaconBlock, // Modified in verge + signature: BLSSignature, + }, + {typeName: "SignedBeaconBlock", jsonCase: "eth2"} +); + +export const BlindedBeaconBlockBody = new ContainerType( + { + ...altairSsz.BeaconBlockBody.fields, + executionPayloadHeader: ExecutionPayloadHeader, // Modified in verge + blsToExecutionChanges: capellaSsz.BeaconBlockBody.fields.blsToExecutionChanges, + // blobKzgCommitments: denebSsz.BlobKzgCommitments, + }, + {typeName: "BlindedBeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const BlindedBeaconBlock = new ContainerType( + { + ...bellatrixSsz.BlindedBeaconBlock.fields, + body: BlindedBeaconBlockBody, // Modified in DENEB + }, + {typeName: "BlindedBeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const SignedBlindedBeaconBlock = new ContainerType( + { + message: BlindedBeaconBlock, // Modified in DENEB + signature: BLSSignature, + }, + {typeName: "SignedBlindedBeaconBlock", jsonCase: "eth2"} +); + +// We don't spread capella.BeaconState fields since we need to replace +// latestExecutionPayloadHeader and we cannot keep order doing that +export const BeaconState = new ContainerType( + { + 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 verge + // Withdrawals + nextWithdrawalIndex: capellaSsz.BeaconState.fields.nextWithdrawalIndex, + nextWithdrawalValidatorIndex: capellaSsz.BeaconState.fields.nextWithdrawalValidatorIndex, + // Deep history valid from Capella onwards + historicalSummaries: capellaSsz.BeaconState.fields.historicalSummaries, + }, + {typeName: "BeaconState", jsonCase: "eth2"} +); + +export const LightClientHeader = new ContainerType( + { + beacon: phase0Ssz.BeaconBlockHeader, + execution: ExecutionPayloadHeader, + executionBranch: new VectorCompositeType(Bytes32, EXECUTION_PAYLOAD_DEPTH), + }, + {typeName: "LightClientHeader", jsonCase: "eth2"} +); + +export const LightClientBootstrap = new ContainerType( + { + header: LightClientHeader, + currentSyncCommittee: altairSsz.SyncCommittee, + currentSyncCommitteeBranch: altairSsz.LightClientBootstrap.fields.currentSyncCommitteeBranch, + }, + {typeName: "LightClientBootstrap", jsonCase: "eth2"} +); + +export const LightClientUpdate = new ContainerType( + { + 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( + { + attestedHeader: LightClientHeader, + finalizedHeader: LightClientHeader, + finalityBranch: altairSsz.LightClientFinalityUpdate.fields.finalityBranch, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, + }, + {typeName: "LightClientFinalityUpdate", jsonCase: "eth2"} +); + +export const LightClientOptimisticUpdate = new ContainerType( + { + attestedHeader: LightClientHeader, + syncAggregate: altairSsz.SyncAggregate, + signatureSlot: Slot, + }, + {typeName: "LightClientOptimisticUpdate", jsonCase: "eth2"} +); + +export const LightClientStore = new ContainerType( + { + snapshot: LightClientBootstrap, + validUpdates: new ListCompositeType(LightClientUpdate, EPOCHS_PER_SYNC_COMMITTEE_PERIOD * SLOTS_PER_EPOCH), + }, + {typeName: "LightClientStore", jsonCase: "eth2"} +); diff --git a/packages/types/src/verge/types.ts b/packages/types/src/verge/types.ts new file mode 100644 index 000000000000..6de7613eb47d --- /dev/null +++ b/packages/types/src/verge/types.ts @@ -0,0 +1,29 @@ +import {ValueOf} from "@chainsafe/ssz"; +import * as ssz from "./sszTypes.js"; + +export type Bytes31 = ValueOf; +export type BanderwagonGroupElement = ValueOf; +export type BanderwagonFieldElement = ValueOf; +export type Stem = ValueOf; + +export type SuffixStateDiff = ValueOf; +export type StemStateDiff = ValueOf; +export type StateDiff = ValueOf; +export type IpaProof = ValueOf; +export type VerkleProof = ValueOf; +export type ExecutionWitness = 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 FullOrBlindedExecutionPayload = ExecutionPayload | ExecutionPayloadHeader; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 37908afaf86c..38e453a4820e 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -73,6 +73,7 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record