@lodestar/state-transition 1.35.0-dev.e18102ed8c → 1.35.0-dev.f2a741bbe4

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 (243) hide show
  1. package/lib/block/externalData.d.ts.map +1 -0
  2. package/lib/block/index.d.ts.map +1 -0
  3. package/lib/block/initiateValidatorExit.d.ts.map +1 -0
  4. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -0
  5. package/lib/block/processAttestationPhase0.d.ts.map +1 -0
  6. package/lib/block/processAttestations.d.ts.map +1 -0
  7. package/lib/block/processAttestationsAltair.d.ts.map +1 -0
  8. package/lib/block/processAttesterSlashing.d.ts.map +1 -0
  9. package/lib/block/processBlobKzgCommitments.d.ts.map +1 -0
  10. package/lib/block/processBlockHeader.d.ts.map +1 -0
  11. package/lib/block/processBlsToExecutionChange.d.ts.map +1 -0
  12. package/lib/block/processConsolidationRequest.d.ts.map +1 -0
  13. package/lib/block/processDeposit.d.ts.map +1 -0
  14. package/lib/block/processDepositRequest.d.ts.map +1 -0
  15. package/lib/block/processEth1Data.d.ts.map +1 -0
  16. package/lib/block/processExecutionPayload.d.ts.map +1 -0
  17. package/lib/block/processExecutionPayload.js +3 -3
  18. package/lib/block/processExecutionPayload.js.map +1 -1
  19. package/lib/block/processOperations.d.ts.map +1 -0
  20. package/lib/block/processProposerSlashing.d.ts.map +1 -0
  21. package/lib/block/processRandao.d.ts.map +1 -0
  22. package/lib/block/processSyncCommittee.d.ts.map +1 -0
  23. package/lib/block/processVoluntaryExit.d.ts.map +1 -0
  24. package/lib/block/processWithdrawalRequest.d.ts.map +1 -0
  25. package/lib/block/processWithdrawals.d.ts.map +1 -0
  26. package/lib/block/slashValidator.d.ts.map +1 -0
  27. package/lib/block/types.d.ts.map +1 -0
  28. package/lib/cache/effectiveBalanceIncrements.d.ts.map +1 -0
  29. package/lib/cache/epochCache.d.ts.map +1 -0
  30. package/lib/cache/epochTransitionCache.d.ts.map +1 -0
  31. package/lib/cache/pubkeyCache.d.ts.map +1 -0
  32. package/lib/cache/rewardCache.d.ts.map +1 -0
  33. package/lib/cache/stateCache.d.ts.map +1 -0
  34. package/lib/cache/syncCommitteeCache.d.ts.map +1 -0
  35. package/lib/cache/types.d.ts +2 -2
  36. package/lib/cache/types.d.ts.map +1 -0
  37. package/lib/constants/constants.d.ts.map +1 -0
  38. package/lib/constants/index.d.ts.map +1 -0
  39. package/lib/epoch/computeUnrealizedCheckpoints.d.ts.map +1 -0
  40. package/lib/epoch/getAttestationDeltas.d.ts.map +1 -0
  41. package/lib/epoch/getRewardsAndPenalties.d.ts.map +1 -0
  42. package/lib/epoch/index.d.ts.map +1 -0
  43. package/lib/epoch/processEffectiveBalanceUpdates.d.ts.map +1 -0
  44. package/lib/epoch/processEth1DataReset.d.ts.map +1 -0
  45. package/lib/epoch/processHistoricalRootsUpdate.d.ts.map +1 -0
  46. package/lib/epoch/processHistoricalSummariesUpdate.d.ts.map +1 -0
  47. package/lib/epoch/processInactivityUpdates.d.ts.map +1 -0
  48. package/lib/epoch/processJustificationAndFinalization.d.ts.map +1 -0
  49. package/lib/epoch/processParticipationFlagUpdates.d.ts.map +1 -0
  50. package/lib/epoch/processParticipationRecordUpdates.d.ts.map +1 -0
  51. package/lib/epoch/processPendingAttestations.d.ts.map +1 -0
  52. package/lib/epoch/processPendingConsolidations.d.ts.map +1 -0
  53. package/lib/epoch/processPendingDeposits.d.ts.map +1 -0
  54. package/lib/epoch/processProposerLookahead.d.ts.map +1 -0
  55. package/lib/epoch/processRandaoMixesReset.d.ts.map +1 -0
  56. package/lib/epoch/processRegistryUpdates.d.ts.map +1 -0
  57. package/lib/epoch/processRewardsAndPenalties.d.ts.map +1 -0
  58. package/lib/epoch/processSlashings.d.ts.map +1 -0
  59. package/lib/epoch/processSlashingsReset.d.ts.map +1 -0
  60. package/lib/epoch/processSyncCommitteeUpdates.d.ts.map +1 -0
  61. package/lib/index.d.ts.map +1 -0
  62. package/lib/metrics.d.ts.map +1 -0
  63. package/lib/signatureSets/attesterSlashings.d.ts.map +1 -0
  64. package/lib/signatureSets/blsToExecutionChange.d.ts.map +1 -0
  65. package/lib/signatureSets/index.d.ts.map +1 -0
  66. package/lib/signatureSets/indexedAttestation.d.ts.map +1 -0
  67. package/lib/signatureSets/proposer.d.ts.map +1 -0
  68. package/lib/signatureSets/proposerSlashings.d.ts.map +1 -0
  69. package/lib/signatureSets/randao.d.ts.map +1 -0
  70. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -0
  71. package/lib/slot/index.d.ts.map +1 -0
  72. package/lib/slot/upgradeStateToAltair.d.ts.map +1 -0
  73. package/lib/slot/upgradeStateToBellatrix.d.ts.map +1 -0
  74. package/lib/slot/upgradeStateToCapella.d.ts.map +1 -0
  75. package/lib/slot/upgradeStateToDeneb.d.ts.map +1 -0
  76. package/lib/slot/upgradeStateToElectra.d.ts.map +1 -0
  77. package/lib/slot/upgradeStateToFulu.d.ts.map +1 -0
  78. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -0
  79. package/lib/stateTransition.d.ts.map +1 -0
  80. package/lib/types.d.ts.map +1 -0
  81. package/lib/util/aggregator.d.ts.map +1 -0
  82. package/lib/util/altair.d.ts.map +1 -0
  83. package/lib/util/array.d.ts.map +1 -0
  84. package/lib/util/attestation.d.ts.map +1 -0
  85. package/lib/util/attesterStatus.d.ts.map +1 -0
  86. package/lib/util/balance.d.ts.map +1 -0
  87. package/lib/util/blindedBlock.d.ts +3 -3
  88. package/lib/util/blindedBlock.d.ts.map +1 -0
  89. package/lib/util/blindedBlock.js.map +1 -1
  90. package/lib/util/blockRoot.d.ts.map +1 -0
  91. package/lib/util/calculateCommitteeAssignments.d.ts.map +1 -0
  92. package/lib/util/capella.d.ts.map +1 -0
  93. package/lib/util/computeAnchorCheckpoint.d.ts.map +1 -0
  94. package/lib/util/deposit.d.ts.map +1 -0
  95. package/lib/util/domain.d.ts.map +1 -0
  96. package/lib/util/electra.d.ts.map +1 -0
  97. package/lib/util/epoch.d.ts.map +1 -0
  98. package/lib/util/epochShuffling.d.ts.map +1 -0
  99. package/lib/util/execution.d.ts +2 -2
  100. package/lib/util/execution.d.ts.map +1 -0
  101. package/lib/util/execution.js.map +1 -1
  102. package/lib/util/finality.d.ts.map +1 -0
  103. package/lib/util/fulu.d.ts.map +1 -0
  104. package/lib/util/genesis.d.ts.map +1 -0
  105. package/lib/util/genesis.js +0 -3
  106. package/lib/util/genesis.js.map +1 -1
  107. package/lib/util/index.d.ts.map +1 -0
  108. package/lib/util/interop.d.ts.map +1 -0
  109. package/lib/util/loadState/findModifiedInactivityScores.d.ts.map +1 -0
  110. package/lib/util/loadState/findModifiedValidators.d.ts.map +1 -0
  111. package/lib/util/loadState/index.d.ts.map +1 -0
  112. package/lib/util/loadState/loadState.d.ts.map +1 -0
  113. package/lib/util/loadState/loadValidator.d.ts.map +1 -0
  114. package/lib/util/rootCache.d.ts.map +1 -0
  115. package/lib/util/seed.d.ts.map +1 -0
  116. package/lib/util/shufflingDecisionRoot.d.ts.map +1 -0
  117. package/lib/util/signatureSets.d.ts.map +1 -0
  118. package/lib/util/signingRoot.d.ts.map +1 -0
  119. package/lib/util/slot.d.ts +0 -1
  120. package/lib/util/slot.d.ts.map +1 -0
  121. package/lib/util/slot.js +3 -7
  122. package/lib/util/slot.js.map +1 -1
  123. package/lib/util/sszBytes.d.ts.map +1 -0
  124. package/lib/util/syncCommittee.d.ts.map +1 -0
  125. package/lib/util/targetUnslashedBalance.d.ts.map +1 -0
  126. package/lib/util/validator.d.ts.map +1 -0
  127. package/lib/util/weakSubjectivity.d.ts.map +1 -0
  128. package/package.json +13 -11
  129. package/src/block/externalData.ts +26 -0
  130. package/src/block/index.ts +81 -0
  131. package/src/block/initiateValidatorExit.ts +62 -0
  132. package/src/block/isValidIndexedAttestation.ts +70 -0
  133. package/src/block/processAttestationPhase0.ts +158 -0
  134. package/src/block/processAttestations.ts +25 -0
  135. package/src/block/processAttestationsAltair.ts +184 -0
  136. package/src/block/processAttesterSlashing.ts +59 -0
  137. package/src/block/processBlobKzgCommitments.ts +21 -0
  138. package/src/block/processBlockHeader.ts +54 -0
  139. package/src/block/processBlsToExecutionChange.ts +78 -0
  140. package/src/block/processConsolidationRequest.ts +147 -0
  141. package/src/block/processDeposit.ts +166 -0
  142. package/src/block/processDepositRequest.ts +19 -0
  143. package/src/block/processEth1Data.ts +86 -0
  144. package/src/block/processExecutionPayload.ts +84 -0
  145. package/src/block/processOperations.ts +83 -0
  146. package/src/block/processProposerSlashing.ts +66 -0
  147. package/src/block/processRandao.ts +27 -0
  148. package/src/block/processSyncCommittee.ts +117 -0
  149. package/src/block/processVoluntaryExit.ts +55 -0
  150. package/src/block/processWithdrawalRequest.ts +98 -0
  151. package/src/block/processWithdrawals.ts +207 -0
  152. package/src/block/slashValidator.ts +98 -0
  153. package/src/block/types.ts +9 -0
  154. package/src/cache/effectiveBalanceIncrements.ts +39 -0
  155. package/src/cache/epochCache.ts +1213 -0
  156. package/src/cache/epochTransitionCache.ts +542 -0
  157. package/src/cache/pubkeyCache.ts +33 -0
  158. package/src/cache/rewardCache.ts +19 -0
  159. package/src/cache/stateCache.ts +268 -0
  160. package/src/cache/syncCommitteeCache.ts +96 -0
  161. package/src/cache/types.ts +18 -0
  162. package/src/constants/constants.ts +12 -0
  163. package/src/constants/index.ts +1 -0
  164. package/src/epoch/computeUnrealizedCheckpoints.ts +55 -0
  165. package/src/epoch/getAttestationDeltas.ts +169 -0
  166. package/src/epoch/getRewardsAndPenalties.ts +137 -0
  167. package/src/epoch/index.ts +202 -0
  168. package/src/epoch/processEffectiveBalanceUpdates.ts +111 -0
  169. package/src/epoch/processEth1DataReset.ts +17 -0
  170. package/src/epoch/processHistoricalRootsUpdate.ts +25 -0
  171. package/src/epoch/processHistoricalSummariesUpdate.ts +23 -0
  172. package/src/epoch/processInactivityUpdates.ts +60 -0
  173. package/src/epoch/processJustificationAndFinalization.ts +90 -0
  174. package/src/epoch/processParticipationFlagUpdates.ts +27 -0
  175. package/src/epoch/processParticipationRecordUpdates.ts +14 -0
  176. package/src/epoch/processPendingAttestations.ts +75 -0
  177. package/src/epoch/processPendingConsolidations.ts +59 -0
  178. package/src/epoch/processPendingDeposits.ts +136 -0
  179. package/src/epoch/processProposerLookahead.ts +39 -0
  180. package/src/epoch/processRandaoMixesReset.ts +18 -0
  181. package/src/epoch/processRegistryUpdates.ts +65 -0
  182. package/src/epoch/processRewardsAndPenalties.ts +58 -0
  183. package/src/epoch/processSlashings.ts +97 -0
  184. package/src/epoch/processSlashingsReset.ts +20 -0
  185. package/src/epoch/processSyncCommitteeUpdates.ts +44 -0
  186. package/src/index.ts +67 -0
  187. package/src/metrics.ts +169 -0
  188. package/src/signatureSets/attesterSlashings.ts +39 -0
  189. package/src/signatureSets/blsToExecutionChange.ts +43 -0
  190. package/src/signatureSets/index.ts +73 -0
  191. package/src/signatureSets/indexedAttestation.ts +51 -0
  192. package/src/signatureSets/proposer.ts +47 -0
  193. package/src/signatureSets/proposerSlashings.ts +41 -0
  194. package/src/signatureSets/randao.ts +31 -0
  195. package/src/signatureSets/voluntaryExits.ts +44 -0
  196. package/src/slot/index.ts +32 -0
  197. package/src/slot/upgradeStateToAltair.ts +149 -0
  198. package/src/slot/upgradeStateToBellatrix.ts +63 -0
  199. package/src/slot/upgradeStateToCapella.ts +71 -0
  200. package/src/slot/upgradeStateToDeneb.ts +40 -0
  201. package/src/slot/upgradeStateToElectra.ts +126 -0
  202. package/src/slot/upgradeStateToFulu.ts +31 -0
  203. package/src/slot/upgradeStateToGloas.ts +29 -0
  204. package/src/stateTransition.ts +305 -0
  205. package/src/types.ts +26 -0
  206. package/src/util/aggregator.ts +33 -0
  207. package/src/util/altair.ts +13 -0
  208. package/src/util/array.ts +53 -0
  209. package/src/util/attestation.ts +36 -0
  210. package/src/util/attesterStatus.ts +83 -0
  211. package/src/util/balance.ts +83 -0
  212. package/src/util/blindedBlock.ts +145 -0
  213. package/src/util/blockRoot.ts +72 -0
  214. package/src/util/calculateCommitteeAssignments.ts +43 -0
  215. package/src/util/capella.ts +8 -0
  216. package/src/util/computeAnchorCheckpoint.ts +38 -0
  217. package/src/util/deposit.ts +22 -0
  218. package/src/util/domain.ts +31 -0
  219. package/src/util/electra.ts +68 -0
  220. package/src/util/epoch.ts +135 -0
  221. package/src/util/epochShuffling.ts +185 -0
  222. package/src/util/execution.ts +179 -0
  223. package/src/util/finality.ts +17 -0
  224. package/src/util/fulu.ts +43 -0
  225. package/src/util/genesis.ts +340 -0
  226. package/src/util/index.ts +29 -0
  227. package/src/util/interop.ts +22 -0
  228. package/src/util/loadState/findModifiedInactivityScores.ts +47 -0
  229. package/src/util/loadState/findModifiedValidators.ts +46 -0
  230. package/src/util/loadState/index.ts +2 -0
  231. package/src/util/loadState/loadState.ts +225 -0
  232. package/src/util/loadState/loadValidator.ts +77 -0
  233. package/src/util/rootCache.ts +37 -0
  234. package/src/util/seed.ts +381 -0
  235. package/src/util/shufflingDecisionRoot.ts +78 -0
  236. package/src/util/signatureSets.ts +65 -0
  237. package/src/util/signingRoot.ts +13 -0
  238. package/src/util/slot.ts +22 -0
  239. package/src/util/sszBytes.ts +52 -0
  240. package/src/util/syncCommittee.ts +69 -0
  241. package/src/util/targetUnslashedBalance.ts +30 -0
  242. package/src/util/validator.ts +105 -0
  243. package/src/util/weakSubjectivity.ts +186 -0
@@ -0,0 +1,27 @@
1
+ import {zeroNode} from "@chainsafe/persistent-merkle-tree";
2
+ import {ssz} from "@lodestar/types";
3
+ import {CachedBeaconStateAltair} from "../types.js";
4
+
5
+ /**
6
+ * Updates `state.previousEpochParticipation` with precalculated epoch participation. Creates a new empty tree for
7
+ * `state.currentEpochParticipation`.
8
+ *
9
+ * PERF: Cost = 'proportional' $VALIDATOR_COUNT. Since it updates all of them at once, it will always recreate both
10
+ * trees completely.
11
+ */
12
+ export function processParticipationFlagUpdates(state: CachedBeaconStateAltair): void {
13
+ // Set view and tree from currentEpochParticipation to previousEpochParticipation
14
+ state.previousEpochParticipation = state.currentEpochParticipation;
15
+
16
+ // We need to replace the node of currentEpochParticipation with a node that represents and empty list of some length.
17
+ // SSZ represents a list as = new BranchNode(chunksNode, lengthNode).
18
+ // Since the chunks represent all zero'ed data we can re-use the pre-compouted zeroNode at chunkDepth to skip any
19
+ // data transformation and create the required tree almost for free.
20
+ const currentEpochParticipationNode = ssz.altair.EpochParticipation.tree_setChunksNode(
21
+ state.currentEpochParticipation.node,
22
+ zeroNode(ssz.altair.EpochParticipation.chunkDepth),
23
+ state.currentEpochParticipation.length
24
+ );
25
+
26
+ state.currentEpochParticipation = ssz.altair.EpochParticipation.getViewDU(currentEpochParticipationNode);
27
+ }
@@ -0,0 +1,14 @@
1
+ import {ssz} from "@lodestar/types";
2
+ import {CachedBeaconStatePhase0} from "../types.js";
3
+
4
+ /**
5
+ * PERF: Should have zero cost. It just moves a rootNode from one key to another. Then it creates an empty tree on the
6
+ * previous key
7
+ */
8
+ export function processParticipationRecordUpdates(state: CachedBeaconStatePhase0): void {
9
+ // rotate current/previous epoch attestations
10
+ state.previousEpochAttestations = state.currentEpochAttestations;
11
+
12
+ // Reset list to empty
13
+ state.currentEpochAttestations = ssz.phase0.EpochAttestations.defaultViewDU();
14
+ }
@@ -0,0 +1,75 @@
1
+ import {byteArrayEquals} from "@chainsafe/ssz";
2
+ import {Epoch, phase0} from "@lodestar/types";
3
+ import {CachedBeaconStatePhase0} from "../types.js";
4
+ import {computeStartSlotAtEpoch, getBlockRootAtSlot} from "../util/index.js";
5
+
6
+ /**
7
+ * Mutates `proposerIndices`, `inclusionDelays` and `flags` from all pending attestations.
8
+ *
9
+ * PERF: Cost 'proportional' to attestation count + how many bits per attestation + how many flags the attestation triggers
10
+ *
11
+ * - On normal mainnet conditions:
12
+ * - previousEpochAttestations: 3403
13
+ * - currentEpochAttestations: 3129
14
+ * - previousEpochAttestationsBits: 83
15
+ * - currentEpochAttestationsBits: 85
16
+ */
17
+ export function processPendingAttestations(
18
+ state: CachedBeaconStatePhase0,
19
+ proposerIndices: number[],
20
+ inclusionDelays: number[],
21
+ flags: number[],
22
+ attestations: phase0.PendingAttestation[],
23
+ epoch: Epoch,
24
+ sourceFlag: number,
25
+ targetFlag: number,
26
+ headFlag: number
27
+ ): void {
28
+ const {epochCtx, slot: stateSlot} = state;
29
+ const prevEpoch = epochCtx.previousShuffling.epoch;
30
+ if (attestations.length === 0) {
31
+ return;
32
+ }
33
+
34
+ // Prevent frequent object get of external CommonJS dependencies
35
+ const byteArrayEqualsFn = byteArrayEquals;
36
+
37
+ const actualTargetBlockRoot = getBlockRootAtSlot(state, computeStartSlotAtEpoch(epoch));
38
+
39
+ for (const att of attestations) {
40
+ // Ignore empty BitArray, from spec test minimal/phase0/epoch_processing/participation_record_updates updated_participation_record
41
+ // See https://github.com/ethereum/consensus-specs/issues/2825
42
+ if (att.aggregationBits.bitLen === 0) {
43
+ continue;
44
+ }
45
+
46
+ const attData = att.data;
47
+ const inclusionDelay = att.inclusionDelay;
48
+ const proposerIndex = att.proposerIndex;
49
+ const attSlot = attData.slot;
50
+ const attVotedTargetRoot = byteArrayEqualsFn(attData.target.root, actualTargetBlockRoot);
51
+ const attVotedHeadRoot =
52
+ attSlot < stateSlot && byteArrayEqualsFn(attData.beaconBlockRoot, getBlockRootAtSlot(state, attSlot));
53
+ const committee = epochCtx.getBeaconCommittee(attSlot, attData.index);
54
+ const participants = att.aggregationBits.intersectValues(committee);
55
+
56
+ if (epoch === prevEpoch) {
57
+ for (const p of participants) {
58
+ if (proposerIndices[p] === -1 || inclusionDelays[p] > inclusionDelay) {
59
+ proposerIndices[p] = proposerIndex;
60
+ inclusionDelays[p] = inclusionDelay;
61
+ }
62
+ }
63
+ }
64
+
65
+ for (const p of participants) {
66
+ flags[p] |= sourceFlag;
67
+ if (attVotedTargetRoot) {
68
+ flags[p] |= targetFlag;
69
+ if (attVotedHeadRoot) {
70
+ flags[p] |= headFlag;
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,59 @@
1
+ import {CachedBeaconStateElectra, EpochTransitionCache} from "../types.js";
2
+ import {decreaseBalance, increaseBalance} from "../util/balance.js";
3
+
4
+ /**
5
+ * Starting from Electra:
6
+ * Process every `pendingConsolidation` in `state.pendingConsolidations`.
7
+ * Churn limit was applied when enqueueing so we don't care about the limit here
8
+ * However we only process consolidations up to current epoch
9
+ *
10
+ * For each valid `pendingConsolidation`, update withdrawal credential of target
11
+ * validator to compounding, decrease balance of source validator and increase balance
12
+ * of target validator.
13
+ *
14
+ * Dequeue all processed consolidations from `state.pendingConsolidation`
15
+ *
16
+ */
17
+ export function processPendingConsolidations(state: CachedBeaconStateElectra, cache: EpochTransitionCache): void {
18
+ const nextEpoch = state.epochCtx.epoch + 1;
19
+ let nextPendingConsolidation = 0;
20
+ const validators = state.validators;
21
+ const cachedBalances = cache.balances;
22
+
23
+ let chunkStartIndex = 0;
24
+ const chunkSize = 100;
25
+ const pendingConsolidationsLength = state.pendingConsolidations.length;
26
+ outer: while (chunkStartIndex < pendingConsolidationsLength) {
27
+ const consolidationChunk = state.pendingConsolidations.getReadonlyByRange(chunkStartIndex, chunkSize);
28
+
29
+ for (const pendingConsolidation of consolidationChunk) {
30
+ const {sourceIndex, targetIndex} = pendingConsolidation;
31
+ const sourceValidator = validators.getReadonly(sourceIndex);
32
+
33
+ if (sourceValidator.slashed) {
34
+ nextPendingConsolidation++;
35
+ continue;
36
+ }
37
+
38
+ if (sourceValidator.withdrawableEpoch > nextEpoch) {
39
+ break outer;
40
+ }
41
+
42
+ // Calculate the consolidated balance
43
+ const sourceEffectiveBalance = Math.min(state.balances.get(sourceIndex), sourceValidator.effectiveBalance);
44
+
45
+ // Move active balance to target. Excess balance is withdrawable.
46
+ decreaseBalance(state, sourceIndex, sourceEffectiveBalance);
47
+ increaseBalance(state, targetIndex, sourceEffectiveBalance);
48
+ if (cachedBalances) {
49
+ cachedBalances[sourceIndex] -= sourceEffectiveBalance;
50
+ cachedBalances[targetIndex] += sourceEffectiveBalance;
51
+ }
52
+
53
+ nextPendingConsolidation++;
54
+ }
55
+ chunkStartIndex += chunkSize;
56
+ }
57
+
58
+ state.pendingConsolidations = state.pendingConsolidations.sliceFrom(nextPendingConsolidation);
59
+ }
@@ -0,0 +1,136 @@
1
+ import {FAR_FUTURE_EPOCH, ForkSeq, GENESIS_SLOT, MAX_PENDING_DEPOSITS_PER_EPOCH} from "@lodestar/params";
2
+ import {electra} from "@lodestar/types";
3
+ import {addValidatorToRegistry, isValidDepositSignature} from "../block/processDeposit.js";
4
+ import {CachedBeaconStateElectra, EpochTransitionCache} from "../types.js";
5
+ import {increaseBalance} from "../util/balance.js";
6
+ import {hasCompoundingWithdrawalCredential, isValidatorKnown} from "../util/electra.js";
7
+ import {computeStartSlotAtEpoch} from "../util/epoch.js";
8
+ import {getActivationExitChurnLimit} from "../util/validator.js";
9
+
10
+ /**
11
+ * Starting from Electra:
12
+ * Process pending balance deposits from state subject to churn limit and depositBalanceToConsume.
13
+ * For each eligible `deposit`, call `increaseBalance()`.
14
+ * Remove the processed deposits from `state.pendingDeposits`.
15
+ * Update `state.depositBalanceToConsume` for the next epoch
16
+ *
17
+ * TODO Electra: Update ssz library to support batch push to `pendingDeposits`
18
+ */
19
+ export function processPendingDeposits(state: CachedBeaconStateElectra, cache: EpochTransitionCache): void {
20
+ const nextEpoch = state.epochCtx.epoch + 1;
21
+ const availableForProcessing = state.depositBalanceToConsume + BigInt(getActivationExitChurnLimit(state.epochCtx));
22
+ let processedAmount = 0;
23
+ let nextDepositIndex = 0;
24
+ const depositsToPostpone = [];
25
+ let isChurnLimitReached = false;
26
+ const finalizedSlot = computeStartSlotAtEpoch(state.finalizedCheckpoint.epoch);
27
+
28
+ let startIndex = 0;
29
+ // TODO: is this a good number?
30
+ const chunk = 100;
31
+ const pendingDepositsLength = state.pendingDeposits.length;
32
+ outer: while (startIndex < pendingDepositsLength) {
33
+ const deposits = state.pendingDeposits.getReadonlyByRange(startIndex, chunk);
34
+
35
+ for (const deposit of deposits) {
36
+ // Do not process deposit requests if Eth1 bridge deposits are not yet applied.
37
+ if (
38
+ // Is deposit request
39
+ deposit.slot > GENESIS_SLOT &&
40
+ // There are pending Eth1 bridge deposits
41
+ state.eth1DepositIndex < state.depositRequestsStartIndex
42
+ ) {
43
+ break outer;
44
+ }
45
+
46
+ // Check if deposit has been finalized, otherwise, stop processing.
47
+ if (deposit.slot > finalizedSlot) {
48
+ break outer;
49
+ }
50
+
51
+ // Check if number of processed deposits has not reached the limit, otherwise, stop processing.
52
+ if (nextDepositIndex >= MAX_PENDING_DEPOSITS_PER_EPOCH) {
53
+ break outer;
54
+ }
55
+
56
+ // Read validator state
57
+ let isValidatorExited = false;
58
+ let isValidatorWithdrawn = false;
59
+
60
+ const validatorIndex = state.epochCtx.getValidatorIndex(deposit.pubkey);
61
+ if (isValidatorKnown(state, validatorIndex)) {
62
+ const validator = state.validators.getReadonly(validatorIndex);
63
+ isValidatorExited = validator.exitEpoch < FAR_FUTURE_EPOCH;
64
+ isValidatorWithdrawn = validator.withdrawableEpoch < nextEpoch;
65
+ }
66
+
67
+ if (isValidatorWithdrawn) {
68
+ // Deposited balance will never become active. Increase balance but do not consume churn
69
+ applyPendingDeposit(state, deposit, cache);
70
+ } else if (isValidatorExited) {
71
+ // Validator is exiting, postpone the deposit until after withdrawable epoch
72
+ depositsToPostpone.push(deposit);
73
+ } else {
74
+ // Check if deposit fits in the churn, otherwise, do no more deposit processing in this epoch.
75
+ isChurnLimitReached = processedAmount + deposit.amount > availableForProcessing;
76
+ if (isChurnLimitReached) {
77
+ break outer;
78
+ }
79
+ // Consume churn and apply deposit.
80
+ processedAmount += deposit.amount;
81
+ applyPendingDeposit(state, deposit, cache);
82
+ }
83
+
84
+ // Regardless of how the deposit was handled, we move on in the queue.
85
+ nextDepositIndex++;
86
+ }
87
+
88
+ startIndex += chunk;
89
+ }
90
+
91
+ const remainingPendingDeposits = state.pendingDeposits.sliceFrom(nextDepositIndex);
92
+ state.pendingDeposits = remainingPendingDeposits;
93
+
94
+ // TODO Electra: add a function in ListCompositeTreeView to support batch push operation
95
+ for (const deposit of depositsToPostpone) {
96
+ state.pendingDeposits.push(deposit);
97
+ }
98
+
99
+ // Accumulate churn only if the churn limit has been hit.
100
+ if (isChurnLimitReached) {
101
+ state.depositBalanceToConsume = availableForProcessing - BigInt(processedAmount);
102
+ } else {
103
+ state.depositBalanceToConsume = 0n;
104
+ }
105
+ }
106
+
107
+ function applyPendingDeposit(
108
+ state: CachedBeaconStateElectra,
109
+ deposit: electra.PendingDeposit,
110
+ cache: EpochTransitionCache
111
+ ): void {
112
+ const validatorIndex = state.epochCtx.getValidatorIndex(deposit.pubkey);
113
+ const {pubkey, withdrawalCredentials, amount, signature} = deposit;
114
+ const cachedBalances = cache.balances;
115
+
116
+ if (!isValidatorKnown(state, validatorIndex)) {
117
+ // Verify the deposit signature (proof of possession) which is not checked by the deposit contract
118
+ if (isValidDepositSignature(state.config, pubkey, withdrawalCredentials, amount, signature)) {
119
+ addValidatorToRegistry(ForkSeq.electra, state, pubkey, withdrawalCredentials, amount);
120
+ const newValidatorIndex = state.validators.length - 1;
121
+ cache.isCompoundingValidatorArr[newValidatorIndex] = hasCompoundingWithdrawalCredential(withdrawalCredentials);
122
+ // set balance, so that the next deposit of same pubkey will increase the balance correctly
123
+ // this is to fix the double deposit issue found in mekong
124
+ // see https://github.com/ChainSafe/lodestar/pull/7255
125
+ if (cachedBalances) {
126
+ cachedBalances[newValidatorIndex] = amount;
127
+ }
128
+ }
129
+ } else {
130
+ // Increase balance
131
+ increaseBalance(state, validatorIndex, amount);
132
+ if (cachedBalances) {
133
+ cachedBalances[validatorIndex] += amount;
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,39 @@
1
+ import {ForkSeq, MIN_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {ssz} from "@lodestar/types";
3
+ import {CachedBeaconStateFulu, EpochTransitionCache} from "../types.js";
4
+ import {computeEpochShuffling} from "../util/epochShuffling.js";
5
+ import {computeProposerIndices} from "../util/seed.js";
6
+
7
+ /**
8
+ * This function updates the `proposer_lookahead` field in the beacon state
9
+ * by shifting out proposer indices from the earliest epoch and appending new
10
+ * proposer indices for the latest epoch. With `MIN_SEED_LOOKAHEAD` set to `1`,
11
+ * this means that at the start of epoch `N`, the proposer lookahead for epoch
12
+ * `N+1` will be computed and included in the beacon state's lookahead.
13
+ */
14
+ export function processProposerLookahead(
15
+ fork: ForkSeq,
16
+ state: CachedBeaconStateFulu,
17
+ cache: EpochTransitionCache
18
+ ): void {
19
+ // Shift out proposers in the first epoch
20
+ const remainingProposerLookahead = state.proposerLookahead.getAll().slice(SLOTS_PER_EPOCH);
21
+
22
+ // Fill in the last epoch with new proposer indices
23
+ const epoch = state.epochCtx.epoch + MIN_SEED_LOOKAHEAD + 1;
24
+
25
+ const shuffling =
26
+ state.epochCtx.shufflingCache?.getSync(epoch, cache.nextShufflingDecisionRoot, {
27
+ state,
28
+ activeIndices: cache.nextShufflingActiveIndices,
29
+ }) ??
30
+ // Only for testing. shufflingCache should always be available in prod
31
+ computeEpochShuffling(state, cache.nextShufflingActiveIndices, epoch);
32
+
33
+ const lastEpochProposerLookahead = computeProposerIndices(fork, state, shuffling, epoch);
34
+
35
+ state.proposerLookahead = ssz.fulu.ProposerLookahead.toViewDU([
36
+ ...remainingProposerLookahead,
37
+ ...lastEpochProposerLookahead,
38
+ ]);
39
+ }
@@ -0,0 +1,18 @@
1
+ import {EPOCHS_PER_HISTORICAL_VECTOR} from "@lodestar/params";
2
+ import {CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js";
3
+
4
+ /**
5
+ * Write next randaoMix
6
+ *
7
+ * PERF: Almost no (constant) cost
8
+ */
9
+ export function processRandaoMixesReset(state: CachedBeaconStateAllForks, cache: EpochTransitionCache): void {
10
+ const currentEpoch = cache.currentEpoch;
11
+ const nextEpoch = currentEpoch + 1;
12
+
13
+ // set randao mix
14
+ state.randaoMixes.set(
15
+ nextEpoch % EPOCHS_PER_HISTORICAL_VECTOR,
16
+ state.randaoMixes.get(currentEpoch % EPOCHS_PER_HISTORICAL_VECTOR)
17
+ );
18
+ }
@@ -0,0 +1,65 @@
1
+ import {ForkSeq} from "@lodestar/params";
2
+ import {initiateValidatorExit} from "../block/index.js";
3
+ import {CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js";
4
+ import {computeActivationExitEpoch} from "../util/index.js";
5
+
6
+ /**
7
+ * Update validator registry for validators that activate + exit
8
+ *
9
+ * PERF: Cost 'proportional' to only validators that active + exit. For mainnet conditions:
10
+ * - indicesEligibleForActivationQueue: Maxing deposits triggers 512 validator mutations
11
+ * - indicesEligibleForActivation: 4 per epoch
12
+ * - indicesToEject: Potentially the entire validator set. On a massive offline event this could trigger many mutations
13
+ * per epoch. Note that once mutated that validator can't be added to indicesToEject.
14
+ *
15
+ * - On normal mainnet conditions only 4 validators will be updated
16
+ * - indicesEligibleForActivation: ~4000
17
+ * - indicesEligibleForActivationQueue: 0
18
+ * - indicesToEject: 0
19
+ */
20
+ export function processRegistryUpdates(
21
+ fork: ForkSeq,
22
+ state: CachedBeaconStateAllForks,
23
+ cache: EpochTransitionCache
24
+ ): void {
25
+ const {epochCtx} = state;
26
+
27
+ // Get the validators sub tree once for all the loop
28
+ const validators = state.validators;
29
+
30
+ // TODO: Batch set this properties in the tree at once with setMany() or setNodes()
31
+
32
+ // process ejections
33
+ for (const index of cache.indicesToEject) {
34
+ // set validator exit epoch and withdrawable epoch
35
+ // TODO: Figure out a way to quickly set properties on the validators tree
36
+ initiateValidatorExit(fork, state, validators.get(index));
37
+ }
38
+
39
+ // set new activation eligibilities
40
+ for (const index of cache.indicesEligibleForActivationQueue) {
41
+ validators.get(index).activationEligibilityEpoch = epochCtx.epoch + 1;
42
+ }
43
+
44
+ const finalityEpoch = state.finalizedCheckpoint.epoch;
45
+ // this avoids an array allocation compared to `slice(0, epochCtx.activationChurnLimit)`
46
+ const len =
47
+ fork < ForkSeq.electra
48
+ ? Math.min(cache.indicesEligibleForActivation.length, epochCtx.activationChurnLimit)
49
+ : cache.indicesEligibleForActivation.length;
50
+ const activationEpoch = computeActivationExitEpoch(cache.currentEpoch);
51
+ // dequeue validators for activation up to churn limit
52
+ for (let i = 0; i < len; i++) {
53
+ const validatorIndex = cache.indicesEligibleForActivation[i];
54
+ const validator = validators.get(validatorIndex);
55
+ // placement in queue is finalized
56
+ if (validator.activationEligibilityEpoch > finalityEpoch) {
57
+ // remaining validators all have an activationEligibilityEpoch that is higher anyway, break early
58
+ // activationEligibilityEpoch has been sorted in epoch process in ascending order.
59
+ // At that point the finalityEpoch was not known because processJustificationAndFinalization() wasn't called yet.
60
+ // So we need to filter by finalityEpoch here to comply with the spec.
61
+ break;
62
+ }
63
+ validator.activationEpoch = activationEpoch;
64
+ }
65
+ }
@@ -0,0 +1,58 @@
1
+ import {ForkSeq, GENESIS_EPOCH} from "@lodestar/params";
2
+ import {ssz} from "@lodestar/types";
3
+ import {
4
+ CachedBeaconStateAllForks,
5
+ CachedBeaconStateAltair,
6
+ CachedBeaconStatePhase0,
7
+ EpochTransitionCache,
8
+ } from "../types.js";
9
+ import {getAttestationDeltas} from "./getAttestationDeltas.js";
10
+ import {getRewardsAndPenaltiesAltair} from "./getRewardsAndPenalties.js";
11
+
12
+ /**
13
+ * This data is reused and never gc.
14
+ */
15
+ const balances = new Array<number>();
16
+ /**
17
+ * Iterate over all validator and compute rewards and penalties to apply to balances.
18
+ *
19
+ * PERF: Cost = 'proportional' to $VALIDATOR_COUNT. Extra work is done per validator the more status flags are set
20
+ */
21
+ export function processRewardsAndPenalties(
22
+ state: CachedBeaconStateAllForks,
23
+ cache: EpochTransitionCache,
24
+ slashingPenalties: number[] = []
25
+ ): void {
26
+ // No rewards are applied at the end of `GENESIS_EPOCH` because rewards are for work done in the previous epoch
27
+ if (cache.currentEpoch === GENESIS_EPOCH) {
28
+ return;
29
+ }
30
+
31
+ const [rewards, penalties] = getRewardsAndPenalties(state, cache);
32
+ balances.length = state.balances.length;
33
+ state.balances.getAll(balances);
34
+
35
+ for (let i = 0, len = rewards.length; i < len; i++) {
36
+ const result = balances[i] + rewards[i] - penalties[i] - (slashingPenalties[i] ?? 0);
37
+ balances[i] = Math.max(result, 0);
38
+ }
39
+
40
+ // important: do not change state one balance at a time. Set them all at once, constructing the tree in one go
41
+ // cache the balances array, too
42
+ state.balances = ssz.phase0.Balances.toViewDU(balances);
43
+
44
+ // For processEffectiveBalanceUpdates() to prevent having to re-compute the balances array.
45
+ // For validator metrics
46
+ cache.balances = balances;
47
+ }
48
+
49
+ // Note: abstracted in separate function for easier spec tests
50
+ export function getRewardsAndPenalties(
51
+ state: CachedBeaconStateAllForks,
52
+ epochTransitionCache: EpochTransitionCache
53
+ ): [number[], number[]] {
54
+ const fork = state.config.getForkSeq(state.slot);
55
+ return fork === ForkSeq.phase0
56
+ ? getAttestationDeltas(state as CachedBeaconStatePhase0, epochTransitionCache)
57
+ : getRewardsAndPenaltiesAltair(state as CachedBeaconStateAltair, epochTransitionCache);
58
+ }
@@ -0,0 +1,97 @@
1
+ import {
2
+ EFFECTIVE_BALANCE_INCREMENT,
3
+ ForkSeq,
4
+ PROPORTIONAL_SLASHING_MULTIPLIER,
5
+ PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR,
6
+ PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX,
7
+ } from "@lodestar/params";
8
+ import {BeaconStateAllForks, CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js";
9
+ import {decreaseBalance} from "../util/index.js";
10
+
11
+ /**
12
+ * Update validator registry for validators that activate + exit
13
+ * updateBalance is an optimization:
14
+ * - For spec test, it's true
15
+ * - For processEpoch flow, it's false, i.e to only update balances once in processRewardsAndPenalties()
16
+ *
17
+ * PERF: almost no (constant) cost.
18
+ * - Total slashings by increment is computed once and stored in state.epochCtx.totalSlashingsByIncrement so no need to compute here
19
+ * - Penalties for validators with the same effective balance are the same and computed once
20
+ * - No need to apply penalties to validators here, do it once in processRewardsAndPenalties()
21
+ * - indicesToSlash: max len is 8704. But it's very unlikely since it would require all validators on the same
22
+ * committees to sign slashable attestations.
23
+ * - On normal mainnet conditions indicesToSlash = 0
24
+ *
25
+ * @returns slashing penalties to be applied in processRewardsAndPenalties()
26
+ */
27
+ export function processSlashings(
28
+ state: CachedBeaconStateAllForks,
29
+ cache: EpochTransitionCache,
30
+ updateBalance = true
31
+ ): number[] {
32
+ // Return early if there no index to slash
33
+ if (cache.indicesToSlash.length === 0) {
34
+ return [];
35
+ }
36
+
37
+ const totalBalanceByIncrement = cache.totalActiveStakeByIncrement;
38
+ const fork = state.config.getForkSeq(state.slot);
39
+ const proportionalSlashingMultiplier =
40
+ fork === ForkSeq.phase0
41
+ ? PROPORTIONAL_SLASHING_MULTIPLIER
42
+ : fork === ForkSeq.altair
43
+ ? PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR
44
+ : PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX;
45
+
46
+ const {effectiveBalanceIncrements} = state.epochCtx;
47
+ const adjustedTotalSlashingBalanceByIncrement = Math.min(
48
+ state.epochCtx.totalSlashingsByIncrement * proportionalSlashingMultiplier,
49
+ totalBalanceByIncrement
50
+ );
51
+ const increment = EFFECTIVE_BALANCE_INCREMENT;
52
+
53
+ const penaltyPerEffectiveBalanceIncrement = Math.floor(
54
+ (adjustedTotalSlashingBalanceByIncrement * increment) / totalBalanceByIncrement
55
+ );
56
+ const penalties: number[] = [];
57
+
58
+ const penaltiesByEffectiveBalanceIncrement = new Map<number, number>();
59
+ for (const index of cache.indicesToSlash) {
60
+ const effectiveBalanceIncrement = effectiveBalanceIncrements[index];
61
+ let penalty = penaltiesByEffectiveBalanceIncrement.get(effectiveBalanceIncrement);
62
+ if (penalty === undefined) {
63
+ if (fork < ForkSeq.electra) {
64
+ const penaltyNumeratorByIncrement = effectiveBalanceIncrement * adjustedTotalSlashingBalanceByIncrement;
65
+ penalty = Math.floor(penaltyNumeratorByIncrement / totalBalanceByIncrement) * increment;
66
+ } else {
67
+ penalty = penaltyPerEffectiveBalanceIncrement * effectiveBalanceIncrement;
68
+ }
69
+ penaltiesByEffectiveBalanceIncrement.set(effectiveBalanceIncrement, penalty);
70
+ }
71
+
72
+ if (updateBalance) {
73
+ // for spec test only
74
+ decreaseBalance(state, index, penalty);
75
+ } else {
76
+ // do it later in processRewardsAndPenalties()
77
+ penalties[index] = penalty;
78
+ }
79
+ }
80
+
81
+ return penalties;
82
+ }
83
+
84
+ /**
85
+ * Get total slashings by increment.
86
+ * By default, total slashings are computed every time we run processSlashings() function above.
87
+ * We improve it by computing it once and store it in state.epochCtx.totalSlashingsByIncrement
88
+ * Every change to state.slashings should update totalSlashingsByIncrement.
89
+ */
90
+ export function getTotalSlashingsByIncrement(state: BeaconStateAllForks): number {
91
+ let totalSlashingsByIncrement = 0;
92
+ const slashings = state.slashings.getAll();
93
+ for (let i = 0; i < slashings.length; i++) {
94
+ totalSlashingsByIncrement += Math.floor(slashings[i] / EFFECTIVE_BALANCE_INCREMENT);
95
+ }
96
+ return totalSlashingsByIncrement;
97
+ }
@@ -0,0 +1,20 @@
1
+ import {EFFECTIVE_BALANCE_INCREMENT, EPOCHS_PER_SLASHINGS_VECTOR} from "@lodestar/params";
2
+ import {CachedBeaconStateAllForks, EpochTransitionCache} from "../types.js";
3
+
4
+ /**
5
+ * Reset the next slashings balance accumulator
6
+ *
7
+ * PERF: Almost no (constant) cost
8
+ */
9
+ export function processSlashingsReset(state: CachedBeaconStateAllForks, cache: EpochTransitionCache): void {
10
+ const nextEpoch = cache.currentEpoch + 1;
11
+
12
+ // reset slashings
13
+ const slashIndex = nextEpoch % EPOCHS_PER_SLASHINGS_VECTOR;
14
+ const oldSlashingValueByIncrement = Math.floor(state.slashings.get(slashIndex) / EFFECTIVE_BALANCE_INCREMENT);
15
+ state.slashings.set(slashIndex, 0);
16
+ state.epochCtx.totalSlashingsByIncrement = Math.max(
17
+ 0,
18
+ state.epochCtx.totalSlashingsByIncrement - oldSlashingValueByIncrement
19
+ );
20
+ }
@@ -0,0 +1,44 @@
1
+ import {aggregateSerializedPublicKeys} from "@chainsafe/blst";
2
+ import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD, ForkSeq} from "@lodestar/params";
3
+ import {ssz} from "@lodestar/types";
4
+ import {CachedBeaconStateAltair} from "../types.js";
5
+ import {getNextSyncCommitteeIndices} from "../util/seed.js";
6
+
7
+ /**
8
+ * Rotate nextSyncCommittee to currentSyncCommittee if sync committee period is over.
9
+ *
10
+ * PERF: Once every `EPOCHS_PER_SYNC_COMMITTEE_PERIOD`, do an expensive operation to compute the next committee.
11
+ * Calculating the next sync committee has a proportional cost to $VALIDATOR_COUNT
12
+ */
13
+ export function processSyncCommitteeUpdates(fork: ForkSeq, state: CachedBeaconStateAltair): void {
14
+ const nextEpoch = state.epochCtx.epoch + 1;
15
+
16
+ if (nextEpoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD === 0) {
17
+ const activeValidatorIndices = state.epochCtx.nextActiveIndices;
18
+ const {effectiveBalanceIncrements} = state.epochCtx;
19
+
20
+ const nextSyncCommitteeIndices = getNextSyncCommitteeIndices(
21
+ fork,
22
+ state,
23
+ activeValidatorIndices,
24
+ effectiveBalanceIncrements
25
+ );
26
+ const validators = state.validators;
27
+
28
+ // Using the index2pubkey cache is slower because it needs the serialized pubkey.
29
+ const nextSyncCommitteePubkeys = [];
30
+ for (const index of nextSyncCommitteeIndices) {
31
+ nextSyncCommitteePubkeys.push(validators.getReadonly(index).pubkey);
32
+ }
33
+
34
+ // Rotate syncCommittee in state
35
+ state.currentSyncCommittee = state.nextSyncCommittee;
36
+ state.nextSyncCommittee = ssz.altair.SyncCommittee.toViewDU({
37
+ pubkeys: nextSyncCommitteePubkeys,
38
+ aggregatePubkey: aggregateSerializedPublicKeys(nextSyncCommitteePubkeys).toBytes(),
39
+ });
40
+
41
+ // Rotate syncCommittee cache
42
+ state.epochCtx.rotateSyncCommitteeIndexed(nextSyncCommitteeIndices);
43
+ }
44
+ }