@lodestar/state-transition 1.46.0-dev.8b30182711 → 1.46.0-dev.8bab23d909
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/block/processDeposit.d.ts +15 -0
- package/lib/block/processDeposit.d.ts.map +1 -1
- package/lib/block/processDeposit.js +73 -1
- package/lib/block/processDeposit.js.map +1 -1
- package/lib/block/processExecutionPayloadBid.d.ts +2 -2
- package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
- package/lib/block/processExecutionPayloadBid.js +8 -3
- package/lib/block/processExecutionPayloadBid.js.map +1 -1
- package/lib/cache/builderDepositSignatureCache.d.ts +48 -0
- package/lib/cache/builderDepositSignatureCache.d.ts.map +1 -0
- package/lib/cache/builderDepositSignatureCache.js +70 -0
- package/lib/cache/builderDepositSignatureCache.js.map +1 -0
- package/lib/cache/epochCache.d.ts +9 -0
- package/lib/cache/epochCache.d.ts.map +1 -1
- package/lib/cache/epochCache.js +13 -0
- package/lib/cache/epochCache.js.map +1 -1
- package/lib/cache/stateCache.d.ts +2 -1
- package/lib/cache/stateCache.d.ts.map +1 -1
- package/lib/cache/stateCache.js.map +1 -1
- package/lib/cache/types.d.ts +1 -0
- package/lib/cache/types.d.ts.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/lightClient/spec/utils.d.ts.map +1 -1
- package/lib/lightClient/spec/utils.js +6 -0
- package/lib/lightClient/spec/utils.js.map +1 -1
- package/lib/signatureSets/executionPayloadBid.d.ts +2 -2
- package/lib/signatureSets/executionPayloadBid.d.ts.map +1 -1
- package/lib/signatureSets/executionPayloadBid.js +7 -5
- package/lib/signatureSets/executionPayloadBid.js.map +1 -1
- package/lib/slot/index.d.ts +1 -0
- package/lib/slot/index.d.ts.map +1 -1
- package/lib/slot/index.js +1 -0
- package/lib/slot/index.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +13 -7
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/slot/upgradeStateToHeze.d.ts +7 -0
- package/lib/slot/upgradeStateToHeze.d.ts.map +1 -0
- package/lib/slot/upgradeStateToHeze.js +83 -0
- package/lib/slot/upgradeStateToHeze.js.map +1 -0
- package/lib/stateTransition.d.ts.map +1 -1
- package/lib/stateTransition.js +4 -1
- package/lib/stateTransition.js.map +1 -1
- package/lib/stateView/beaconStateView.d.ts +3 -0
- package/lib/stateView/beaconStateView.d.ts.map +1 -1
- package/lib/stateView/beaconStateView.js +7 -0
- package/lib/stateView/beaconStateView.js.map +1 -1
- package/lib/stateView/interface.d.ts +16 -3
- package/lib/stateView/interface.d.ts.map +1 -1
- package/lib/stateView/interface.js +4 -1
- package/lib/stateView/interface.js.map +1 -1
- package/lib/stateView/nativeBeaconStateView.d.ts +3 -0
- package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -1
- package/lib/stateView/nativeBeaconStateView.js +6 -0
- package/lib/stateView/nativeBeaconStateView.js.map +1 -1
- package/lib/testUtils/util.d.ts +16 -1
- package/lib/testUtils/util.d.ts.map +1 -1
- package/lib/testUtils/util.js +55 -2
- package/lib/testUtils/util.js.map +1 -1
- package/lib/types.d.ts +2 -2
- package/lib/types.d.ts.map +1 -1
- package/lib/util/genesis.d.ts.map +1 -1
- package/lib/util/genesis.js +5 -0
- package/lib/util/genesis.js.map +1 -1
- package/lib/util/index.d.ts +1 -0
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js +1 -0
- package/lib/util/index.js.map +1 -1
- package/lib/util/pendingDepositsLookup.d.ts +7 -10
- package/lib/util/pendingDepositsLookup.d.ts.map +1 -1
- package/lib/util/pendingDepositsLookup.js +10 -19
- package/lib/util/pendingDepositsLookup.js.map +1 -1
- package/lib/util/preVerifyBuilderDeposits.d.ts +52 -0
- package/lib/util/preVerifyBuilderDeposits.d.ts.map +1 -0
- package/lib/util/preVerifyBuilderDeposits.js +124 -0
- package/lib/util/preVerifyBuilderDeposits.js.map +1 -0
- package/package.json +7 -7
- package/src/block/processDeposit.ts +75 -1
- package/src/block/processExecutionPayloadBid.ts +18 -8
- package/src/cache/builderDepositSignatureCache.ts +81 -0
- package/src/cache/epochCache.ts +14 -0
- package/src/cache/stateCache.ts +2 -0
- package/src/cache/types.ts +1 -0
- package/src/index.ts +4 -0
- package/src/lightClient/spec/utils.ts +7 -0
- package/src/signatureSets/executionPayloadBid.ts +7 -9
- package/src/slot/index.ts +1 -0
- package/src/slot/upgradeStateToGloas.ts +17 -10
- package/src/slot/upgradeStateToHeze.ts +91 -0
- package/src/stateTransition.ts +5 -0
- package/src/stateView/beaconStateView.ts +13 -0
- package/src/stateView/interface.ts +21 -2
- package/src/stateView/nativeBeaconStateView.ts +9 -0
- package/src/testUtils/util.ts +73 -3
- package/src/types.ts +2 -0
- package/src/util/genesis.ts +6 -0
- package/src/util/index.ts +1 -0
- package/src/util/pendingDepositsLookup.ts +13 -24
- package/src/util/preVerifyBuilderDeposits.ts +153 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {PubkeyHex, electra} from "@lodestar/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Caches deposit signature-verification results — passes (`true`) and failures (`false`) — so the
|
|
5
|
+
* Fulu → Gloas fork transition can skip the bulk verification cost AND skip re-verifying deposits
|
|
6
|
+
* already proven invalid. Covers both builder-prefix deposits (onboarding) and validator deposits
|
|
7
|
+
* that share a pubkey with a builder deposit (the ones `hasPendingValidator` checks at the fork).
|
|
8
|
+
*
|
|
9
|
+
* Keyed by the deposit's struct **value object** — `electra.PendingDeposit` is a
|
|
10
|
+
* `ContainerNodeStructType`, so its `node.value` is a stable object reused across
|
|
11
|
+
* prepareNextSlot-derived states and carried by reference through the gloas pendingDeposits
|
|
12
|
+
* migration. No merkle-root computation, no slot bucketing.
|
|
13
|
+
*
|
|
14
|
+
* Also tracks builder pubkeys seen so the scanner knows which validator deposits to pre-verify (only
|
|
15
|
+
* those sharing a pubkey with a builder deposit ever reach `hasPendingValidator`). The mapped boolean
|
|
16
|
+
* records whether a *valid* validator deposit for that pubkey has been found: once true, the scanner
|
|
17
|
+
* stops pre-verifying further validator deposits for it — `hasPendingValidator` short-circuits on the
|
|
18
|
+
* first valid signature at the fork, so the rest would never be read.
|
|
19
|
+
*
|
|
20
|
+
* Lifecycle owned by `prepareNextSlot`: filled during the pre-fork window, then `clear()`ed once the
|
|
21
|
+
* Gloas fork is finalized. `onboardBuildersFromPendingDeposits()` only reads it. A plain `Map` (not a
|
|
22
|
+
* `WeakMap`) is used so the drop is deterministic and the contents are inspectable for logging.
|
|
23
|
+
*
|
|
24
|
+
* Single instance across the application (created in `EpochCache.createFromState`, shared
|
|
25
|
+
* by-reference through `clone()`).
|
|
26
|
+
*/
|
|
27
|
+
export class BuilderDepositSignatureCache {
|
|
28
|
+
private validityByDeposit = new Map<electra.PendingDeposit, boolean>();
|
|
29
|
+
/** builder pubkey → whether a valid validator deposit sharing it has already been pre-verified. */
|
|
30
|
+
private builderPubkeys = new Map<PubkeyHex, boolean>();
|
|
31
|
+
|
|
32
|
+
setSignatureValidity(deposit: electra.PendingDeposit, isValid: boolean): void {
|
|
33
|
+
this.validityByDeposit.set(deposit, isValid);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getSignatureValidity(deposit: electra.PendingDeposit): boolean | null {
|
|
37
|
+
return this.validityByDeposit.get(deposit) ?? null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
isVerified(deposit: electra.PendingDeposit): boolean {
|
|
41
|
+
return this.validityByDeposit.has(deposit);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Record a builder deposit's pubkey so the scanner pre-verifies validator deposits for it.
|
|
45
|
+
* Idempotent; never downgrades a pubkey already marked as having a valid validator deposit. */
|
|
46
|
+
addBuilderPubkey(pubkeyHex: PubkeyHex): void {
|
|
47
|
+
if (!this.builderPubkeys.has(pubkeyHex)) {
|
|
48
|
+
this.builderPubkeys.set(pubkeyHex, false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
isBuilderPubkey(pubkeyHex: PubkeyHex): boolean {
|
|
53
|
+
return this.builderPubkeys.has(pubkeyHex);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** True once a valid validator deposit sharing this builder pubkey has been pre-verified — the
|
|
57
|
+
* scanner then skips this pubkey's remaining validator deposits (as `hasPendingValidator` would). */
|
|
58
|
+
hasValidValidatorDeposit(pubkeyHex: PubkeyHex): boolean {
|
|
59
|
+
return this.builderPubkeys.get(pubkeyHex) === true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Mark that a valid validator deposit sharing this builder pubkey was found. */
|
|
63
|
+
setValidValidatorDeposit(pubkeyHex: PubkeyHex): void {
|
|
64
|
+
this.builderPubkeys.set(pubkeyHex, true);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Cumulative deposits verified & cached this window (pass + fail). */
|
|
68
|
+
get cachedDepositCount(): number {
|
|
69
|
+
return this.validityByDeposit.size;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Distinct builder pubkeys tracked this window. */
|
|
73
|
+
get builderPubkeyCount(): number {
|
|
74
|
+
return this.builderPubkeys.size;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
clear(): void {
|
|
78
|
+
this.validityByDeposit.clear();
|
|
79
|
+
this.builderPubkeys.clear();
|
|
80
|
+
}
|
|
81
|
+
}
|
package/src/cache/epochCache.ts
CHANGED
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
} from "../util/shuffling.js";
|
|
54
54
|
import {computeBaseRewardPerIncrement, computeSyncParticipantReward} from "../util/syncCommittee.js";
|
|
55
55
|
import {sumTargetUnslashedBalanceIncrements} from "../util/targetUnslashedBalance.js";
|
|
56
|
+
import {BuilderDepositSignatureCache} from "./builderDepositSignatureCache.js";
|
|
56
57
|
import {EffectiveBalanceIncrements, getEffectiveBalanceIncrementsWithLen} from "./effectiveBalanceIncrements.js";
|
|
57
58
|
import {EpochTransitionCache} from "./epochTransitionCache.js";
|
|
58
59
|
import {PubkeyCache, createPubkeyCache, syncPubkeys} from "./pubkeyCache.js";
|
|
@@ -112,6 +113,13 @@ export class EpochCache {
|
|
|
112
113
|
* Couples both index→pubkey and pubkey→index lookups, keeping them in sync atomically.
|
|
113
114
|
*/
|
|
114
115
|
pubkeyCache: PubkeyCache;
|
|
116
|
+
/**
|
|
117
|
+
* Shared across all clones of the same chain head. Holds builder deposit signature validity
|
|
118
|
+
* pre-verified by the prepareForNextSlot scheduler in the `GLOAS_PREVERIFY_WINDOW_EPOCHS` epochs
|
|
119
|
+
* leading up to GLOAS_FORK_EPOCH, so `onboardBuildersFromPendingDeposits()` at the fork transition
|
|
120
|
+
* can skip the bulk verification cost. There should only exist one for the entire application.
|
|
121
|
+
*/
|
|
122
|
+
builderDepositSignatureCache: BuilderDepositSignatureCache;
|
|
115
123
|
/**
|
|
116
124
|
* Indexes of the block proposers for the current epoch.
|
|
117
125
|
* For pre-fulu, this is computed and cached from the current shuffling.
|
|
@@ -245,6 +253,7 @@ export class EpochCache {
|
|
|
245
253
|
constructor(data: {
|
|
246
254
|
config: BeaconConfig;
|
|
247
255
|
pubkeyCache: PubkeyCache;
|
|
256
|
+
builderDepositSignatureCache: BuilderDepositSignatureCache;
|
|
248
257
|
proposers: number[];
|
|
249
258
|
proposersPrevEpoch: number[] | null;
|
|
250
259
|
proposersNextEpoch: ProposersDeferred;
|
|
@@ -277,6 +286,7 @@ export class EpochCache {
|
|
|
277
286
|
}) {
|
|
278
287
|
this.config = data.config;
|
|
279
288
|
this.pubkeyCache = data.pubkeyCache;
|
|
289
|
+
this.builderDepositSignatureCache = data.builderDepositSignatureCache;
|
|
280
290
|
this.proposers = data.proposers;
|
|
281
291
|
this.proposersPrevEpoch = data.proposersPrevEpoch;
|
|
282
292
|
this.proposersNextEpoch = data.proposersNextEpoch;
|
|
@@ -514,6 +524,8 @@ export class EpochCache {
|
|
|
514
524
|
return new EpochCache({
|
|
515
525
|
config,
|
|
516
526
|
pubkeyCache,
|
|
527
|
+
// Created once per application (shared by-reference through clone()).
|
|
528
|
+
builderDepositSignatureCache: new BuilderDepositSignatureCache(),
|
|
517
529
|
proposers,
|
|
518
530
|
// On first epoch, set to null to prevent unnecessary work since this is only used for metrics
|
|
519
531
|
proposersPrevEpoch: null,
|
|
@@ -558,6 +570,8 @@ export class EpochCache {
|
|
|
558
570
|
config: this.config,
|
|
559
571
|
// Common append-only structures shared with all states, no need to clone
|
|
560
572
|
pubkeyCache: this.pubkeyCache,
|
|
573
|
+
// Singleton per application, shared by-reference across clones
|
|
574
|
+
builderDepositSignatureCache: this.builderDepositSignatureCache,
|
|
561
575
|
// Immutable data
|
|
562
576
|
proposers: this.proposers,
|
|
563
577
|
proposersPrevEpoch: this.proposersPrevEpoch,
|
package/src/cache/stateCache.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
BeaconStateExecutions,
|
|
14
14
|
BeaconStateFulu,
|
|
15
15
|
BeaconStateGloas,
|
|
16
|
+
BeaconStateHeze,
|
|
16
17
|
BeaconStatePhase0,
|
|
17
18
|
} from "./types.js";
|
|
18
19
|
|
|
@@ -137,6 +138,7 @@ export type CachedBeaconStateDeneb = CachedBeaconState<BeaconStateDeneb>;
|
|
|
137
138
|
export type CachedBeaconStateElectra = CachedBeaconState<BeaconStateElectra>;
|
|
138
139
|
export type CachedBeaconStateFulu = CachedBeaconState<BeaconStateFulu>;
|
|
139
140
|
export type CachedBeaconStateGloas = CachedBeaconState<BeaconStateGloas>;
|
|
141
|
+
export type CachedBeaconStateHeze = CachedBeaconState<BeaconStateHeze>;
|
|
140
142
|
|
|
141
143
|
export type CachedBeaconStateAllForks = CachedBeaconState<BeaconStateAllForks>;
|
|
142
144
|
export type CachedBeaconStateExecutions = CachedBeaconState<BeaconStateExecutions>;
|
package/src/cache/types.ts
CHANGED
|
@@ -11,6 +11,7 @@ export type BeaconStateDeneb = CompositeViewDU<SSZTypesFor<ForkName.deneb, "Beac
|
|
|
11
11
|
export type BeaconStateElectra = CompositeViewDU<SSZTypesFor<ForkName.electra, "BeaconState">>;
|
|
12
12
|
export type BeaconStateFulu = CompositeViewDU<SSZTypesFor<ForkName.fulu, "BeaconState">>;
|
|
13
13
|
export type BeaconStateGloas = CompositeViewDU<SSZTypesFor<ForkName.gloas, "BeaconState">>;
|
|
14
|
+
export type BeaconStateHeze = CompositeViewDU<SSZTypesFor<ForkName.heze, "BeaconState">>;
|
|
14
15
|
|
|
15
16
|
export type BeaconStateAllForks = CompositeViewDU<SSZTypesFor<ForkAll, "BeaconState">>;
|
|
16
17
|
export type BeaconStateExecutions = CompositeViewDU<SSZTypesFor<ForkPostBellatrix & ForkPreGloas, "BeaconState">>;
|
package/src/index.ts
CHANGED
|
@@ -52,6 +52,7 @@ export {
|
|
|
52
52
|
type IBeaconStateViewElectra,
|
|
53
53
|
type IBeaconStateViewFulu,
|
|
54
54
|
type IBeaconStateViewGloas,
|
|
55
|
+
type IBeaconStateViewHeze,
|
|
55
56
|
isStatePostAltair,
|
|
56
57
|
isStatePostBellatrix,
|
|
57
58
|
isStatePostCapella,
|
|
@@ -59,6 +60,7 @@ export {
|
|
|
59
60
|
isStatePostElectra,
|
|
60
61
|
isStatePostFulu,
|
|
61
62
|
isStatePostGloas,
|
|
63
|
+
isStatePostHeze,
|
|
62
64
|
} from "./stateView/interface.js";
|
|
63
65
|
export {createBeaconStateView, createBeaconStateViewForHistoricalRegen} from "./stateView/stateViewFactory.js";
|
|
64
66
|
export type {
|
|
@@ -71,6 +73,7 @@ export type {
|
|
|
71
73
|
BeaconStateExecutions,
|
|
72
74
|
BeaconStateFulu,
|
|
73
75
|
BeaconStateGloas,
|
|
76
|
+
BeaconStateHeze,
|
|
74
77
|
// Non-cached states
|
|
75
78
|
BeaconStatePhase0,
|
|
76
79
|
CachedBeaconStateAllForks,
|
|
@@ -82,6 +85,7 @@ export type {
|
|
|
82
85
|
CachedBeaconStateExecutions,
|
|
83
86
|
CachedBeaconStateFulu,
|
|
84
87
|
CachedBeaconStateGloas,
|
|
88
|
+
CachedBeaconStateHeze,
|
|
85
89
|
CachedBeaconStatePhase0,
|
|
86
90
|
} from "./types.js";
|
|
87
91
|
export * from "./util/index.js";
|
|
@@ -263,6 +263,7 @@ export function upgradeLightClientHeader(
|
|
|
263
263
|
// Break if no further upgrades is required else fall through
|
|
264
264
|
if (ForkSeq[targetFork] <= ForkSeq.fulu) break;
|
|
265
265
|
|
|
266
|
+
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
266
267
|
case ForkName.gloas:
|
|
267
268
|
if (isGloasLightClientHeader(upgradedHeader)) {
|
|
268
269
|
break;
|
|
@@ -272,6 +273,12 @@ export function upgradeLightClientHeader(
|
|
|
272
273
|
|
|
273
274
|
// Break if no further upgrades is required else fall through
|
|
274
275
|
if (ForkSeq[targetFork] <= ForkSeq.gloas) break;
|
|
276
|
+
|
|
277
|
+
case ForkName.heze:
|
|
278
|
+
// No changes to LightClientHeader in Heze
|
|
279
|
+
|
|
280
|
+
// Break if no further upgrades is required else fall through
|
|
281
|
+
if (ForkSeq[targetFork] <= ForkSeq.heze) break;
|
|
275
282
|
}
|
|
276
283
|
return upgradedHeader;
|
|
277
284
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import {BeaconConfig} from "@lodestar/config";
|
|
2
|
-
import {DOMAIN_BEACON_BUILDER} from "@lodestar/params";
|
|
3
|
-
import {
|
|
2
|
+
import {DOMAIN_BEACON_BUILDER, isForkPostGloas} from "@lodestar/params";
|
|
3
|
+
import {ExecutionPayloadBid, ssz, sszTypesFor} from "@lodestar/types";
|
|
4
4
|
import {computeSigningRoot} from "../util/index.js";
|
|
5
5
|
|
|
6
|
-
export function getExecutionPayloadBidSigningRoot(
|
|
7
|
-
config
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
): Uint8Array {
|
|
11
|
-
const domain = config.getDomain(stateSlot, DOMAIN_BEACON_BUILDER);
|
|
6
|
+
export function getExecutionPayloadBidSigningRoot(config: BeaconConfig, bid: ExecutionPayloadBid): Uint8Array {
|
|
7
|
+
const fork = config.getForkName(bid.slot);
|
|
8
|
+
const domain = config.getDomain(bid.slot, DOMAIN_BEACON_BUILDER);
|
|
9
|
+
const sszType = isForkPostGloas(fork) ? sszTypesFor(fork).ExecutionPayloadBid : ssz.gloas.ExecutionPayloadBid;
|
|
12
10
|
|
|
13
|
-
return computeSigningRoot(
|
|
11
|
+
return computeSigningRoot(sszType, bid, domain);
|
|
14
12
|
}
|
package/src/slot/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export {upgradeStateToDeneb} from "./upgradeStateToDeneb.js";
|
|
|
10
10
|
export {upgradeStateToElectra} from "./upgradeStateToElectra.js";
|
|
11
11
|
export {upgradeStateToFulu} from "./upgradeStateToFulu.js";
|
|
12
12
|
export {upgradeStateToGloas} from "./upgradeStateToGloas.js";
|
|
13
|
+
export {upgradeStateToHeze} from "./upgradeStateToHeze.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Dial state to next slot. Common for all forks
|
|
@@ -172,8 +172,8 @@ function onboardBuildersFromPendingDeposits(state: CachedBeaconStateGloas): void
|
|
|
172
172
|
const pendingDeposits = ssz.gloas.PendingDeposits.defaultViewDU();
|
|
173
173
|
const pendingDepositsLookup = PendingDepositsLookup.buildEmpty();
|
|
174
174
|
|
|
175
|
-
for (
|
|
176
|
-
const
|
|
175
|
+
for (const deposit of state.pendingDeposits.getAllReadonly()) {
|
|
176
|
+
const depositValue = deposit.toValue();
|
|
177
177
|
|
|
178
178
|
const validatorIndex = state.epochCtx.getValidatorIndex(deposit.pubkey);
|
|
179
179
|
const pubkeyHex = toPubkeyHex(deposit.pubkey);
|
|
@@ -181,7 +181,7 @@ function onboardBuildersFromPendingDeposits(state: CachedBeaconStateGloas): void
|
|
|
181
181
|
// Deposits for existing validators stay in the pending queue
|
|
182
182
|
if (isValidatorKnown(state, validatorIndex)) {
|
|
183
183
|
pendingDeposits.push(deposit);
|
|
184
|
-
pendingDepositsLookup.add(
|
|
184
|
+
pendingDepositsLookup.add(depositValue, pubkeyHex);
|
|
185
185
|
continue;
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -202,26 +202,33 @@ function onboardBuildersFromPendingDeposits(state: CachedBeaconStateGloas): void
|
|
|
202
202
|
// can pick it up later.
|
|
203
203
|
if (!isBuilderWithdrawalCredential(deposit.withdrawalCredentials)) {
|
|
204
204
|
pendingDeposits.push(deposit);
|
|
205
|
-
pendingDepositsLookup.add(
|
|
205
|
+
pendingDepositsLookup.add(depositValue, pubkeyHex);
|
|
206
206
|
continue;
|
|
207
207
|
}
|
|
208
|
-
if (
|
|
208
|
+
if (
|
|
209
|
+
pendingDepositsLookup.hasPendingValidator(state.config, pubkeyHex, state.epochCtx.builderDepositSignatureCache)
|
|
210
|
+
) {
|
|
209
211
|
pendingDeposits.push(deposit);
|
|
210
|
-
pendingDepositsLookup.add(
|
|
212
|
+
pendingDepositsLookup.add(depositValue, pubkeyHex);
|
|
211
213
|
continue;
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
// Verify the deposit signature (proof of possession). If invalid the deposit is silently
|
|
215
217
|
// dropped — stake is forfeited, matching the validator deposit contract behavior.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
+
//
|
|
219
|
+
// The prepareNextSlot scheduler pre-verifies these signatures in the epochs before the fork
|
|
220
|
+
// A cache miss falls back to verifying this one deposit — no worse than pre-cache.
|
|
221
|
+
const cached = state.epochCtx.builderDepositSignatureCache.getSignatureValidity(depositValue);
|
|
222
|
+
const isValid =
|
|
223
|
+
cached ??
|
|
224
|
+
isValidDepositSignature(
|
|
218
225
|
state.config,
|
|
219
226
|
deposit.pubkey,
|
|
220
227
|
deposit.withdrawalCredentials,
|
|
221
228
|
deposit.amount,
|
|
222
229
|
deposit.signature
|
|
223
|
-
)
|
|
224
|
-
) {
|
|
230
|
+
);
|
|
231
|
+
if (!isValid) {
|
|
225
232
|
continue;
|
|
226
233
|
}
|
|
227
234
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {ssz} from "@lodestar/types";
|
|
2
|
+
import {getCachedBeaconState} from "../cache/stateCache.js";
|
|
3
|
+
import {CachedBeaconStateGloas, CachedBeaconStateHeze} from "../types.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Upgrade a state from Gloas to Heze.
|
|
7
|
+
* Spec: heze/fork.md `upgrade_to_heze`.
|
|
8
|
+
*/
|
|
9
|
+
export function upgradeStateToHeze(stateGloas: CachedBeaconStateGloas): CachedBeaconStateHeze {
|
|
10
|
+
const {config} = stateGloas;
|
|
11
|
+
|
|
12
|
+
ssz.gloas.BeaconState.commitViewDU(stateGloas);
|
|
13
|
+
const stateHezeCloned = stateGloas;
|
|
14
|
+
|
|
15
|
+
const stateHezeView = ssz.heze.BeaconState.defaultViewDU();
|
|
16
|
+
|
|
17
|
+
stateHezeView.genesisTime = stateHezeCloned.genesisTime;
|
|
18
|
+
stateHezeView.genesisValidatorsRoot = stateHezeCloned.genesisValidatorsRoot;
|
|
19
|
+
stateHezeView.slot = stateHezeCloned.slot;
|
|
20
|
+
stateHezeView.fork = ssz.phase0.Fork.toViewDU({
|
|
21
|
+
previousVersion: stateGloas.fork.currentVersion,
|
|
22
|
+
currentVersion: config.HEZE_FORK_VERSION,
|
|
23
|
+
epoch: stateGloas.epochCtx.epoch,
|
|
24
|
+
});
|
|
25
|
+
stateHezeView.latestBlockHeader = stateHezeCloned.latestBlockHeader;
|
|
26
|
+
stateHezeView.blockRoots = stateHezeCloned.blockRoots;
|
|
27
|
+
stateHezeView.stateRoots = stateHezeCloned.stateRoots;
|
|
28
|
+
stateHezeView.historicalRoots = stateHezeCloned.historicalRoots;
|
|
29
|
+
stateHezeView.eth1Data = stateHezeCloned.eth1Data;
|
|
30
|
+
stateHezeView.eth1DataVotes = stateHezeCloned.eth1DataVotes;
|
|
31
|
+
stateHezeView.eth1DepositIndex = stateHezeCloned.eth1DepositIndex;
|
|
32
|
+
stateHezeView.validators = stateHezeCloned.validators;
|
|
33
|
+
stateHezeView.balances = stateHezeCloned.balances;
|
|
34
|
+
stateHezeView.randaoMixes = stateHezeCloned.randaoMixes;
|
|
35
|
+
stateHezeView.slashings = stateHezeCloned.slashings;
|
|
36
|
+
stateHezeView.previousEpochParticipation = stateHezeCloned.previousEpochParticipation;
|
|
37
|
+
stateHezeView.currentEpochParticipation = stateHezeCloned.currentEpochParticipation;
|
|
38
|
+
stateHezeView.justificationBits = stateHezeCloned.justificationBits;
|
|
39
|
+
stateHezeView.previousJustifiedCheckpoint = stateHezeCloned.previousJustifiedCheckpoint;
|
|
40
|
+
stateHezeView.currentJustifiedCheckpoint = stateHezeCloned.currentJustifiedCheckpoint;
|
|
41
|
+
stateHezeView.finalizedCheckpoint = stateHezeCloned.finalizedCheckpoint;
|
|
42
|
+
stateHezeView.inactivityScores = stateHezeCloned.inactivityScores;
|
|
43
|
+
stateHezeView.currentSyncCommittee = stateHezeCloned.currentSyncCommittee;
|
|
44
|
+
stateHezeView.nextSyncCommittee = stateHezeCloned.nextSyncCommittee;
|
|
45
|
+
stateHezeView.latestBlockHash = stateHezeCloned.latestBlockHash;
|
|
46
|
+
stateHezeView.nextWithdrawalIndex = stateHezeCloned.nextWithdrawalIndex;
|
|
47
|
+
stateHezeView.nextWithdrawalValidatorIndex = stateHezeCloned.nextWithdrawalValidatorIndex;
|
|
48
|
+
stateHezeView.historicalSummaries = stateHezeCloned.historicalSummaries;
|
|
49
|
+
stateHezeView.depositRequestsStartIndex = stateHezeCloned.depositRequestsStartIndex;
|
|
50
|
+
stateHezeView.depositBalanceToConsume = stateHezeCloned.depositBalanceToConsume;
|
|
51
|
+
stateHezeView.exitBalanceToConsume = stateHezeCloned.exitBalanceToConsume;
|
|
52
|
+
stateHezeView.earliestExitEpoch = stateHezeCloned.earliestExitEpoch;
|
|
53
|
+
stateHezeView.consolidationBalanceToConsume = stateHezeCloned.consolidationBalanceToConsume;
|
|
54
|
+
stateHezeView.earliestConsolidationEpoch = stateHezeCloned.earliestConsolidationEpoch;
|
|
55
|
+
stateHezeView.pendingDeposits = stateHezeCloned.pendingDeposits;
|
|
56
|
+
stateHezeView.pendingPartialWithdrawals = stateHezeCloned.pendingPartialWithdrawals;
|
|
57
|
+
stateHezeView.pendingConsolidations = stateHezeCloned.pendingConsolidations;
|
|
58
|
+
stateHezeView.proposerLookahead = stateHezeCloned.proposerLookahead;
|
|
59
|
+
stateHezeView.builders = stateHezeCloned.builders;
|
|
60
|
+
stateHezeView.nextWithdrawalBuilderIndex = stateHezeCloned.nextWithdrawalBuilderIndex;
|
|
61
|
+
stateHezeView.executionPayloadAvailability = stateHezeCloned.executionPayloadAvailability;
|
|
62
|
+
stateHezeView.builderPendingPayments = stateHezeCloned.builderPendingPayments;
|
|
63
|
+
stateHezeView.builderPendingWithdrawals = stateHezeCloned.builderPendingWithdrawals;
|
|
64
|
+
|
|
65
|
+
// [Modified in Heze:EIP7805] inclusion_list_bits = Bitvector[INCLUSION_LIST_COMMITTEE_SIZE]() (default zero)
|
|
66
|
+
const oldBid = stateHezeCloned.latestExecutionPayloadBid;
|
|
67
|
+
const newBid = ssz.heze.ExecutionPayloadBid.defaultViewDU();
|
|
68
|
+
newBid.parentBlockHash = oldBid.parentBlockHash;
|
|
69
|
+
newBid.parentBlockRoot = oldBid.parentBlockRoot;
|
|
70
|
+
newBid.blockHash = oldBid.blockHash;
|
|
71
|
+
newBid.prevRandao = oldBid.prevRandao;
|
|
72
|
+
newBid.feeRecipient = oldBid.feeRecipient;
|
|
73
|
+
newBid.gasLimit = oldBid.gasLimit;
|
|
74
|
+
newBid.builderIndex = oldBid.builderIndex;
|
|
75
|
+
newBid.slot = oldBid.slot;
|
|
76
|
+
newBid.value = oldBid.value;
|
|
77
|
+
newBid.executionPayment = oldBid.executionPayment;
|
|
78
|
+
newBid.blobKzgCommitments = oldBid.blobKzgCommitments;
|
|
79
|
+
newBid.executionRequestsRoot = oldBid.executionRequestsRoot;
|
|
80
|
+
stateHezeView.latestExecutionPayloadBid = newBid;
|
|
81
|
+
|
|
82
|
+
stateHezeView.payloadExpectedWithdrawals = stateHezeCloned.payloadExpectedWithdrawals;
|
|
83
|
+
stateHezeView.ptcWindow = stateHezeCloned.ptcWindow;
|
|
84
|
+
|
|
85
|
+
const stateHeze = getCachedBeaconState(stateHezeView, stateGloas);
|
|
86
|
+
stateHeze.commit();
|
|
87
|
+
// biome-ignore lint/complexity/useLiteralKeys: It is a protected attribute
|
|
88
|
+
stateHeze["clearCache"]();
|
|
89
|
+
|
|
90
|
+
return stateHeze;
|
|
91
|
+
}
|
package/src/stateTransition.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
upgradeStateToDeneb,
|
|
17
17
|
upgradeStateToElectra,
|
|
18
18
|
upgradeStateToGloas,
|
|
19
|
+
upgradeStateToHeze,
|
|
19
20
|
} from "./slot/index.js";
|
|
20
21
|
import {upgradeStateToFulu} from "./slot/upgradeStateToFulu.js";
|
|
21
22
|
import {
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
CachedBeaconStateDeneb,
|
|
27
28
|
CachedBeaconStateElectra,
|
|
28
29
|
CachedBeaconStateFulu,
|
|
30
|
+
CachedBeaconStateGloas,
|
|
29
31
|
CachedBeaconStatePhase0,
|
|
30
32
|
} from "./types.js";
|
|
31
33
|
import {computeEpochAtSlot} from "./util/index.js";
|
|
@@ -278,6 +280,9 @@ function processSlotsWithTransientCache(
|
|
|
278
280
|
if (stateEpoch === config.GLOAS_FORK_EPOCH) {
|
|
279
281
|
postState = upgradeStateToGloas(postState as CachedBeaconStateFulu) as CachedBeaconStateAllForks;
|
|
280
282
|
}
|
|
283
|
+
if (stateEpoch === config.HEZE_FORK_EPOCH) {
|
|
284
|
+
postState = upgradeStateToHeze(postState as CachedBeaconStateGloas) as CachedBeaconStateAllForks;
|
|
285
|
+
}
|
|
281
286
|
|
|
282
287
|
{
|
|
283
288
|
const timer = metrics?.epochTransitionStepTime.startTimer({step: EpochTransitionStep.finalProcessEpoch});
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
} from "../util/execution.js";
|
|
68
68
|
import {canBuilderCoverBid} from "../util/gloas.js";
|
|
69
69
|
import {loadState} from "../util/loadState/loadState.js";
|
|
70
|
+
import {PreVerifyBuilderDepositsResult, preVerifyBuilderDepositsPreGloas} from "../util/preVerifyBuilderDeposits.js";
|
|
70
71
|
import {getRandaoMix} from "../util/seed.js";
|
|
71
72
|
import {getLatestWeakSubjectivityCheckpointEpoch} from "../util/weakSubjectivity.js";
|
|
72
73
|
import {IBeaconStateView, IBeaconStateViewGloas, IBeaconStateViewLatestFork, isStatePostGloas} from "./interface.js";
|
|
@@ -341,6 +342,18 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
341
342
|
return this._proposerLookahead;
|
|
342
343
|
}
|
|
343
344
|
|
|
345
|
+
preVerifyBuilderDepositsPreGloas(maxBuilderDeposits: number, maxDurationMs: number): PreVerifyBuilderDepositsResult {
|
|
346
|
+
return preVerifyBuilderDepositsPreGloas(
|
|
347
|
+
this.cachedState as CachedBeaconStateFulu,
|
|
348
|
+
maxBuilderDeposits,
|
|
349
|
+
maxDurationMs
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
clearPreGloasBuilderDepositCache(): void {
|
|
354
|
+
this.cachedState.epochCtx.builderDepositSignatureCache.clear();
|
|
355
|
+
}
|
|
356
|
+
|
|
344
357
|
// gloas
|
|
345
358
|
|
|
346
359
|
get latestBlockHash(): Bytes32 {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ForkPostElectra,
|
|
10
10
|
ForkPostFulu,
|
|
11
11
|
ForkPostGloas,
|
|
12
|
+
ForkPostHeze,
|
|
12
13
|
isForkPostAltair,
|
|
13
14
|
isForkPostBellatrix,
|
|
14
15
|
isForkPostCapella,
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
isForkPostElectra,
|
|
17
18
|
isForkPostFulu,
|
|
18
19
|
isForkPostGloas,
|
|
20
|
+
isForkPostHeze,
|
|
19
21
|
} from "@lodestar/params";
|
|
20
22
|
import {
|
|
21
23
|
BeaconBlock,
|
|
@@ -50,6 +52,7 @@ import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
|
|
|
50
52
|
import {SyncCommitteeWitness} from "../lightClient/types.js";
|
|
51
53
|
import {StateTransitionModules, StateTransitionOpts} from "../stateTransition.js";
|
|
52
54
|
import {EpochShuffling} from "../util/epochShuffling.js";
|
|
55
|
+
import {PreVerifyBuilderDepositsResult} from "../util/preVerifyBuilderDeposits.js";
|
|
53
56
|
|
|
54
57
|
/**
|
|
55
58
|
* A read-only view of the BeaconState.
|
|
@@ -240,6 +243,13 @@ export interface IBeaconStateViewElectra extends IBeaconStateViewDeneb {
|
|
|
240
243
|
export interface IBeaconStateViewFulu extends IBeaconStateViewElectra {
|
|
241
244
|
forkName: ForkPostFulu;
|
|
242
245
|
proposerLookahead: fulu.ProposerLookahead;
|
|
246
|
+
/**
|
|
247
|
+
* Pre-verify a slice of builder-prefix pending deposits and cache the results on the underlying
|
|
248
|
+
* `BuilderDepositSignatureCache` (Fulu is the fork immediately before Gloas).
|
|
249
|
+
*/
|
|
250
|
+
preVerifyBuilderDepositsPreGloas(maxBuilderDeposits: number, maxDurationMs: number): PreVerifyBuilderDepositsResult;
|
|
251
|
+
/** Drop the pre-Gloas builder-deposit signature cache (called once the Gloas fork is finalized). */
|
|
252
|
+
clearPreGloasBuilderDepositCache(): void;
|
|
243
253
|
}
|
|
244
254
|
|
|
245
255
|
/** Gloas+ state fields — use isStatePostGloas() guard */
|
|
@@ -267,14 +277,19 @@ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
|
|
|
267
277
|
withParentPayloadApplied(executionRequests: gloas.ExecutionRequests): IBeaconStateViewGloas;
|
|
268
278
|
}
|
|
269
279
|
|
|
280
|
+
/** Heze+ state fields — use isStatePostHeze() guard */
|
|
281
|
+
export interface IBeaconStateViewHeze extends IBeaconStateViewGloas {
|
|
282
|
+
forkName: ForkPostHeze;
|
|
283
|
+
}
|
|
284
|
+
|
|
270
285
|
/**
|
|
271
286
|
* Type constraint for the concrete BeaconStateView class.
|
|
272
|
-
* Requires all fields from the latest fork interface (
|
|
287
|
+
* Requires all fields from the latest fork interface (IBeaconStateViewHeze) but keeps
|
|
273
288
|
* forkName as ForkName since the class wraps any fork's state.
|
|
274
289
|
* Sub-interfaces retain their narrowed forkName discriminants for caller-side type guards.
|
|
275
290
|
*/
|
|
276
291
|
export type IBeaconStateViewLatestFork = Omit<
|
|
277
|
-
|
|
292
|
+
IBeaconStateViewHeze,
|
|
278
293
|
"forkName" | "latestExecutionPayloadHeader" | "payloadBlockNumber"
|
|
279
294
|
> & {
|
|
280
295
|
forkName: ForkName;
|
|
@@ -337,3 +352,7 @@ export function isStatePostFulu(state: IBeaconStateView): state is IBeaconStateV
|
|
|
337
352
|
export function isStatePostGloas(state: IBeaconStateView): state is IBeaconStateViewGloas {
|
|
338
353
|
return isForkPostGloas(state.forkName);
|
|
339
354
|
}
|
|
355
|
+
|
|
356
|
+
export function isStatePostHeze(state: IBeaconStateView): state is IBeaconStateViewHeze {
|
|
357
|
+
return isForkPostHeze(state.forkName);
|
|
358
|
+
}
|
|
@@ -35,6 +35,7 @@ import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
|
|
|
35
35
|
import {SyncCommitteeWitness} from "../lightClient/types.js";
|
|
36
36
|
import {StateTransitionModules, StateTransitionOpts} from "../stateTransition.js";
|
|
37
37
|
import {EpochShuffling} from "../util/epochShuffling.js";
|
|
38
|
+
import {PreVerifyBuilderDepositsResult} from "../util/preVerifyBuilderDeposits.js";
|
|
38
39
|
import {
|
|
39
40
|
IBeaconStateView,
|
|
40
41
|
IBeaconStateViewGloas,
|
|
@@ -845,6 +846,14 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
845
846
|
return this._proposerLookahead;
|
|
846
847
|
}
|
|
847
848
|
|
|
849
|
+
preVerifyBuilderDepositsPreGloas(maxBuilderDeposits: number, maxDurationMs: number): PreVerifyBuilderDepositsResult {
|
|
850
|
+
return this.binding.preVerifyBuilderDepositsPreGloas(maxBuilderDeposits, maxDurationMs);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
clearPreGloasBuilderDepositCache(): void {
|
|
854
|
+
this.binding.clearPreGloasBuilderDepositCache();
|
|
855
|
+
}
|
|
856
|
+
|
|
848
857
|
// ─── gloas ───────────────────────────────────────────────────────────────
|
|
849
858
|
|
|
850
859
|
get latestBlockHash(): Bytes32 {
|
package/src/testUtils/util.ts
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
import {PublicKey, SecretKey} from "@chainsafe/blst";
|
|
2
2
|
import {BitArray, fromHexString} from "@chainsafe/ssz";
|
|
3
|
-
import {createBeaconConfig, createChainForkConfig} from "@lodestar/config";
|
|
3
|
+
import {BeaconConfig, createBeaconConfig, createChainForkConfig} from "@lodestar/config";
|
|
4
4
|
import {config} from "@lodestar/config/default";
|
|
5
5
|
import {
|
|
6
|
+
BUILDER_WITHDRAWAL_PREFIX,
|
|
7
|
+
DOMAIN_DEPOSIT,
|
|
6
8
|
EPOCHS_PER_ETH1_VOTING_PERIOD,
|
|
7
9
|
EPOCHS_PER_HISTORICAL_VECTOR,
|
|
8
10
|
ForkName,
|
|
9
11
|
ForkSeq,
|
|
12
|
+
GENESIS_SLOT,
|
|
10
13
|
MAX_ATTESTATIONS,
|
|
11
14
|
MAX_EFFECTIVE_BALANCE,
|
|
12
15
|
SLOTS_PER_EPOCH,
|
|
13
16
|
SLOTS_PER_HISTORICAL_ROOT,
|
|
14
17
|
} from "@lodestar/params";
|
|
15
|
-
import {BeaconState, Slot, phase0, ssz} from "@lodestar/types";
|
|
18
|
+
import {BeaconState, Slot, electra, phase0, ssz} from "@lodestar/types";
|
|
16
19
|
import {getEffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
|
|
20
|
+
import {ZERO_HASH} from "../constants/index.js";
|
|
17
21
|
import {
|
|
18
22
|
computeCommitteeCount,
|
|
19
23
|
computeEpochAtSlot,
|
|
20
24
|
createCachedBeaconState,
|
|
21
25
|
createPubkeyCache,
|
|
22
|
-
interopSecretKey,
|
|
23
26
|
newFilledArray,
|
|
24
27
|
processSlots,
|
|
25
28
|
} from "../index.js";
|
|
@@ -32,6 +35,9 @@ import {
|
|
|
32
35
|
CachedBeaconStateElectra,
|
|
33
36
|
CachedBeaconStatePhase0,
|
|
34
37
|
} from "../types.js";
|
|
38
|
+
import {computeDomain} from "../util/domain.js";
|
|
39
|
+
import {interopSecretKey} from "../util/interop.js";
|
|
40
|
+
import {computeSigningRoot} from "../util/signingRoot.js";
|
|
35
41
|
import {getNextSyncCommittee} from "../util/syncCommittee.js";
|
|
36
42
|
import {getActiveValidatorIndices} from "../util/validator.js";
|
|
37
43
|
import {interopPubkeysCached} from "./interop.js";
|
|
@@ -541,3 +547,67 @@ export function generateTestCachedBeaconStateOnlyValidators({
|
|
|
541
547
|
{skipSyncPubkeys: true}
|
|
542
548
|
);
|
|
543
549
|
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Generate `count` validly-signed builder pending deposits with distinct interop pubkeys
|
|
553
|
+
* [startIndex, startIndex + count). Withdrawal credentials carry the BUILDER_WITHDRAWAL_PREFIX so
|
|
554
|
+
* `isBuilderWithdrawalCredential` recognizes them. Used to exercise the pre-Gloas builder-deposit
|
|
555
|
+
* pre-verify scanner and the fork-transition onboarding path.
|
|
556
|
+
*/
|
|
557
|
+
export function generateBuilderPendingDeposits(
|
|
558
|
+
config: BeaconConfig,
|
|
559
|
+
count: number,
|
|
560
|
+
startIndex: number
|
|
561
|
+
): electra.PendingDeposit[] {
|
|
562
|
+
// Deposits use a fork-agnostic domain, see `isValidDepositSignature`
|
|
563
|
+
const domain = computeDomain(DOMAIN_DEPOSIT, config.GENESIS_FORK_VERSION, ZERO_HASH);
|
|
564
|
+
const amount = 1_000_000_000; // 1 ETH in Gwei
|
|
565
|
+
const deposits: electra.PendingDeposit[] = [];
|
|
566
|
+
|
|
567
|
+
for (let i = 0; i < count; i++) {
|
|
568
|
+
const sk = interopSecretKey(startIndex + i);
|
|
569
|
+
const pubkey = sk.toPublicKey().toBytes();
|
|
570
|
+
|
|
571
|
+
const withdrawalCredentials = Buffer.alloc(32, 0);
|
|
572
|
+
withdrawalCredentials[0] = BUILDER_WITHDRAWAL_PREFIX;
|
|
573
|
+
// bytes [12, 32) hold the 20-byte builder execution address
|
|
574
|
+
withdrawalCredentials.fill((startIndex + i) & 0xff, 12, 32);
|
|
575
|
+
|
|
576
|
+
const signingRoot = computeSigningRoot(ssz.phase0.DepositMessage, {pubkey, withdrawalCredentials, amount}, domain);
|
|
577
|
+
|
|
578
|
+
deposits.push({
|
|
579
|
+
pubkey,
|
|
580
|
+
withdrawalCredentials,
|
|
581
|
+
amount,
|
|
582
|
+
signature: sk.sign(signingRoot).toBytes(),
|
|
583
|
+
slot: GENESIS_SLOT,
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return deposits;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Generate a validly-signed *validator* (0x00-prefix) pending deposit for interop key `keyIndex`.
|
|
592
|
+
* Shares its pubkey with `generateBuilderPendingDeposits(config, 1, keyIndex)`, so it stands in for a
|
|
593
|
+
* validator deposit competing with a builder deposit for the same pubkey (the `hasPendingValidator`
|
|
594
|
+
* path). `amount` lets callers build otherwise-identical-but-distinct deposit value objects.
|
|
595
|
+
*/
|
|
596
|
+
export function generateValidatorPendingDeposit(
|
|
597
|
+
config: BeaconConfig,
|
|
598
|
+
keyIndex: number,
|
|
599
|
+
amount = 32_000_000_000
|
|
600
|
+
): electra.PendingDeposit {
|
|
601
|
+
const domain = computeDomain(DOMAIN_DEPOSIT, config.GENESIS_FORK_VERSION, ZERO_HASH);
|
|
602
|
+
const sk = interopSecretKey(keyIndex);
|
|
603
|
+
const pubkey = sk.toPublicKey().toBytes();
|
|
604
|
+
const withdrawalCredentials = Buffer.alloc(32, 0); // 0x00 prefix → validator, not a builder
|
|
605
|
+
const signingRoot = computeSigningRoot(ssz.phase0.DepositMessage, {pubkey, withdrawalCredentials, amount}, domain);
|
|
606
|
+
return {
|
|
607
|
+
pubkey,
|
|
608
|
+
withdrawalCredentials,
|
|
609
|
+
amount,
|
|
610
|
+
signature: sk.sign(signingRoot).toBytes(),
|
|
611
|
+
slot: GENESIS_SLOT,
|
|
612
|
+
};
|
|
613
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type {
|
|
|
10
10
|
CachedBeaconStateExecutions,
|
|
11
11
|
CachedBeaconStateFulu,
|
|
12
12
|
CachedBeaconStateGloas,
|
|
13
|
+
CachedBeaconStateHeze,
|
|
13
14
|
CachedBeaconStatePhase0,
|
|
14
15
|
} from "./cache/stateCache.js";
|
|
15
16
|
export type {
|
|
@@ -22,6 +23,7 @@ export type {
|
|
|
22
23
|
BeaconStateExecutions,
|
|
23
24
|
BeaconStateFulu,
|
|
24
25
|
BeaconStateGloas,
|
|
26
|
+
BeaconStateHeze,
|
|
25
27
|
BeaconStatePhase0,
|
|
26
28
|
ShufflingGetter,
|
|
27
29
|
} from "./cache/types.js";
|