From b8c5f3d6f7415ba9b70a872794f8c4ef1a964f8f Mon Sep 17 00:00:00 2001 From: Navie Chan Date: Sun, 2 Jun 2024 19:40:13 +0200 Subject: [PATCH 1/2] initial commit --- .../config/src/chainConfig/configs/mainnet.ts | 9 +++++ .../config/src/chainConfig/configs/minimal.ts | 8 ++++ packages/config/src/chainConfig/types.ts | 16 ++++++++ packages/config/src/forkConfig/index.ts | 10 ++++- packages/params/src/forkName.ts | 2 + .../state-transition/src/cache/stateCache.ts | 2 + packages/state-transition/src/cache/types.ts | 6 ++- packages/state-transition/src/epoch/index.ts | 6 +++ .../src/epoch/processNetExcessPenalties.ts | 14 +++++++ packages/state-transition/src/util/eip7716.ts | 25 ++++++++++++ packages/types/src/allForks/sszTypes.ts | 38 +++++++++++++++++++ packages/types/src/eip7716/index.ts | 4 ++ packages/types/src/eip7716/sszTypes.ts | 22 +++++++++++ packages/types/src/eip7716/types.ts | 8 ++++ packages/types/src/sszTypes.ts | 1 + packages/validator/src/util/params.ts | 9 +++++ 16 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 packages/state-transition/src/epoch/processNetExcessPenalties.ts create mode 100644 packages/state-transition/src/util/eip7716.ts create mode 100644 packages/types/src/eip7716/index.ts create mode 100644 packages/types/src/eip7716/sszTypes.ts create mode 100644 packages/types/src/eip7716/types.ts diff --git a/packages/config/src/chainConfig/configs/mainnet.ts b/packages/config/src/chainConfig/configs/mainnet.ts index 883688ca821b..3a4b62304d11 100644 --- a/packages/config/src/chainConfig/configs/mainnet.ts +++ b/packages/config/src/chainConfig/configs/mainnet.ts @@ -48,6 +48,10 @@ export const chainConfig: ChainConfig = { // Deneb DENEB_FORK_VERSION: b("0x04000000"), DENEB_FORK_EPOCH: 269568, // March 13, 2024, 01:55:35pm UTC + + // EIP-7716 + EIP7716_FORK_VERSION: b("0x05000000"), + EIP7716_FORK_EPOCH: Infinity, // Time parameters // --------------------------------------------------------------- @@ -98,4 +102,9 @@ export const chainConfig: ChainConfig = { // Deneb // `2**12` (= 4096 epochs, ~18 days) MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096, + + // eip7716 + PENALTY_ADJUSTMENT_FACTOR: 4096, + MAX_PENALTY_FACTOR: 4, + PENALTY_RECOVERY_RATE: 1, }; diff --git a/packages/config/src/chainConfig/configs/minimal.ts b/packages/config/src/chainConfig/configs/minimal.ts index 23cd14e763ec..b22e133d0e25 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, + // EIP-7716 + EIP7716_FORK_VERSION: b("0x05000000"), + EIP7716_FORK_EPOCH: Infinity, // Time parameters // --------------------------------------------------------------- @@ -96,4 +99,9 @@ export const chainConfig: ChainConfig = { // Deneb // `2**12` (= 4096 epochs, ~18 days) MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096, + + // eip7716 + PENALTY_ADJUSTMENT_FACTOR: 4096, + MAX_PENALTY_FACTOR: 4, + PENALTY_RECOVERY_RATE: 1, }; diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 657c8a6c14b4..a92d529a946f 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; + // EIP-7716 + EIP7716_FORK_VERSION: Uint8Array; + EIP7716_FORK_EPOCH: number; // Time parameters SECONDS_PER_SLOT: number; @@ -69,6 +72,11 @@ export type ChainConfig = { // Networking MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: number; + + // EIP-7716 + PENALTY_ADJUSTMENT_FACTOR: number, + MAX_PENALTY_FACTOR: number, + PENALTY_RECOVERY_RATE: number, }; export const chainConfigTypes: SpecTypes = { @@ -99,6 +107,9 @@ export const chainConfigTypes: SpecTypes = { // DENEB DENEB_FORK_VERSION: "bytes", DENEB_FORK_EPOCH: "number", + // EIP-7716 + EIP7716_FORK_VERSION: "bytes", + EIP7716_FORK_EPOCH: "number", // Time parameters SECONDS_PER_SLOT: "number", @@ -128,6 +139,11 @@ export const chainConfigTypes: SpecTypes = { // Networking MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: "number", + + // EIP-7716 + PENALTY_ADJUSTMENT_FACTOR: "number", + MAX_PENALTY_FACTOR: "number", + PENALTY_RECOVERY_RATE: "number", }; /** Allows values in a Spec file */ diff --git a/packages/config/src/forkConfig/index.ts b/packages/config/src/forkConfig/index.ts index d630f1ddfc88..6c592722aff2 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 eip7716: ForkInfo = { + name: ForkName.eip7716, + seq: ForkSeq.eip7716, + epoch: config.EIP7716_FORK_EPOCH, + version: config.EIP7716_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, eip7716}; // 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..fa1a6e86c218 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", + eip7716 = "eip7716", } /** @@ -18,6 +19,7 @@ export enum ForkSeq { bellatrix = 2, capella = 3, deneb = 4, + eip7716 = 5, } export type ForkPreLightClient = ForkName.phase0; diff --git a/packages/state-transition/src/cache/stateCache.ts b/packages/state-transition/src/cache/stateCache.ts index 482f5bd8ff40..15365fbc80df 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, + BeaconStateEip7716, } 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 CachedBeaconStateEIP7716 = 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..2224bee42c38 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 BeaconStateEip7716 = 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 + | BeaconStateEip7716; -export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateDeneb; +export type BeaconStateExecutions = BeaconStateBellatrix | BeaconStateCapella | BeaconStateDeneb | BeaconStateEip7716; export type ShufflingGetter = (shufflingEpoch: Epoch, dependentRoot: RootHex) => EpochShuffling | null; diff --git a/packages/state-transition/src/epoch/index.ts b/packages/state-transition/src/epoch/index.ts index b55ebe291fb9..7fca84e864e5 100644 --- a/packages/state-transition/src/epoch/index.ts +++ b/packages/state-transition/src/epoch/index.ts @@ -27,6 +27,8 @@ import {processRewardsAndPenalties} from "./processRewardsAndPenalties.js"; import {processSlashings} from "./processSlashings.js"; import {processSlashingsReset} from "./processSlashingsReset.js"; import {processSyncCommitteeUpdates} from "./processSyncCommitteeUpdates.js"; +import { processNetExcessPenalties } from "./processNetExcessPenalties.js"; +import { CachedBeaconStateEIP7716 } from "../cache/stateCache.js"; // For spec tests export {getRewardsAndPenalties} from "./processRewardsAndPenalties.js"; @@ -45,6 +47,7 @@ export { processParticipationFlagUpdates, processSyncCommitteeUpdates, processHistoricalSummariesUpdate, + processNetExcessPenalties, }; export {computeUnrealizedCheckpoints} from "./computeUnrealizedCheckpoints.js"; @@ -156,4 +159,7 @@ export function processEpoch( timer?.(); } } + if (fork === ForkSeq.eip7716) { + processNetExcessPenalties(state as CachedBeaconStateEIP7716); + } } diff --git a/packages/state-transition/src/epoch/processNetExcessPenalties.ts b/packages/state-transition/src/epoch/processNetExcessPenalties.ts new file mode 100644 index 000000000000..3c876505b94e --- /dev/null +++ b/packages/state-transition/src/epoch/processNetExcessPenalties.ts @@ -0,0 +1,14 @@ +import { + EFFECTIVE_BALANCE_INCREMENT, + ForkSeq, + HYSTERESIS_DOWNWARD_MULTIPLIER, + HYSTERESIS_QUOTIENT, + HYSTERESIS_UPWARD_MULTIPLIER, + MAX_EFFECTIVE_BALANCE, + TIMELY_TARGET_FLAG_INDEX, +} from "@lodestar/params"; +import {EpochTransitionCache, CachedBeaconStateAllForks, BeaconStateAltair} from "../types.js"; + +export function processNetExcessPenalties(state: CachedBeaconStateAllForks): void { + // TODO +} diff --git a/packages/state-transition/src/util/eip7716.ts b/packages/state-transition/src/util/eip7716.ts new file mode 100644 index 000000000000..80d3aabc42c9 --- /dev/null +++ b/packages/state-transition/src/util/eip7716.ts @@ -0,0 +1,25 @@ +import {digest} from "@chainsafe/as-sha256"; +import {BLSSignature} from "@lodestar/types"; +import {intDiv, bytesToBigInt} from "@lodestar/utils"; +import { + TARGET_AGGREGATORS_PER_COMMITTEE, + SYNC_COMMITTEE_SIZE, + SYNC_COMMITTEE_SUBNET_COUNT, + TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE, +} from "@lodestar/params"; + +export function computePenaltyFactor() { + +} + +export function committeeSlotOfValidator() { + +} + +export function participatingBalanceSlot() { + +} + +export function getSlotCommittees() { + +} \ No newline at end of file diff --git a/packages/types/src/allForks/sszTypes.ts b/packages/types/src/allForks/sszTypes.ts index 7174bc52e89c..f21b3ce7acf7 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 eip7716} from "../eip7716/index.js"; /** * Index the ssz types that differ by fork @@ -44,6 +45,13 @@ export const allForks = { BeaconState: deneb.BeaconState, Metadata: altair.Metadata, }, + eip7716: { + BeaconBlockBody: deneb.BeaconBlockBody, + BeaconBlock: deneb.BeaconBlock, + SignedBeaconBlock: deneb.SignedBeaconBlock, + Metadata: altair.Metadata, + BeaconState: eip7716.BeaconState, + }, }; /** @@ -85,6 +93,17 @@ export const allForksExecution = { SignedBuilderBid: deneb.SignedBuilderBid, SSEPayloadAttributes: deneb.SSEPayloadAttributes, }, + eip7716: { + BeaconBlockBody: deneb.BeaconBlockBody, + BeaconBlock: deneb.BeaconBlock, + SignedBeaconBlock: deneb.SignedBeaconBlock, + ExecutionPayload: deneb.ExecutionPayload, + ExecutionPayloadHeader: deneb.ExecutionPayloadHeader, + BuilderBid: deneb.BuilderBid, + SignedBuilderBid: deneb.SignedBuilderBid, + SSEPayloadAttributes: deneb.SSEPayloadAttributes, + BeaconState: eip7716.BeaconState, + }, }; /** @@ -107,6 +126,11 @@ export const allForksBlinded = { BeaconBlock: deneb.BlindedBeaconBlock, SignedBeaconBlock: deneb.SignedBlindedBeaconBlock, }, + eip7716: { + BeaconBlockBody: deneb.BlindedBeaconBlockBody, + BeaconBlock: deneb.BlindedBeaconBlock, + SignedBeaconBlock: deneb.SignedBlindedBeaconBlock, + }, }; export const allForksLightClient = { @@ -150,6 +174,16 @@ export const allForksLightClient = { LightClientOptimisticUpdate: deneb.LightClientOptimisticUpdate, LightClientStore: deneb.LightClientStore, }, + eip7716: { + BeaconBlock: deneb.BeaconBlock, + BeaconBlockBody: deneb.BeaconBlockBody, + LightClientHeader: deneb.LightClientHeader, + LightClientBootstrap: deneb.LightClientBootstrap, + LightClientUpdate: deneb.LightClientUpdate, + LightClientFinalityUpdate: deneb.LightClientFinalityUpdate, + LightClientOptimisticUpdate: deneb.LightClientOptimisticUpdate, + LightClientStore: deneb.LightClientStore, + }, }; export const allForksBlobs = { @@ -157,4 +191,8 @@ export const allForksBlobs = { BlobSidecar: deneb.BlobSidecar, ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, }, + eip7716: { + BlobSidecar: deneb.BlobSidecar, + ExecutionPayloadAndBlobsBundle: deneb.ExecutionPayloadAndBlobsBundle, + }, }; diff --git a/packages/types/src/eip7716/index.ts b/packages/types/src/eip7716/index.ts new file mode 100644 index 000000000000..981b2015e02a --- /dev/null +++ b/packages/types/src/eip7716/index.ts @@ -0,0 +1,4 @@ +export * from "./types.js"; +import * as ts from "./types.js"; +import * as ssz from "./sszTypes.js"; +export {ts, ssz}; diff --git a/packages/types/src/eip7716/sszTypes.ts b/packages/types/src/eip7716/sszTypes.ts new file mode 100644 index 000000000000..fe1da89715b7 --- /dev/null +++ b/packages/types/src/eip7716/sszTypes.ts @@ -0,0 +1,22 @@ +import {ContainerType, VectorBasicType} from "@chainsafe/ssz"; +import { + TIMELY_HEAD_FLAG_INDEX, +} from "@lodestar/params"; +import {ssz as primitiveSsz} from "../primitive/index.js"; +import {ssz as denebSsz} from "../capella/index.js"; + +const {UintNum64} = + primitiveSsz; + +export const BeaconState = new ContainerType( + { + ...denebSsz.BeaconState.fields, + netExcessPenalties: new VectorBasicType(UintNum64, TIMELY_HEAD_FLAG_INDEX) + }, + {typeName: "BeaconState", jsonCase: "eth2"} +); + +export const SignedBeaconBlock = denebSsz.SignedBeaconBlock; +export const BeaconBlock = denebSsz.BeaconBlock; +export const BeaconBlockBody = denebSsz.BeaconBlockBody; +export const ExecutionPayload = denebSsz.ExecutionPayload; \ No newline at end of file diff --git a/packages/types/src/eip7716/types.ts b/packages/types/src/eip7716/types.ts new file mode 100644 index 000000000000..9877e34f8060 --- /dev/null +++ b/packages/types/src/eip7716/types.ts @@ -0,0 +1,8 @@ +import {ValueOf} from "@chainsafe/ssz"; +import * as ssz from "./sszTypes.js"; + +export type BeaconState = ValueOf; +export type SignedBeaconBlock = ValueOf; +export type BeaconBlock = ValueOf; +export type BeaconBlockBody = ValueOf; +export type ExecutionPayload = ValueOf; \ No newline at end of file diff --git a/packages/types/src/sszTypes.ts b/packages/types/src/sszTypes.ts index 2a7df948a447..91397098c6f2 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 eip7716} from "./eip7716/index.js"; import {ssz as allForksSsz} from "./allForks/index.js"; export const allForks = allForksSsz.allForks; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 8ccaf9fe75ba..1d91fa20023d 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: Mon, 3 Jun 2024 21:45:45 +0200 Subject: [PATCH 2/2] Add processNetExcessPenalties --- .../config/src/chainConfig/configs/mainnet.ts | 2 +- packages/config/src/chainConfig/types.ts | 6 +- packages/state-transition/src/epoch/index.ts | 4 +- .../src/epoch/processNetExcessPenalties.ts | 22 +++--- packages/state-transition/src/types.ts | 2 + packages/state-transition/src/util/eip7716.ts | 78 ++++++++++++++++--- packages/types/src/allForks/types.ts | 5 ++ packages/types/src/eip7716/sszTypes.ts | 11 +-- packages/types/src/eip7716/types.ts | 2 +- 9 files changed, 94 insertions(+), 38 deletions(-) diff --git a/packages/config/src/chainConfig/configs/mainnet.ts b/packages/config/src/chainConfig/configs/mainnet.ts index 3a4b62304d11..08eebbc24ac0 100644 --- a/packages/config/src/chainConfig/configs/mainnet.ts +++ b/packages/config/src/chainConfig/configs/mainnet.ts @@ -48,7 +48,7 @@ export const chainConfig: ChainConfig = { // Deneb DENEB_FORK_VERSION: b("0x04000000"), DENEB_FORK_EPOCH: 269568, // March 13, 2024, 01:55:35pm UTC - + // EIP-7716 EIP7716_FORK_VERSION: b("0x05000000"), EIP7716_FORK_EPOCH: Infinity, diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index a92d529a946f..32383bec389c 100644 --- a/packages/config/src/chainConfig/types.ts +++ b/packages/config/src/chainConfig/types.ts @@ -74,9 +74,9 @@ export type ChainConfig = { MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: number; // EIP-7716 - PENALTY_ADJUSTMENT_FACTOR: number, - MAX_PENALTY_FACTOR: number, - PENALTY_RECOVERY_RATE: number, + PENALTY_ADJUSTMENT_FACTOR: number; + MAX_PENALTY_FACTOR: number; + PENALTY_RECOVERY_RATE: number; }; export const chainConfigTypes: SpecTypes = { diff --git a/packages/state-transition/src/epoch/index.ts b/packages/state-transition/src/epoch/index.ts index 7fca84e864e5..bc50079ce753 100644 --- a/packages/state-transition/src/epoch/index.ts +++ b/packages/state-transition/src/epoch/index.ts @@ -13,6 +13,7 @@ import { EpochTransitionCache, } from "../types.js"; import {BeaconStateTransitionMetrics} from "../metrics.js"; +import {CachedBeaconStateEIP7716} from "../cache/stateCache.js"; import {processEffectiveBalanceUpdates} from "./processEffectiveBalanceUpdates.js"; import {processEth1DataReset} from "./processEth1DataReset.js"; import {processHistoricalRootsUpdate} from "./processHistoricalRootsUpdate.js"; @@ -27,8 +28,7 @@ import {processRewardsAndPenalties} from "./processRewardsAndPenalties.js"; import {processSlashings} from "./processSlashings.js"; import {processSlashingsReset} from "./processSlashingsReset.js"; import {processSyncCommitteeUpdates} from "./processSyncCommitteeUpdates.js"; -import { processNetExcessPenalties } from "./processNetExcessPenalties.js"; -import { CachedBeaconStateEIP7716 } from "../cache/stateCache.js"; +import {processNetExcessPenalties} from "./processNetExcessPenalties.js"; // For spec tests export {getRewardsAndPenalties} from "./processRewardsAndPenalties.js"; diff --git a/packages/state-transition/src/epoch/processNetExcessPenalties.ts b/packages/state-transition/src/epoch/processNetExcessPenalties.ts index 3c876505b94e..ac3eecf1694d 100644 --- a/packages/state-transition/src/epoch/processNetExcessPenalties.ts +++ b/packages/state-transition/src/epoch/processNetExcessPenalties.ts @@ -1,14 +1,12 @@ -import { - EFFECTIVE_BALANCE_INCREMENT, - ForkSeq, - HYSTERESIS_DOWNWARD_MULTIPLIER, - HYSTERESIS_QUOTIENT, - HYSTERESIS_UPWARD_MULTIPLIER, - MAX_EFFECTIVE_BALANCE, - TIMELY_TARGET_FLAG_INDEX, -} from "@lodestar/params"; -import {EpochTransitionCache, CachedBeaconStateAllForks, BeaconStateAltair} from "../types.js"; +import {PARTICIPATION_FLAG_WEIGHTS, SLOTS_PER_EPOCH} from "@lodestar/params"; +import {CachedBeaconStateEIP7716} from "../types.js"; +import {getPreviousEpoch} from "../util/epoch.js"; +import {computePenaltyFactor} from "../util/eip7716.js"; -export function processNetExcessPenalties(state: CachedBeaconStateAllForks): void { - // TODO +export function processNetExcessPenalties(state: CachedBeaconStateEIP7716): void { + const lastSlotPrevEpoch = getPreviousEpoch(state) + SLOTS_PER_EPOCH - 1; + for (let flagIndex = 0; flagIndex < PARTICIPATION_FLAG_WEIGHTS.length; flagIndex++) { + const {netExcessPenalty} = computePenaltyFactor(state, lastSlotPrevEpoch, flagIndex); + state.netExcessPenalties.set(flagIndex, netExcessPenalty); + } } diff --git a/packages/state-transition/src/types.ts b/packages/state-transition/src/types.ts index 6b6b1f6260b2..e02b4fa523ab 100644 --- a/packages/state-transition/src/types.ts +++ b/packages/state-transition/src/types.ts @@ -9,6 +9,7 @@ export type { CachedBeaconStateBellatrix, CachedBeaconStateCapella, CachedBeaconStateDeneb, + CachedBeaconStateEIP7716, } from "./cache/stateCache.js"; export type { @@ -19,4 +20,5 @@ export type { BeaconStateBellatrix, BeaconStateCapella, BeaconStateDeneb, + BeaconStateEip7716, } from "./cache/types.js"; diff --git a/packages/state-transition/src/util/eip7716.ts b/packages/state-transition/src/util/eip7716.ts index 80d3aabc42c9..265af75801f9 100644 --- a/packages/state-transition/src/util/eip7716.ts +++ b/packages/state-transition/src/util/eip7716.ts @@ -1,25 +1,79 @@ -import {digest} from "@chainsafe/as-sha256"; -import {BLSSignature} from "@lodestar/types"; -import {intDiv, bytesToBigInt} from "@lodestar/utils"; +import {Epoch, Slot, ValidatorIndex} from "@lodestar/types"; import { - TARGET_AGGREGATORS_PER_COMMITTEE, - SYNC_COMMITTEE_SIZE, - SYNC_COMMITTEE_SUBNET_COUNT, - TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE, + SLOTS_PER_EPOCH, } from "@lodestar/params"; +import {CachedBeaconStateEIP7716} from "../types.js"; +import {computeEpochAtSlot, computeStartSlotAtEpoch} from "./epoch.js"; -export function computePenaltyFactor() { +export function computePenaltyFactor( + state: CachedBeaconStateEIP7716, + atSlot: Slot, + flagIndex: number +): {penaltyFactor: number; netExcessPenalty: number} { + let netExcessPenalty = state.netExcessPenalties.get(flagIndex); + const epoch = computeEpochAtSlot(atSlot); + const {PENALTY_ADJUSTMENT_FACTOR, MAX_PENALTY_FACTOR, PENALTY_RECOVERY_RATE} = state.config; + let penaltyFactor = 1; + for (let slot = computeStartSlotAtEpoch(epoch); slot < atSlot; slot++) { + const totalBalance = getSlotCommitteeBalance(state, slot); + const participatingBalance = participatingBalanceSlot(state, slot, flagIndex); + penaltyFactor = Math.min( + Math.floor( + ((totalBalance - participatingBalance) * PENALTY_ADJUSTMENT_FACTOR) / (netExcessPenalty * totalBalance + 1) + ), + MAX_PENALTY_FACTOR + ); + netExcessPenalty = Math.max(PENALTY_RECOVERY_RATE, netExcessPenalty + penaltyFactor) - PENALTY_RECOVERY_RATE; + } + + return {penaltyFactor, netExcessPenalty}; +} + +export function committeeSlotOfValidator(state: CachedBeaconStateEIP7716, index: ValidatorIndex, epoch: Epoch) { + for (let slot = epoch * SLOTS_PER_EPOCH; slot < (epoch + 1) * SLOTS_PER_EPOCH; slot++) { + if (index in getSlotCommittees(state, slot)) { + return slot; + } + } + throw new Error(`Validator with index ${index} is not active`); } -export function committeeSlotOfValidator() { +export function participatingBalanceSlot(state: CachedBeaconStateEIP7716, slot: Slot, flagIndex: number) { + const inCurrentEpoch = computeEpochAtSlot(slot) === state.epochCtx.epoch; + const epochParticipation = inCurrentEpoch ? state.currentEpochParticipation : state.previousEpochParticipation; + const flagBit = 1 << flagIndex; + const participatingIndices = getSlotCommittees(state, slot).filter( + (index) => (epochParticipation.get(index) & flagBit) === flagBit + ); + + return participatingIndices + .map((participatingIndex) => state.balances.get(participatingIndex)) + .reduce((total, balance) => total + balance, 0); } -export function participatingBalanceSlot() { +export function getSlotCommittees(state: CachedBeaconStateEIP7716, slot: Slot): Uint32Array { + const committees = state.epochCtx.getShufflingAtSlot(slot).committees[slot % SLOTS_PER_EPOCH].flat(); + // Create a new Uint32Array to flatten `committees` + const totalLength = committees.reduce((acc, curr) => acc + curr.length, 0); + const result = new Uint32Array(totalLength); + + let offset = 0; + for (const committee of committees) { + result.set(committee, offset); + offset += committee.length; + } + return result; } -export function getSlotCommittees() { +export function getSlotCommitteeBalance(state: CachedBeaconStateEIP7716, slot: Slot): number { + const validatorIndices = getSlotCommittees(state, slot); -} \ No newline at end of file + // 32eth * 1mil / 32 is still within number range + // WIth maxEB = 2048, total max balance could be 2^46 + return validatorIndices + .map((validatorIndex) => state.balances.get(validatorIndex)) + .reduce((total, balance) => total + balance, 0); +} diff --git a/packages/types/src/allForks/types.ts b/packages/types/src/allForks/types.ts index 59768a5a3308..8b8760474ef8 100644 --- a/packages/types/src/allForks/types.ts +++ b/packages/types/src/allForks/types.ts @@ -10,6 +10,7 @@ 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 eip7716Ssz} from "../eip7716/index.js"; // Re-export union types for types that are _known_ to differ @@ -178,6 +179,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconBlockBody | typeof capellaSsz.BeaconBlockBody | typeof denebSsz.BeaconBlockBody + | typeof eip7716Ssz.BeaconBlockBody >; BeaconBlock: AllForksTypeOf< | typeof phase0Ssz.BeaconBlock @@ -185,6 +187,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconBlock | typeof capellaSsz.BeaconBlock | typeof denebSsz.BeaconBlock + | typeof eip7716Ssz.BeaconBlock >; SignedBeaconBlock: AllForksTypeOf< | typeof phase0Ssz.SignedBeaconBlock @@ -192,6 +195,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.SignedBeaconBlock | typeof capellaSsz.SignedBeaconBlock | typeof denebSsz.SignedBeaconBlock + | typeof eip7716Ssz.SignedBeaconBlock >; BeaconState: AllForksTypeOf< | typeof phase0Ssz.BeaconState @@ -199,6 +203,7 @@ export type AllForksSSZTypes = { | typeof bellatrixSsz.BeaconState | typeof capellaSsz.BeaconState | typeof denebSsz.BeaconState + | typeof eip7716Ssz.BeaconState >; Metadata: AllForksTypeOf; }; diff --git a/packages/types/src/eip7716/sszTypes.ts b/packages/types/src/eip7716/sszTypes.ts index fe1da89715b7..d9f7b78f3098 100644 --- a/packages/types/src/eip7716/sszTypes.ts +++ b/packages/types/src/eip7716/sszTypes.ts @@ -1,17 +1,14 @@ import {ContainerType, VectorBasicType} from "@chainsafe/ssz"; -import { - TIMELY_HEAD_FLAG_INDEX, -} from "@lodestar/params"; +import {TIMELY_HEAD_FLAG_INDEX} from "@lodestar/params"; import {ssz as primitiveSsz} from "../primitive/index.js"; import {ssz as denebSsz} from "../capella/index.js"; -const {UintNum64} = - primitiveSsz; +const {UintNum64} = primitiveSsz; export const BeaconState = new ContainerType( { ...denebSsz.BeaconState.fields, - netExcessPenalties: new VectorBasicType(UintNum64, TIMELY_HEAD_FLAG_INDEX) + netExcessPenalties: new VectorBasicType(UintNum64, TIMELY_HEAD_FLAG_INDEX), }, {typeName: "BeaconState", jsonCase: "eth2"} ); @@ -19,4 +16,4 @@ export const BeaconState = new ContainerType( export const SignedBeaconBlock = denebSsz.SignedBeaconBlock; export const BeaconBlock = denebSsz.BeaconBlock; export const BeaconBlockBody = denebSsz.BeaconBlockBody; -export const ExecutionPayload = denebSsz.ExecutionPayload; \ No newline at end of file +export const ExecutionPayload = denebSsz.ExecutionPayload; diff --git a/packages/types/src/eip7716/types.ts b/packages/types/src/eip7716/types.ts index 9877e34f8060..151e85958c7c 100644 --- a/packages/types/src/eip7716/types.ts +++ b/packages/types/src/eip7716/types.ts @@ -5,4 +5,4 @@ export type BeaconState = ValueOf; export type SignedBeaconBlock = ValueOf; export type BeaconBlock = ValueOf; export type BeaconBlockBody = ValueOf; -export type ExecutionPayload = ValueOf; \ No newline at end of file +export type ExecutionPayload = ValueOf;