@lodestar/state-transition 1.44.0-dev.481dde6669 → 1.44.0-dev.4b91ba8c2f
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/index.d.ts +2 -0
- package/lib/block/index.d.ts.map +1 -1
- package/lib/block/index.js +3 -1
- package/lib/block/index.js.map +1 -1
- package/lib/block/processBuilderDepositRequest.d.ts +10 -0
- package/lib/block/processBuilderDepositRequest.d.ts.map +1 -0
- package/lib/block/processBuilderDepositRequest.js +27 -0
- package/lib/block/processBuilderDepositRequest.js.map +1 -0
- package/lib/block/processBuilderExitRequest.d.ts +13 -0
- package/lib/block/processBuilderExitRequest.d.ts.map +1 -0
- package/lib/block/processBuilderExitRequest.js +29 -0
- package/lib/block/processBuilderExitRequest.js.map +1 -0
- package/lib/block/processDepositRequest.d.ts +2 -8
- package/lib/block/processDepositRequest.d.ts.map +1 -1
- package/lib/block/processDepositRequest.js +2 -98
- package/lib/block/processDepositRequest.js.map +1 -1
- package/lib/block/processExecutionPayloadBid.d.ts +2 -3
- package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
- package/lib/block/processExecutionPayloadBid.js +16 -8
- package/lib/block/processExecutionPayloadBid.js.map +1 -1
- package/lib/block/processParentExecutionPayload.d.ts +2 -2
- package/lib/block/processParentExecutionPayload.d.ts.map +1 -1
- package/lib/block/processParentExecutionPayload.js +16 -8
- package/lib/block/processParentExecutionPayload.js.map +1 -1
- package/lib/block/processProposerSlashing.d.ts.map +1 -1
- package/lib/block/processProposerSlashing.js +9 -1
- package/lib/block/processProposerSlashing.js.map +1 -1
- package/lib/block/processVoluntaryExit.d.ts +1 -1
- package/lib/block/processVoluntaryExit.d.ts.map +1 -1
- package/lib/block/processVoluntaryExit.js +1 -35
- package/lib/block/processVoluntaryExit.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +35 -29
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateView/beaconStateView.d.ts +1 -1
- package/lib/stateView/beaconStateView.d.ts.map +1 -1
- package/lib/stateView/beaconStateView.js.map +1 -1
- package/lib/stateView/interface.d.ts +1 -1
- package/lib/stateView/interface.d.ts.map +1 -1
- package/lib/stateView/nativeBeaconStateView.d.ts +1 -1
- package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -1
- package/lib/stateView/nativeBeaconStateView.js.map +1 -1
- package/lib/util/epochShuffling.js +1 -1
- package/lib/util/epochShuffling.js.map +1 -1
- package/lib/util/gloas.d.ts +17 -0
- package/lib/util/gloas.d.ts.map +1 -1
- package/lib/util/gloas.js +59 -1
- package/lib/util/gloas.js.map +1 -1
- package/lib/util/shuffling.d.ts +2 -2
- package/lib/util/shuffling.d.ts.map +1 -1
- package/lib/util/shuffling.js +4 -3
- package/lib/util/shuffling.js.map +1 -1
- package/package.json +7 -7
- package/src/block/index.ts +6 -1
- package/src/block/processBuilderDepositRequest.ts +43 -0
- package/src/block/processBuilderExitRequest.ts +39 -0
- package/src/block/processDepositRequest.ts +3 -124
- package/src/block/processExecutionPayloadBid.ts +24 -9
- package/src/block/processParentExecutionPayload.ts +23 -11
- package/src/block/processProposerSlashing.ts +9 -4
- package/src/block/processVoluntaryExit.ts +2 -61
- package/src/slot/upgradeStateToGloas.ts +49 -33
- package/src/stateView/beaconStateView.ts +1 -1
- package/src/stateView/interface.ts +1 -1
- package/src/stateView/nativeBeaconStateView.ts +1 -1
- package/src/util/epochShuffling.ts +1 -1
- package/src/util/gloas.ts +76 -1
- package/src/util/shuffling.ts +4 -3
|
@@ -32,6 +32,10 @@ export function processProposerSlashing(
|
|
|
32
32
|
);
|
|
33
33
|
|
|
34
34
|
if (fork >= ForkSeq.gloas) {
|
|
35
|
+
// Remove the BuilderPendingPayment corresponding to this proposal if it is still in the
|
|
36
|
+
// 2-epoch window. Only clear it when the slashed validator is the proposer associated with
|
|
37
|
+
// the payment; otherwise an unrelated same-slot equivocation could grief an honest proposer's
|
|
38
|
+
// payment.
|
|
35
39
|
const slot = Number(proposerSlashing.signedHeader1.message.slot);
|
|
36
40
|
const proposalEpoch = computeEpochAtSlot(slot);
|
|
37
41
|
const currentEpoch = state.epochCtx.epoch;
|
|
@@ -45,10 +49,11 @@ export function processProposerSlashing(
|
|
|
45
49
|
: undefined;
|
|
46
50
|
|
|
47
51
|
if (paymentIndex !== undefined) {
|
|
48
|
-
(state as CachedBeaconStateGloas).builderPendingPayments
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
const builderPendingPayments = (state as CachedBeaconStateGloas).builderPendingPayments;
|
|
53
|
+
const payment = builderPendingPayments.get(paymentIndex);
|
|
54
|
+
if (payment.proposerIndex === proposerSlashing.signedHeader1.message.proposerIndex) {
|
|
55
|
+
builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
|
|
56
|
+
}
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
59
|
|
|
@@ -2,14 +2,7 @@ import {FAR_FUTURE_EPOCH, ForkSeq} from "@lodestar/params";
|
|
|
2
2
|
import {phase0} from "@lodestar/types";
|
|
3
3
|
import {verifyVoluntaryExitSignature} from "../signatureSets/index.js";
|
|
4
4
|
import {BeaconStateView} from "../stateView/beaconStateView.js";
|
|
5
|
-
import {CachedBeaconStateAllForks, CachedBeaconStateElectra
|
|
6
|
-
import {
|
|
7
|
-
convertValidatorIndexToBuilderIndex,
|
|
8
|
-
getPendingBalanceToWithdrawForBuilder,
|
|
9
|
-
initiateBuilderExit,
|
|
10
|
-
isActiveBuilder,
|
|
11
|
-
isBuilderIndex,
|
|
12
|
-
} from "../util/gloas.js";
|
|
5
|
+
import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js";
|
|
13
6
|
import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/index.js";
|
|
14
7
|
import {initiateValidatorExit} from "./index.js";
|
|
15
8
|
|
|
@@ -24,7 +17,7 @@ export enum VoluntaryExitValidity {
|
|
|
24
17
|
}
|
|
25
18
|
|
|
26
19
|
/**
|
|
27
|
-
* Process a VoluntaryExit operation. Initiates the exit of a validator
|
|
20
|
+
* Process a VoluntaryExit operation. Initiates the exit of a validator.
|
|
28
21
|
*
|
|
29
22
|
* PERF: Work depends on number of VoluntaryExit per block. On regular networks the average is 0 / block.
|
|
30
23
|
*/
|
|
@@ -34,21 +27,11 @@ export function processVoluntaryExit(
|
|
|
34
27
|
signedVoluntaryExit: phase0.SignedVoluntaryExit,
|
|
35
28
|
verifySignature = true
|
|
36
29
|
): void {
|
|
37
|
-
const voluntaryExit = signedVoluntaryExit.message;
|
|
38
|
-
|
|
39
30
|
const validity = getVoluntaryExitValidity(fork, state, signedVoluntaryExit, verifySignature);
|
|
40
31
|
if (validity !== VoluntaryExitValidity.valid) {
|
|
41
32
|
throw Error(`Invalid voluntary exit at forkSeq=${fork} reason=${validity}`);
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
if (fork >= ForkSeq.gloas && isBuilderIndex(voluntaryExit.validatorIndex)) {
|
|
45
|
-
initiateBuilderExit(
|
|
46
|
-
state as CachedBeaconStateGloas,
|
|
47
|
-
convertValidatorIndexToBuilderIndex(voluntaryExit.validatorIndex)
|
|
48
|
-
);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
35
|
const validator = state.validators.get(signedVoluntaryExit.message.validatorIndex);
|
|
53
36
|
initiateValidatorExit(fork, state, validator);
|
|
54
37
|
}
|
|
@@ -67,51 +50,9 @@ export function getVoluntaryExitValidity(
|
|
|
67
50
|
return VoluntaryExitValidity.earlyEpoch;
|
|
68
51
|
}
|
|
69
52
|
|
|
70
|
-
// Check if this is a builder exit
|
|
71
|
-
if (fork >= ForkSeq.gloas && isBuilderIndex(voluntaryExit.validatorIndex)) {
|
|
72
|
-
return getBuilderVoluntaryExitValidity(state as CachedBeaconStateGloas, signedVoluntaryExit, verifySignature);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
53
|
return getValidatorVoluntaryExitValidity(fork, state, signedVoluntaryExit, verifySignature);
|
|
76
54
|
}
|
|
77
55
|
|
|
78
|
-
function getBuilderVoluntaryExitValidity(
|
|
79
|
-
state: CachedBeaconStateGloas,
|
|
80
|
-
signedVoluntaryExit: phase0.SignedVoluntaryExit,
|
|
81
|
-
verifySignature: boolean
|
|
82
|
-
): VoluntaryExitValidity {
|
|
83
|
-
const {config, epochCtx} = state;
|
|
84
|
-
const builderIndex = convertValidatorIndexToBuilderIndex(signedVoluntaryExit.message.validatorIndex);
|
|
85
|
-
|
|
86
|
-
if (builderIndex >= state.builders.length) {
|
|
87
|
-
return VoluntaryExitValidity.inactive;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const builder = state.builders.getReadonly(builderIndex);
|
|
91
|
-
|
|
92
|
-
// Verify the builder is active
|
|
93
|
-
if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) {
|
|
94
|
-
return builder.withdrawableEpoch !== FAR_FUTURE_EPOCH
|
|
95
|
-
? VoluntaryExitValidity.alreadyExited
|
|
96
|
-
: VoluntaryExitValidity.inactive;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Only exit builder if it has no pending withdrawals in the queue
|
|
100
|
-
if (getPendingBalanceToWithdrawForBuilder(state, builderIndex) !== 0) {
|
|
101
|
-
return VoluntaryExitValidity.pendingWithdrawals;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Verify signature
|
|
105
|
-
if (
|
|
106
|
-
verifySignature &&
|
|
107
|
-
!verifyVoluntaryExitSignature(config, epochCtx.pubkeyCache, new BeaconStateView(state), signedVoluntaryExit)
|
|
108
|
-
) {
|
|
109
|
-
return VoluntaryExitValidity.invalidSignature;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return VoluntaryExitValidity.valid;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
56
|
function getValidatorVoluntaryExitValidity(
|
|
116
57
|
fork: ForkSeq,
|
|
117
58
|
state: CachedBeaconStateAllForks,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
1
|
+
import {PAYLOAD_BUILDER_VERSION, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
2
2
|
import {ssz} from "@lodestar/types";
|
|
3
3
|
import {toPubkeyHex} from "@lodestar/utils";
|
|
4
|
-
import {
|
|
4
|
+
import {isValidDepositSignature} from "../block/processDeposit.js";
|
|
5
5
|
import {getCachedBeaconState} from "../cache/stateCache.js";
|
|
6
6
|
import {CachedBeaconStateFulu, CachedBeaconStateGloas} from "../types.js";
|
|
7
|
-
import {initializePtcWindow, isBuilderWithdrawalCredential} from "../util/gloas.js";
|
|
7
|
+
import {addBuilderToRegistry, initializePtcWindow, isBuilderWithdrawalCredential} from "../util/gloas.js";
|
|
8
8
|
import {isValidatorKnown} from "../util/index.js";
|
|
9
9
|
import {PendingDepositsLookup} from "../util/pendingDepositsLookup.js";
|
|
10
10
|
|
|
@@ -48,9 +48,9 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
|
|
|
48
48
|
stateGloasView.currentSyncCommittee = stateGloasCloned.currentSyncCommittee;
|
|
49
49
|
stateGloasView.nextSyncCommittee = stateGloasCloned.nextSyncCommittee;
|
|
50
50
|
stateGloasView.latestExecutionPayloadBid.blockHash = stateFulu.latestExecutionPayloadHeader.blockHash;
|
|
51
|
-
stateGloasView.latestExecutionPayloadBid.gasLimit =
|
|
52
|
-
stateGloasView.latestExecutionPayloadBid.executionRequestsRoot = ssz.
|
|
53
|
-
ssz.
|
|
51
|
+
stateGloasView.latestExecutionPayloadBid.gasLimit = stateFulu.latestExecutionPayloadHeader.gasLimit;
|
|
52
|
+
stateGloasView.latestExecutionPayloadBid.executionRequestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(
|
|
53
|
+
ssz.gloas.ExecutionRequests.defaultValue()
|
|
54
54
|
);
|
|
55
55
|
stateGloasView.nextWithdrawalIndex = stateGloasCloned.nextWithdrawalIndex;
|
|
56
56
|
stateGloasView.nextWithdrawalValidatorIndex = stateGloasCloned.nextWithdrawalValidatorIndex;
|
|
@@ -90,7 +90,8 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
|
|
|
90
90
|
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.8/specs/gloas/fork.md#new-onboard_builders_from_pending_deposits
|
|
91
91
|
*/
|
|
92
92
|
function onboardBuildersFromPendingDeposits(state: CachedBeaconStateGloas): void {
|
|
93
|
-
// Track pubkeys of new builders added when applying deposits
|
|
93
|
+
// Track pubkeys of new builders added when applying deposits. `state.builders` starts empty
|
|
94
|
+
// at the fork, so every builder pubkey here is one added in an earlier iteration.
|
|
94
95
|
const builderPubkeys = new Set<string>();
|
|
95
96
|
|
|
96
97
|
const pendingDeposits = ssz.electra.PendingDeposits.defaultViewDU();
|
|
@@ -109,40 +110,55 @@ function onboardBuildersFromPendingDeposits(state: CachedBeaconStateGloas): void
|
|
|
109
110
|
continue;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (!isBuilderWithdrawalCredential(deposit.withdrawalCredentials)) {
|
|
121
|
-
pendingDeposits.push(deposit);
|
|
122
|
-
pendingDepositsLookup.add(deposit, pubkeyHex);
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (pendingDepositsLookup.hasPendingValidator(state.config, pubkeyHex)) {
|
|
126
|
-
pendingDeposits.push(deposit);
|
|
127
|
-
pendingDepositsLookup.add(deposit, pubkeyHex);
|
|
128
|
-
continue;
|
|
113
|
+
if (builderPubkeys.has(pubkeyHex)) {
|
|
114
|
+
// Top up an already-onboarded builder
|
|
115
|
+
// TODO GLOAS: linear search; consider builder pubkey cache when we drop the upgrade-time set
|
|
116
|
+
for (let j = 0; j < state.builders.length; j++) {
|
|
117
|
+
if (toPubkeyHex(state.builders.getReadonly(j).pubkey) === pubkeyHex) {
|
|
118
|
+
state.builders.get(j).balance += deposit.amount;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
129
121
|
}
|
|
122
|
+
continue;
|
|
130
123
|
}
|
|
131
124
|
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
125
|
+
// Deposits for non-builders stay in the pending queue. If there is a valid pending
|
|
126
|
+
// deposit for a new validator with this pubkey, keep this deposit pending so the validator
|
|
127
|
+
// can pick it up later.
|
|
128
|
+
if (!isBuilderWithdrawalCredential(deposit.withdrawalCredentials)) {
|
|
129
|
+
pendingDeposits.push(deposit);
|
|
130
|
+
pendingDepositsLookup.add(deposit, pubkeyHex);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (pendingDepositsLookup.hasPendingValidator(state.config, pubkeyHex)) {
|
|
134
|
+
pendingDeposits.push(deposit);
|
|
135
|
+
pendingDepositsLookup.add(deposit, pubkeyHex);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Verify the deposit signature (proof of possession). If invalid the deposit is silently
|
|
140
|
+
// dropped — stake is forfeited, matching the validator deposit contract behavior.
|
|
141
|
+
if (
|
|
142
|
+
!isValidDepositSignature(
|
|
143
|
+
state.config,
|
|
144
|
+
deposit.pubkey,
|
|
145
|
+
deposit.withdrawalCredentials,
|
|
146
|
+
deposit.amount,
|
|
147
|
+
deposit.signature
|
|
148
|
+
)
|
|
149
|
+
) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
addBuilderToRegistry(
|
|
136
154
|
state,
|
|
137
155
|
deposit.pubkey,
|
|
138
|
-
|
|
156
|
+
PAYLOAD_BUILDER_VERSION,
|
|
157
|
+
deposit.withdrawalCredentials.subarray(12),
|
|
139
158
|
deposit.amount,
|
|
140
|
-
deposit.signature,
|
|
141
159
|
deposit.slot
|
|
142
160
|
);
|
|
143
|
-
|
|
144
|
-
builderPubkeys.add(pubkeyHex);
|
|
145
|
-
}
|
|
161
|
+
builderPubkeys.add(pubkeyHex);
|
|
146
162
|
}
|
|
147
163
|
|
|
148
164
|
state.pendingDeposits = pendingDeposits;
|
|
@@ -831,7 +831,7 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
831
831
|
/**
|
|
832
832
|
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/validator.md#executionpayload
|
|
833
833
|
*/
|
|
834
|
-
withParentPayloadApplied(executionRequests:
|
|
834
|
+
withParentPayloadApplied(executionRequests: gloas.ExecutionRequests): IBeaconStateViewGloas {
|
|
835
835
|
if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
|
|
836
836
|
throw new Error("withParentPayloadApplied is not available before Gloas");
|
|
837
837
|
}
|
|
@@ -264,7 +264,7 @@ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
|
|
|
264
264
|
* operation selection (e.g. voluntary exits) see the same post-apply state that the block
|
|
265
265
|
* processor will see at import.
|
|
266
266
|
*/
|
|
267
|
-
withParentPayloadApplied(executionRequests:
|
|
267
|
+
withParentPayloadApplied(executionRequests: gloas.ExecutionRequests): IBeaconStateViewGloas;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
/**
|
|
@@ -905,7 +905,7 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
905
905
|
return this.binding.getIndicesInPayloadTimelinessCommittee(validatorIndex, slot);
|
|
906
906
|
}
|
|
907
907
|
|
|
908
|
-
withParentPayloadApplied(executionRequests:
|
|
908
|
+
withParentPayloadApplied(executionRequests: gloas.ExecutionRequests): IBeaconStateViewGloas {
|
|
909
909
|
const view = new NativeBeaconStateView(this.binding.withParentPayloadApplied(executionRequests));
|
|
910
910
|
if (!isStatePostGloas(view)) {
|
|
911
911
|
throw new Error("Expected gloas state from withParentPayloadApplied");
|
|
@@ -124,7 +124,7 @@ export async function computeEpochShufflingAsync(
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
export function calculateDecisionRoot(state: BeaconStateAllForks, epoch: Epoch): RootHex {
|
|
127
|
-
const pivotSlot = computeStartSlotAtEpoch(epoch - 1) - 1;
|
|
127
|
+
const pivotSlot = Math.max(GENESIS_SLOT, computeStartSlotAtEpoch(epoch - 1) - 1);
|
|
128
128
|
return toRootHex(getBlockRootAtSlot(state, pivotSlot));
|
|
129
129
|
}
|
|
130
130
|
|
package/src/util/gloas.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import {PublicKey, Signature, verify} from "@chainsafe/blst";
|
|
2
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
1
3
|
import {
|
|
2
4
|
BUILDER_INDEX_FLAG,
|
|
3
5
|
BUILDER_PAYMENT_THRESHOLD_DENOMINATOR,
|
|
4
6
|
BUILDER_PAYMENT_THRESHOLD_NUMERATOR,
|
|
5
7
|
BUILDER_WITHDRAWAL_PREFIX,
|
|
8
|
+
DOMAIN_BUILDER_DEPOSIT,
|
|
6
9
|
EFFECTIVE_BALANCE_INCREMENT,
|
|
7
10
|
FAR_FUTURE_EPOCH,
|
|
8
11
|
MIN_DEPOSIT_AMOUNT,
|
|
@@ -10,15 +13,18 @@ import {
|
|
|
10
13
|
PTC_SIZE,
|
|
11
14
|
SLOTS_PER_EPOCH,
|
|
12
15
|
} from "@lodestar/params";
|
|
13
|
-
import {BuilderIndex, Epoch, ValidatorIndex, gloas} from "@lodestar/types";
|
|
16
|
+
import {BuilderIndex, Epoch, ValidatorIndex, gloas, ssz} from "@lodestar/types";
|
|
14
17
|
import {AttestationData} from "@lodestar/types/phase0";
|
|
15
18
|
import {byteArrayEquals} from "@lodestar/utils";
|
|
19
|
+
import {ZERO_HASH} from "../constants/index.js";
|
|
16
20
|
import {CachedBeaconStateFulu, CachedBeaconStateGloas} from "../types.js";
|
|
17
21
|
import {getBlockRootAtSlot} from "./blockRoot.js";
|
|
22
|
+
import {computeDomain} from "./domain.js";
|
|
18
23
|
import {computeEpochAtSlot} from "./epoch.js";
|
|
19
24
|
import {computeEpochShuffling} from "./epochShuffling.js";
|
|
20
25
|
import {RootCache} from "./rootCache.js";
|
|
21
26
|
import {computePayloadTimelinessCommitteesForEpoch} from "./seed.js";
|
|
27
|
+
import {computeSigningRoot} from "./signingRoot.js";
|
|
22
28
|
import {getActiveValidatorIndices} from "./validator.js";
|
|
23
29
|
|
|
24
30
|
export function isBuilderWithdrawalCredential(withdrawalCredentials: Uint8Array): boolean {
|
|
@@ -241,3 +247,72 @@ export function getPtcWindowEpochCacheData(state: CachedBeaconStateGloas): {
|
|
|
241
247
|
nextPayloadTimelinessCommittees: toUint32Arrays(nextPtcWindow),
|
|
242
248
|
};
|
|
243
249
|
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Add a new builder to the builders registry. Reuses slots from exited and fully withdrawn
|
|
253
|
+
* builders when available, otherwise appends.
|
|
254
|
+
*
|
|
255
|
+
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.11/specs/gloas/beacon-chain.md#new-add_builder_to_registry
|
|
256
|
+
*/
|
|
257
|
+
export function addBuilderToRegistry(
|
|
258
|
+
state: CachedBeaconStateGloas,
|
|
259
|
+
pubkey: Uint8Array,
|
|
260
|
+
version: number,
|
|
261
|
+
executionAddress: Uint8Array,
|
|
262
|
+
amount: number,
|
|
263
|
+
slot: number
|
|
264
|
+
): void {
|
|
265
|
+
const currentEpoch = computeEpochAtSlot(state.slot);
|
|
266
|
+
const depositEpoch = computeEpochAtSlot(slot);
|
|
267
|
+
|
|
268
|
+
let builderIndex = state.builders.length;
|
|
269
|
+
for (let i = 0; i < state.builders.length; i++) {
|
|
270
|
+
const builder = state.builders.getReadonly(i);
|
|
271
|
+
if (builder.withdrawableEpoch <= currentEpoch && builder.balance === 0) {
|
|
272
|
+
builderIndex = i;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const newBuilder = ssz.gloas.Builder.toViewDU({
|
|
278
|
+
pubkey,
|
|
279
|
+
version,
|
|
280
|
+
executionAddress,
|
|
281
|
+
balance: amount,
|
|
282
|
+
depositEpoch,
|
|
283
|
+
withdrawableEpoch: FAR_FUTURE_EPOCH,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
if (builderIndex < state.builders.length) {
|
|
287
|
+
state.builders.set(builderIndex, newBuilder);
|
|
288
|
+
} else {
|
|
289
|
+
state.builders.push(newBuilder);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Verify the proof of possession on a builder deposit request.
|
|
295
|
+
*
|
|
296
|
+
* The dedicated `DOMAIN_BUILDER_DEPOSIT` (vs the validator `DOMAIN_DEPOSIT`) prevents replay
|
|
297
|
+
* of validator deposit signatures against the builder deposit contract and vice versa.
|
|
298
|
+
*
|
|
299
|
+
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.11/specs/gloas/beacon-chain.md#new-is_valid_builder_deposit_signature
|
|
300
|
+
*/
|
|
301
|
+
export function isValidBuilderDepositSignature(
|
|
302
|
+
config: BeaconConfig,
|
|
303
|
+
pubkey: Uint8Array,
|
|
304
|
+
withdrawalCredentials: Uint8Array,
|
|
305
|
+
amount: number,
|
|
306
|
+
signature: Uint8Array
|
|
307
|
+
): boolean {
|
|
308
|
+
const depositMessage = {pubkey, withdrawalCredentials, amount};
|
|
309
|
+
const domain = computeDomain(DOMAIN_BUILDER_DEPOSIT, config.GENESIS_FORK_VERSION, ZERO_HASH);
|
|
310
|
+
const signingRoot = computeSigningRoot(ssz.phase0.DepositMessage, depositMessage, domain);
|
|
311
|
+
try {
|
|
312
|
+
const publicKey = PublicKey.fromBytes(pubkey, true);
|
|
313
|
+
const sig = Signature.fromBytes(signature, true);
|
|
314
|
+
return verify(signingRoot, publicKey, sig);
|
|
315
|
+
} catch (_e) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
}
|
package/src/util/shuffling.ts
CHANGED
|
@@ -21,8 +21,8 @@ import {EpochShuffling} from "./epochShuffling.js";
|
|
|
21
21
|
* Computed for the requested epoch, not `state.epoch`, so it is correct when `state` is one
|
|
22
22
|
* epoch off the requested epoch (e.g. serving next-epoch duties from the current state).
|
|
23
23
|
*
|
|
24
|
-
* Returns `null` when the
|
|
25
|
-
*
|
|
24
|
+
* Returns `null` when the decision block is not in the state's past (the genesis block decides
|
|
25
|
+
* its own shuffling, or it has not been produced yet); caller supplies the fallback.
|
|
26
26
|
*/
|
|
27
27
|
export function proposerShufflingDecisionRoot(
|
|
28
28
|
fork: ForkName,
|
|
@@ -30,7 +30,8 @@ export function proposerShufflingDecisionRoot(
|
|
|
30
30
|
proposalEpoch: Epoch
|
|
31
31
|
): Root | null {
|
|
32
32
|
const decisionSlot = proposerShufflingDecisionSlot(fork, proposalEpoch);
|
|
33
|
-
|
|
33
|
+
// Return null when the decision block is not in the state's past or the getBlockRootAtSlot() api will throw
|
|
34
|
+
if (decisionSlot >= state.slot) {
|
|
34
35
|
return null;
|
|
35
36
|
}
|
|
36
37
|
return state.getBlockRootAtSlot(decisionSlot);
|