@lodestar/state-transition 1.38.0-dev.bc1fed4d3d → 1.38.0-dev.ebc352f211

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 (191) hide show
  1. package/lib/block/index.d.ts +4 -1
  2. package/lib/block/index.d.ts.map +1 -1
  3. package/lib/block/index.js +18 -9
  4. package/lib/block/index.js.map +1 -1
  5. package/lib/block/isValidIndexedAttestation.d.ts +3 -2
  6. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
  7. package/lib/block/isValidIndexedAttestation.js +4 -4
  8. package/lib/block/isValidIndexedAttestation.js.map +1 -1
  9. package/lib/block/isValidIndexedPayloadAttestation.d.ts +4 -0
  10. package/lib/block/isValidIndexedPayloadAttestation.d.ts.map +1 -0
  11. package/lib/block/isValidIndexedPayloadAttestation.js +14 -0
  12. package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -0
  13. package/lib/block/processAttestationPhase0.d.ts.map +1 -1
  14. package/lib/block/processAttestationPhase0.js +7 -2
  15. package/lib/block/processAttestationPhase0.js.map +1 -1
  16. package/lib/block/processAttestationsAltair.d.ts +3 -3
  17. package/lib/block/processAttestationsAltair.d.ts.map +1 -1
  18. package/lib/block/processAttestationsAltair.js +47 -5
  19. package/lib/block/processAttestationsAltair.js.map +1 -1
  20. package/lib/block/processAttesterSlashing.d.ts +2 -1
  21. package/lib/block/processAttesterSlashing.d.ts.map +1 -1
  22. package/lib/block/processAttesterSlashing.js +5 -4
  23. package/lib/block/processAttesterSlashing.js.map +1 -1
  24. package/lib/block/processConsolidationRequest.d.ts +3 -2
  25. package/lib/block/processConsolidationRequest.d.ts.map +1 -1
  26. package/lib/block/processConsolidationRequest.js +2 -2
  27. package/lib/block/processConsolidationRequest.js.map +1 -1
  28. package/lib/block/processDepositRequest.d.ts +2 -2
  29. package/lib/block/processDepositRequest.d.ts.map +1 -1
  30. package/lib/block/processDepositRequest.js.map +1 -1
  31. package/lib/block/processExecutionPayload.d.ts.map +1 -1
  32. package/lib/block/processExecutionPayload.js +4 -6
  33. package/lib/block/processExecutionPayload.js.map +1 -1
  34. package/lib/block/processExecutionPayloadBid.d.ts +5 -0
  35. package/lib/block/processExecutionPayloadBid.d.ts.map +1 -0
  36. package/lib/block/processExecutionPayloadBid.js +89 -0
  37. package/lib/block/processExecutionPayloadBid.js.map +1 -0
  38. package/lib/block/processExecutionPayloadEnvelope.d.ts +4 -0
  39. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -0
  40. package/lib/block/processExecutionPayloadEnvelope.js +118 -0
  41. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -0
  42. package/lib/block/processOperations.d.ts.map +1 -1
  43. package/lib/block/processOperations.js +8 -2
  44. package/lib/block/processOperations.js.map +1 -1
  45. package/lib/block/processPayloadAttestation.d.ts +4 -0
  46. package/lib/block/processPayloadAttestation.d.ts.map +1 -0
  47. package/lib/block/processPayloadAttestation.js +16 -0
  48. package/lib/block/processPayloadAttestation.js.map +1 -0
  49. package/lib/block/processProposerSlashing.d.ts.map +1 -1
  50. package/lib/block/processProposerSlashing.js +17 -2
  51. package/lib/block/processProposerSlashing.js.map +1 -1
  52. package/lib/block/processRandao.js +1 -1
  53. package/lib/block/processRandao.js.map +1 -1
  54. package/lib/block/processSyncCommittee.d.ts +2 -1
  55. package/lib/block/processSyncCommittee.d.ts.map +1 -1
  56. package/lib/block/processSyncCommittee.js +3 -4
  57. package/lib/block/processSyncCommittee.js.map +1 -1
  58. package/lib/block/processVoluntaryExit.js +2 -2
  59. package/lib/block/processVoluntaryExit.js.map +1 -1
  60. package/lib/block/processWithdrawalRequest.d.ts +2 -2
  61. package/lib/block/processWithdrawalRequest.d.ts.map +1 -1
  62. package/lib/block/processWithdrawalRequest.js +1 -1
  63. package/lib/block/processWithdrawalRequest.js.map +1 -1
  64. package/lib/block/processWithdrawals.d.ts +4 -3
  65. package/lib/block/processWithdrawals.d.ts.map +1 -1
  66. package/lib/block/processWithdrawals.js +89 -19
  67. package/lib/block/processWithdrawals.js.map +1 -1
  68. package/lib/cache/epochCache.d.ts +5 -1
  69. package/lib/cache/epochCache.d.ts.map +1 -1
  70. package/lib/cache/epochCache.js +34 -1
  71. package/lib/cache/epochCache.js.map +1 -1
  72. package/lib/epoch/index.d.ts +4 -2
  73. package/lib/epoch/index.d.ts.map +1 -1
  74. package/lib/epoch/index.js +10 -1
  75. package/lib/epoch/index.js.map +1 -1
  76. package/lib/epoch/processBuilderPendingPayments.d.ts +6 -0
  77. package/lib/epoch/processBuilderPendingPayments.d.ts.map +1 -0
  78. package/lib/epoch/processBuilderPendingPayments.js +28 -0
  79. package/lib/epoch/processBuilderPendingPayments.js.map +1 -0
  80. package/lib/index.d.ts +1 -1
  81. package/lib/index.d.ts.map +1 -1
  82. package/lib/index.js.map +1 -1
  83. package/lib/signatureSets/attesterSlashings.d.ts +4 -3
  84. package/lib/signatureSets/attesterSlashings.d.ts.map +1 -1
  85. package/lib/signatureSets/attesterSlashings.js +6 -6
  86. package/lib/signatureSets/attesterSlashings.js.map +1 -1
  87. package/lib/signatureSets/index.d.ts +3 -1
  88. package/lib/signatureSets/index.d.ts.map +1 -1
  89. package/lib/signatureSets/index.js +9 -8
  90. package/lib/signatureSets/index.js.map +1 -1
  91. package/lib/signatureSets/indexedAttestation.d.ts +4 -3
  92. package/lib/signatureSets/indexedAttestation.d.ts.map +1 -1
  93. package/lib/signatureSets/indexedAttestation.js +6 -6
  94. package/lib/signatureSets/indexedAttestation.js.map +1 -1
  95. package/lib/signatureSets/indexedPayloadAttestation.d.ts +6 -0
  96. package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -0
  97. package/lib/signatureSets/indexedPayloadAttestation.js +11 -0
  98. package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -0
  99. package/lib/signatureSets/proposer.d.ts +5 -4
  100. package/lib/signatureSets/proposer.d.ts.map +1 -1
  101. package/lib/signatureSets/proposer.js +12 -12
  102. package/lib/signatureSets/proposer.js.map +1 -1
  103. package/lib/signatureSets/proposerSlashings.d.ts +3 -2
  104. package/lib/signatureSets/proposerSlashings.d.ts.map +1 -1
  105. package/lib/signatureSets/proposerSlashings.js +4 -5
  106. package/lib/signatureSets/proposerSlashings.js.map +1 -1
  107. package/lib/signatureSets/randao.d.ts +3 -2
  108. package/lib/signatureSets/randao.d.ts.map +1 -1
  109. package/lib/signatureSets/randao.js +4 -5
  110. package/lib/signatureSets/randao.js.map +1 -1
  111. package/lib/signatureSets/voluntaryExits.d.ts +4 -3
  112. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  113. package/lib/signatureSets/voluntaryExits.js +6 -7
  114. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  115. package/lib/slot/index.d.ts +2 -1
  116. package/lib/slot/index.d.ts.map +1 -1
  117. package/lib/slot/index.js +6 -2
  118. package/lib/slot/index.js.map +1 -1
  119. package/lib/slot/upgradeStateToAltair.js +1 -1
  120. package/lib/slot/upgradeStateToAltair.js.map +1 -1
  121. package/lib/slot/upgradeStateToGloas.d.ts +0 -1
  122. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  123. package/lib/slot/upgradeStateToGloas.js +47 -5
  124. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  125. package/lib/stateTransition.js +5 -4
  126. package/lib/stateTransition.js.map +1 -1
  127. package/lib/util/electra.d.ts +5 -5
  128. package/lib/util/electra.d.ts.map +1 -1
  129. package/lib/util/electra.js +2 -1
  130. package/lib/util/electra.js.map +1 -1
  131. package/lib/util/epoch.d.ts +3 -3
  132. package/lib/util/epoch.d.ts.map +1 -1
  133. package/lib/util/epoch.js.map +1 -1
  134. package/lib/util/execution.d.ts +1 -16
  135. package/lib/util/execution.d.ts.map +1 -1
  136. package/lib/util/execution.js +1 -44
  137. package/lib/util/execution.js.map +1 -1
  138. package/lib/util/gloas.d.ts +11 -0
  139. package/lib/util/gloas.d.ts.map +1 -0
  140. package/lib/util/gloas.js +35 -0
  141. package/lib/util/gloas.js.map +1 -0
  142. package/lib/util/seed.d.ts +5 -1
  143. package/lib/util/seed.d.ts.map +1 -1
  144. package/lib/util/seed.js +33 -1
  145. package/lib/util/seed.js.map +1 -1
  146. package/lib/util/validator.d.ts +2 -2
  147. package/lib/util/validator.d.ts.map +1 -1
  148. package/lib/util/validator.js +14 -1
  149. package/lib/util/validator.js.map +1 -1
  150. package/package.json +6 -6
  151. package/src/block/index.ts +32 -15
  152. package/src/block/isValidIndexedAttestation.ts +5 -2
  153. package/src/block/isValidIndexedPayloadAttestation.ts +23 -0
  154. package/src/block/processAttestationPhase0.ts +13 -2
  155. package/src/block/processAttestationsAltair.ts +63 -6
  156. package/src/block/processAttesterSlashing.ts +6 -3
  157. package/src/block/processConsolidationRequest.ts +6 -5
  158. package/src/block/processDepositRequest.ts +5 -2
  159. package/src/block/processExecutionPayload.ts +8 -14
  160. package/src/block/processExecutionPayloadBid.ts +120 -0
  161. package/src/block/processExecutionPayloadEnvelope.ts +181 -0
  162. package/src/block/processOperations.ts +16 -4
  163. package/src/block/processPayloadAttestation.ts +25 -0
  164. package/src/block/processProposerSlashing.ts +25 -4
  165. package/src/block/processRandao.ts +1 -1
  166. package/src/block/processSyncCommittee.ts +4 -3
  167. package/src/block/processVoluntaryExit.ts +2 -2
  168. package/src/block/processWithdrawalRequest.ts +4 -4
  169. package/src/block/processWithdrawals.ts +118 -27
  170. package/src/cache/epochCache.ts +58 -1
  171. package/src/epoch/index.ts +12 -0
  172. package/src/epoch/processBuilderPendingPayments.ts +31 -0
  173. package/src/index.ts +2 -0
  174. package/src/signatureSets/attesterSlashings.ts +7 -3
  175. package/src/signatureSets/index.ts +10 -6
  176. package/src/signatureSets/indexedAttestation.ts +14 -3
  177. package/src/signatureSets/indexedPayloadAttestation.ts +24 -0
  178. package/src/signatureSets/proposer.ts +13 -7
  179. package/src/signatureSets/proposerSlashings.ts +5 -3
  180. package/src/signatureSets/randao.ts +13 -5
  181. package/src/signatureSets/voluntaryExits.ts +7 -4
  182. package/src/slot/index.ts +11 -3
  183. package/src/slot/upgradeStateToAltair.ts +2 -1
  184. package/src/slot/upgradeStateToGloas.ts +49 -5
  185. package/src/stateTransition.ts +5 -5
  186. package/src/util/electra.ts +15 -6
  187. package/src/util/epoch.ts +6 -3
  188. package/src/util/execution.ts +0 -60
  189. package/src/util/gloas.ts +58 -0
  190. package/src/util/seed.ts +57 -1
  191. package/src/util/validator.ts +21 -2
@@ -10,7 +10,8 @@ import {
10
10
  } from "@lodestar/params";
11
11
  import {ValidatorIndex, capella, ssz} from "@lodestar/types";
12
12
  import {MapDef, toRootHex} from "@lodestar/utils";
13
- import {CachedBeaconStateCapella, CachedBeaconStateElectra} from "../types.js";
13
+ import {CachedBeaconStateCapella, CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
14
+ import {isBuilderPaymentWithdrawable, isParentBlockFull} from "../util/gloas.ts";
14
15
  import {
15
16
  decreaseBalance,
16
17
  getMaxEffectiveBalance,
@@ -21,31 +22,48 @@ import {
21
22
 
22
23
  export function processWithdrawals(
23
24
  fork: ForkSeq,
24
- state: CachedBeaconStateCapella | CachedBeaconStateElectra,
25
- payload: capella.FullOrBlindedExecutionPayload
25
+ state: CachedBeaconStateCapella | CachedBeaconStateElectra | CachedBeaconStateGloas,
26
+ payload?: capella.FullOrBlindedExecutionPayload
26
27
  ): void {
28
+ // Return early if the parent block is empty
29
+ if (fork >= ForkSeq.gloas && !isParentBlockFull(state as CachedBeaconStateGloas)) {
30
+ return;
31
+ }
32
+
27
33
  // processedPartialWithdrawalsCount is withdrawals coming from EL since electra (EIP-7002)
28
- const {withdrawals: expectedWithdrawals, processedPartialWithdrawalsCount} = getExpectedWithdrawals(fork, state);
34
+ // processedBuilderWithdrawalsCount is withdrawals coming from builder payment since gloas (EIP-7732)
35
+ const {
36
+ withdrawals: expectedWithdrawals,
37
+ processedPartialWithdrawalsCount,
38
+ processedBuilderWithdrawalsCount,
39
+ } = getExpectedWithdrawals(fork, state);
29
40
  const numWithdrawals = expectedWithdrawals.length;
30
41
 
31
- if (isCapellaPayloadHeader(payload)) {
32
- const expectedWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(expectedWithdrawals);
33
- const actualWithdrawalsRoot = payload.withdrawalsRoot;
34
- if (!byteArrayEquals(expectedWithdrawalsRoot, actualWithdrawalsRoot)) {
35
- throw Error(
36
- `Invalid withdrawalsRoot of executionPayloadHeader, expected=${toRootHex(
37
- expectedWithdrawalsRoot
38
- )}, actual=${toRootHex(actualWithdrawalsRoot)}`
39
- );
40
- }
41
- } else {
42
- if (expectedWithdrawals.length !== payload.withdrawals.length) {
43
- throw Error(`Invalid withdrawals length expected=${numWithdrawals} actual=${payload.withdrawals.length}`);
42
+ // After gloas, withdrawals are verified later in processExecutionPayloadEnvelope
43
+ if (fork < ForkSeq.gloas) {
44
+ if (payload === undefined) {
45
+ throw Error("payload is required for pre-gloas processWithdrawals");
44
46
  }
45
- for (let i = 0; i < numWithdrawals; i++) {
46
- const withdrawal = expectedWithdrawals[i];
47
- if (!ssz.capella.Withdrawal.equals(withdrawal, payload.withdrawals[i])) {
48
- throw Error(`Withdrawal mismatch at index=${i}`);
47
+
48
+ if (isCapellaPayloadHeader(payload)) {
49
+ const expectedWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(expectedWithdrawals);
50
+ const actualWithdrawalsRoot = payload.withdrawalsRoot;
51
+ if (!byteArrayEquals(expectedWithdrawalsRoot, actualWithdrawalsRoot)) {
52
+ throw Error(
53
+ `Invalid withdrawalsRoot of executionPayloadHeader, expected=${toRootHex(
54
+ expectedWithdrawalsRoot
55
+ )}, actual=${toRootHex(actualWithdrawalsRoot)}`
56
+ );
57
+ }
58
+ } else {
59
+ if (expectedWithdrawals.length !== payload.withdrawals.length) {
60
+ throw Error(`Invalid withdrawals length expected=${numWithdrawals} actual=${payload.withdrawals.length}`);
61
+ }
62
+ for (let i = 0; i < numWithdrawals; i++) {
63
+ const withdrawal = expectedWithdrawals[i];
64
+ if (!ssz.capella.Withdrawal.equals(withdrawal, payload.withdrawals[i])) {
65
+ throw Error(`Withdrawal mismatch at index=${i}`);
66
+ }
49
67
  }
50
68
  }
51
69
  }
@@ -62,6 +80,24 @@ export function processWithdrawals(
62
80
  );
63
81
  }
64
82
 
83
+ if (fork >= ForkSeq.gloas) {
84
+ const stateGloas = state as CachedBeaconStateGloas;
85
+ stateGloas.latestWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(expectedWithdrawals);
86
+
87
+ const unprocessedWithdrawals = stateGloas.builderPendingWithdrawals
88
+ .getAllReadonly()
89
+ .slice(0, processedBuilderWithdrawalsCount)
90
+ .filter((w) => !isBuilderPaymentWithdrawable(stateGloas, w));
91
+ const remainingWithdrawals = stateGloas.builderPendingWithdrawals
92
+ .sliceFrom(processedBuilderWithdrawalsCount)
93
+ .getAllReadonly();
94
+
95
+ stateGloas.builderPendingWithdrawals = ssz.gloas.BeaconState.fields.builderPendingWithdrawals.toViewDU([
96
+ ...unprocessedWithdrawals,
97
+ ...remainingWithdrawals,
98
+ ]);
99
+ }
100
+
65
101
  // Update the nextWithdrawalIndex
66
102
  const latestWithdrawal = expectedWithdrawals.at(-1);
67
103
  if (latestWithdrawal) {
@@ -82,11 +118,12 @@ export function processWithdrawals(
82
118
 
83
119
  export function getExpectedWithdrawals(
84
120
  fork: ForkSeq,
85
- state: CachedBeaconStateCapella | CachedBeaconStateElectra
121
+ state: CachedBeaconStateCapella | CachedBeaconStateElectra | CachedBeaconStateGloas
86
122
  ): {
87
123
  withdrawals: capella.Withdrawal[];
88
124
  sampledValidators: number;
89
125
  processedPartialWithdrawalsCount: number;
126
+ processedBuilderWithdrawalsCount: number;
90
127
  } {
91
128
  if (fork < ForkSeq.capella) {
92
129
  throw new Error(`getExpectedWithdrawals not supported at forkSeq=${fork} < ForkSeq.capella`);
@@ -99,17 +136,71 @@ export function getExpectedWithdrawals(
99
136
  const withdrawals: capella.Withdrawal[] = [];
100
137
  const withdrawnBalances = new MapDef<ValidatorIndex, number>(() => 0);
101
138
  const isPostElectra = fork >= ForkSeq.electra;
139
+ const isPostGloas = fork >= ForkSeq.gloas;
102
140
  // partialWithdrawalsCount is withdrawals coming from EL since electra (EIP-7002)
103
141
  let processedPartialWithdrawalsCount = 0;
142
+ // builderWithdrawalsCount is withdrawals coming from builder payments since Gloas (EIP-7732)
143
+ let processedBuilderWithdrawalsCount = 0;
144
+
145
+ if (isPostGloas) {
146
+ const stateGloas = state as CachedBeaconStateGloas;
147
+
148
+ const allBuilderPendingWithdrawals =
149
+ stateGloas.builderPendingWithdrawals.length <= MAX_WITHDRAWALS_PER_PAYLOAD
150
+ ? stateGloas.builderPendingWithdrawals.getAllReadonly()
151
+ : null;
152
+
153
+ for (let i = 0; i < stateGloas.builderPendingWithdrawals.length; i++) {
154
+ const withdrawal = allBuilderPendingWithdrawals
155
+ ? allBuilderPendingWithdrawals[i]
156
+ : stateGloas.builderPendingWithdrawals.getReadonly(i);
157
+
158
+ if (withdrawal.withdrawableEpoch > epoch || withdrawals.length + 1 === MAX_WITHDRAWALS_PER_PAYLOAD) {
159
+ break;
160
+ }
161
+
162
+ if (isBuilderPaymentWithdrawable(stateGloas, withdrawal)) {
163
+ const totalWithdrawn = withdrawnBalances.getOrDefault(withdrawal.builderIndex);
164
+ const balance = state.balances.get(withdrawal.builderIndex) - totalWithdrawn;
165
+ const builder = state.validators.get(withdrawal.builderIndex);
166
+
167
+ let withdrawableBalance = 0;
168
+
169
+ if (builder.slashed) {
170
+ withdrawableBalance = balance < withdrawal.amount ? balance : withdrawal.amount;
171
+ } else if (balance > MIN_ACTIVATION_BALANCE) {
172
+ withdrawableBalance =
173
+ balance - MIN_ACTIVATION_BALANCE < withdrawal.amount ? balance - MIN_ACTIVATION_BALANCE : withdrawal.amount;
174
+ }
175
+
176
+ if (withdrawableBalance > 0) {
177
+ withdrawals.push({
178
+ index: withdrawalIndex,
179
+ validatorIndex: withdrawal.builderIndex,
180
+ address: withdrawal.feeRecipient,
181
+ amount: BigInt(withdrawableBalance),
182
+ });
183
+ withdrawalIndex++;
184
+ withdrawnBalances.set(withdrawal.builderIndex, totalWithdrawn + withdrawableBalance);
185
+ }
186
+ }
187
+ processedBuilderWithdrawalsCount++;
188
+ }
189
+ }
104
190
 
105
191
  if (isPostElectra) {
192
+ // In pre-gloas, partialWithdrawalBound == MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP
193
+ const partialWithdrawalBound = Math.min(
194
+ withdrawals.length + MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
195
+ MAX_WITHDRAWALS_PER_PAYLOAD - 1
196
+ );
106
197
  const stateElectra = state as CachedBeaconStateElectra;
107
198
 
108
199
  // MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP = 8, PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728 so we should only call getAllReadonly() if it makes sense
109
200
  // pendingPartialWithdrawals comes from EIP-7002 smart contract where it takes fee so it's more likely than not validator is in correct condition to withdraw
110
201
  // also we may break early if withdrawableEpoch > epoch
111
202
  const allPendingPartialWithdrawals =
112
- stateElectra.pendingPartialWithdrawals.length <= MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP
203
+ stateElectra.pendingPartialWithdrawals.length <= partialWithdrawalBound
113
204
  ? stateElectra.pendingPartialWithdrawals.getAllReadonly()
114
205
  : null;
115
206
 
@@ -118,7 +209,7 @@ export function getExpectedWithdrawals(
118
209
  const withdrawal = allPendingPartialWithdrawals
119
210
  ? allPendingPartialWithdrawals[i]
120
211
  : stateElectra.pendingPartialWithdrawals.getReadonly(i);
121
- if (withdrawal.withdrawableEpoch > epoch || withdrawals.length === MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP) {
212
+ if (withdrawal.withdrawableEpoch > epoch || withdrawals.length === partialWithdrawalBound) {
122
213
  break;
123
214
  }
124
215
 
@@ -147,11 +238,11 @@ export function getExpectedWithdrawals(
147
238
  }
148
239
  }
149
240
 
150
- const bound = Math.min(validators.length, MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP);
241
+ const withdrawalBound = Math.min(validators.length, MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP);
151
242
  let n = 0;
152
243
  // Just run a bounded loop max iterating over all withdrawals
153
244
  // however breaks out once we have MAX_WITHDRAWALS_PER_PAYLOAD
154
- for (n = 0; n < bound; n++) {
245
+ for (n = 0; n < withdrawalBound; n++) {
155
246
  // Get next validator in turn
156
247
  const validatorIndex = (nextWithdrawalValidatorIndex + n) % validators.length;
157
248
 
@@ -203,5 +294,5 @@ export function getExpectedWithdrawals(
203
294
  }
204
295
  }
205
296
 
206
- return {withdrawals, sampledValidators: n, processedPartialWithdrawalsCount};
297
+ return {withdrawals, sampledValidators: n, processedPartialWithdrawalsCount, processedBuilderWithdrawalsCount};
207
298
  }
@@ -24,6 +24,7 @@ import {
24
24
  SyncPeriod,
25
25
  ValidatorIndex,
26
26
  electra,
27
+ gloas,
27
28
  phase0,
28
29
  } from "@lodestar/types";
29
30
  import {LodestarError} from "@lodestar/utils";
@@ -46,6 +47,7 @@ import {
46
47
  getSeed,
47
48
  isActiveValidator,
48
49
  isAggregatorFromCommitteeLength,
50
+ naiveGetPayloadTimlinessCommitteeIndices,
49
51
  } from "../util/index.js";
50
52
  import {computeBaseRewardPerIncrement, computeSyncParticipantReward} from "../util/syncCommittee.js";
51
53
  import {sumTargetUnslashedBalanceIncrements} from "../util/targetUnslashedBalance.js";
@@ -59,7 +61,7 @@ import {
59
61
  computeSyncCommitteeCache,
60
62
  getSyncCommitteeCache,
61
63
  } from "./syncCommitteeCache.js";
62
- import {BeaconStateAllForks, BeaconStateAltair} from "./types.js";
64
+ import {BeaconStateAllForks, BeaconStateAltair, BeaconStateGloas} from "./types.js";
63
65
 
64
66
  /** `= PROPOSER_WEIGHT / (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)` */
65
67
  export const PROPOSER_WEIGHT_FACTOR = PROPOSER_WEIGHT / (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT);
@@ -238,6 +240,10 @@ export class EpochCache {
238
240
  /** TODO: Indexed SyncCommitteeCache */
239
241
  nextSyncCommitteeIndexed: SyncCommitteeCache;
240
242
 
243
+ // TODO GLOAS: See if we need to cached PTC for prev/next epoch
244
+ // PTC for current epoch
245
+ payloadTimelinessCommittee: ValidatorIndex[][];
246
+
241
247
  // TODO: Helper stats
242
248
  syncPeriod: SyncPeriod;
243
249
 
@@ -276,6 +282,7 @@ export class EpochCache {
276
282
  previousTargetUnslashedBalanceIncrements: number;
277
283
  currentSyncCommitteeIndexed: SyncCommitteeCache;
278
284
  nextSyncCommitteeIndexed: SyncCommitteeCache;
285
+ payloadTimelinessCommittee: ValidatorIndex[][];
279
286
  epoch: Epoch;
280
287
  syncPeriod: SyncPeriod;
281
288
  }) {
@@ -307,6 +314,7 @@ export class EpochCache {
307
314
  this.previousTargetUnslashedBalanceIncrements = data.previousTargetUnslashedBalanceIncrements;
308
315
  this.currentSyncCommitteeIndexed = data.currentSyncCommitteeIndexed;
309
316
  this.nextSyncCommitteeIndexed = data.nextSyncCommitteeIndexed;
317
+ this.payloadTimelinessCommittee = data.payloadTimelinessCommittee;
310
318
  this.epoch = data.epoch;
311
319
  this.syncPeriod = data.syncPeriod;
312
320
  }
@@ -485,6 +493,17 @@ export class EpochCache {
485
493
  nextSyncCommitteeIndexed = new SyncCommitteeCacheEmpty();
486
494
  }
487
495
 
496
+ // Compute PTC for this epoch
497
+ let payloadTimelinessCommittee: ValidatorIndex[][] = [];
498
+ if (currentEpoch >= config.GLOAS_FORK_EPOCH) {
499
+ payloadTimelinessCommittee = naiveGetPayloadTimlinessCommitteeIndices(
500
+ state as BeaconStateGloas,
501
+ currentShuffling,
502
+ effectiveBalanceIncrements,
503
+ currentEpoch
504
+ );
505
+ }
506
+
488
507
  // Precompute churnLimit for efficient initiateValidatorExit() during block proposing MUST be recompute everytime the
489
508
  // active validator indices set changes in size. Validators change active status only when:
490
509
  // - validator.activation_epoch is set. Only changes in process_registry_updates() if validator can be activated. If
@@ -559,6 +578,7 @@ export class EpochCache {
559
578
  currentTargetUnslashedBalanceIncrements,
560
579
  currentSyncCommitteeIndexed,
561
580
  nextSyncCommitteeIndexed,
581
+ payloadTimelinessCommittee: payloadTimelinessCommittee,
562
582
  epoch: currentEpoch,
563
583
  syncPeriod: computeSyncPeriodAtEpoch(currentEpoch),
564
584
  });
@@ -605,6 +625,7 @@ export class EpochCache {
605
625
  currentTargetUnslashedBalanceIncrements: this.currentTargetUnslashedBalanceIncrements,
606
626
  currentSyncCommitteeIndexed: this.currentSyncCommitteeIndexed,
607
627
  nextSyncCommitteeIndexed: this.nextSyncCommitteeIndexed,
628
+ payloadTimelinessCommittee: this.payloadTimelinessCommittee,
608
629
  epoch: this.epoch,
609
630
  syncPeriod: this.syncPeriod,
610
631
  });
@@ -750,6 +771,14 @@ export class EpochCache {
750
771
  const epochAfterUpcoming = upcomingEpoch + 1;
751
772
 
752
773
  this.proposersPrevEpoch = this.proposers;
774
+ if (upcomingEpoch >= this.config.GLOAS_FORK_EPOCH) {
775
+ this.payloadTimelinessCommittee = naiveGetPayloadTimlinessCommitteeIndices(
776
+ state as BeaconStateGloas,
777
+ this.currentShuffling,
778
+ this.effectiveBalanceIncrements,
779
+ upcomingEpoch
780
+ );
781
+ }
753
782
  if (upcomingEpoch >= this.config.FULU_FORK_EPOCH) {
754
783
  // Populate proposer cache with lookahead from state
755
784
  const proposerLookahead = (state as CachedBeaconStateFulu).proposerLookahead.getAll();
@@ -1151,6 +1180,34 @@ export class EpochCache {
1151
1180
  isPostElectra(): boolean {
1152
1181
  return this.epoch >= this.config.ELECTRA_FORK_EPOCH;
1153
1182
  }
1183
+
1184
+ getPayloadTimelinessCommittee(slot: Slot): ValidatorIndex[] {
1185
+ const epoch = computeEpochAtSlot(slot);
1186
+
1187
+ if (epoch < this.config.GLOAS_FORK_EPOCH) {
1188
+ throw new Error("Payload Timeliness Committee is not available before gloas fork");
1189
+ }
1190
+
1191
+ if (epoch === this.epoch) {
1192
+ return this.payloadTimelinessCommittee[slot % SLOTS_PER_EPOCH];
1193
+ }
1194
+
1195
+ throw new Error(`Payload Timeliness Committee is not available for slot=${slot}`);
1196
+ }
1197
+
1198
+ getIndexedPayloadAttestation(
1199
+ slot: Slot,
1200
+ payloadAttestation: gloas.PayloadAttestation
1201
+ ): gloas.IndexedPayloadAttestation {
1202
+ const payloadTimelinessCommittee = this.getPayloadTimelinessCommittee(slot);
1203
+ const attestingIndices = payloadAttestation.aggregationBits.intersectValues(payloadTimelinessCommittee);
1204
+
1205
+ return {
1206
+ attestingIndices: attestingIndices.sort((a, b) => a - b),
1207
+ data: payloadAttestation.data,
1208
+ signature: payloadAttestation.signature,
1209
+ };
1210
+ }
1154
1211
  }
1155
1212
 
1156
1213
  function getEffectiveBalanceIncrementsByteLen(validatorCount: number): number {
@@ -12,9 +12,11 @@ import {
12
12
  CachedBeaconStateCapella,
13
13
  CachedBeaconStateElectra,
14
14
  CachedBeaconStateFulu,
15
+ CachedBeaconStateGloas,
15
16
  CachedBeaconStatePhase0,
16
17
  EpochTransitionCache,
17
18
  } from "../types.js";
19
+ import {processBuilderPendingPayments} from "./processBuilderPendingPayments.ts";
18
20
  import {processEffectiveBalanceUpdates} from "./processEffectiveBalanceUpdates.js";
19
21
  import {processEth1DataReset} from "./processEth1DataReset.js";
20
22
  import {processHistoricalRootsUpdate} from "./processHistoricalRootsUpdate.js";
@@ -53,6 +55,7 @@ export {
53
55
  processPendingDeposits,
54
56
  processPendingConsolidations,
55
57
  processProposerLookahead,
58
+ processBuilderPendingPayments,
56
59
  };
57
60
 
58
61
  export {computeUnrealizedCheckpoints} from "./computeUnrealizedCheckpoints.js";
@@ -78,6 +81,7 @@ export enum EpochTransitionStep {
78
81
  processPendingDeposits = "processPendingDeposits",
79
82
  processPendingConsolidations = "processPendingConsolidations",
80
83
  processProposerLookahead = "processProposerLookahead",
84
+ processBuilderPendingPayments = "processBuilderPendingPayments",
81
85
  }
82
86
 
83
87
  export function processEpoch(
@@ -154,6 +158,14 @@ export function processEpoch(
154
158
  }
155
159
  }
156
160
 
161
+ if (fork >= ForkSeq.gloas) {
162
+ const timer = metrics?.epochTransitionStepTime.startTimer({
163
+ step: EpochTransitionStep.processBuilderPendingPayments,
164
+ });
165
+ processBuilderPendingPayments(state as CachedBeaconStateGloas);
166
+ timer?.();
167
+ }
168
+
157
169
  {
158
170
  const timer = metrics?.epochTransitionStepTime.startTimer({
159
171
  step: EpochTransitionStep.processEffectiveBalanceUpdates,
@@ -0,0 +1,31 @@
1
+ import {SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {ssz} from "@lodestar/types";
3
+ import {CachedBeaconStateGloas} from "../types.ts";
4
+ import {computeExitEpochAndUpdateChurn} from "../util/epoch.ts";
5
+ import {getBuilderPaymentQuorumThreshold} from "../util/gloas.ts";
6
+
7
+ /**
8
+ * Processes the builder pending payments from the previous epoch.
9
+ */
10
+ export function processBuilderPendingPayments(state: CachedBeaconStateGloas): void {
11
+ const quorum = getBuilderPaymentQuorumThreshold(state);
12
+
13
+ for (let i = 0; i < SLOTS_PER_EPOCH; i++) {
14
+ 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
+
19
+ state.builderPendingWithdrawals.push(payment.withdrawal);
20
+ }
21
+ }
22
+
23
+ // TODO GLOAS: Optimize this
24
+ for (let i = 0; i < state.builderPendingPayments.length; i++) {
25
+ if (i < SLOTS_PER_EPOCH) {
26
+ state.builderPendingPayments.set(i, state.builderPendingPayments.get(i + SLOTS_PER_EPOCH).clone());
27
+ } else {
28
+ state.builderPendingPayments.set(i, ssz.gloas.BuilderPendingPayment.defaultViewDU());
29
+ }
30
+ }
31
+ }
package/src/index.ts CHANGED
@@ -52,6 +52,7 @@ export type {
52
52
  BeaconStateElectra,
53
53
  BeaconStateExecutions,
54
54
  BeaconStateFulu,
55
+ BeaconStateGloas,
55
56
  // Non-cached states
56
57
  BeaconStatePhase0,
57
58
  CachedBeaconStateAllForks,
@@ -62,6 +63,7 @@ export type {
62
63
  CachedBeaconStateElectra,
63
64
  CachedBeaconStateExecutions,
64
65
  CachedBeaconStateFulu,
66
+ CachedBeaconStateGloas,
65
67
  CachedBeaconStatePhase0,
66
68
  } from "./types.js";
67
69
  export * from "./util/index.js";
@@ -1,29 +1,33 @@
1
1
  import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
2
2
  import {AttesterSlashing, IndexedAttestationBigint, SignedBeaconBlock, ssz} from "@lodestar/types";
3
+ import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
3
4
  import {CachedBeaconStateAllForks} from "../types.js";
4
5
  import {ISignatureSet, SignatureSetType, computeSigningRoot, computeStartSlotAtEpoch} from "../util/index.js";
5
6
 
6
7
  /** Get signature sets from all AttesterSlashing objects in a block */
7
8
  export function getAttesterSlashingsSignatureSets(
9
+ index2pubkey: Index2PubkeyCache,
8
10
  state: CachedBeaconStateAllForks,
9
11
  signedBlock: SignedBeaconBlock
10
12
  ): ISignatureSet[] {
11
13
  return signedBlock.message.body.attesterSlashings.flatMap((attesterSlashing) =>
12
- getAttesterSlashingSignatureSets(state, attesterSlashing)
14
+ getAttesterSlashingSignatureSets(index2pubkey, state, attesterSlashing)
13
15
  );
14
16
  }
15
17
 
16
18
  /** Get signature sets from a single AttesterSlashing object */
17
19
  export function getAttesterSlashingSignatureSets(
20
+ index2pubkey: Index2PubkeyCache,
18
21
  state: CachedBeaconStateAllForks,
19
22
  attesterSlashing: AttesterSlashing
20
23
  ): ISignatureSet[] {
21
24
  return [attesterSlashing.attestation1, attesterSlashing.attestation2].map((attestation) =>
22
- getIndexedAttestationBigintSignatureSet(state, attestation)
25
+ getIndexedAttestationBigintSignatureSet(index2pubkey, state, attestation)
23
26
  );
24
27
  }
25
28
 
26
29
  export function getIndexedAttestationBigintSignatureSet(
30
+ index2pubkey: Index2PubkeyCache,
27
31
  state: CachedBeaconStateAllForks,
28
32
  indexedAttestation: IndexedAttestationBigint
29
33
  ): ISignatureSet {
@@ -32,7 +36,7 @@ export function getIndexedAttestationBigintSignatureSet(
32
36
 
33
37
  return {
34
38
  type: SignatureSetType.aggregate,
35
- pubkeys: indexedAttestation.attestingIndices.map((i) => state.epochCtx.index2pubkey[i]),
39
+ pubkeys: indexedAttestation.attestingIndices.map((i) => index2pubkey[i]),
36
40
  signingRoot: computeSigningRoot(ssz.phase0.AttestationDataBigint, indexedAttestation.data, domain),
37
41
  signature: indexedAttestation.signature,
38
42
  };
@@ -1,6 +1,7 @@
1
1
  import {ForkSeq} from "@lodestar/params";
2
2
  import {IndexedAttestation, SignedBeaconBlock, altair, capella} from "@lodestar/types";
3
3
  import {getSyncCommitteeSignatureSet} from "../block/processSyncCommittee.js";
4
+ import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
4
5
  import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "../types.js";
5
6
  import {ISignatureSet} from "../util/index.js";
6
7
  import {getAttesterSlashingsSignatureSets} from "./attesterSlashings.js";
@@ -14,6 +15,7 @@ import {getVoluntaryExitsSignatureSets} from "./voluntaryExits.js";
14
15
  export * from "./attesterSlashings.js";
15
16
  export * from "./blsToExecutionChange.js";
16
17
  export * from "./indexedAttestation.js";
18
+ export * from "./indexedPayloadAttestation.js";
17
19
  export * from "./proposer.js";
18
20
  export * from "./proposerSlashings.js";
19
21
  export * from "./randao.js";
@@ -24,6 +26,7 @@ export * from "./voluntaryExits.js";
24
26
  * Deposits are not included because they can legally have invalid signatures.
25
27
  */
26
28
  export function getBlockSignatureSets(
29
+ index2pubkey: Index2PubkeyCache,
27
30
  state: CachedBeaconStateAllForks,
28
31
  signedBlock: SignedBeaconBlock,
29
32
  indexedAttestations: IndexedAttestation[],
@@ -36,20 +39,21 @@ export function getBlockSignatureSets(
36
39
  const fork = state.config.getForkSeq(signedBlock.message.slot);
37
40
 
38
41
  const signatureSets = [
39
- getRandaoRevealSignatureSet(state, signedBlock.message),
40
- ...getProposerSlashingsSignatureSets(state, signedBlock),
41
- ...getAttesterSlashingsSignatureSets(state, signedBlock),
42
- ...getAttestationsSignatureSets(state, signedBlock, indexedAttestations),
43
- ...getVoluntaryExitsSignatureSets(state, signedBlock),
42
+ getRandaoRevealSignatureSet(index2pubkey, state, signedBlock.message),
43
+ ...getProposerSlashingsSignatureSets(index2pubkey, state, signedBlock),
44
+ ...getAttesterSlashingsSignatureSets(index2pubkey, state, signedBlock),
45
+ ...getAttestationsSignatureSets(index2pubkey, state, signedBlock, indexedAttestations),
46
+ ...getVoluntaryExitsSignatureSets(index2pubkey, state, signedBlock),
44
47
  ];
45
48
 
46
49
  if (!opts?.skipProposerSignature) {
47
- signatureSets.push(getBlockProposerSignatureSet(state, signedBlock));
50
+ signatureSets.push(getBlockProposerSignatureSet(index2pubkey, state, signedBlock));
48
51
  }
49
52
 
50
53
  // Only after altair fork, validate tSyncCommitteeSignature
51
54
  if (fork >= ForkSeq.altair) {
52
55
  const syncCommitteeSignatureSet = getSyncCommitteeSignatureSet(
56
+ index2pubkey,
53
57
  state as CachedBeaconStateAltair,
54
58
  (signedBlock as altair.SignedBeaconBlock).message
55
59
  );
@@ -1,5 +1,6 @@
1
1
  import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
2
2
  import {IndexedAttestation, SignedBeaconBlock, phase0, ssz} from "@lodestar/types";
3
+ import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
3
4
  import {CachedBeaconStateAllForks} from "../types.js";
4
5
  import {
5
6
  ISignatureSet,
@@ -19,25 +20,33 @@ export function getAttestationDataSigningRoot(
19
20
  }
20
21
 
21
22
  export function getAttestationWithIndicesSignatureSet(
23
+ index2pubkey: Index2PubkeyCache,
22
24
  state: CachedBeaconStateAllForks,
23
25
  attestation: Pick<phase0.Attestation, "data" | "signature">,
24
26
  attestingIndices: number[]
25
27
  ): ISignatureSet {
26
28
  return createAggregateSignatureSetFromComponents(
27
- attestingIndices.map((i) => state.epochCtx.index2pubkey[i]),
29
+ attestingIndices.map((i) => index2pubkey[i]),
28
30
  getAttestationDataSigningRoot(state, attestation.data),
29
31
  attestation.signature
30
32
  );
31
33
  }
32
34
 
33
35
  export function getIndexedAttestationSignatureSet(
36
+ index2pubkey: Index2PubkeyCache,
34
37
  state: CachedBeaconStateAllForks,
35
38
  indexedAttestation: IndexedAttestation
36
39
  ): ISignatureSet {
37
- return getAttestationWithIndicesSignatureSet(state, indexedAttestation, indexedAttestation.attestingIndices);
40
+ return getAttestationWithIndicesSignatureSet(
41
+ index2pubkey,
42
+ state,
43
+ indexedAttestation,
44
+ indexedAttestation.attestingIndices
45
+ );
38
46
  }
39
47
 
40
48
  export function getAttestationsSignatureSets(
49
+ index2pubkey: Index2PubkeyCache,
41
50
  state: CachedBeaconStateAllForks,
42
51
  signedBlock: SignedBeaconBlock,
43
52
  indexedAttestations: IndexedAttestation[]
@@ -47,5 +56,7 @@ export function getAttestationsSignatureSets(
47
56
  `Indexed attestations length mismatch: got ${indexedAttestations.length}, expected ${signedBlock.message.body.attestations.length}`
48
57
  );
49
58
  }
50
- return indexedAttestations.map((indexedAttestation) => getIndexedAttestationSignatureSet(state, indexedAttestation));
59
+ return indexedAttestations.map((indexedAttestation) =>
60
+ getIndexedAttestationSignatureSet(index2pubkey, state, indexedAttestation)
61
+ );
51
62
  }
@@ -0,0 +1,24 @@
1
+ import {DOMAIN_PTC_ATTESTER} from "@lodestar/params";
2
+ import {gloas, ssz} from "@lodestar/types";
3
+ import {CachedBeaconStateGloas} from "../types.ts";
4
+ import {ISignatureSet, computeSigningRoot, createAggregateSignatureSetFromComponents} from "../util/index.ts";
5
+
6
+ export function getIndexedPayloadAttestationSignatureSet(
7
+ state: CachedBeaconStateGloas,
8
+ indexedPayloadAttestation: gloas.IndexedPayloadAttestation
9
+ ): ISignatureSet {
10
+ return createAggregateSignatureSetFromComponents(
11
+ indexedPayloadAttestation.attestingIndices.map((i) => state.epochCtx.index2pubkey[i]),
12
+ getPayloadAttestationDataSigningRoot(state, indexedPayloadAttestation.data),
13
+ indexedPayloadAttestation.signature
14
+ );
15
+ }
16
+
17
+ export function getPayloadAttestationDataSigningRoot(
18
+ state: CachedBeaconStateGloas,
19
+ data: gloas.PayloadAttestationData
20
+ ): Uint8Array {
21
+ const domain = state.config.getDomain(state.slot, DOMAIN_PTC_ATTESTER);
22
+
23
+ return computeSigningRoot(ssz.gloas.PayloadAttestationData, data, domain);
24
+ }