@lodestar/state-transition 1.40.0-dev.0ae7a89ead → 1.40.0-dev.193b2e2047

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.
Files changed (142) hide show
  1. package/lib/block/index.d.ts +1 -0
  2. package/lib/block/index.d.ts.map +1 -1
  3. package/lib/block/index.js +1 -0
  4. package/lib/block/index.js.map +1 -1
  5. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
  6. package/lib/block/isValidIndexedAttestation.js +2 -2
  7. package/lib/block/isValidIndexedAttestation.js.map +1 -1
  8. package/lib/block/isValidIndexedPayloadAttestation.d.ts.map +1 -1
  9. package/lib/block/isValidIndexedPayloadAttestation.js +1 -1
  10. package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -1
  11. package/lib/block/processAttestationsAltair.d.ts.map +1 -1
  12. package/lib/block/processAttestationsAltair.js +2 -2
  13. package/lib/block/processAttestationsAltair.js.map +1 -1
  14. package/lib/block/processConsolidationRequest.d.ts +1 -2
  15. package/lib/block/processConsolidationRequest.d.ts.map +1 -1
  16. package/lib/block/processConsolidationRequest.js +2 -2
  17. package/lib/block/processConsolidationRequest.js.map +1 -1
  18. package/lib/block/processDepositRequest.d.ts +8 -2
  19. package/lib/block/processDepositRequest.d.ts.map +1 -1
  20. package/lib/block/processDepositRequest.js +81 -8
  21. package/lib/block/processDepositRequest.js.map +1 -1
  22. package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
  23. package/lib/block/processExecutionPayloadBid.js +16 -29
  24. package/lib/block/processExecutionPayloadBid.js.map +1 -1
  25. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -1
  26. package/lib/block/processExecutionPayloadEnvelope.js +26 -25
  27. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -1
  28. package/lib/block/processOperations.js +2 -2
  29. package/lib/block/processOperations.js.map +1 -1
  30. package/lib/block/processProposerSlashing.js +2 -2
  31. package/lib/block/processProposerSlashing.js.map +1 -1
  32. package/lib/block/processSyncCommittee.d.ts +1 -2
  33. package/lib/block/processSyncCommittee.d.ts.map +1 -1
  34. package/lib/block/processSyncCommittee.js +5 -5
  35. package/lib/block/processSyncCommittee.js.map +1 -1
  36. package/lib/block/processVoluntaryExit.d.ts +1 -1
  37. package/lib/block/processVoluntaryExit.d.ts.map +1 -1
  38. package/lib/block/processVoluntaryExit.js +45 -3
  39. package/lib/block/processVoluntaryExit.js.map +1 -1
  40. package/lib/block/processWithdrawalRequest.js +1 -1
  41. package/lib/block/processWithdrawalRequest.js.map +1 -1
  42. package/lib/block/processWithdrawals.d.ts +1 -0
  43. package/lib/block/processWithdrawals.d.ts.map +1 -1
  44. package/lib/block/processWithdrawals.js +121 -66
  45. package/lib/block/processWithdrawals.js.map +1 -1
  46. package/lib/epoch/processBuilderPendingPayments.d.ts.map +1 -1
  47. package/lib/epoch/processBuilderPendingPayments.js +1 -4
  48. package/lib/epoch/processBuilderPendingPayments.js.map +1 -1
  49. package/lib/signatureSets/attesterSlashings.d.ts +3 -4
  50. package/lib/signatureSets/attesterSlashings.d.ts.map +1 -1
  51. package/lib/signatureSets/attesterSlashings.js +6 -6
  52. package/lib/signatureSets/attesterSlashings.js.map +1 -1
  53. package/lib/signatureSets/blsToExecutionChange.d.ts +3 -3
  54. package/lib/signatureSets/blsToExecutionChange.d.ts.map +1 -1
  55. package/lib/signatureSets/blsToExecutionChange.js.map +1 -1
  56. package/lib/signatureSets/executionPayloadBid.d.ts +4 -0
  57. package/lib/signatureSets/executionPayloadBid.d.ts.map +1 -0
  58. package/lib/signatureSets/executionPayloadBid.js +8 -0
  59. package/lib/signatureSets/executionPayloadBid.js.map +1 -0
  60. package/lib/signatureSets/executionPayloadEnvelope.d.ts +4 -0
  61. package/lib/signatureSets/executionPayloadEnvelope.d.ts.map +1 -0
  62. package/lib/signatureSets/executionPayloadEnvelope.js +8 -0
  63. package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -0
  64. package/lib/signatureSets/index.d.ts +3 -2
  65. package/lib/signatureSets/index.d.ts.map +1 -1
  66. package/lib/signatureSets/index.js +10 -8
  67. package/lib/signatureSets/index.js.map +1 -1
  68. package/lib/signatureSets/indexedAttestation.d.ts +3 -4
  69. package/lib/signatureSets/indexedAttestation.d.ts.map +1 -1
  70. package/lib/signatureSets/indexedAttestation.js +6 -6
  71. package/lib/signatureSets/indexedAttestation.js.map +1 -1
  72. package/lib/signatureSets/indexedPayloadAttestation.d.ts +5 -4
  73. package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -1
  74. package/lib/signatureSets/indexedPayloadAttestation.js +3 -3
  75. package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -1
  76. package/lib/signatureSets/proposer.d.ts +3 -3
  77. package/lib/signatureSets/proposer.d.ts.map +1 -1
  78. package/lib/signatureSets/proposer.js +12 -12
  79. package/lib/signatureSets/proposer.js.map +1 -1
  80. package/lib/signatureSets/proposerSlashings.d.ts +2 -3
  81. package/lib/signatureSets/proposerSlashings.d.ts.map +1 -1
  82. package/lib/signatureSets/proposerSlashings.js +6 -6
  83. package/lib/signatureSets/proposerSlashings.js.map +1 -1
  84. package/lib/signatureSets/randao.d.ts +1 -1
  85. package/lib/signatureSets/randao.d.ts.map +1 -1
  86. package/lib/signatureSets/randao.js +4 -4
  87. package/lib/signatureSets/randao.js.map +1 -1
  88. package/lib/signatureSets/voluntaryExits.d.ts +2 -2
  89. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  90. package/lib/signatureSets/voluntaryExits.js +6 -6
  91. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  92. package/lib/util/electra.d.ts.map +1 -1
  93. package/lib/util/electra.js +1 -2
  94. package/lib/util/electra.js.map +1 -1
  95. package/lib/util/gloas.d.ts +46 -5
  96. package/lib/util/gloas.d.ts.map +1 -1
  97. package/lib/util/gloas.js +91 -5
  98. package/lib/util/gloas.js.map +1 -1
  99. package/lib/util/index.d.ts +1 -0
  100. package/lib/util/index.d.ts.map +1 -1
  101. package/lib/util/index.js +1 -0
  102. package/lib/util/index.js.map +1 -1
  103. package/lib/util/signatureSets.d.ts +38 -5
  104. package/lib/util/signatureSets.d.ts.map +1 -1
  105. package/lib/util/signatureSets.js +48 -6
  106. package/lib/util/signatureSets.js.map +1 -1
  107. package/lib/util/validator.d.ts +6 -1
  108. package/lib/util/validator.d.ts.map +1 -1
  109. package/lib/util/validator.js +26 -16
  110. package/lib/util/validator.js.map +1 -1
  111. package/package.json +7 -7
  112. package/src/block/index.ts +1 -0
  113. package/src/block/isValidIndexedAttestation.ts +3 -2
  114. package/src/block/isValidIndexedPayloadAttestation.ts +4 -1
  115. package/src/block/processAttestationsAltair.ts +2 -8
  116. package/src/block/processConsolidationRequest.ts +2 -3
  117. package/src/block/processDepositRequest.ts +101 -8
  118. package/src/block/processExecutionPayloadBid.ts +20 -42
  119. package/src/block/processExecutionPayloadEnvelope.ts +34 -30
  120. package/src/block/processOperations.ts +2 -2
  121. package/src/block/processProposerSlashing.ts +2 -2
  122. package/src/block/processSyncCommittee.ts +3 -6
  123. package/src/block/processVoluntaryExit.ts +60 -5
  124. package/src/block/processWithdrawalRequest.ts +1 -1
  125. package/src/block/processWithdrawals.ts +168 -70
  126. package/src/epoch/processBuilderPendingPayments.ts +1 -5
  127. package/src/signatureSets/attesterSlashings.ts +3 -7
  128. package/src/signatureSets/blsToExecutionChange.ts +3 -3
  129. package/src/signatureSets/executionPayloadBid.ts +14 -0
  130. package/src/signatureSets/executionPayloadEnvelope.ts +13 -0
  131. package/src/signatureSets/index.ts +8 -9
  132. package/src/signatureSets/indexedAttestation.ts +2 -7
  133. package/src/signatureSets/indexedPayloadAttestation.ts +9 -7
  134. package/src/signatureSets/proposer.ts +8 -12
  135. package/src/signatureSets/proposerSlashings.ts +4 -7
  136. package/src/signatureSets/randao.ts +4 -8
  137. package/src/signatureSets/voluntaryExits.ts +5 -10
  138. package/src/util/electra.ts +1 -4
  139. package/src/util/gloas.ts +116 -10
  140. package/src/util/index.ts +1 -0
  141. package/src/util/signatureSets.ts +84 -8
  142. package/src/util/validator.ts +31 -16
@@ -2,22 +2,29 @@ import {byteArrayEquals} from "@chainsafe/ssz";
2
2
  import {
3
3
  FAR_FUTURE_EPOCH,
4
4
  ForkSeq,
5
+ MAX_BUILDERS_PER_WITHDRAWALS_SWEEP,
5
6
  MAX_EFFECTIVE_BALANCE,
6
7
  MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
7
8
  MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP,
8
9
  MAX_WITHDRAWALS_PER_PAYLOAD,
9
10
  MIN_ACTIVATION_BALANCE,
10
11
  } from "@lodestar/params";
11
- import {ValidatorIndex, capella, ssz} from "@lodestar/types";
12
+ import {BuilderIndex, ValidatorIndex, capella, ssz} from "@lodestar/types";
12
13
  import {toRootHex} from "@lodestar/utils";
13
14
  import {CachedBeaconStateCapella, CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
14
- import {isBuilderPaymentWithdrawable, isParentBlockFull} from "../util/gloas.ts";
15
+ import {
16
+ convertBuilderIndexToValidatorIndex,
17
+ convertValidatorIndexToBuilderIndex,
18
+ isBuilderIndex,
19
+ isParentBlockFull,
20
+ } from "../util/gloas.ts";
15
21
  import {
16
22
  decreaseBalance,
17
23
  getMaxEffectiveBalance,
18
24
  hasEth1WithdrawalCredential,
19
25
  hasExecutionWithdrawalCredential,
20
26
  isCapellaPayloadHeader,
27
+ isPartiallyWithdrawableValidator,
21
28
  } from "../util/index.js";
22
29
 
23
30
  export function processWithdrawals(
@@ -32,9 +39,14 @@ export function processWithdrawals(
32
39
 
33
40
  // processedBuilderWithdrawalsCount is withdrawals coming from builder payment since gloas (EIP-7732)
34
41
  // processedPartialWithdrawalsCount is withdrawals coming from EL since electra (EIP-7002)
42
+ // processedBuildersSweepCount is withdrawals from builder sweep since gloas (EIP-7732)
35
43
  // processedValidatorSweepCount is withdrawals coming from validator sweep
36
- const {expectedWithdrawals, processedBuilderWithdrawalsCount, processedPartialWithdrawalsCount} =
37
- getExpectedWithdrawals(fork, state);
44
+ const {
45
+ expectedWithdrawals,
46
+ processedBuilderWithdrawalsCount,
47
+ processedPartialWithdrawalsCount,
48
+ processedBuildersSweepCount,
49
+ } = getExpectedWithdrawals(fork, state);
38
50
  const numWithdrawals = expectedWithdrawals.length;
39
51
 
40
52
  // After gloas, withdrawals are verified later in processExecutionPayloadEnvelope
@@ -78,20 +90,20 @@ export function processWithdrawals(
78
90
 
79
91
  if (fork >= ForkSeq.gloas) {
80
92
  const stateGloas = state as CachedBeaconStateGloas;
81
- stateGloas.latestWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(expectedWithdrawals);
82
-
83
- const unprocessedWithdrawals = stateGloas.builderPendingWithdrawals
84
- .getAllReadonly()
85
- .slice(0, processedBuilderWithdrawalsCount)
86
- .filter((w) => !isBuilderPaymentWithdrawable(stateGloas, w));
87
- const remainingWithdrawals = stateGloas.builderPendingWithdrawals
88
- .sliceFrom(processedBuilderWithdrawalsCount)
89
- .getAllReadonly();
90
-
91
- stateGloas.builderPendingWithdrawals = ssz.gloas.BeaconState.fields.builderPendingWithdrawals.toViewDU([
92
- ...unprocessedWithdrawals,
93
- ...remainingWithdrawals,
94
- ]);
93
+
94
+ // Store expected withdrawals for verification
95
+ stateGloas.payloadExpectedWithdrawals = ssz.capella.Withdrawals.toViewDU(expectedWithdrawals);
96
+
97
+ // Update builder pending withdrawals queue
98
+ stateGloas.builderPendingWithdrawals = stateGloas.builderPendingWithdrawals.sliceFrom(
99
+ processedBuilderWithdrawalsCount
100
+ );
101
+
102
+ // Update next builder index for sweep
103
+ if (stateGloas.builders.length > 0) {
104
+ const nextIndex = stateGloas.nextWithdrawalBuilderIndex + processedBuildersSweepCount;
105
+ stateGloas.nextWithdrawalBuilderIndex = nextIndex % stateGloas.builders.length;
106
+ }
95
107
  }
96
108
  // Update the nextWithdrawalIndex
97
109
  // https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.0/specs/capella/beacon-chain.md#new-update_next_withdrawal_index
@@ -116,10 +128,14 @@ export function processWithdrawals(
116
128
  function getBuilderWithdrawals(
117
129
  state: CachedBeaconStateGloas,
118
130
  withdrawalIndex: number,
119
- balanceAfterWithdrawals: Map<ValidatorIndex, number>
131
+ priorWithdrawals: capella.Withdrawal[],
132
+ builderBalanceAfterWithdrawals: Map<number, number>
120
133
  ): {builderWithdrawals: capella.Withdrawal[]; withdrawalIndex: number; processedCount: number} {
134
+ const withdrawalsLimit = MAX_WITHDRAWALS_PER_PAYLOAD - 1;
135
+ if (priorWithdrawals.length > withdrawalsLimit) {
136
+ throw Error(`Prior withdrawals exceed limit: ${priorWithdrawals.length} > ${withdrawalsLimit}`);
137
+ }
121
138
  const builderWithdrawals: capella.Withdrawal[] = [];
122
- const epoch = state.epochCtx.epoch;
123
139
  const allBuilderPendingWithdrawals =
124
140
  state.builderPendingWithdrawals.length <= MAX_WITHDRAWALS_PER_PAYLOAD
125
141
  ? state.builderPendingWithdrawals.getAllReadonly()
@@ -127,55 +143,103 @@ function getBuilderWithdrawals(
127
143
 
128
144
  let processedCount = 0;
129
145
  for (let i = 0; i < state.builderPendingWithdrawals.length; i++) {
146
+ // Check combined length against limit
147
+ const allWithdrawals = priorWithdrawals.length + builderWithdrawals.length;
148
+ if (allWithdrawals >= withdrawalsLimit) {
149
+ break;
150
+ }
151
+
130
152
  const withdrawal = allBuilderPendingWithdrawals
131
153
  ? allBuilderPendingWithdrawals[i]
132
154
  : state.builderPendingWithdrawals.getReadonly(i);
133
155
 
134
- if (withdrawal.withdrawableEpoch > epoch || builderWithdrawals.length === MAX_WITHDRAWALS_PER_PAYLOAD) {
135
- break;
156
+ const builderIndex = withdrawal.builderIndex;
157
+
158
+ // Get builder balance (from builder.balance, not state.balances)
159
+ let balance = builderBalanceAfterWithdrawals.get(builderIndex);
160
+ if (balance === undefined) {
161
+ balance = state.builders.getReadonly(builderIndex).balance;
162
+ builderBalanceAfterWithdrawals.set(builderIndex, balance);
136
163
  }
137
164
 
138
- if (isBuilderPaymentWithdrawable(state, withdrawal)) {
139
- const builderIndex = withdrawal.builderIndex;
140
- const builder = state.validators.get(withdrawal.builderIndex);
165
+ // Use the withdrawal amount directly as specified in the spec
166
+ builderWithdrawals.push({
167
+ index: withdrawalIndex,
168
+ validatorIndex: convertBuilderIndexToValidatorIndex(builderIndex),
169
+ address: withdrawal.feeRecipient,
170
+ amount: BigInt(withdrawal.amount),
171
+ });
172
+ withdrawalIndex++;
173
+ builderBalanceAfterWithdrawals.set(builderIndex, balance - withdrawal.amount);
141
174
 
142
- let balance = balanceAfterWithdrawals.get(builderIndex);
143
- if (balance === undefined) {
144
- balance = state.balances.get(builderIndex);
145
- balanceAfterWithdrawals.set(builderIndex, balance);
146
- }
175
+ processedCount++;
176
+ }
147
177
 
148
- let withdrawableBalance = 0;
178
+ return {builderWithdrawals, withdrawalIndex, processedCount};
179
+ }
149
180
 
150
- if (builder.slashed) {
151
- withdrawableBalance = balance < withdrawal.amount ? balance : withdrawal.amount;
152
- } else if (balance > MIN_ACTIVATION_BALANCE) {
153
- withdrawableBalance =
154
- balance - MIN_ACTIVATION_BALANCE < withdrawal.amount ? balance - MIN_ACTIVATION_BALANCE : withdrawal.amount;
155
- }
181
+ function getBuildersSweepWithdrawals(
182
+ state: CachedBeaconStateGloas,
183
+ withdrawalIndex: number,
184
+ numPriorWithdrawal: number,
185
+ builderBalanceAfterWithdrawals: Map<number, number>
186
+ ): {buildersSweepWithdrawals: capella.Withdrawal[]; withdrawalIndex: number; processedCount: number} {
187
+ const withdrawalsLimit = MAX_WITHDRAWALS_PER_PAYLOAD - 1;
188
+ if (numPriorWithdrawal > withdrawalsLimit) {
189
+ throw Error(`Prior withdrawals exceed limit: ${numPriorWithdrawal} > ${withdrawalsLimit}`);
190
+ }
191
+ const buildersSweepWithdrawals: capella.Withdrawal[] = [];
192
+ const epoch = state.epochCtx.epoch;
193
+ const builders = state.builders;
156
194
 
157
- if (withdrawableBalance > 0) {
158
- builderWithdrawals.push({
159
- index: withdrawalIndex,
160
- validatorIndex: withdrawal.builderIndex,
161
- address: withdrawal.feeRecipient,
162
- amount: BigInt(withdrawableBalance),
163
- });
164
- withdrawalIndex++;
165
- balanceAfterWithdrawals.set(builderIndex, balance - withdrawableBalance);
166
- }
195
+ // Return early if no builders
196
+ if (builders.length === 0) {
197
+ return {buildersSweepWithdrawals, withdrawalIndex, processedCount: 0};
198
+ }
199
+
200
+ const buildersLimit = Math.min(builders.length, MAX_BUILDERS_PER_WITHDRAWALS_SWEEP);
201
+ let processedCount = 0;
202
+
203
+ for (let n = 0; n < buildersLimit; n++) {
204
+ if (buildersSweepWithdrawals.length + numPriorWithdrawal >= withdrawalsLimit) {
205
+ break;
206
+ }
207
+
208
+ // Get next builder in turn
209
+ const builderIndex = (state.nextWithdrawalBuilderIndex + n) % builders.length;
210
+ const builder = builders.getReadonly(builderIndex);
211
+
212
+ // Get builder balance
213
+ let balance = builderBalanceAfterWithdrawals.get(builderIndex);
214
+ if (balance === undefined) {
215
+ balance = builder.balance;
216
+ builderBalanceAfterWithdrawals.set(builderIndex, balance);
217
+ }
218
+
219
+ // Check if builder is withdrawable and has balance
220
+ if (builder.withdrawableEpoch <= epoch && balance > 0) {
221
+ // Withdraw full balance to builder's execution address
222
+ buildersSweepWithdrawals.push({
223
+ index: withdrawalIndex,
224
+ validatorIndex: convertBuilderIndexToValidatorIndex(builderIndex),
225
+ address: builder.executionAddress,
226
+ amount: BigInt(balance),
227
+ });
228
+ withdrawalIndex++;
229
+ builderBalanceAfterWithdrawals.set(builderIndex, 0);
167
230
  }
231
+
168
232
  processedCount++;
169
233
  }
170
234
 
171
- return {builderWithdrawals, withdrawalIndex, processedCount};
235
+ return {buildersSweepWithdrawals, withdrawalIndex, processedCount};
172
236
  }
173
237
 
174
238
  function getPendingPartialWithdrawals(
175
239
  state: CachedBeaconStateElectra,
176
240
  withdrawalIndex: number,
177
241
  numPriorWithdrawal: number,
178
- balanceAfterWithdrawals: Map<ValidatorIndex, number>
242
+ validatorBalanceAfterWithdrawals: Map<ValidatorIndex, number>
179
243
  ): {pendingPartialWithdrawals: capella.Withdrawal[]; withdrawalIndex: number; processedCount: number} {
180
244
  const epoch = state.epochCtx.epoch;
181
245
  const pendingPartialWithdrawals: capella.Withdrawal[] = [];
@@ -203,17 +267,17 @@ function getPendingPartialWithdrawals(
203
267
  : state.pendingPartialWithdrawals.getReadonly(i);
204
268
  if (
205
269
  withdrawal.withdrawableEpoch > epoch ||
206
- pendingPartialWithdrawals.length + numPriorWithdrawal === partialWithdrawalBound
270
+ pendingPartialWithdrawals.length + numPriorWithdrawal >= partialWithdrawalBound
207
271
  ) {
208
272
  break;
209
273
  }
210
274
 
211
275
  const validatorIndex = withdrawal.validatorIndex;
212
276
  const validator = validators.getReadonly(validatorIndex);
213
- let balance = balanceAfterWithdrawals.get(validatorIndex);
277
+ let balance = validatorBalanceAfterWithdrawals.get(validatorIndex);
214
278
  if (balance === undefined) {
215
279
  balance = state.balances.get(validatorIndex);
216
- balanceAfterWithdrawals.set(validatorIndex, balance);
280
+ validatorBalanceAfterWithdrawals.set(validatorIndex, balance);
217
281
  }
218
282
 
219
283
  if (
@@ -231,7 +295,7 @@ function getPendingPartialWithdrawals(
231
295
  amount: withdrawableBalance,
232
296
  });
233
297
  withdrawalIndex++;
234
- balanceAfterWithdrawals.set(validatorIndex, balance - Number(withdrawableBalance));
298
+ validatorBalanceAfterWithdrawals.set(validatorIndex, balance - Number(withdrawableBalance));
235
299
  }
236
300
  processedCount++;
237
301
  }
@@ -244,7 +308,7 @@ function getValidatorsSweepWithdrawals(
244
308
  state: CachedBeaconStateCapella | CachedBeaconStateElectra | CachedBeaconStateGloas,
245
309
  withdrawalIndex: number,
246
310
  numPriorWithdrawal: number,
247
- balanceAfterWithdrawals: Map<ValidatorIndex, number>
311
+ validatorBalanceAfterWithdrawals: Map<ValidatorIndex, number>
248
312
  ): {sweepWithdrawals: capella.Withdrawal[]; processedCount: number} {
249
313
  const sweepWithdrawals: capella.Withdrawal[] = [];
250
314
  const epoch = state.epochCtx.epoch;
@@ -264,13 +328,13 @@ function getValidatorsSweepWithdrawals(
264
328
  const validatorIndex = (nextWithdrawalValidatorIndex + n) % validators.length;
265
329
 
266
330
  const validator = validators.getReadonly(validatorIndex);
267
- let balance = balanceAfterWithdrawals.get(validatorIndex);
331
+ let balance = validatorBalanceAfterWithdrawals.get(validatorIndex);
268
332
  if (balance === undefined) {
269
333
  balance = balances.get(validatorIndex);
270
- balanceAfterWithdrawals.set(validatorIndex, balance);
334
+ validatorBalanceAfterWithdrawals.set(validatorIndex, balance);
271
335
  }
272
336
 
273
- const {withdrawableEpoch, withdrawalCredentials, effectiveBalance} = validator;
337
+ const {withdrawableEpoch, withdrawalCredentials} = validator;
274
338
  const hasWithdrawableCredentials = isPostElectra
275
339
  ? hasExecutionWithdrawalCredential(withdrawalCredentials)
276
340
  : hasEth1WithdrawalCredential(withdrawalCredentials);
@@ -290,13 +354,11 @@ function getValidatorsSweepWithdrawals(
290
354
  amount: BigInt(balance),
291
355
  });
292
356
  withdrawalIndex++;
293
- balanceAfterWithdrawals.set(validatorIndex, 0);
294
- } else if (
295
- effectiveBalance === (isPostElectra ? getMaxEffectiveBalance(withdrawalCredentials) : MAX_EFFECTIVE_BALANCE) &&
296
- balance > effectiveBalance
297
- ) {
357
+ validatorBalanceAfterWithdrawals.set(validatorIndex, 0);
358
+ } else if (isPartiallyWithdrawableValidator(fork, validator, balance)) {
298
359
  // capella partial withdrawal
299
- const partialAmount = balance - effectiveBalance;
360
+ const maxEffectiveBalance = isPostElectra ? getMaxEffectiveBalance(withdrawalCredentials) : MAX_EFFECTIVE_BALANCE;
361
+ const partialAmount = balance - maxEffectiveBalance;
300
362
  sweepWithdrawals.push({
301
363
  index: withdrawalIndex,
302
364
  validatorIndex,
@@ -304,7 +366,7 @@ function getValidatorsSweepWithdrawals(
304
366
  amount: BigInt(partialAmount),
305
367
  });
306
368
  withdrawalIndex++;
307
- balanceAfterWithdrawals.set(validatorIndex, balance - partialAmount);
369
+ validatorBalanceAfterWithdrawals.set(validatorIndex, balance - partialAmount);
308
370
  }
309
371
  processedCount++;
310
372
  }
@@ -317,7 +379,16 @@ function applyWithdrawals(
317
379
  withdrawals: capella.Withdrawal[]
318
380
  ): void {
319
381
  for (const withdrawal of withdrawals) {
320
- decreaseBalance(state, withdrawal.validatorIndex, Number(withdrawal.amount));
382
+ if (isBuilderIndex(withdrawal.validatorIndex)) {
383
+ // Handle builder withdrawal
384
+ const builderIndex = convertValidatorIndexToBuilderIndex(withdrawal.validatorIndex);
385
+ const builder = (state as CachedBeaconStateGloas).builders.get(builderIndex);
386
+ const withdrawalAmount = Number(withdrawal.amount);
387
+ builder.balance -= Math.min(withdrawalAmount, builder.balance);
388
+ } else {
389
+ // Handle validator withdrawal
390
+ decreaseBalance(state, withdrawal.validatorIndex, Number(withdrawal.amount));
391
+ }
321
392
  }
322
393
  }
323
394
 
@@ -328,6 +399,7 @@ export function getExpectedWithdrawals(
328
399
  expectedWithdrawals: capella.Withdrawal[];
329
400
  processedBuilderWithdrawalsCount: number;
330
401
  processedPartialWithdrawalsCount: number;
402
+ processedBuildersSweepCount: number;
331
403
  processedValidatorSweepCount: number;
332
404
  } {
333
405
  if (fork < ForkSeq.capella) {
@@ -337,20 +409,28 @@ export function getExpectedWithdrawals(
337
409
  let withdrawalIndex = state.nextWithdrawalIndex;
338
410
 
339
411
  const expectedWithdrawals: capella.Withdrawal[] = [];
340
- // Map to track balances after applying withdrawals
412
+ // Separate maps to track balances after applying withdrawals
341
413
  // https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.0/specs/capella/beacon-chain.md#new-get_balance_after_withdrawals
342
- const balanceAfterWithdrawals = new Map<ValidatorIndex, number>();
414
+ const builderBalanceAfterWithdrawals = new Map<BuilderIndex, number>();
415
+ const validatorBalanceAfterWithdrawals = new Map<ValidatorIndex, number>();
343
416
  // partialWithdrawalsCount is withdrawals coming from EL since electra (EIP-7002)
344
417
  let processedPartialWithdrawalsCount = 0;
345
418
  // builderWithdrawalsCount is withdrawals coming from builder payments since Gloas (EIP-7732)
346
419
  let processedBuilderWithdrawalsCount = 0;
420
+ // buildersSweepCount is withdrawals from builder sweep since Gloas (EIP-7732)
421
+ let processedBuildersSweepCount = 0;
347
422
 
348
423
  if (fork >= ForkSeq.gloas) {
349
424
  const {
350
425
  builderWithdrawals,
351
426
  withdrawalIndex: newWithdrawalIndex,
352
427
  processedCount,
353
- } = getBuilderWithdrawals(state as CachedBeaconStateGloas, withdrawalIndex, balanceAfterWithdrawals);
428
+ } = getBuilderWithdrawals(
429
+ state as CachedBeaconStateGloas,
430
+ withdrawalIndex,
431
+ expectedWithdrawals,
432
+ builderBalanceAfterWithdrawals
433
+ );
354
434
 
355
435
  expectedWithdrawals.push(...builderWithdrawals);
356
436
  withdrawalIndex = newWithdrawalIndex;
@@ -366,7 +446,7 @@ export function getExpectedWithdrawals(
366
446
  state as CachedBeaconStateElectra,
367
447
  withdrawalIndex,
368
448
  expectedWithdrawals.length,
369
- balanceAfterWithdrawals
449
+ validatorBalanceAfterWithdrawals
370
450
  );
371
451
 
372
452
  expectedWithdrawals.push(...pendingPartialWithdrawals);
@@ -374,12 +454,29 @@ export function getExpectedWithdrawals(
374
454
  processedPartialWithdrawalsCount = processedCount;
375
455
  }
376
456
 
457
+ if (fork >= ForkSeq.gloas) {
458
+ const {
459
+ buildersSweepWithdrawals,
460
+ withdrawalIndex: newWithdrawalIndex,
461
+ processedCount,
462
+ } = getBuildersSweepWithdrawals(
463
+ state as CachedBeaconStateGloas,
464
+ withdrawalIndex,
465
+ expectedWithdrawals.length,
466
+ builderBalanceAfterWithdrawals
467
+ );
468
+
469
+ expectedWithdrawals.push(...buildersSweepWithdrawals);
470
+ withdrawalIndex = newWithdrawalIndex;
471
+ processedBuildersSweepCount = processedCount;
472
+ }
473
+
377
474
  const {sweepWithdrawals, processedCount: processedValidatorSweepCount} = getValidatorsSweepWithdrawals(
378
475
  fork,
379
476
  state,
380
477
  withdrawalIndex,
381
478
  expectedWithdrawals.length,
382
- balanceAfterWithdrawals
479
+ validatorBalanceAfterWithdrawals
383
480
  );
384
481
 
385
482
  expectedWithdrawals.push(...sweepWithdrawals);
@@ -388,6 +485,7 @@ export function getExpectedWithdrawals(
388
485
  expectedWithdrawals,
389
486
  processedBuilderWithdrawalsCount,
390
487
  processedPartialWithdrawalsCount,
488
+ processedBuildersSweepCount,
391
489
  processedValidatorSweepCount,
392
490
  };
393
491
  }
@@ -1,7 +1,6 @@
1
1
  import {SLOTS_PER_EPOCH} from "@lodestar/params";
2
2
  import {ssz} from "@lodestar/types";
3
3
  import {CachedBeaconStateGloas} from "../types.ts";
4
- import {computeExitEpochAndUpdateChurn} from "../util/epoch.ts";
5
4
  import {getBuilderPaymentQuorumThreshold} from "../util/gloas.ts";
6
5
 
7
6
  /**
@@ -12,10 +11,7 @@ export function processBuilderPendingPayments(state: CachedBeaconStateGloas): vo
12
11
 
13
12
  for (let i = 0; i < SLOTS_PER_EPOCH; i++) {
14
13
  const payment = state.builderPendingPayments.get(i);
15
- if (payment.weight > quorum) {
16
- const exitQueueEpoch = computeExitEpochAndUpdateChurn(state, BigInt(payment.withdrawal.amount));
17
- payment.withdrawal.withdrawableEpoch = exitQueueEpoch + state.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
18
-
14
+ if (payment.weight >= quorum) {
19
15
  state.builderPendingWithdrawals.push(payment.withdrawal);
20
16
  }
21
17
  }
@@ -1,38 +1,34 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
2
  import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
3
3
  import {AttesterSlashing, IndexedAttestationBigint, SignedBeaconBlock, Slot, ssz} from "@lodestar/types";
4
- import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
5
4
  import {ISignatureSet, SignatureSetType, computeSigningRoot, computeStartSlotAtEpoch} from "../util/index.js";
6
5
 
7
6
  /** Get signature sets from all AttesterSlashing objects in a block */
8
7
  export function getAttesterSlashingsSignatureSets(
9
8
  config: BeaconConfig,
10
- index2pubkey: Index2PubkeyCache,
11
9
  signedBlock: SignedBeaconBlock
12
10
  ): ISignatureSet[] {
13
11
  // the getDomain() api requires the state slot as 1st param, however it's the same to block.slot in state-transition
14
12
  // and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
15
13
  const blockSlot = signedBlock.message.slot;
16
14
  return signedBlock.message.body.attesterSlashings.flatMap((attesterSlashing) =>
17
- getAttesterSlashingSignatureSets(config, index2pubkey, blockSlot, attesterSlashing)
15
+ getAttesterSlashingSignatureSets(config, blockSlot, attesterSlashing)
18
16
  );
19
17
  }
20
18
 
21
19
  /** Get signature sets from a single AttesterSlashing object */
22
20
  export function getAttesterSlashingSignatureSets(
23
21
  config: BeaconConfig,
24
- index2pubkey: Index2PubkeyCache,
25
22
  stateSlot: Slot,
26
23
  attesterSlashing: AttesterSlashing
27
24
  ): ISignatureSet[] {
28
25
  return [attesterSlashing.attestation1, attesterSlashing.attestation2].map((attestation) =>
29
- getIndexedAttestationBigintSignatureSet(config, index2pubkey, stateSlot, attestation)
26
+ getIndexedAttestationBigintSignatureSet(config, stateSlot, attestation)
30
27
  );
31
28
  }
32
29
 
33
30
  export function getIndexedAttestationBigintSignatureSet(
34
31
  config: BeaconConfig,
35
- index2pubkey: Index2PubkeyCache,
36
32
  stateSlot: Slot,
37
33
  indexedAttestation: IndexedAttestationBigint
38
34
  ): ISignatureSet {
@@ -41,7 +37,7 @@ export function getIndexedAttestationBigintSignatureSet(
41
37
 
42
38
  return {
43
39
  type: SignatureSetType.aggregate,
44
- pubkeys: indexedAttestation.attestingIndices.map((i) => index2pubkey[i]),
40
+ indices: indexedAttestation.attestingIndices.map((i) => Number(i)),
45
41
  signingRoot: computeSigningRoot(ssz.phase0.AttestationDataBigint, indexedAttestation.data, domain),
46
42
  signature: indexedAttestation.signature,
47
43
  };
@@ -2,7 +2,7 @@ import {PublicKey} from "@chainsafe/blst";
2
2
  import {BeaconConfig} from "@lodestar/config";
3
3
  import {DOMAIN_BLS_TO_EXECUTION_CHANGE, ForkName} from "@lodestar/params";
4
4
  import {capella, ssz} from "@lodestar/types";
5
- import {ISignatureSet, SignatureSetType, computeSigningRoot, verifySignatureSet} from "../util/index.js";
5
+ import {SignatureSetType, SingleSignatureSet, computeSigningRoot, verifySignatureSet} from "../util/index.js";
6
6
 
7
7
  export function verifyBlsToExecutionChangeSignature(
8
8
  config: BeaconConfig,
@@ -17,7 +17,7 @@ export function verifyBlsToExecutionChangeSignature(
17
17
  export function getBlsToExecutionChangeSignatureSet(
18
18
  config: BeaconConfig,
19
19
  signedBLSToExecutionChange: capella.SignedBLSToExecutionChange
20
- ): ISignatureSet {
20
+ ): SingleSignatureSet {
21
21
  // signatureFork for signing domain is fixed
22
22
  const signatureFork = ForkName.phase0;
23
23
  const domain = config.getDomainAtFork(signatureFork, DOMAIN_BLS_TO_EXECUTION_CHANGE);
@@ -35,7 +35,7 @@ export function getBlsToExecutionChangeSignatureSet(
35
35
  export function getBlsToExecutionChangeSignatureSets(
36
36
  config: BeaconConfig,
37
37
  signedBlock: capella.SignedBeaconBlock
38
- ): ISignatureSet[] {
38
+ ): SingleSignatureSet[] {
39
39
  return signedBlock.message.body.blsToExecutionChanges.map((blsToExecutionChange) =>
40
40
  getBlsToExecutionChangeSignatureSet(config, blsToExecutionChange)
41
41
  );
@@ -0,0 +1,14 @@
1
+ import {BeaconConfig} from "@lodestar/config";
2
+ import {DOMAIN_BEACON_BUILDER} from "@lodestar/params";
3
+ import {Slot, gloas, ssz} from "@lodestar/types";
4
+ import {computeSigningRoot} from "../util/index.js";
5
+
6
+ export function getExecutionPayloadBidSigningRoot(
7
+ config: BeaconConfig,
8
+ stateSlot: Slot,
9
+ bid: gloas.ExecutionPayloadBid
10
+ ): Uint8Array {
11
+ const domain = config.getDomain(stateSlot, DOMAIN_BEACON_BUILDER);
12
+
13
+ return computeSigningRoot(ssz.gloas.ExecutionPayloadBid, bid, domain);
14
+ }
@@ -0,0 +1,13 @@
1
+ import {BeaconConfig} from "@lodestar/config";
2
+ import {DOMAIN_BEACON_BUILDER} from "@lodestar/params";
3
+ import {gloas, ssz} from "@lodestar/types";
4
+ import {computeSigningRoot} from "../util/index.js";
5
+
6
+ export function getExecutionPayloadEnvelopeSigningRoot(
7
+ config: BeaconConfig,
8
+ envelope: gloas.ExecutionPayloadEnvelope
9
+ ): Uint8Array {
10
+ const domain = config.getDomain(envelope.slot, DOMAIN_BEACON_BUILDER);
11
+
12
+ return computeSigningRoot(ssz.gloas.ExecutionPayloadEnvelope, envelope, domain);
13
+ }
@@ -2,7 +2,6 @@ import {BeaconConfig} from "@lodestar/config";
2
2
  import {ForkSeq} from "@lodestar/params";
3
3
  import {IndexedAttestation, SignedBeaconBlock, altair, capella} from "@lodestar/types";
4
4
  import {getSyncCommitteeSignatureSet} from "../block/processSyncCommittee.js";
5
- import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
6
5
  import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
7
6
  import {ISignatureSet} from "../util/index.js";
8
7
  import {getAttesterSlashingsSignatureSets} from "./attesterSlashings.js";
@@ -15,6 +14,8 @@ import {getVoluntaryExitsSignatureSets} from "./voluntaryExits.js";
15
14
 
16
15
  export * from "./attesterSlashings.js";
17
16
  export * from "./blsToExecutionChange.js";
17
+ export * from "./executionPayloadBid.js";
18
+ export * from "./executionPayloadEnvelope.js";
18
19
  export * from "./indexedAttestation.js";
19
20
  export * from "./indexedPayloadAttestation.js";
20
21
  export * from "./proposer.js";
@@ -28,7 +29,6 @@ export * from "./voluntaryExits.js";
28
29
  */
29
30
  export function getBlockSignatureSets(
30
31
  config: BeaconConfig,
31
- index2pubkey: Index2PubkeyCache,
32
32
  currentSyncCommitteeIndexed: SyncCommitteeCache,
33
33
  signedBlock: SignedBeaconBlock,
34
34
  indexedAttestations: IndexedAttestation[],
@@ -41,22 +41,21 @@ export function getBlockSignatureSets(
41
41
  const fork = config.getForkSeq(signedBlock.message.slot);
42
42
 
43
43
  const signatureSets = [
44
- getRandaoRevealSignatureSet(config, index2pubkey, signedBlock.message),
45
- ...getProposerSlashingsSignatureSets(config, index2pubkey, signedBlock),
46
- ...getAttesterSlashingsSignatureSets(config, index2pubkey, signedBlock),
47
- ...getAttestationsSignatureSets(config, index2pubkey, signedBlock, indexedAttestations),
48
- ...getVoluntaryExitsSignatureSets(config, index2pubkey, signedBlock),
44
+ getRandaoRevealSignatureSet(config, signedBlock.message),
45
+ ...getProposerSlashingsSignatureSets(config, signedBlock),
46
+ ...getAttesterSlashingsSignatureSets(config, signedBlock),
47
+ ...getAttestationsSignatureSets(config, signedBlock, indexedAttestations),
48
+ ...getVoluntaryExitsSignatureSets(config, signedBlock),
49
49
  ];
50
50
 
51
51
  if (!opts?.skipProposerSignature) {
52
- signatureSets.push(getBlockProposerSignatureSet(config, index2pubkey, signedBlock));
52
+ signatureSets.push(getBlockProposerSignatureSet(config, signedBlock));
53
53
  }
54
54
 
55
55
  // Only after altair fork, validate tSyncCommitteeSignature
56
56
  if (fork >= ForkSeq.altair) {
57
57
  const syncCommitteeSignatureSet = getSyncCommitteeSignatureSet(
58
58
  config,
59
- index2pubkey,
60
59
  currentSyncCommitteeIndexed,
61
60
  (signedBlock as altair.SignedBeaconBlock).message
62
61
  );
@@ -1,7 +1,6 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
2
  import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
3
3
  import {IndexedAttestation, SignedBeaconBlock, Slot, phase0, ssz} from "@lodestar/types";
4
- import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
5
4
  import {
6
5
  ISignatureSet,
7
6
  computeSigningRoot,
@@ -22,13 +21,12 @@ export function getAttestationDataSigningRoot(
22
21
 
23
22
  export function getAttestationWithIndicesSignatureSet(
24
23
  config: BeaconConfig,
25
- index2pubkey: Index2PubkeyCache,
26
24
  stateSlot: Slot,
27
25
  attestation: Pick<phase0.Attestation, "data" | "signature">,
28
26
  attestingIndices: number[]
29
27
  ): ISignatureSet {
30
28
  return createAggregateSignatureSetFromComponents(
31
- attestingIndices.map((i) => index2pubkey[i]),
29
+ attestingIndices,
32
30
  getAttestationDataSigningRoot(config, stateSlot, attestation.data),
33
31
  attestation.signature
34
32
  );
@@ -36,13 +34,11 @@ export function getAttestationWithIndicesSignatureSet(
36
34
 
37
35
  export function getIndexedAttestationSignatureSet(
38
36
  config: BeaconConfig,
39
- index2pubkey: Index2PubkeyCache,
40
37
  stateSlot: Slot,
41
38
  indexedAttestation: IndexedAttestation
42
39
  ): ISignatureSet {
43
40
  return getAttestationWithIndicesSignatureSet(
44
41
  config,
45
- index2pubkey,
46
42
  stateSlot,
47
43
  indexedAttestation,
48
44
  indexedAttestation.attestingIndices
@@ -51,7 +47,6 @@ export function getIndexedAttestationSignatureSet(
51
47
 
52
48
  export function getAttestationsSignatureSets(
53
49
  config: BeaconConfig,
54
- index2pubkey: Index2PubkeyCache,
55
50
  signedBlock: SignedBeaconBlock,
56
51
  indexedAttestations: IndexedAttestation[]
57
52
  ): ISignatureSet[] {
@@ -64,6 +59,6 @@ export function getAttestationsSignatureSets(
64
59
  // and the same epoch when we verify blocks in batch in beacon-node. So we can safely use block.slot here.
65
60
  const blockSlot = signedBlock.message.slot;
66
61
  return indexedAttestations.map((indexedAttestation) =>
67
- getIndexedAttestationSignatureSet(config, index2pubkey, blockSlot, indexedAttestation)
62
+ getIndexedAttestationSignatureSet(config, blockSlot, indexedAttestation)
68
63
  );
69
64
  }