diff --git a/packages/beacon-node/src/chain/blocks/utils/elephantWithWings.ts b/packages/beacon-node/src/chain/blocks/utils/elephantWithWings.ts new file mode 100644 index 000000000000..dbdcf108a968 --- /dev/null +++ b/packages/beacon-node/src/chain/blocks/utils/elephantWithWings.ts @@ -0,0 +1,20 @@ +export const VERKLE_ELEPHANTWITHWINGS_BANNER = String.raw` + :~~. + :!:^::^^!!^ + :!^^ !:J. + .!!:! .^7^ + .?7~: ^#! !!!^!~. + :!^ GP~..^^?^ ^!: + !^ Y5 #7: + .:^^~7~. ! ~~6800~~^ ^6800^ ~~6800 68 00 68~~ 00~ || + .~! 55J ~~6800~~ :! !^ .YP :JP 7B. ^7# + ^7 :: 5 #:::^::~~6800~~^^?!?.~:: :! !^ 6800 .^PJ JG ~!B + :G ^7 ^ !^68 00^ ~~6800~~ :! !: .&~ ^#! ^!55~~~6800~~7G~ !7B + 7G ~!5J ^? !.----. ~! ~~6800~~ .7#: 6800 ~ + ?G :^?G. ...... ^?^.!G. ?G :^?G. ...... ^?^.!G. ! + ~! ~~6800~~ .7#: 6800 G~ 7G ~!5J ^? !.----. :: + :! !: .&~ ^#! ^!55~~~6800~~7G~ !7B :G ^7 ^ !^68 00^ ~~6800~~ :: + :! !^ 6800 .^PJ JG ~!B ^7 :: 5 ^^ !!!! #:::^::~~6800~~^^?!?.~ + :! !^ .YP :JP 7B. ^7# .~!~ ~~6800 ^6800^ ~~6800~~ + 6800 68 00 68~~ 00~ 6800 +`; diff --git a/packages/beacon-node/src/chain/blocks/verifyBlock.ts b/packages/beacon-node/src/chain/blocks/verifyBlock.ts index 4d21342bd8cc..f3ce54809779 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlock.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlock.ts @@ -17,6 +17,7 @@ import {BlockInput, ImportBlockOpts, BlockInputType} from "./types.js"; import {POS_PANDA_MERGE_TRANSITION_BANNER} from "./utils/pandaMergeTransitionBanner.js"; import {CAPELLA_OWL_BANNER} from "./utils/ownBanner.js"; import {DENEB_BLOWFISH_BANNER} from "./utils/blowfishBanner.js"; +import {VERKLE_ELEPHANTWITHWINGS_BANNER} from "./utils/elephantWithWings.js"; import {verifyBlocksStateTransitionOnly} from "./verifyBlocksStateTransitionOnly.js"; import {verifyBlocksSignatures} from "./verifyBlocksSignatures.js"; import {verifyBlocksExecutionPayload, SegmentExecStatus} from "./verifyBlocksExecutionPayloads.js"; @@ -152,6 +153,11 @@ export async function verifyBlocksInEpoch( this.logger.info("Activating withdrawals", {epoch: this.config.CAPELLA_FORK_EPOCH}); break; + case ForkName.verkle: + this.logger.info(VERKLE_ELEPHANTWITHWINGS_BANNER); + this.logger.info("Activating verkle", {epoch: this.config.VERKLE_FORK_EPOCH}); + break; + case ForkName.deneb: this.logger.info(DENEB_BLOWFISH_BANNER); this.logger.info("Activating blobs", {epoch: this.config.DENEB_FORK_EPOCH}); diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index 85f514c953b0..dcaba3b6586c 100644 --- a/packages/beacon-node/src/execution/engine/types.ts +++ b/packages/beacon-node/src/execution/engine/types.ts @@ -1,4 +1,4 @@ -import {capella, deneb, Wei, bellatrix, Root, ExecutionPayload} from "@lodestar/types"; +import {capella, deneb, Wei, bellatrix, Root, verkle, ExecutionPayload, ssz} from "@lodestar/types"; import { BYTES_PER_LOGS_BLOOM, FIELD_ELEMENTS_PER_BLOB, @@ -18,7 +18,6 @@ import { } from "../../eth1/provider/utils.js"; import {ExecutionPayloadStatus, BlobsBundle, PayloadAttributes, VersionedHashes} from "./interface.js"; import {WithdrawalV1} from "./payloadIdCache.js"; - /* eslint-disable @typescript-eslint/naming-convention */ export type EngineApiRpcParamTypes = { @@ -141,6 +140,7 @@ export type ExecutionPayloadRpc = { blobGasUsed?: QUANTITY; // DENEB excessBlobGas?: QUANTITY; // DENEB parentBeaconBlockRoot?: QUANTITY; // DENEB + executionWitness?: Record; // DENEB }; export type WithdrawalRpc = { @@ -212,6 +212,20 @@ export function serializeExecutionPayload(fork: ForkName, data: ExecutionPayload payload.excessBlobGas = numToQuantity(excessBlobGas); } + // VERKLE adds executionWitness to the ExecutionPayload + if (ForkSeq[fork] >= ForkSeq.verkle) { + const {executionWitness} = data as verkle.ExecutionPayload; + // right now the caseMap of ssz ExecutionWitness is camel cased and can + // directly be used to serialize tojson + payload.executionWitness = ssz.verkle.ExecutionWitness.toJson(executionWitness); + // serialization with ssz serialize suffix diff's suffix to a string while geth expects num + (payload.executionWitness as verkle.ExecutionWitness).stateDiff.forEach((sDiff) => { + sDiff.suffixDiffs.forEach((sfDiff) => { + sfDiff.suffix = Number(sfDiff.suffix); + }); + }); + } + return payload; } @@ -297,6 +311,15 @@ export function parseExecutionPayload( (executionPayload as deneb.ExecutionPayload).excessBlobGas = quantityToBigint(excessBlobGas); } + // VERKLE adds execution witness to the payload + if (ForkSeq[fork] >= ForkSeq.verkle) { + // 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; + (executionPayload as verkle.ExecutionPayload).executionWitness = + ssz.verkle.ExecutionWitness.fromJson(executionWitness); + } + return {executionPayload, executionPayloadValue, blobsBundle, shouldOverrideBuilder}; } diff --git a/packages/beacon-node/test/spec/presets/fork.test.ts b/packages/beacon-node/test/spec/presets/fork.test.ts index 228ab6a38935..5f06470fec69 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.verkle: + return slotFns.upgradeStateToVerkle(preState as CachedBeaconStateDeneb); } }, options: { diff --git a/packages/beacon-node/test/spec/presets/transition.test.ts b/packages/beacon-node/test/spec/presets/transition.test.ts index d9925f292677..fc6dfbed1189 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 = { phase0: { @@ -57,6 +59,14 @@ function getForkConfig({ prevVersion: Buffer.from([0, 0, 0, 3]), prevForkName: ForkName.capella, }, + verkle: { + name: ForkName.verkle, + seq: ForkSeq.verkle, + epoch: verkle, + 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 verkle = 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, verkle}); 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..6c54fe38cadf 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.verkle: + return createChainForkConfig({ + ALTAIR_FORK_EPOCH: 0, + BELLATRIX_FORK_EPOCH: 0, + CAPELLA_FORK_EPOCH: 0, + DENEB_FORK_EPOCH: 0, + VERKLE_FORK_EPOCH: forkEpoch, + }); } } diff --git a/packages/config/src/chainConfig/configs/mainnet.ts b/packages/config/src/chainConfig/configs/mainnet.ts index 883688ca821b..daf58d3ab583 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 + // VERKLE + VERKLE_FORK_VERSION: b("0x05000000"), + VERKLE_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..f4e825b50d71 100644 --- a/packages/config/src/chainConfig/configs/minimal.ts +++ b/packages/config/src/chainConfig/configs/minimal.ts @@ -45,6 +45,9 @@ export const chainConfig: ChainConfig = { // Deneb DENEB_FORK_VERSION: b("0x04000001"), DENEB_FORK_EPOCH: Infinity, + // Verkle + VERKLE_FORK_VERSION: b("0x05000001"), + VERKLE_FORK_EPOCH: Infinity, // Time parameters // --------------------------------------------------------------- diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 45f05bfaa724..5ad5ee61f4bf 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; + // VERKLE + VERKLE_FORK_VERSION: Uint8Array; + VERKLE_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", + // VERKLE + VERKLE_FORK_VERSION: "bytes", + VERKLE_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 16d8952548b8..9a1374d32ab3 100644 --- a/packages/config/src/forkConfig/index.ts +++ b/packages/config/src/forkConfig/index.ts @@ -59,10 +59,18 @@ export function createForkConfig(config: ChainConfig): ForkConfig { prevVersion: config.CAPELLA_FORK_VERSION, prevForkName: ForkName.capella, }; + const verkle: ForkInfo = { + name: ForkName.verkle, + seq: ForkSeq.verkle, + epoch: config.VERKLE_FORK_EPOCH, + version: config.VERKLE_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, verkle, 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 a5f6d49d1cef..8c262eb854a5 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", + verkle = "verkle", } /** @@ -17,7 +18,9 @@ export enum ForkSeq { altair = 1, bellatrix = 2, capella = 3, - deneb = 4, + // Verkle is scheduled after capella for now + verkle = 4, + deneb = 5, } function exclude(coll: T[], val: U[]): Exclude[] { @@ -72,9 +75,22 @@ 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 const forkVerge = exclude(forkAll, [ForkName.phase0, ForkName.altair, ForkName.bellatrix, ForkName.capella]); +export function isForkVerge(fork: ForkName): fork is ForkVerge { + return isForkWithdrawals(fork) && fork !== ForkName.capella; +} + +export type ForkPreBlobs = ForkPreVerge | ForkName.verkle; export type ForkBlobs = Exclude; -export const forkBlobs = exclude(forkAll, [ForkName.phase0, ForkName.altair, ForkName.bellatrix, ForkName.capella]); +export const forkBlobs = exclude(forkAll, [ + ForkName.phase0, + ForkName.altair, + ForkName.bellatrix, + ForkName.capella, + ForkName.verkle, +]); export function isForkBlobs(fork: ForkName): fork is ForkBlobs { - return isForkWithdrawals(fork) && fork !== ForkName.capella; + return isForkVerge(fork) && fork !== ForkName.verkle; } diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 6a95e3ca632e..f8ab6039cee6 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -244,3 +244,10 @@ 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; + +// TODO: Verkle 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 f4e637e5d665..eac520f07fa8 100644 --- a/packages/state-transition/src/cache/stateCache.ts +++ b/packages/state-transition/src/cache/stateCache.ts @@ -10,6 +10,7 @@ import { BeaconStateBellatrix, BeaconStateCapella, BeaconStateDeneb, + BeaconStateVerkle, } from "./types.js"; import {RewardCache, createEmptyRewardCache} from "./rewardCache.js"; @@ -130,6 +131,7 @@ export type CachedBeaconStateAltair = CachedBeaconState; export type CachedBeaconStateBellatrix = CachedBeaconState; export type CachedBeaconStateCapella = CachedBeaconState; export type CachedBeaconStateDeneb = CachedBeaconState; +export type CachedBeaconStateVerkle = 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 d6d8a3c37904..f832e396623f 100644 --- a/packages/state-transition/src/cache/types.ts +++ b/packages/state-transition/src/cache/types.ts @@ -7,6 +7,7 @@ export type BeaconStatePhase0 = CompositeViewDU>; export type BeaconStateBellatrix = CompositeViewDU>; export type BeaconStateCapella = CompositeViewDU>; +export type BeaconStateVerkle = CompositeViewDU>; export type BeaconStateDeneb = CompositeViewDU>; export type BeaconStateAllForks = CompositeViewDU>; diff --git a/packages/state-transition/src/slot/index.ts b/packages/state-transition/src/slot/index.ts index 6c4add1d1230..2a34d31e19b6 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 {upgradeStateToVerkle} from "./upgradeStateToVerkle.js"; /** * Dial state to next slot. Common for all forks diff --git a/packages/state-transition/src/slot/upgradeStateToVerkle.ts b/packages/state-transition/src/slot/upgradeStateToVerkle.ts new file mode 100644 index 000000000000..04cd3b8deb29 --- /dev/null +++ b/packages/state-transition/src/slot/upgradeStateToVerkle.ts @@ -0,0 +1,26 @@ +import {ssz} from "@lodestar/types"; +import {CachedBeaconStateCapella, CachedBeaconStateVerkle} from "../types.js"; +import {getCachedBeaconState} from "../cache/stateCache.js"; + +/** + * Upgrade a state from Capella (Eventualy DENEB) to Verkle. + */ +export function upgradeStateToVerkle(stateCapella: CachedBeaconStateCapella): CachedBeaconStateVerkle { + const {config} = stateCapella; + + const stateCapellaNode = ssz.capella.BeaconState.commitViewDU(stateCapella); + const stateVerkleView = ssz.verkle.BeaconState.getViewDU(stateCapellaNode); + + const stateVerkle = getCachedBeaconState(stateVerkleView, stateCapella); + + stateVerkle.fork = ssz.phase0.Fork.toViewDU({ + previousVersion: stateCapella.fork.currentVersion, + currentVersion: config.VERKLE_FORK_VERSION, + epoch: stateCapella.epochCtx.epoch, + }); + + // latestExecutionPayloadHeader's executionWitnessRoot will have default zero root + + stateVerkle.commit(); + return stateVerkle; +} diff --git a/packages/state-transition/src/stateTransition.ts b/packages/state-transition/src/stateTransition.ts index a454bf4b34d0..e7c3d3ef9974 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 {EpochTransitionStep, processEpoch} from "./epoch/index.js"; import {BlockExternalData, DataAvailableStatus, ExecutionPayloadStatus} from "./block/externalData.js"; import {ProcessBlockOpts} from "./block/types.js"; +import {upgradeStateToVerkle} from "./slot/upgradeStateToVerkle.js"; // Multifork capable state transition @@ -239,6 +241,9 @@ function processSlotsWithTransientCache( if (stateSlot === config.DENEB_FORK_EPOCH) { postState = upgradeStateToDeneb(postState as CachedBeaconStateCapella) as CachedBeaconStateAllForks; } + if (stateSlot === config.VERKLE_FORK_EPOCH) { + postState = upgradeStateToVerkle(postState as CachedBeaconStateCapella) as CachedBeaconStateAllForks; + } } else { postState.slot++; } diff --git a/packages/state-transition/src/types.ts b/packages/state-transition/src/types.ts index 6b6b1f6260b2..9fc45daef715 100644 --- a/packages/state-transition/src/types.ts +++ b/packages/state-transition/src/types.ts @@ -9,6 +9,7 @@ export type { CachedBeaconStateBellatrix, CachedBeaconStateCapella, CachedBeaconStateDeneb, + CachedBeaconStateVerkle, } from "./cache/stateCache.js"; export type { @@ -19,4 +20,5 @@ export type { BeaconStateBellatrix, BeaconStateCapella, BeaconStateDeneb, + BeaconStateVerkle, } from "./cache/types.js"; diff --git a/packages/state-transition/src/util/execution.ts b/packages/state-transition/src/util/execution.ts index 1c5046354fcb..1efde47e9d61 100644 --- a/packages/state-transition/src/util/execution.ts +++ b/packages/state-transition/src/util/execution.ts @@ -2,6 +2,7 @@ import { bellatrix, capella, deneb, + verkle, isBlindedBeaconBlockBody, ssz, BeaconBlock, @@ -170,5 +171,11 @@ export function executionPayloadToPayloadHeader(fork: ForkSeq, payload: Executio ).excessBlobGas; } + if (fork >= ForkSeq.verkle) { + // https://github.com/ethereum/consensus-specs/blob/db74090c1e8dc1fb2c052bae268e22dc63061e32/specs/verge/beacon-chain.md#process_execution_payload + (bellatrixPayloadFields as verkle.ExecutionPayloadHeader).executionWitnessRoot = + ssz.verkle.ExecutionWitness.hashTreeRoot((payload as verkle.ExecutionPayload).executionWitness); + } + return bellatrixPayloadFields; } diff --git a/packages/state-transition/src/util/genesis.ts b/packages/state-transition/src/util/genesis.ts index 1041c33d0eb3..150c0b4e714d 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.verkle.ExecutionPayloadHeader > ): CachedBeaconStateAllForks { const stateView = getGenesisBeaconState( @@ -284,6 +285,15 @@ export function initializeBeaconStateFromEth1( ssz.deneb.ExecutionPayloadHeader.defaultViewDU(); } + if (GENESIS_SLOT >= config.VERKLE_FORK_EPOCH) { + const stateVerkle = state as CompositeViewDU; + stateVerkle.fork.previousVersion = config.VERKLE_FORK_VERSION; + stateVerkle.fork.currentVersion = config.VERKLE_FORK_VERSION; + stateVerkle.latestExecutionPayloadHeader = + (executionPayloadHeader as CompositeViewDU) ?? + ssz.verkle.ExecutionPayloadHeader.defaultViewDU(); + } + state.commit(); return state; diff --git a/packages/types/src/sszTypes.ts b/packages/types/src/sszTypes.ts index 60980fa0822a..800458f33558 100644 --- a/packages/types/src/sszTypes.ts +++ b/packages/types/src/sszTypes.ts @@ -5,9 +5,10 @@ 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 verkle} from "./verkle/index.js"; export * from "./primitive/sszTypes.js"; -export {phase0, altair, bellatrix, capella, deneb}; +export {phase0, altair, bellatrix, capella, deneb, verkle}; /** * Index the ssz types that differ by fork @@ -18,7 +19,8 @@ const typesByFork = { [ForkName.altair]: {...phase0, ...altair}, [ForkName.bellatrix]: {...phase0, ...altair, ...bellatrix}, [ForkName.capella]: {...phase0, ...altair, ...bellatrix, ...capella}, - [ForkName.deneb]: {...phase0, ...altair, ...bellatrix, ...capella, ...deneb}, + [ForkName.verkle]: {...phase0, ...altair, ...bellatrix, ...capella, ...verkle}, + [ForkName.deneb]: {...phase0, ...altair, ...bellatrix, ...capella, ...verkle, ...deneb}, }; /** diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 3602299ae5e1..52705c5ac6be 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -5,6 +5,7 @@ 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 {Slot} from "./primitive/types.js"; +import {verkle} from "./types.js"; export * from "./primitive/types.js"; export {ts as phase0} from "./phase0/index.js"; @@ -12,6 +13,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 verkle} from "./verkle/index.js"; /** Common non-spec type to represent roots as strings */ export type RootHex = string; @@ -102,6 +104,31 @@ type TypesByFork = { SyncCommittee: altair.SyncCommittee; SyncAggregate: altair.SyncAggregate; }; + [ForkName.verkle]: { + BeaconBlockHeader: phase0.BeaconBlockHeader; + SignedBeaconBlockHeader: phase0.SignedBeaconBlockHeader; + BeaconBlock: verkle.BeaconBlock; + BeaconBlockBody: verkle.BeaconBlockBody; + BeaconState: verkle.BeaconState; + SignedBeaconBlock: verkle.SignedBeaconBlock; + Metadata: altair.Metadata; + LightClientHeader: verkle.LightClientHeader; + LightClientBootstrap: verkle.LightClientBootstrap; + LightClientUpdate: verkle.LightClientUpdate; + LightClientFinalityUpdate: verkle.LightClientFinalityUpdate; + LightClientOptimisticUpdate: verkle.LightClientOptimisticUpdate; + LightClientStore: verkle.LightClientStore; + BlindedBeaconBlock: verkle.BlindedBeaconBlock; + BlindedBeaconBlockBody: verkle.BlindedBeaconBlockBody; + SignedBlindedBeaconBlock: verkle.SignedBlindedBeaconBlock; + ExecutionPayload: verkle.ExecutionPayload; + ExecutionPayloadHeader: verkle.ExecutionPayloadHeader; + BuilderBid: verkle.BuilderBid; + SignedBuilderBid: verkle.SignedBuilderBid; + SSEPayloadAttributes: capella.SSEPayloadAttributes; + SyncCommittee: altair.SyncCommittee; + SyncAggregate: altair.SyncAggregate; + }; [ForkName.deneb]: { BeaconBlockHeader: phase0.BeaconBlockHeader; SignedBeaconBlockHeader: phase0.SignedBeaconBlockHeader; diff --git a/packages/types/src/verkle/index.ts b/packages/types/src/verkle/index.ts new file mode 100644 index 000000000000..7856cd729620 --- /dev/null +++ b/packages/types/src/verkle/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/verkle/sszTypes.ts b/packages/types/src/verkle/sszTypes.ts new file mode 100644 index 000000000000..13ff67f0c198 --- /dev/null +++ b/packages/types/src/verkle/sszTypes.ts @@ -0,0 +1,304 @@ +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, UintBn256, BLSPubkey} = 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, + parentStateRoot: Root, + }, + { + typeName: "ExecutionWitness", + casingMap: {stateDiff: "stateDiff", verkleProof: "verkleProof", parentStateRoot: "parentStateRoot"}, + } +); + +// 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 verkle + }, + {typeName: "ExecutionPayload", jsonCase: "eth2"} +); + +export const ExecutionPayloadHeader = new ContainerType( + { + ...capellaSsz.ExecutionPayloadHeader.fields, + executionWitnessRoot: Root, // New in verkle + }, + {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 verkle + 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 verkle + }, + {typeName: "BeaconBlock", jsonCase: "eth2", cachePermanentRootStruct: true} +); + +export const SignedBeaconBlock = new ContainerType( + { + message: BeaconBlock, // Modified in verkle + signature: BLSSignature, + }, + {typeName: "SignedBeaconBlock", jsonCase: "eth2"} +); + +export const BuilderBid = new ContainerType( + { + header: ExecutionPayloadHeader, + value: UintBn256, + pubkey: BLSPubkey, + }, + {typeName: "BuilderBid", jsonCase: "eth2"} +); + +export const SignedBuilderBid = new ContainerType( + { + message: BuilderBid, + signature: BLSSignature, + }, + {typeName: "SignedBuilderBid", jsonCase: "eth2"} +); + +export const BlindedBeaconBlockBody = new ContainerType( + { + ...altairSsz.BeaconBlockBody.fields, + executionPayloadHeader: ExecutionPayloadHeader, // Modified in verkle + 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 verkle + // 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/verkle/types.ts b/packages/types/src/verkle/types.ts new file mode 100644 index 000000000000..41e43a8c6d07 --- /dev/null +++ b/packages/types/src/verkle/types.ts @@ -0,0 +1,39 @@ +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; + +export type BuilderBid = ValueOf; +export type SignedBuilderBid = 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/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 8ccaf9fe75ba..e561f414a7e2 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -73,6 +73,7 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record