@lodestar/state-transition 1.35.0-dev.8ea34e52ba → 1.35.0-dev.901d719660

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 (283) hide show
  1. package/lib/block/externalData.d.ts.map +1 -0
  2. package/lib/block/index.d.ts +2 -2
  3. package/lib/block/index.d.ts.map +1 -0
  4. package/lib/block/index.js +2 -2
  5. package/lib/block/index.js.map +1 -1
  6. package/lib/block/initiateValidatorExit.d.ts.map +1 -0
  7. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -0
  8. package/lib/block/processAttestationPhase0.d.ts.map +1 -0
  9. package/lib/block/processAttestationPhase0.js +1 -2
  10. package/lib/block/processAttestationPhase0.js.map +1 -1
  11. package/lib/block/processAttestations.d.ts.map +1 -0
  12. package/lib/block/processAttestationsAltair.d.ts +1 -1
  13. package/lib/block/processAttestationsAltair.d.ts.map +1 -0
  14. package/lib/block/processAttestationsAltair.js +1 -1
  15. package/lib/block/processAttestationsAltair.js.map +1 -1
  16. package/lib/block/processAttesterSlashing.d.ts.map +1 -0
  17. package/lib/block/processAttesterSlashing.js.map +1 -1
  18. package/lib/block/processBlobKzgCommitments.d.ts.map +1 -0
  19. package/lib/block/processBlockHeader.d.ts.map +1 -0
  20. package/lib/block/processBlsToExecutionChange.d.ts.map +1 -0
  21. package/lib/block/processBlsToExecutionChange.js.map +1 -1
  22. package/lib/block/processConsolidationRequest.d.ts.map +1 -0
  23. package/lib/block/processConsolidationRequest.js.map +1 -1
  24. package/lib/block/processDeposit.d.ts +2 -2
  25. package/lib/block/processDeposit.d.ts.map +1 -0
  26. package/lib/block/processDeposit.js +1 -1
  27. package/lib/block/processDeposit.js.map +1 -1
  28. package/lib/block/processDepositRequest.d.ts.map +1 -0
  29. package/lib/block/processDepositRequest.js.map +1 -1
  30. package/lib/block/processEth1Data.d.ts.map +1 -0
  31. package/lib/block/processExecutionPayload.d.ts.map +1 -0
  32. package/lib/block/processExecutionPayload.js +3 -3
  33. package/lib/block/processExecutionPayload.js.map +1 -1
  34. package/lib/block/processOperations.d.ts.map +1 -0
  35. package/lib/block/processOperations.js.map +1 -1
  36. package/lib/block/processProposerSlashing.d.ts.map +1 -0
  37. package/lib/block/processRandao.d.ts.map +1 -0
  38. package/lib/block/processSyncCommittee.d.ts.map +1 -0
  39. package/lib/block/processSyncCommittee.js +1 -2
  40. package/lib/block/processSyncCommittee.js.map +1 -1
  41. package/lib/block/processVoluntaryExit.d.ts.map +1 -0
  42. package/lib/block/processWithdrawalRequest.d.ts.map +1 -0
  43. package/lib/block/processWithdrawalRequest.js.map +1 -1
  44. package/lib/block/processWithdrawals.d.ts.map +1 -0
  45. package/lib/block/processWithdrawals.js.map +1 -1
  46. package/lib/block/slashValidator.d.ts.map +1 -0
  47. package/lib/block/slashValidator.js.map +1 -1
  48. package/lib/block/types.d.ts.map +1 -0
  49. package/lib/cache/effectiveBalanceIncrements.d.ts.map +1 -0
  50. package/lib/cache/epochCache.d.ts.map +1 -0
  51. package/lib/cache/epochTransitionCache.d.ts.map +1 -0
  52. package/lib/cache/epochTransitionCache.js.map +1 -1
  53. package/lib/cache/pubkeyCache.d.ts.map +1 -0
  54. package/lib/cache/rewardCache.d.ts.map +1 -0
  55. package/lib/cache/stateCache.d.ts.map +1 -0
  56. package/lib/cache/syncCommitteeCache.d.ts.map +1 -0
  57. package/lib/cache/types.d.ts +2 -2
  58. package/lib/cache/types.d.ts.map +1 -0
  59. package/lib/constants/constants.d.ts.map +1 -0
  60. package/lib/constants/index.d.ts.map +1 -0
  61. package/lib/epoch/computeUnrealizedCheckpoints.d.ts.map +1 -0
  62. package/lib/epoch/getAttestationDeltas.d.ts.map +1 -0
  63. package/lib/epoch/getRewardsAndPenalties.d.ts.map +1 -0
  64. package/lib/epoch/index.d.ts.map +1 -0
  65. package/lib/epoch/index.js.map +1 -1
  66. package/lib/epoch/processEffectiveBalanceUpdates.d.ts.map +1 -0
  67. package/lib/epoch/processEth1DataReset.d.ts.map +1 -0
  68. package/lib/epoch/processHistoricalRootsUpdate.d.ts.map +1 -0
  69. package/lib/epoch/processHistoricalSummariesUpdate.d.ts.map +1 -0
  70. package/lib/epoch/processInactivityUpdates.d.ts.map +1 -0
  71. package/lib/epoch/processJustificationAndFinalization.d.ts.map +1 -0
  72. package/lib/epoch/processParticipationFlagUpdates.d.ts.map +1 -0
  73. package/lib/epoch/processParticipationRecordUpdates.d.ts.map +1 -0
  74. package/lib/epoch/processPendingAttestations.d.ts.map +1 -0
  75. package/lib/epoch/processPendingConsolidations.d.ts.map +1 -0
  76. package/lib/epoch/processPendingDeposits.d.ts.map +1 -0
  77. package/lib/epoch/processProposerLookahead.d.ts.map +1 -0
  78. package/lib/epoch/processRandaoMixesReset.d.ts.map +1 -0
  79. package/lib/epoch/processRegistryUpdates.d.ts.map +1 -0
  80. package/lib/epoch/processRewardsAndPenalties.d.ts.map +1 -0
  81. package/lib/epoch/processSlashings.d.ts.map +1 -0
  82. package/lib/epoch/processSlashings.js.map +1 -1
  83. package/lib/epoch/processSlashingsReset.d.ts.map +1 -0
  84. package/lib/epoch/processSyncCommitteeUpdates.d.ts.map +1 -0
  85. package/lib/index.d.ts +17 -17
  86. package/lib/index.d.ts.map +1 -0
  87. package/lib/index.js +16 -16
  88. package/lib/index.js.map +1 -1
  89. package/lib/metrics.d.ts.map +1 -0
  90. package/lib/signatureSets/attesterSlashings.d.ts.map +1 -0
  91. package/lib/signatureSets/blsToExecutionChange.d.ts.map +1 -0
  92. package/lib/signatureSets/blsToExecutionChange.js.map +1 -1
  93. package/lib/signatureSets/index.d.ts +1 -1
  94. package/lib/signatureSets/index.d.ts.map +1 -0
  95. package/lib/signatureSets/index.js +1 -1
  96. package/lib/signatureSets/index.js.map +1 -1
  97. package/lib/signatureSets/indexedAttestation.d.ts.map +1 -0
  98. package/lib/signatureSets/proposer.d.ts.map +1 -0
  99. package/lib/signatureSets/proposerSlashings.d.ts.map +1 -0
  100. package/lib/signatureSets/randao.d.ts.map +1 -0
  101. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -0
  102. package/lib/slot/index.d.ts.map +1 -0
  103. package/lib/slot/upgradeStateToAltair.d.ts.map +1 -0
  104. package/lib/slot/upgradeStateToBellatrix.d.ts.map +1 -0
  105. package/lib/slot/upgradeStateToCapella.d.ts.map +1 -0
  106. package/lib/slot/upgradeStateToDeneb.d.ts +1 -2
  107. package/lib/slot/upgradeStateToDeneb.d.ts.map +1 -0
  108. package/lib/slot/upgradeStateToDeneb.js.map +1 -1
  109. package/lib/slot/upgradeStateToElectra.d.ts.map +1 -0
  110. package/lib/slot/upgradeStateToFulu.d.ts.map +1 -0
  111. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -0
  112. package/lib/stateTransition.d.ts.map +1 -0
  113. package/lib/types.d.ts +2 -2
  114. package/lib/types.d.ts.map +1 -0
  115. package/lib/util/aggregator.d.ts.map +1 -0
  116. package/lib/util/altair.d.ts.map +1 -0
  117. package/lib/util/array.d.ts.map +1 -0
  118. package/lib/util/attestation.d.ts.map +1 -0
  119. package/lib/util/attesterStatus.d.ts.map +1 -0
  120. package/lib/util/balance.d.ts.map +1 -0
  121. package/lib/util/blindedBlock.d.ts.map +1 -0
  122. package/lib/util/blindedBlock.js.map +1 -1
  123. package/lib/util/blockRoot.d.ts.map +1 -0
  124. package/lib/util/calculateCommitteeAssignments.d.ts.map +1 -0
  125. package/lib/util/capella.d.ts.map +1 -0
  126. package/lib/util/computeAnchorCheckpoint.d.ts.map +1 -0
  127. package/lib/util/deposit.d.ts.map +1 -0
  128. package/lib/util/domain.d.ts.map +1 -0
  129. package/lib/util/electra.d.ts.map +1 -0
  130. package/lib/util/epoch.d.ts.map +1 -0
  131. package/lib/util/epochShuffling.d.ts.map +1 -0
  132. package/lib/util/execution.d.ts.map +1 -0
  133. package/lib/util/execution.js.map +1 -1
  134. package/lib/util/finality.d.ts.map +1 -0
  135. package/lib/util/fulu.d.ts.map +1 -0
  136. package/lib/util/genesis.d.ts.map +1 -0
  137. package/lib/util/genesis.js +0 -3
  138. package/lib/util/genesis.js.map +1 -1
  139. package/lib/util/index.d.ts +5 -5
  140. package/lib/util/index.d.ts.map +1 -0
  141. package/lib/util/index.js +5 -5
  142. package/lib/util/index.js.map +1 -1
  143. package/lib/util/interop.d.ts.map +1 -0
  144. package/lib/util/interop.js +1 -1
  145. package/lib/util/interop.js.map +1 -1
  146. package/lib/util/loadState/findModifiedInactivityScores.d.ts.map +1 -0
  147. package/lib/util/loadState/findModifiedValidators.d.ts.map +1 -0
  148. package/lib/util/loadState/index.d.ts.map +1 -0
  149. package/lib/util/loadState/loadState.d.ts.map +1 -0
  150. package/lib/util/loadState/loadValidator.d.ts.map +1 -0
  151. package/lib/util/rootCache.d.ts.map +1 -0
  152. package/lib/util/seed.d.ts.map +1 -0
  153. package/lib/util/seed.js +1 -2
  154. package/lib/util/seed.js.map +1 -1
  155. package/lib/util/shufflingDecisionRoot.d.ts.map +1 -0
  156. package/lib/util/signatureSets.d.ts.map +1 -0
  157. package/lib/util/signingRoot.d.ts.map +1 -0
  158. package/lib/util/slot.d.ts +0 -1
  159. package/lib/util/slot.d.ts.map +1 -0
  160. package/lib/util/slot.js +3 -7
  161. package/lib/util/slot.js.map +1 -1
  162. package/lib/util/sszBytes.d.ts.map +1 -0
  163. package/lib/util/syncCommittee.d.ts.map +1 -0
  164. package/lib/util/targetUnslashedBalance.d.ts.map +1 -0
  165. package/lib/util/validator.d.ts.map +1 -0
  166. package/lib/util/weakSubjectivity.d.ts.map +1 -0
  167. package/lib/util/weakSubjectivity.js.map +1 -1
  168. package/package.json +13 -11
  169. package/src/block/externalData.ts +26 -0
  170. package/src/block/index.ts +81 -0
  171. package/src/block/initiateValidatorExit.ts +62 -0
  172. package/src/block/isValidIndexedAttestation.ts +70 -0
  173. package/src/block/processAttestationPhase0.ts +158 -0
  174. package/src/block/processAttestations.ts +25 -0
  175. package/src/block/processAttestationsAltair.ts +184 -0
  176. package/src/block/processAttesterSlashing.ts +59 -0
  177. package/src/block/processBlobKzgCommitments.ts +21 -0
  178. package/src/block/processBlockHeader.ts +54 -0
  179. package/src/block/processBlsToExecutionChange.ts +78 -0
  180. package/src/block/processConsolidationRequest.ts +147 -0
  181. package/src/block/processDeposit.ts +166 -0
  182. package/src/block/processDepositRequest.ts +19 -0
  183. package/src/block/processEth1Data.ts +86 -0
  184. package/src/block/processExecutionPayload.ts +84 -0
  185. package/src/block/processOperations.ts +83 -0
  186. package/src/block/processProposerSlashing.ts +66 -0
  187. package/src/block/processRandao.ts +27 -0
  188. package/src/block/processSyncCommittee.ts +117 -0
  189. package/src/block/processVoluntaryExit.ts +55 -0
  190. package/src/block/processWithdrawalRequest.ts +98 -0
  191. package/src/block/processWithdrawals.ts +207 -0
  192. package/src/block/slashValidator.ts +98 -0
  193. package/src/block/types.ts +9 -0
  194. package/src/cache/effectiveBalanceIncrements.ts +39 -0
  195. package/src/cache/epochCache.ts +1213 -0
  196. package/src/cache/epochTransitionCache.ts +542 -0
  197. package/src/cache/pubkeyCache.ts +33 -0
  198. package/src/cache/rewardCache.ts +19 -0
  199. package/src/cache/stateCache.ts +268 -0
  200. package/src/cache/syncCommitteeCache.ts +96 -0
  201. package/src/cache/types.ts +18 -0
  202. package/src/constants/constants.ts +12 -0
  203. package/src/constants/index.ts +1 -0
  204. package/src/epoch/computeUnrealizedCheckpoints.ts +55 -0
  205. package/src/epoch/getAttestationDeltas.ts +169 -0
  206. package/src/epoch/getRewardsAndPenalties.ts +137 -0
  207. package/src/epoch/index.ts +202 -0
  208. package/src/epoch/processEffectiveBalanceUpdates.ts +111 -0
  209. package/src/epoch/processEth1DataReset.ts +17 -0
  210. package/src/epoch/processHistoricalRootsUpdate.ts +25 -0
  211. package/src/epoch/processHistoricalSummariesUpdate.ts +23 -0
  212. package/src/epoch/processInactivityUpdates.ts +60 -0
  213. package/src/epoch/processJustificationAndFinalization.ts +90 -0
  214. package/src/epoch/processParticipationFlagUpdates.ts +27 -0
  215. package/src/epoch/processParticipationRecordUpdates.ts +14 -0
  216. package/src/epoch/processPendingAttestations.ts +75 -0
  217. package/src/epoch/processPendingConsolidations.ts +59 -0
  218. package/src/epoch/processPendingDeposits.ts +136 -0
  219. package/src/epoch/processProposerLookahead.ts +39 -0
  220. package/src/epoch/processRandaoMixesReset.ts +18 -0
  221. package/src/epoch/processRegistryUpdates.ts +65 -0
  222. package/src/epoch/processRewardsAndPenalties.ts +58 -0
  223. package/src/epoch/processSlashings.ts +97 -0
  224. package/src/epoch/processSlashingsReset.ts +20 -0
  225. package/src/epoch/processSyncCommitteeUpdates.ts +44 -0
  226. package/src/index.ts +67 -0
  227. package/src/metrics.ts +169 -0
  228. package/src/signatureSets/attesterSlashings.ts +39 -0
  229. package/src/signatureSets/blsToExecutionChange.ts +43 -0
  230. package/src/signatureSets/index.ts +73 -0
  231. package/src/signatureSets/indexedAttestation.ts +51 -0
  232. package/src/signatureSets/proposer.ts +47 -0
  233. package/src/signatureSets/proposerSlashings.ts +41 -0
  234. package/src/signatureSets/randao.ts +31 -0
  235. package/src/signatureSets/voluntaryExits.ts +44 -0
  236. package/src/slot/index.ts +32 -0
  237. package/src/slot/upgradeStateToAltair.ts +149 -0
  238. package/src/slot/upgradeStateToBellatrix.ts +63 -0
  239. package/src/slot/upgradeStateToCapella.ts +71 -0
  240. package/src/slot/upgradeStateToDeneb.ts +40 -0
  241. package/src/slot/upgradeStateToElectra.ts +126 -0
  242. package/src/slot/upgradeStateToFulu.ts +31 -0
  243. package/src/slot/upgradeStateToGloas.ts +29 -0
  244. package/src/stateTransition.ts +305 -0
  245. package/src/types.ts +26 -0
  246. package/src/util/aggregator.ts +33 -0
  247. package/src/util/altair.ts +13 -0
  248. package/src/util/array.ts +53 -0
  249. package/src/util/attestation.ts +36 -0
  250. package/src/util/attesterStatus.ts +83 -0
  251. package/src/util/balance.ts +83 -0
  252. package/src/util/blindedBlock.ts +144 -0
  253. package/src/util/blockRoot.ts +72 -0
  254. package/src/util/calculateCommitteeAssignments.ts +43 -0
  255. package/src/util/capella.ts +8 -0
  256. package/src/util/computeAnchorCheckpoint.ts +38 -0
  257. package/src/util/deposit.ts +22 -0
  258. package/src/util/domain.ts +31 -0
  259. package/src/util/electra.ts +68 -0
  260. package/src/util/epoch.ts +135 -0
  261. package/src/util/epochShuffling.ts +185 -0
  262. package/src/util/execution.ts +177 -0
  263. package/src/util/finality.ts +17 -0
  264. package/src/util/fulu.ts +43 -0
  265. package/src/util/genesis.ts +340 -0
  266. package/src/util/index.ts +29 -0
  267. package/src/util/interop.ts +22 -0
  268. package/src/util/loadState/findModifiedInactivityScores.ts +47 -0
  269. package/src/util/loadState/findModifiedValidators.ts +46 -0
  270. package/src/util/loadState/index.ts +2 -0
  271. package/src/util/loadState/loadState.ts +225 -0
  272. package/src/util/loadState/loadValidator.ts +77 -0
  273. package/src/util/rootCache.ts +37 -0
  274. package/src/util/seed.ts +381 -0
  275. package/src/util/shufflingDecisionRoot.ts +78 -0
  276. package/src/util/signatureSets.ts +65 -0
  277. package/src/util/signingRoot.ts +13 -0
  278. package/src/util/slot.ts +22 -0
  279. package/src/util/sszBytes.ts +52 -0
  280. package/src/util/syncCommittee.ts +69 -0
  281. package/src/util/targetUnslashedBalance.ts +30 -0
  282. package/src/util/validator.ts +105 -0
  283. package/src/util/weakSubjectivity.ts +186 -0
@@ -0,0 +1,147 @@
1
+ import {FAR_FUTURE_EPOCH, MIN_ACTIVATION_BALANCE, PENDING_CONSOLIDATIONS_LIMIT} from "@lodestar/params";
2
+ import {electra, ssz} from "@lodestar/types";
3
+ import {CachedBeaconStateElectra} from "../types.js";
4
+ import {hasEth1WithdrawalCredential} from "../util/capella.js";
5
+ import {
6
+ hasCompoundingWithdrawalCredential,
7
+ hasExecutionWithdrawalCredential,
8
+ isPubkeyKnown,
9
+ switchToCompoundingValidator,
10
+ } from "../util/electra.js";
11
+ import {computeConsolidationEpochAndUpdateChurn} from "../util/epoch.js";
12
+ import {getConsolidationChurnLimit, getPendingBalanceToWithdraw, isActiveValidator} from "../util/validator.js";
13
+
14
+ // TODO Electra: Clean up necessary as there is a lot of overlap with isValidSwitchToCompoundRequest
15
+ export function processConsolidationRequest(
16
+ state: CachedBeaconStateElectra,
17
+ consolidationRequest: electra.ConsolidationRequest
18
+ ): void {
19
+ const {sourcePubkey, targetPubkey, sourceAddress} = consolidationRequest;
20
+ if (!isPubkeyKnown(state, sourcePubkey) || !isPubkeyKnown(state, targetPubkey)) {
21
+ return;
22
+ }
23
+
24
+ const sourceIndex = state.epochCtx.getValidatorIndex(sourcePubkey);
25
+ const targetIndex = state.epochCtx.getValidatorIndex(targetPubkey);
26
+
27
+ if (sourceIndex === null || targetIndex === null) {
28
+ return;
29
+ }
30
+
31
+ if (isValidSwitchToCompoundRequest(state, consolidationRequest)) {
32
+ switchToCompoundingValidator(state, sourceIndex);
33
+ // Early return since we have already switched validator to compounding
34
+ return;
35
+ }
36
+
37
+ // Verify that source != target, so a consolidation cannot be used as an exit.
38
+ if (sourceIndex === targetIndex) {
39
+ return;
40
+ }
41
+
42
+ // If the pending consolidations queue is full, consolidation requests are ignored
43
+ if (state.pendingConsolidations.length >= PENDING_CONSOLIDATIONS_LIMIT) {
44
+ return;
45
+ }
46
+
47
+ // If there is too little available consolidation churn limit, consolidation requests are ignored
48
+ if (getConsolidationChurnLimit(state.epochCtx) <= MIN_ACTIVATION_BALANCE) {
49
+ return;
50
+ }
51
+
52
+ const sourceValidator = state.validators.get(sourceIndex);
53
+ const targetValidator = state.validators.getReadonly(targetIndex);
54
+ const sourceWithdrawalAddress = sourceValidator.withdrawalCredentials.subarray(12);
55
+ const currentEpoch = state.epochCtx.epoch;
56
+
57
+ // Verify source withdrawal credentials
58
+ const hasCorrectCredential = hasExecutionWithdrawalCredential(sourceValidator.withdrawalCredentials);
59
+ const isCorrectSourceAddress = Buffer.compare(sourceWithdrawalAddress, sourceAddress) === 0;
60
+ if (!(hasCorrectCredential && isCorrectSourceAddress)) {
61
+ return;
62
+ }
63
+
64
+ // Verify that target has compounding withdrawal credentials
65
+ if (!hasCompoundingWithdrawalCredential(targetValidator.withdrawalCredentials)) {
66
+ return;
67
+ }
68
+
69
+ // Verify the source and the target are active
70
+ if (!isActiveValidator(sourceValidator, currentEpoch) || !isActiveValidator(targetValidator, currentEpoch)) {
71
+ return;
72
+ }
73
+
74
+ // Verify exits for source and target have not been initiated
75
+ if (sourceValidator.exitEpoch !== FAR_FUTURE_EPOCH || targetValidator.exitEpoch !== FAR_FUTURE_EPOCH) {
76
+ return;
77
+ }
78
+
79
+ // Verify the source has been active long enough
80
+ if (currentEpoch < sourceValidator.activationEpoch + state.config.SHARD_COMMITTEE_PERIOD) {
81
+ return;
82
+ }
83
+
84
+ // Verify the source has no pending withdrawals in the queue
85
+ if (getPendingBalanceToWithdraw(state, sourceIndex) > 0) {
86
+ return;
87
+ }
88
+
89
+ // Initiate source validator exit and append pending consolidation
90
+ // TODO Electra: See if we can get rid of big int
91
+ const exitEpoch = computeConsolidationEpochAndUpdateChurn(state, BigInt(sourceValidator.effectiveBalance));
92
+ sourceValidator.exitEpoch = exitEpoch;
93
+ sourceValidator.withdrawableEpoch = exitEpoch + state.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
94
+
95
+ const pendingConsolidation = ssz.electra.PendingConsolidation.toViewDU({
96
+ sourceIndex,
97
+ targetIndex,
98
+ });
99
+ state.pendingConsolidations.push(pendingConsolidation);
100
+ }
101
+
102
+ /**
103
+ * Determine if we should set consolidation target validator to compounding credential
104
+ */
105
+ function isValidSwitchToCompoundRequest(
106
+ state: CachedBeaconStateElectra,
107
+ consolidationRequest: electra.ConsolidationRequest
108
+ ): boolean {
109
+ const {sourcePubkey, targetPubkey, sourceAddress} = consolidationRequest;
110
+ const sourceIndex = state.epochCtx.getValidatorIndex(sourcePubkey);
111
+ const targetIndex = state.epochCtx.getValidatorIndex(targetPubkey);
112
+
113
+ // Verify pubkey exists
114
+ if (sourceIndex === null) {
115
+ // this check is mainly to make the compiler happy, pubkey is checked by the consumer already
116
+ return false;
117
+ }
118
+
119
+ // Switch to compounding requires source and target be equal
120
+ if (sourceIndex !== targetIndex) {
121
+ return false;
122
+ }
123
+
124
+ const sourceValidator = state.validators.getReadonly(sourceIndex);
125
+ const sourceWithdrawalAddress = sourceValidator.withdrawalCredentials.subarray(12);
126
+ // Verify request has been authorized
127
+ if (Buffer.compare(sourceWithdrawalAddress, sourceAddress) !== 0) {
128
+ return false;
129
+ }
130
+
131
+ // Verify source withdrawal credentials
132
+ if (!hasEth1WithdrawalCredential(sourceValidator.withdrawalCredentials)) {
133
+ return false;
134
+ }
135
+
136
+ // Verify the source is active
137
+ if (!isActiveValidator(sourceValidator, state.epochCtx.epoch)) {
138
+ return false;
139
+ }
140
+
141
+ // Verify exit for source has not been initiated
142
+ if (sourceValidator.exitEpoch !== FAR_FUTURE_EPOCH) {
143
+ return false;
144
+ }
145
+
146
+ return true;
147
+ }
@@ -0,0 +1,166 @@
1
+ import {PublicKey, Signature, verify} from "@chainsafe/blst";
2
+ import {BeaconConfig} from "@lodestar/config";
3
+ import {
4
+ DEPOSIT_CONTRACT_TREE_DEPTH,
5
+ DOMAIN_DEPOSIT,
6
+ EFFECTIVE_BALANCE_INCREMENT,
7
+ FAR_FUTURE_EPOCH,
8
+ ForkSeq,
9
+ GENESIS_SLOT,
10
+ MAX_EFFECTIVE_BALANCE,
11
+ } from "@lodestar/params";
12
+ import {BLSPubkey, Bytes32, UintNum64, electra, phase0, ssz} from "@lodestar/types";
13
+ import {verifyMerkleBranch} from "@lodestar/utils";
14
+ import {ZERO_HASH} from "../constants/index.js";
15
+ import {CachedBeaconStateAllForks, CachedBeaconStateAltair, CachedBeaconStateElectra} from "../types.js";
16
+ import {computeDomain, computeSigningRoot, getMaxEffectiveBalance, increaseBalance} from "../util/index.js";
17
+
18
+ /**
19
+ * Process a Deposit operation. Potentially adds a new validator to the registry. Mutates the validators and balances
20
+ * trees, pushing contigious values at the end.
21
+ *
22
+ * PERF: Work depends on number of Deposit per block. On regular networks the average is 0 / block.
23
+ */
24
+ export function processDeposit(fork: ForkSeq, state: CachedBeaconStateAllForks, deposit: phase0.Deposit): void {
25
+ // verify the merkle branch
26
+ if (
27
+ !verifyMerkleBranch(
28
+ ssz.phase0.DepositData.hashTreeRoot(deposit.data),
29
+ deposit.proof,
30
+ DEPOSIT_CONTRACT_TREE_DEPTH + 1,
31
+ state.eth1DepositIndex,
32
+ state.eth1Data.depositRoot
33
+ )
34
+ ) {
35
+ throw new Error("Deposit has invalid merkle proof");
36
+ }
37
+
38
+ // deposits must be processed in order
39
+ state.eth1DepositIndex += 1;
40
+
41
+ applyDeposit(fork, state, deposit.data);
42
+ }
43
+
44
+ /**
45
+ * Adds a new validator into the registry. Or increase balance if already exist.
46
+ * Follows applyDeposit() in consensus spec. Will be used by processDeposit() and processDepositRequest()
47
+ *
48
+ */
49
+ export function applyDeposit(
50
+ fork: ForkSeq,
51
+ state: CachedBeaconStateAllForks,
52
+ deposit: phase0.DepositData | electra.DepositRequest
53
+ ): void {
54
+ const {config, epochCtx, validators} = state;
55
+ const {pubkey, withdrawalCredentials, amount, signature} = deposit;
56
+
57
+ const cachedIndex = epochCtx.getValidatorIndex(pubkey);
58
+ const isNewValidator = cachedIndex === null || !Number.isSafeInteger(cachedIndex) || cachedIndex >= validators.length;
59
+
60
+ if (fork < ForkSeq.electra) {
61
+ if (isNewValidator) {
62
+ if (isValidDepositSignature(config, pubkey, withdrawalCredentials, amount, signature)) {
63
+ addValidatorToRegistry(fork, state, pubkey, withdrawalCredentials, amount);
64
+ }
65
+ } else {
66
+ // increase balance by deposit amount right away pre-electra
67
+ increaseBalance(state, cachedIndex, amount);
68
+ }
69
+ } else {
70
+ const stateElectra = state as CachedBeaconStateElectra;
71
+ const pendingDeposit = ssz.electra.PendingDeposit.toViewDU({
72
+ pubkey,
73
+ withdrawalCredentials,
74
+ amount,
75
+ signature,
76
+ slot: GENESIS_SLOT, // Use GENESIS_SLOT to distinguish from a pending deposit request
77
+ });
78
+
79
+ if (isNewValidator) {
80
+ if (isValidDepositSignature(config, pubkey, withdrawalCredentials, amount, deposit.signature)) {
81
+ addValidatorToRegistry(fork, state, pubkey, withdrawalCredentials, 0);
82
+ stateElectra.pendingDeposits.push(pendingDeposit);
83
+ }
84
+ } else {
85
+ stateElectra.pendingDeposits.push(pendingDeposit);
86
+ }
87
+ }
88
+ }
89
+
90
+ export function addValidatorToRegistry(
91
+ fork: ForkSeq,
92
+ state: CachedBeaconStateAllForks,
93
+ pubkey: BLSPubkey,
94
+ withdrawalCredentials: Bytes32,
95
+ amount: UintNum64
96
+ ): void {
97
+ const {validators, epochCtx} = state;
98
+ // add validator and balance entries
99
+ const effectiveBalance = Math.min(
100
+ amount - (amount % EFFECTIVE_BALANCE_INCREMENT),
101
+ fork < ForkSeq.electra ? MAX_EFFECTIVE_BALANCE : getMaxEffectiveBalance(withdrawalCredentials)
102
+ );
103
+ validators.push(
104
+ ssz.phase0.Validator.toViewDU({
105
+ pubkey,
106
+ withdrawalCredentials,
107
+ activationEligibilityEpoch: FAR_FUTURE_EPOCH,
108
+ activationEpoch: FAR_FUTURE_EPOCH,
109
+ exitEpoch: FAR_FUTURE_EPOCH,
110
+ withdrawableEpoch: FAR_FUTURE_EPOCH,
111
+ effectiveBalance,
112
+ slashed: false,
113
+ })
114
+ );
115
+
116
+ const validatorIndex = validators.length - 1;
117
+ // TODO Electra: Review this
118
+ // Updating here is better than updating at once on epoch transition
119
+ // - Simplify genesis fn applyDeposits(): effectiveBalanceIncrements is populated immediately
120
+ // - Keep related code together to reduce risk of breaking this cache
121
+ // - Should have equal performance since it sets a value in a flat array
122
+ epochCtx.effectiveBalanceIncrementsSet(validatorIndex, effectiveBalance);
123
+
124
+ // now that there is a new validator, update the epoch context with the new pubkey
125
+ epochCtx.addPubkey(validatorIndex, pubkey);
126
+
127
+ // Only after altair:
128
+ if (fork >= ForkSeq.altair) {
129
+ const stateAltair = state as CachedBeaconStateAltair;
130
+
131
+ stateAltair.inactivityScores.push(0);
132
+
133
+ // add participation caches
134
+ stateAltair.previousEpochParticipation.push(0);
135
+ stateAltair.currentEpochParticipation.push(0);
136
+ }
137
+
138
+ state.balances.push(amount);
139
+ }
140
+
141
+ export function isValidDepositSignature(
142
+ config: BeaconConfig,
143
+ pubkey: Uint8Array,
144
+ withdrawalCredentials: Uint8Array,
145
+ amount: number,
146
+ depositSignature: Uint8Array
147
+ ): boolean {
148
+ // verify the deposit signature (proof of posession) which is not checked by the deposit contract
149
+ const depositMessage = {
150
+ pubkey,
151
+ withdrawalCredentials,
152
+ amount,
153
+ };
154
+ // fork-agnostic domain since deposits are valid across forks
155
+ const domain = computeDomain(DOMAIN_DEPOSIT, config.GENESIS_FORK_VERSION, ZERO_HASH);
156
+ const signingRoot = computeSigningRoot(ssz.phase0.DepositMessage, depositMessage, domain);
157
+ try {
158
+ // Pubkeys must be checked for group + inf. This must be done only once when the validator deposit is processed
159
+ const publicKey = PublicKey.fromBytes(pubkey, true);
160
+ const signature = Signature.fromBytes(depositSignature, true);
161
+
162
+ return verify(signingRoot, publicKey, signature);
163
+ } catch (_e) {
164
+ return false; // Catch all BLS errors: failed key validation, failed signature validation, invalid signature
165
+ }
166
+ }
@@ -0,0 +1,19 @@
1
+ import {UNSET_DEPOSIT_REQUESTS_START_INDEX} from "@lodestar/params";
2
+ import {electra, ssz} from "@lodestar/types";
3
+ import {CachedBeaconStateElectra} from "../types.js";
4
+
5
+ export function processDepositRequest(state: CachedBeaconStateElectra, depositRequest: electra.DepositRequest): void {
6
+ if (state.depositRequestsStartIndex === UNSET_DEPOSIT_REQUESTS_START_INDEX) {
7
+ state.depositRequestsStartIndex = depositRequest.index;
8
+ }
9
+
10
+ // Create pending deposit
11
+ const pendingDeposit = ssz.electra.PendingDeposit.toViewDU({
12
+ pubkey: depositRequest.pubkey,
13
+ withdrawalCredentials: depositRequest.withdrawalCredentials,
14
+ amount: depositRequest.amount,
15
+ signature: depositRequest.signature,
16
+ slot: state.slot,
17
+ });
18
+ state.pendingDeposits.push(pendingDeposit);
19
+ }
@@ -0,0 +1,86 @@
1
+ import {Node} from "@chainsafe/persistent-merkle-tree";
2
+ import {CompositeViewDU} from "@chainsafe/ssz";
3
+ import {EPOCHS_PER_ETH1_VOTING_PERIOD, SLOTS_PER_EPOCH} from "@lodestar/params";
4
+ import {phase0, ssz} from "@lodestar/types";
5
+ import {BeaconStateAllForks, CachedBeaconStateAllForks} from "../types.js";
6
+
7
+ /**
8
+ * Store vote counts for every eth-execution block that has votes; if any eth-execution block wins majority support within a 1024-slot
9
+ * voting period, formally accept that eth-execution block and set it as the official "latest known eth-execution block" in the eth-consensus state.
10
+ *
11
+ * PERF: Processing cost depends on the current amount of votes.
12
+ * - Best case: Vote is already decided, zero work. See becomesNewEth1Data conditions
13
+ * - Worst case: 1023 votes and no majority vote yet.
14
+ */
15
+ export function processEth1Data(state: CachedBeaconStateAllForks, eth1Data: phase0.Eth1Data): void {
16
+ // Convert to view first to hash once and compare hashes
17
+ const eth1DataView = ssz.phase0.Eth1Data.toViewDU(eth1Data);
18
+
19
+ if (becomesNewEth1Data(state, eth1DataView)) {
20
+ state.eth1Data = eth1DataView;
21
+ }
22
+
23
+ state.eth1DataVotes.push(eth1DataView);
24
+ }
25
+
26
+ /**
27
+ * Returns true if adding the given `eth1Data` to `state.eth1DataVotes` would
28
+ * result in a change to `state.eth1Data`.
29
+ */
30
+ export function becomesNewEth1Data(
31
+ state: BeaconStateAllForks,
32
+ newEth1Data: CompositeViewDU<typeof ssz.phase0.Eth1Data>
33
+ ): boolean {
34
+ const SLOTS_PER_ETH1_VOTING_PERIOD = EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH;
35
+
36
+ // If there are not more than 50% votes, then we do not have to count to find a winner.
37
+ if ((state.eth1DataVotes.length + 1) * 2 <= SLOTS_PER_ETH1_VOTING_PERIOD) {
38
+ return false;
39
+ }
40
+
41
+ // Nothing to do if the state already has this as eth1data (happens a lot after majority vote is in)
42
+ if (isEqualEth1DataView(state.eth1Data, newEth1Data)) {
43
+ return false;
44
+ }
45
+
46
+ // Close to half the EPOCHS_PER_ETH1_VOTING_PERIOD it can be expensive to do so many comparisions.
47
+ // `eth1DataVotes.getAllReadonly()` navigates the tree once to fetch all the LeafNodes efficiently.
48
+ // Then isEqualEth1DataView compares cached roots (HashObject as of Jan 2022) which is much cheaper
49
+ // than doing structural equality, which requires tree -> value conversions
50
+ let sameVotesCount = 0;
51
+ // biome-ignore lint/complexity/noForEach: ssz api
52
+ state.eth1DataVotes.forEach((eth1DataVote) => {
53
+ if (isEqualEth1DataView(eth1DataVote, newEth1Data)) {
54
+ sameVotesCount++;
55
+ }
56
+ });
57
+
58
+ // The +1 is to account for the `eth1Data` supplied to the function.
59
+ if ((sameVotesCount + 1) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD) {
60
+ return true;
61
+ }
62
+ return false;
63
+ }
64
+
65
+ function isEqualEth1DataView(
66
+ eth1DataA: CompositeViewDU<typeof ssz.phase0.Eth1Data>,
67
+ eth1DataB: CompositeViewDU<typeof ssz.phase0.Eth1Data>
68
+ ): boolean {
69
+ return isEqualNode(eth1DataA.node, eth1DataB.node);
70
+ }
71
+
72
+ // TODO: Upstream to persistent-merkle-tree
73
+ function isEqualNode(nA: Node, nB: Node): boolean {
74
+ const hA = nA.rootHashObject;
75
+ const hB = nB.rootHashObject;
76
+ return (
77
+ hA.h0 === hB.h0 &&
78
+ hA.h1 === hB.h1 &&
79
+ hA.h2 === hB.h2 &&
80
+ hA.h3 === hB.h3 &&
81
+ hA.h4 === hB.h4 &&
82
+ hA.h5 === hB.h5 &&
83
+ hA.h6 === hB.h6 &&
84
+ hA.h7 === hB.h7
85
+ );
86
+ }
@@ -0,0 +1,84 @@
1
+ import {byteArrayEquals} from "@chainsafe/ssz";
2
+ import {ForkName, ForkSeq, isForkPostDeneb} from "@lodestar/params";
3
+ import {BeaconBlockBody, BlindedBeaconBlockBody, deneb, isExecutionPayload} from "@lodestar/types";
4
+ import {toHex, toRootHex} from "@lodestar/utils";
5
+ import {CachedBeaconStateBellatrix, CachedBeaconStateCapella} from "../types.js";
6
+ import {
7
+ executionPayloadToPayloadHeader,
8
+ getFullOrBlindedPayloadFromBody,
9
+ isMergeTransitionComplete,
10
+ } from "../util/execution.js";
11
+ import {computeEpochAtSlot, computeTimeAtSlot, getRandaoMix} from "../util/index.js";
12
+ import {BlockExternalData, ExecutionPayloadStatus} from "./externalData.js";
13
+
14
+ export function processExecutionPayload(
15
+ fork: ForkSeq,
16
+ state: CachedBeaconStateBellatrix | CachedBeaconStateCapella,
17
+ body: BeaconBlockBody | BlindedBeaconBlockBody,
18
+ externalData: Omit<BlockExternalData, "dataAvailabilityStatus">
19
+ ): void {
20
+ const payload = getFullOrBlindedPayloadFromBody(body);
21
+ const forkName = ForkName[ForkSeq[fork] as ForkName];
22
+ // Verify consistency of the parent hash, block number, base fee per gas and gas limit
23
+ // with respect to the previous execution payload header
24
+ if (isMergeTransitionComplete(state)) {
25
+ const {latestExecutionPayloadHeader} = state;
26
+ if (!byteArrayEquals(payload.parentHash, latestExecutionPayloadHeader.blockHash)) {
27
+ throw Error(
28
+ `Invalid execution payload parentHash ${toRootHex(payload.parentHash)} latest blockHash ${toRootHex(
29
+ latestExecutionPayloadHeader.blockHash
30
+ )}`
31
+ );
32
+ }
33
+ }
34
+
35
+ // Verify random
36
+ const expectedRandom = getRandaoMix(state, state.epochCtx.epoch);
37
+ if (!byteArrayEquals(payload.prevRandao, expectedRandom)) {
38
+ throw Error(`Invalid execution payload random ${toHex(payload.prevRandao)} expected=${toHex(expectedRandom)}`);
39
+ }
40
+
41
+ // Verify timestamp
42
+ //
43
+ // Note: inlined function in if statement
44
+ // def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64:
45
+ // slots_since_genesis = slot - GENESIS_SLOT
46
+ // return uint64(state.genesis_time + slots_since_genesis * SLOT_DURATION_MS / 1000)
47
+ if (payload.timestamp !== computeTimeAtSlot(state.config, state.slot, state.genesisTime)) {
48
+ throw Error(`Invalid timestamp ${payload.timestamp} genesisTime=${state.genesisTime} slot=${state.slot}`);
49
+ }
50
+
51
+ if (isForkPostDeneb(forkName)) {
52
+ const maxBlobsPerBlock = state.config.getMaxBlobsPerBlock(computeEpochAtSlot(state.slot));
53
+ const blobKzgCommitmentsLen = (body as deneb.BeaconBlockBody).blobKzgCommitments?.length ?? 0;
54
+ if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
55
+ throw Error(`blobKzgCommitmentsLen of ${blobKzgCommitmentsLen} exceeds limit=${maxBlobsPerBlock}`);
56
+ }
57
+ }
58
+
59
+ // Verify the execution payload is valid
60
+ //
61
+ // if executionEngine is null, executionEngine.onPayload MUST be called after running processBlock to get the
62
+ // correct randao mix. Since executionEngine will be an async call in most cases it is called afterwards to keep
63
+ // the state transition sync
64
+ //
65
+ // Equivalent to `assert executionEngine.notifyNewPayload(payload)`
66
+ if (isExecutionPayload(payload)) {
67
+ switch (externalData.executionPayloadStatus) {
68
+ case ExecutionPayloadStatus.preMerge:
69
+ throw Error("executionPayloadStatus preMerge");
70
+ case ExecutionPayloadStatus.invalid:
71
+ throw Error("Invalid execution payload");
72
+ case ExecutionPayloadStatus.valid:
73
+ break; // ok
74
+ }
75
+ }
76
+
77
+ const payloadHeader = isExecutionPayload(payload) ? executionPayloadToPayloadHeader(fork, payload) : payload;
78
+
79
+ // TODO Deneb: Types are not happy by default. Since it's a generic type going through ViewDU
80
+ // transformation then into all forks compatible probably some weird intersection incompatibility happens
81
+ state.latestExecutionPayloadHeader = state.config
82
+ .getPostBellatrixForkTypes(state.slot)
83
+ .ExecutionPayloadHeader.toViewDU(payloadHeader) as typeof state.latestExecutionPayloadHeader;
84
+ }
@@ -0,0 +1,83 @@
1
+ import {ForkSeq} from "@lodestar/params";
2
+ import {BeaconBlockBody, capella, electra} from "@lodestar/types";
3
+ import {BeaconStateTransitionMetrics} from "../metrics.js";
4
+ import {CachedBeaconStateAllForks, CachedBeaconStateCapella, CachedBeaconStateElectra} from "../types.js";
5
+ import {getEth1DepositCount} from "../util/deposit.js";
6
+ import {processAttestations} from "./processAttestations.js";
7
+ import {processAttesterSlashing} from "./processAttesterSlashing.js";
8
+ import {processBlsToExecutionChange} from "./processBlsToExecutionChange.js";
9
+ import {processConsolidationRequest} from "./processConsolidationRequest.js";
10
+ import {processDeposit} from "./processDeposit.js";
11
+ import {processDepositRequest} from "./processDepositRequest.js";
12
+ import {processProposerSlashing} from "./processProposerSlashing.js";
13
+ import {processVoluntaryExit} from "./processVoluntaryExit.js";
14
+ import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
15
+ import {ProcessBlockOpts} from "./types.js";
16
+
17
+ export {
18
+ processProposerSlashing,
19
+ processAttesterSlashing,
20
+ processAttestations,
21
+ processDeposit,
22
+ processVoluntaryExit,
23
+ processWithdrawalRequest,
24
+ processBlsToExecutionChange,
25
+ processDepositRequest,
26
+ processConsolidationRequest,
27
+ };
28
+
29
+ export function processOperations(
30
+ fork: ForkSeq,
31
+ state: CachedBeaconStateAllForks,
32
+ body: BeaconBlockBody,
33
+ opts: ProcessBlockOpts = {verifySignatures: true},
34
+ metrics?: BeaconStateTransitionMetrics | null
35
+ ): void {
36
+ // verify that outstanding deposits are processed up to the maximum number of deposits
37
+ const maxDeposits = getEth1DepositCount(state);
38
+ if (body.deposits.length !== maxDeposits) {
39
+ throw new Error(
40
+ `Block contains incorrect number of deposits: depositCount=${body.deposits.length} expected=${maxDeposits}`
41
+ );
42
+ }
43
+
44
+ for (const proposerSlashing of body.proposerSlashings) {
45
+ processProposerSlashing(fork, state, proposerSlashing, opts.verifySignatures);
46
+ }
47
+ for (const attesterSlashing of body.attesterSlashings) {
48
+ processAttesterSlashing(fork, state, attesterSlashing, opts.verifySignatures);
49
+ }
50
+
51
+ processAttestations(fork, state, body.attestations, opts.verifySignatures, metrics);
52
+
53
+ for (const deposit of body.deposits) {
54
+ processDeposit(fork, state, deposit);
55
+ }
56
+
57
+ for (const voluntaryExit of body.voluntaryExits) {
58
+ processVoluntaryExit(fork, state, voluntaryExit, opts.verifySignatures);
59
+ }
60
+
61
+ if (fork >= ForkSeq.capella) {
62
+ for (const blsToExecutionChange of (body as capella.BeaconBlockBody).blsToExecutionChanges) {
63
+ processBlsToExecutionChange(state as CachedBeaconStateCapella, blsToExecutionChange);
64
+ }
65
+ }
66
+
67
+ if (fork >= ForkSeq.electra) {
68
+ const stateElectra = state as CachedBeaconStateElectra;
69
+ const bodyElectra = body as electra.BeaconBlockBody;
70
+
71
+ for (const depositRequest of bodyElectra.executionRequests.deposits) {
72
+ processDepositRequest(stateElectra, depositRequest);
73
+ }
74
+
75
+ for (const elWithdrawalRequest of bodyElectra.executionRequests.withdrawals) {
76
+ processWithdrawalRequest(fork, stateElectra, elWithdrawalRequest);
77
+ }
78
+
79
+ for (const elConsolidationRequest of bodyElectra.executionRequests.consolidations) {
80
+ processConsolidationRequest(stateElectra, elConsolidationRequest);
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,66 @@
1
+ import {ForkSeq} from "@lodestar/params";
2
+ import {phase0, ssz} from "@lodestar/types";
3
+ import {getProposerSlashingSignatureSets} from "../signatureSets/index.js";
4
+ import {CachedBeaconStateAllForks} from "../types.js";
5
+ import {isSlashableValidator} from "../util/index.js";
6
+ import {verifySignatureSet} from "../util/signatureSets.js";
7
+ import {slashValidator} from "./slashValidator.js";
8
+
9
+ /**
10
+ * Process a ProposerSlashing operation. Initiates the exit of a validator, decreases the balance of the slashed
11
+ * validator and increases the block proposer balance.
12
+ *
13
+ * PERF: Work depends on number of ProposerSlashing per block. On regular networks the average is 0 / block.
14
+ */
15
+ export function processProposerSlashing(
16
+ fork: ForkSeq,
17
+ state: CachedBeaconStateAllForks,
18
+ proposerSlashing: phase0.ProposerSlashing,
19
+ verifySignatures = true
20
+ ): void {
21
+ assertValidProposerSlashing(state, proposerSlashing, verifySignatures);
22
+
23
+ slashValidator(fork, state, proposerSlashing.signedHeader1.message.proposerIndex);
24
+ }
25
+
26
+ export function assertValidProposerSlashing(
27
+ state: CachedBeaconStateAllForks,
28
+ proposerSlashing: phase0.ProposerSlashing,
29
+ verifySignatures = true
30
+ ): void {
31
+ const header1 = proposerSlashing.signedHeader1.message;
32
+ const header2 = proposerSlashing.signedHeader2.message;
33
+
34
+ // verify header slots match
35
+ if (header1.slot !== header2.slot) {
36
+ throw new Error(`ProposerSlashing slots do not match: slot1=${header1.slot} slot2=${header2.slot}`);
37
+ }
38
+
39
+ // verify header proposer indices match
40
+ if (header1.proposerIndex !== header2.proposerIndex) {
41
+ throw new Error(
42
+ `ProposerSlashing proposer indices do not match: proposerIndex1=${header1.proposerIndex} proposerIndex2=${header2.proposerIndex}`
43
+ );
44
+ }
45
+
46
+ // verify headers are different
47
+ if (ssz.phase0.BeaconBlockHeaderBigint.equals(header1, header2)) {
48
+ throw new Error("ProposerSlashing headers are equal");
49
+ }
50
+
51
+ // verify the proposer is slashable
52
+ const proposer = state.validators.getReadonly(header1.proposerIndex);
53
+ if (!isSlashableValidator(proposer, state.epochCtx.epoch)) {
54
+ throw new Error("ProposerSlashing proposer is not slashable");
55
+ }
56
+
57
+ // verify signatures
58
+ if (verifySignatures) {
59
+ const signatureSets = getProposerSlashingSignatureSets(state, proposerSlashing);
60
+ for (let i = 0; i < signatureSets.length; i++) {
61
+ if (!verifySignatureSet(signatureSets[i])) {
62
+ throw new Error(`ProposerSlashing header${i + 1} signature invalid`);
63
+ }
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,27 @@
1
+ import {digest} from "@chainsafe/as-sha256";
2
+ import {EPOCHS_PER_HISTORICAL_VECTOR} from "@lodestar/params";
3
+ import {BeaconBlock} from "@lodestar/types";
4
+ import {xor} from "@lodestar/utils";
5
+ import {verifyRandaoSignature} from "../signatureSets/index.js";
6
+ import {CachedBeaconStateAllForks} from "../types.js";
7
+ import {getRandaoMix} from "../util/index.js";
8
+
9
+ /**
10
+ * Commit a randao reveal to generate pseudorandomness seeds
11
+ *
12
+ * PERF: Fixed work independent of block contents.
13
+ */
14
+ export function processRandao(state: CachedBeaconStateAllForks, block: BeaconBlock, verifySignature = true): void {
15
+ const {epochCtx} = state;
16
+ const epoch = epochCtx.epoch;
17
+ const randaoReveal = block.body.randaoReveal;
18
+
19
+ // verify RANDAO reveal
20
+ if (verifySignature && !verifyRandaoSignature(state, block)) {
21
+ throw new Error("RANDAO reveal is an invalid signature");
22
+ }
23
+
24
+ // mix in RANDAO reveal
25
+ const randaoMix = xor(getRandaoMix(state, epoch), digest(randaoReveal));
26
+ state.randaoMixes.set(epoch % EPOCHS_PER_HISTORICAL_VECTOR, randaoMix);
27
+ }