@lodestar/state-transition 1.41.0-dev.702f7932c2 → 1.41.0-dev.95cf2edc4c

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 (159) hide show
  1. package/lib/block/isValidIndexedAttestation.d.ts +3 -3
  2. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
  3. package/lib/block/isValidIndexedAttestation.js +4 -4
  4. package/lib/block/isValidIndexedAttestation.js.map +1 -1
  5. package/lib/block/isValidIndexedPayloadAttestation.js +1 -1
  6. package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -1
  7. package/lib/block/processAttestationPhase0.js +1 -1
  8. package/lib/block/processAttestationPhase0.js.map +1 -1
  9. package/lib/block/processAttestationsAltair.js +1 -1
  10. package/lib/block/processAttestationsAltair.js.map +1 -1
  11. package/lib/block/processAttesterSlashing.d.ts +2 -2
  12. package/lib/block/processAttesterSlashing.d.ts.map +1 -1
  13. package/lib/block/processAttesterSlashing.js +3 -3
  14. package/lib/block/processAttesterSlashing.js.map +1 -1
  15. package/lib/block/processDepositRequest.d.ts +1 -1
  16. package/lib/block/processDepositRequest.d.ts.map +1 -1
  17. package/lib/block/processDepositRequest.js +6 -5
  18. package/lib/block/processDepositRequest.js.map +1 -1
  19. package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
  20. package/lib/block/processExecutionPayloadBid.js +5 -0
  21. package/lib/block/processExecutionPayloadBid.js.map +1 -1
  22. package/lib/block/processExecutionPayloadEnvelope.js +5 -10
  23. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -1
  24. package/lib/block/processProposerSlashing.d.ts +2 -2
  25. package/lib/block/processProposerSlashing.d.ts.map +1 -1
  26. package/lib/block/processProposerSlashing.js +3 -3
  27. package/lib/block/processProposerSlashing.js.map +1 -1
  28. package/lib/block/processRandao.js +1 -1
  29. package/lib/block/processRandao.js.map +1 -1
  30. package/lib/block/processSyncCommittee.js +1 -1
  31. package/lib/block/processSyncCommittee.js.map +1 -1
  32. package/lib/block/processVoluntaryExit.js +1 -1
  33. package/lib/block/processVoluntaryExit.js.map +1 -1
  34. package/lib/block/processWithdrawalRequest.js +2 -2
  35. package/lib/block/processWithdrawalRequest.js.map +1 -1
  36. package/lib/block/processWithdrawals.d.ts.map +1 -1
  37. package/lib/block/processWithdrawals.js +9 -1
  38. package/lib/block/processWithdrawals.js.map +1 -1
  39. package/lib/cache/epochCache.d.ts +9 -18
  40. package/lib/cache/epochCache.d.ts.map +1 -1
  41. package/lib/cache/epochCache.js +28 -41
  42. package/lib/cache/epochCache.js.map +1 -1
  43. package/lib/cache/pubkeyCache.d.ts +21 -6
  44. package/lib/cache/pubkeyCache.d.ts.map +1 -1
  45. package/lib/cache/pubkeyCache.js +39 -14
  46. package/lib/cache/pubkeyCache.js.map +1 -1
  47. package/lib/cache/stateCache.d.ts +1 -1
  48. package/lib/cache/stateCache.d.ts.map +1 -1
  49. package/lib/cache/stateCache.js +3 -7
  50. package/lib/cache/stateCache.js.map +1 -1
  51. package/lib/cache/syncCommitteeCache.d.ts +3 -2
  52. package/lib/cache/syncCommitteeCache.d.ts.map +1 -1
  53. package/lib/cache/syncCommitteeCache.js +4 -4
  54. package/lib/cache/syncCommitteeCache.js.map +1 -1
  55. package/lib/index.d.ts +3 -1
  56. package/lib/index.d.ts.map +1 -1
  57. package/lib/index.js +2 -1
  58. package/lib/index.js.map +1 -1
  59. package/lib/lightClient/proofs.d.ts +10 -0
  60. package/lib/lightClient/proofs.d.ts.map +1 -0
  61. package/lib/lightClient/proofs.js +63 -0
  62. package/lib/lightClient/proofs.js.map +1 -0
  63. package/lib/lightClient/types.d.ts +34 -0
  64. package/lib/lightClient/types.d.ts.map +1 -0
  65. package/lib/lightClient/types.js +2 -0
  66. package/lib/lightClient/types.js.map +1 -0
  67. package/lib/rewards/attestationsRewards.d.ts +2 -2
  68. package/lib/rewards/attestationsRewards.d.ts.map +1 -1
  69. package/lib/rewards/attestationsRewards.js +4 -4
  70. package/lib/rewards/attestationsRewards.js.map +1 -1
  71. package/lib/rewards/syncCommitteeRewards.d.ts +2 -2
  72. package/lib/rewards/syncCommitteeRewards.d.ts.map +1 -1
  73. package/lib/rewards/syncCommitteeRewards.js +5 -2
  74. package/lib/rewards/syncCommitteeRewards.js.map +1 -1
  75. package/lib/signatureSets/indexedPayloadAttestation.d.ts +3 -4
  76. package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -1
  77. package/lib/signatureSets/indexedPayloadAttestation.js +4 -4
  78. package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -1
  79. package/lib/signatureSets/proposer.d.ts +2 -2
  80. package/lib/signatureSets/proposer.d.ts.map +1 -1
  81. package/lib/signatureSets/proposer.js +2 -2
  82. package/lib/signatureSets/proposer.js.map +1 -1
  83. package/lib/signatureSets/randao.d.ts +2 -2
  84. package/lib/signatureSets/randao.d.ts.map +1 -1
  85. package/lib/signatureSets/randao.js +2 -2
  86. package/lib/signatureSets/randao.js.map +1 -1
  87. package/lib/signatureSets/voluntaryExits.d.ts +2 -2
  88. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  89. package/lib/signatureSets/voluntaryExits.js +2 -2
  90. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  91. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  92. package/lib/slot/upgradeStateToGloas.js +50 -0
  93. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  94. package/lib/stateTransition.d.ts +2 -1
  95. package/lib/stateTransition.d.ts.map +1 -1
  96. package/lib/stateTransition.js +2 -1
  97. package/lib/stateTransition.js.map +1 -1
  98. package/lib/stateView/beaconStateView.d.ts +144 -0
  99. package/lib/stateView/beaconStateView.d.ts.map +1 -0
  100. package/lib/stateView/beaconStateView.js +496 -0
  101. package/lib/stateView/beaconStateView.js.map +1 -0
  102. package/lib/stateView/index.d.ts +3 -0
  103. package/lib/stateView/index.d.ts.map +1 -0
  104. package/lib/stateView/index.js +3 -0
  105. package/lib/stateView/index.js.map +1 -0
  106. package/lib/stateView/interface.d.ts +118 -0
  107. package/lib/stateView/interface.d.ts.map +1 -0
  108. package/lib/stateView/interface.js +2 -0
  109. package/lib/stateView/interface.js.map +1 -0
  110. package/lib/util/gloas.d.ts.map +1 -1
  111. package/lib/util/gloas.js +10 -3
  112. package/lib/util/gloas.js.map +1 -1
  113. package/lib/util/seed.d.ts +20 -4
  114. package/lib/util/seed.d.ts.map +1 -1
  115. package/lib/util/seed.js +80 -8
  116. package/lib/util/seed.js.map +1 -1
  117. package/lib/util/signatureSets.d.ts +7 -7
  118. package/lib/util/signatureSets.d.ts.map +1 -1
  119. package/lib/util/signatureSets.js +18 -12
  120. package/lib/util/signatureSets.js.map +1 -1
  121. package/lib/util/weakSubjectivity.js +1 -1
  122. package/lib/util/weakSubjectivity.js.map +1 -1
  123. package/package.json +7 -7
  124. package/src/block/isValidIndexedAttestation.ts +5 -5
  125. package/src/block/isValidIndexedPayloadAttestation.ts +2 -2
  126. package/src/block/processAttestationPhase0.ts +1 -1
  127. package/src/block/processAttestationsAltair.ts +1 -1
  128. package/src/block/processAttesterSlashing.ts +4 -4
  129. package/src/block/processDepositRequest.ts +8 -5
  130. package/src/block/processExecutionPayloadBid.ts +8 -0
  131. package/src/block/processExecutionPayloadEnvelope.ts +5 -16
  132. package/src/block/processProposerSlashing.ts +4 -4
  133. package/src/block/processRandao.ts +1 -1
  134. package/src/block/processSyncCommittee.ts +1 -1
  135. package/src/block/processVoluntaryExit.ts +1 -1
  136. package/src/block/processWithdrawalRequest.ts +2 -2
  137. package/src/block/processWithdrawals.ts +10 -1
  138. package/src/cache/epochCache.ts +41 -55
  139. package/src/cache/pubkeyCache.ts +62 -21
  140. package/src/cache/stateCache.ts +4 -8
  141. package/src/cache/syncCommitteeCache.ts +4 -5
  142. package/src/index.ts +3 -1
  143. package/src/lightClient/proofs.ts +83 -0
  144. package/src/lightClient/types.ts +33 -0
  145. package/src/rewards/attestationsRewards.ts +5 -5
  146. package/src/rewards/syncCommitteeRewards.ts +6 -5
  147. package/src/signatureSets/indexedPayloadAttestation.ts +4 -6
  148. package/src/signatureSets/proposer.ts +3 -3
  149. package/src/signatureSets/randao.ts +3 -7
  150. package/src/signatureSets/voluntaryExits.ts +3 -3
  151. package/src/slot/upgradeStateToGloas.ts +74 -0
  152. package/src/stateTransition.ts +2 -1
  153. package/src/stateView/beaconStateView.ts +744 -0
  154. package/src/stateView/index.ts +2 -0
  155. package/src/stateView/interface.ts +196 -0
  156. package/src/util/gloas.ts +11 -3
  157. package/src/util/seed.ts +106 -18
  158. package/src/util/signatureSets.ts +23 -17
  159. package/src/util/weakSubjectivity.ts +1 -1
@@ -0,0 +1,2 @@
1
+ export * from "./beaconStateView.js";
2
+ export * from "./interface.js";
@@ -0,0 +1,196 @@
1
+ import {CompactMultiProof} from "@chainsafe/persistent-merkle-tree";
2
+ import {ByteViews} from "@chainsafe/ssz";
3
+ import {
4
+ BeaconBlock,
5
+ BlindedBeaconBlock,
6
+ BuilderIndex,
7
+ Bytes32,
8
+ Epoch,
9
+ ExecutionPayloadBid,
10
+ ExecutionPayloadHeader,
11
+ Root,
12
+ RootHex,
13
+ SignedBeaconBlock,
14
+ SignedBlindedBeaconBlock,
15
+ Slot,
16
+ ValidatorIndex,
17
+ altair,
18
+ capella,
19
+ electra,
20
+ fulu,
21
+ gloas,
22
+ phase0,
23
+ rewards,
24
+ } from "@lodestar/types";
25
+ import {Checkpoint, Fork} from "@lodestar/types/phase0";
26
+ import {VoluntaryExitValidity} from "../block/processVoluntaryExit.js";
27
+ import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
28
+ import {EpochTransitionCacheOpts} from "../cache/epochTransitionCache.js";
29
+ import {RewardCache} from "../cache/rewardCache.js";
30
+ import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
31
+ import {SyncCommitteeWitness} from "../lightClient/types.js";
32
+ import {StateTransitionModules, StateTransitionOpts} from "../stateTransition.js";
33
+ import {EpochShuffling} from "../util/epochShuffling.js";
34
+
35
+ /**
36
+ * A read-only view of the BeaconState.
37
+ */
38
+ export interface IBeaconStateView {
39
+ // State access
40
+
41
+ // phase0
42
+ slot: Slot;
43
+ fork: Fork;
44
+ epoch: Epoch;
45
+ genesisTime: number;
46
+ genesisValidatorsRoot: Root;
47
+ eth1Data: phase0.Eth1Data;
48
+ latestBlockHeader: phase0.BeaconBlockHeader;
49
+ previousJustifiedCheckpoint: Checkpoint;
50
+ currentJustifiedCheckpoint: Checkpoint;
51
+ finalizedCheckpoint: Checkpoint;
52
+ getBlockRootAtSlot(slot: Slot): Root;
53
+ getBlockRootAtEpoch(epoch: Epoch): Root;
54
+ getStateRootAtSlot(slot: Slot): Root;
55
+ getRandaoMix(epoch: Epoch): Bytes32;
56
+
57
+ // altair
58
+ previousEpochParticipation: number[];
59
+ currentEpochParticipation: number[];
60
+
61
+ // bellatrix
62
+ latestExecutionPayloadHeader: ExecutionPayloadHeader;
63
+
64
+ // capella
65
+ historicalSummaries: capella.HistoricalSummaries;
66
+
67
+ // electra
68
+ pendingDeposits: electra.PendingDeposits;
69
+ pendingDepositsCount: number;
70
+ pendingPartialWithdrawals: electra.PendingPartialWithdrawals;
71
+ pendingPartialWithdrawalsCount: number;
72
+ pendingConsolidations: electra.PendingConsolidations;
73
+ pendingConsolidationsCount: number;
74
+
75
+ // fulu
76
+ proposerLookahead: fulu.ProposerLookahead;
77
+
78
+ // gloas
79
+ executionPayloadAvailability: boolean[];
80
+ latestExecutionPayloadBid: ExecutionPayloadBid;
81
+ getBuilder(index: BuilderIndex): gloas.Builder;
82
+ canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
83
+ validatorPTCCommitteeIndex(validatorIndex: ValidatorIndex, slot: Slot): number;
84
+
85
+ // Shuffling and committees
86
+ getShufflingAtEpoch(epoch: Epoch): EpochShuffling;
87
+ // Decision roots
88
+ previousDecisionRoot: RootHex;
89
+ currentDecisionRoot: RootHex;
90
+ nextDecisionRoot: RootHex;
91
+ getShufflingDecisionRoot(epoch: Epoch): RootHex;
92
+ getPreviousShuffling(): EpochShuffling;
93
+ getCurrentShuffling(): EpochShuffling;
94
+ getNextShuffling(): EpochShuffling;
95
+
96
+ // utils: proposers, anchor checkpoint
97
+ previousProposers: ValidatorIndex[] | null;
98
+ currentProposers: ValidatorIndex[];
99
+ nextProposers: ValidatorIndex[];
100
+ getBeaconProposer(slot: Slot): ValidatorIndex;
101
+ computeAnchorCheckpoint(): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader};
102
+
103
+ // Sync committees
104
+ currentSyncCommittee: altair.SyncCommittee;
105
+ nextSyncCommittee: altair.SyncCommittee;
106
+ currentSyncCommitteeIndexed: SyncCommitteeCache;
107
+ syncProposerReward: number;
108
+ getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache;
109
+
110
+ // Validators and balances
111
+ effectiveBalanceIncrements: EffectiveBalanceIncrements;
112
+ getEffectiveBalanceIncrementsZeroInactive(): EffectiveBalanceIncrements;
113
+ getBalance(index: number): number;
114
+ // readonly
115
+ getValidator(index: ValidatorIndex): phase0.Validator;
116
+ getValidatorsByStatus(statuses: Set<string>, currentEpoch: Epoch): phase0.Validator[];
117
+ validatorCount: number;
118
+ // this get number of active validators in the current shuffling
119
+ activeValidatorCount: number;
120
+ // this is needed for apis only
121
+ getAllValidators(): phase0.Validator[];
122
+ getAllBalances(): number[];
123
+
124
+ // Merge
125
+ isExecutionStateType: boolean;
126
+ isMergeTransitionComplete: boolean;
127
+ // TODO this should go away (or rather only need block)
128
+ isExecutionEnabled(block: BeaconBlock | BlindedBeaconBlock): boolean;
129
+
130
+ // Block production
131
+ getExpectedWithdrawals(): {
132
+ expectedWithdrawals: capella.Withdrawal[];
133
+ processedBuilderWithdrawalsCount: number;
134
+ processedPartialWithdrawalsCount: number;
135
+ processedValidatorSweepCount: number;
136
+ };
137
+
138
+ // API
139
+ proposerRewards: RewardCache;
140
+ computeBlockRewards(block: BeaconBlock, proposerRewards?: RewardCache): Promise<rewards.BlockRewards>;
141
+ computeAttestationsRewards(validatorIds?: (ValidatorIndex | string)[]): Promise<rewards.AttestationsRewards>;
142
+ computeSyncCommitteeRewards(
143
+ block: BeaconBlock,
144
+ validatorIds: (ValidatorIndex | string)[]
145
+ ): Promise<rewards.SyncCommitteeRewards>;
146
+ getLatestWeakSubjectivityCheckpointEpoch(): Epoch;
147
+
148
+ // Validation
149
+ getVoluntaryExitValidity(
150
+ signedVoluntaryExit: phase0.SignedVoluntaryExit,
151
+ verifySignature: boolean
152
+ ): VoluntaryExitValidity;
153
+ isValidVoluntaryExit(signedVoluntaryExit: phase0.SignedVoluntaryExit, verifySignature: boolean): boolean;
154
+
155
+ // Proofs
156
+ getFinalizedRootProof(): Uint8Array[];
157
+ getSyncCommitteesWitness(): SyncCommitteeWitness;
158
+ getSingleProof(gindex: bigint): Uint8Array[];
159
+ createMultiProof(descriptor: Uint8Array): CompactMultiProof;
160
+
161
+ // Fork choice
162
+ computeUnrealizedCheckpoints(): {
163
+ justifiedCheckpoint: phase0.Checkpoint;
164
+ finalizedCheckpoint: phase0.Checkpoint;
165
+ };
166
+
167
+ // this is for backward compatible
168
+ clonedCount: number;
169
+ clonedCountWithTransferCache: number;
170
+ createdWithTransferCache: boolean;
171
+ // TODO is there a better name that is less implementation specific but still conveys the meaning?
172
+ isStateValidatorsNodesPopulated(): boolean;
173
+
174
+ // Serialization
175
+ loadOtherState(stateBytes: Uint8Array, seedValidatorsBytes?: Uint8Array): IBeaconStateView;
176
+ serialize(): Uint8Array;
177
+ serializedSize(): number;
178
+ serializeToBytes(output: ByteViews, offset: number): number;
179
+ serializeValidators(): Uint8Array;
180
+ serializedValidatorsSize(): number;
181
+ serializeValidatorsToBytes(output: ByteViews, offset: number): number;
182
+
183
+ hashTreeRoot(): Uint8Array;
184
+
185
+ // State transition
186
+ stateTransition(
187
+ signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock,
188
+ options: StateTransitionOpts,
189
+ modules: StateTransitionModules
190
+ ): IBeaconStateView;
191
+ processSlots(
192
+ slot: Slot,
193
+ epochTransitionCacheOpts?: EpochTransitionCacheOpts & {dontTransferCache?: boolean},
194
+ modules?: StateTransitionModules
195
+ ): IBeaconStateView;
196
+ }
package/src/util/gloas.ts CHANGED
@@ -28,12 +28,18 @@ export function getBuilderPaymentQuorumThreshold(state: CachedBeaconStateGloas):
28
28
  return Math.floor(quorum / BUILDER_PAYMENT_THRESHOLD_DENOMINATOR);
29
29
  }
30
30
 
31
+ function hasBuilderIndexFlag(index: number): boolean {
32
+ // Equivalent to `(index & BUILDER_INDEX_FLAG) != 0`
33
+ return Math.floor(index / BUILDER_INDEX_FLAG) % 2 === 1;
34
+ }
35
+
31
36
  /**
32
37
  * Check if a validator index represents a builder (has the builder flag set).
33
38
  * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/gloas/beacon-chain.md#new-is_builder_index
34
39
  */
35
40
  export function isBuilderIndex(validatorIndex: number): boolean {
36
- return (validatorIndex & BUILDER_INDEX_FLAG) !== 0;
41
+ // Note: Can't use bitwise AND (&) because BUILDER_INDEX_FLAG exceeds 32 bits in JS bitwise operations.
42
+ return hasBuilderIndexFlag(validatorIndex);
37
43
  }
38
44
 
39
45
  /**
@@ -41,7 +47,8 @@ export function isBuilderIndex(validatorIndex: number): boolean {
41
47
  * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/gloas/beacon-chain.md#new-convert_builder_index_to_validator_index
42
48
  */
43
49
  export function convertBuilderIndexToValidatorIndex(builderIndex: BuilderIndex): ValidatorIndex {
44
- return builderIndex | BUILDER_INDEX_FLAG;
50
+ // Note: Can't use bitwise OR (|) because BUILDER_INDEX_FLAG exceeds 32 bits in JS bitwise operations.
51
+ return hasBuilderIndexFlag(builderIndex) ? builderIndex : builderIndex + BUILDER_INDEX_FLAG;
45
52
  }
46
53
 
47
54
  /**
@@ -49,7 +56,8 @@ export function convertBuilderIndexToValidatorIndex(builderIndex: BuilderIndex):
49
56
  * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/gloas/beacon-chain.md#new-convert_validator_index_to_builder_index
50
57
  */
51
58
  export function convertValidatorIndexToBuilderIndex(validatorIndex: ValidatorIndex): BuilderIndex {
52
- return validatorIndex & ~BUILDER_INDEX_FLAG;
59
+ // Note: Can't use bitwise AND (&) because BUILDER_INDEX_FLAG exceeds 32 bits in JS bitwise operations.
60
+ return hasBuilderIndexFlag(validatorIndex) ? validatorIndex - BUILDER_INDEX_FLAG : validatorIndex;
53
61
  }
54
62
 
55
63
  /**
package/src/util/seed.ts CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  import {Bytes32, DomainType, Epoch, ValidatorIndex} from "@lodestar/types";
22
22
  import {assert, bytesToBigInt, bytesToInt, intToBytes} from "@lodestar/utils";
23
23
  import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
24
- import {BeaconStateAllForks, BeaconStateGloas, CachedBeaconStateAllForks} from "../types.js";
24
+ import {BeaconStateAllForks, CachedBeaconStateAllForks} from "../types.js";
25
25
  import {computeEpochAtSlot, computeStartSlotAtEpoch} from "./epoch.js";
26
26
 
27
27
  /**
@@ -268,29 +268,117 @@ export function getNextSyncCommitteeIndices(
268
268
  );
269
269
  }
270
270
 
271
- export function naiveGetPayloadTimlinessCommitteeIndices(
272
- state: BeaconStateGloas,
273
- shuffling: {committees: Uint32Array[][]},
274
- effectiveBalanceIncrements: EffectiveBalanceIncrements,
275
- epoch: Epoch
276
- ): ValidatorIndex[][] {
271
+ /**
272
+ * Compute PTC for all slots in an epoch eagerly.
273
+ */
274
+ export function computePayloadTimelinessCommitteesForEpoch(
275
+ state: BeaconStateAllForks,
276
+ epoch: number,
277
+ committees: Uint32Array[][],
278
+ effectiveBalanceIncrements: EffectiveBalanceIncrements
279
+ ): Uint32Array[] {
277
280
  const epochSeed = getSeed(state, epoch, DOMAIN_PTC_ATTESTER);
278
- const startSlot = computeStartSlotAtEpoch(epoch);
279
- const committeeIndices = [];
281
+ const startSlot = epoch * SLOTS_PER_EPOCH;
282
+ const result: Uint32Array[] = new Array(SLOTS_PER_EPOCH);
283
+
284
+ // Pre-allocate slot seed buffer once, reuse across all slots
285
+ const slotSeedInput = new Uint8Array(epochSeed.length + 8);
286
+ slotSeedInput.set(epochSeed, 0);
287
+ const slotSeedView = new DataView(slotSeedInput.buffer, slotSeedInput.byteOffset, slotSeedInput.byteLength);
288
+
289
+ for (let i = 0; i < SLOTS_PER_EPOCH; i++) {
290
+ const slot = startSlot + i;
291
+ // Write slot as little-endian uint64 (fits in uint32 range)
292
+ slotSeedView.setUint32(epochSeed.length, slot, true);
293
+ slotSeedView.setUint32(epochSeed.length + 4, 0, true);
294
+ const slotSeed = digest(slotSeedInput);
295
+
296
+ result[i] = computePayloadTimelinessCommitteeForSlot(slotSeed, committees[i], effectiveBalanceIncrements);
297
+ }
298
+ return result;
299
+ }
280
300
 
281
- for (let slot = startSlot; slot < startSlot + SLOTS_PER_EPOCH; slot++) {
282
- const slotCommittees = shuffling.committees[slot % SLOTS_PER_EPOCH];
283
- const indices = naiveComputePayloadTimelinessCommitteeIndices(
284
- effectiveBalanceIncrements,
285
- slotCommittees.flatMap((c) => Array.from(c)),
286
- digest(Buffer.concat([epochSeed, intToBytes(slot, 8)]))
287
- );
288
- committeeIndices.push(indices);
301
+ /**
302
+ * Compute PTC for a single slot.
303
+ */
304
+ export function computePayloadTimelinessCommitteeForSlot(
305
+ slotSeed: Uint8Array,
306
+ slotCommittees: Uint32Array[],
307
+ effectiveBalanceIncrements: EffectiveBalanceIncrements
308
+ ): Uint32Array {
309
+ // Concatenate all committee Uint32Arrays for this slot
310
+ const totalLen = slotCommittees.reduce((sum, c) => sum + c.length, 0);
311
+ const allIndices = new Uint32Array(totalLen);
312
+ let offset = 0;
313
+ for (const c of slotCommittees) {
314
+ allIndices.set(c, offset);
315
+ offset += c.length;
316
+ }
317
+ return computePayloadTimelinessCommitteeIndices(effectiveBalanceIncrements, allIndices, slotSeed);
318
+ }
319
+
320
+ /**
321
+ * Optimized version of PTC indices computation.
322
+ * Avoids BigInt conversions and uses DataView for efficient byte reading.
323
+ */
324
+ export function computePayloadTimelinessCommitteeIndices(
325
+ effectiveBalanceIncrements: EffectiveBalanceIncrements,
326
+ indices: Uint32Array,
327
+ seed: Uint8Array
328
+ ): Uint32Array {
329
+ if (indices.length === 0) {
330
+ throw Error("Validator indices must not be empty");
289
331
  }
290
332
 
291
- return committeeIndices;
333
+ const result = new Uint32Array(PTC_SIZE);
334
+ let resultLen = 0;
335
+
336
+ const MAX_RANDOM_VALUE = 0xffff; // 2^16 - 1
337
+ const MAX_EFFECTIVE_BALANCE_INCREMENT = MAX_EFFECTIVE_BALANCE_ELECTRA / EFFECTIVE_BALANCE_INCREMENT;
338
+ const indicesLen = indices.length;
339
+
340
+ // Pre-allocate hash input buffer: seed + 8 bytes for block index
341
+ const hashInput = new Uint8Array(seed.length + 8);
342
+ hashInput.set(seed, 0);
343
+ const hashInputView = new DataView(hashInput.buffer, hashInput.byteOffset, hashInput.byteLength);
344
+ const seedLen = seed.length;
345
+
346
+ let i = 0;
347
+ let randomBytesView: DataView = new DataView(new ArrayBuffer(0));
348
+ let lastBlock = -1;
349
+
350
+ while (resultLen < PTC_SIZE) {
351
+ const candidateIndex = indices[i % indicesLen];
352
+
353
+ // Only recompute hash every 16 iterations
354
+ const block = i >>> 4; // Math.floor(i / 16)
355
+ if (block !== lastBlock) {
356
+ // Write block as little-endian uint64 (block always fits in uint32 range)
357
+ hashInputView.setUint32(seedLen, block, true);
358
+ hashInputView.setUint32(seedLen + 4, 0, true);
359
+ const randomBytes = digest(hashInput);
360
+ randomBytesView = new DataView(randomBytes.buffer, randomBytes.byteOffset, randomBytes.byteLength);
361
+ lastBlock = block;
362
+ }
363
+
364
+ const randomValue = randomBytesView.getUint16((i & 15) * 2, true);
365
+
366
+ const effectiveBalanceIncrement = effectiveBalanceIncrements[candidateIndex];
367
+ if (effectiveBalanceIncrement * MAX_RANDOM_VALUE >= MAX_EFFECTIVE_BALANCE_INCREMENT * randomValue) {
368
+ result[resultLen++] = candidateIndex;
369
+ }
370
+ i += 1;
371
+ }
372
+
373
+ return result;
292
374
  }
293
375
 
376
+ /**
377
+ * Naive version of PTC indices computation.
378
+ * Used to verify the optimized `computePayloadTimelinessCommitteeIndices`.
379
+ *
380
+ * SLOW CODE - 🐢
381
+ */
294
382
  export function naiveComputePayloadTimelinessCommitteeIndices(
295
383
  effectiveBalanceIncrements: EffectiveBalanceIncrements,
296
384
  indices: ArrayLike<ValidatorIndex>,
@@ -1,6 +1,6 @@
1
1
  import {PublicKey, Signature, aggregatePublicKeys, fastAggregateVerify, verify} from "@chainsafe/blst";
2
2
  import {Root} from "@lodestar/types";
3
- import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
3
+ import {PubkeyCache} from "../cache/pubkeyCache.js";
4
4
 
5
5
  export enum SignatureSetType {
6
6
  single = "single",
@@ -49,18 +49,21 @@ export type ISignatureSet = SingleSignatureSet | IndexedSignatureSet | Aggregate
49
49
 
50
50
  /**
51
51
  * Get the pubkey for a signature set, performing aggregation if necessary.
52
- * Requires index2pubkey cache for indexed and aggregate sets.
52
+ * Requires pubkeyCache for indexed and aggregate sets.
53
53
  */
54
- export function getSignatureSetPubkey(signatureSet: ISignatureSet, index2pubkey: Index2PubkeyCache): PublicKey {
54
+ export function getSignatureSetPubkey(signatureSet: ISignatureSet, pubkeyCache: PubkeyCache): PublicKey {
55
55
  switch (signatureSet.type) {
56
56
  case SignatureSetType.single:
57
57
  return signatureSet.pubkey;
58
58
 
59
- case SignatureSetType.indexed:
60
- return index2pubkey[signatureSet.index];
59
+ case SignatureSetType.indexed: {
60
+ return pubkeyCache.getOrThrow(signatureSet.index);
61
+ }
61
62
 
62
63
  case SignatureSetType.aggregate: {
63
- const pubkeys = signatureSet.indices.map((i) => index2pubkey[i]);
64
+ const pubkeys = signatureSet.indices.map((i) => {
65
+ return pubkeyCache.getOrThrow(i);
66
+ });
64
67
  return aggregatePublicKeys(pubkeys);
65
68
  }
66
69
 
@@ -69,11 +72,11 @@ export function getSignatureSetPubkey(signatureSet: ISignatureSet, index2pubkey:
69
72
  }
70
73
  }
71
74
 
72
- export function verifySignatureSet(signatureSet: SingleSignatureSet, index2pubkey?: Index2PubkeyCache): boolean;
73
- export function verifySignatureSet(signatureSet: IndexedSignatureSet, index2pubkey: Index2PubkeyCache): boolean;
74
- export function verifySignatureSet(signatureSet: AggregatedSignatureSet, index2pubkey: Index2PubkeyCache): boolean;
75
- export function verifySignatureSet(signatureSet: ISignatureSet, index2pubkey: Index2PubkeyCache): boolean;
76
- export function verifySignatureSet(signatureSet: ISignatureSet, index2pubkey?: Index2PubkeyCache): boolean {
75
+ export function verifySignatureSet(signatureSet: SingleSignatureSet, pubkeyCache?: PubkeyCache): boolean;
76
+ export function verifySignatureSet(signatureSet: IndexedSignatureSet, pubkeyCache: PubkeyCache): boolean;
77
+ export function verifySignatureSet(signatureSet: AggregatedSignatureSet, pubkeyCache: PubkeyCache): boolean;
78
+ export function verifySignatureSet(signatureSet: ISignatureSet, pubkeyCache: PubkeyCache): boolean;
79
+ export function verifySignatureSet(signatureSet: ISignatureSet, pubkeyCache?: PubkeyCache): boolean {
77
80
  // All signatures are not trusted and must be group checked (p2.subgroup_check)
78
81
  const signature = Signature.fromBytes(signatureSet.signature, true);
79
82
 
@@ -82,17 +85,20 @@ export function verifySignatureSet(signatureSet: ISignatureSet, index2pubkey?: I
82
85
  return verify(signatureSet.signingRoot, signatureSet.pubkey, signature);
83
86
 
84
87
  case SignatureSetType.indexed: {
85
- if (!index2pubkey) {
86
- throw Error("index2pubkey required for indexed signature set");
88
+ if (!pubkeyCache) {
89
+ throw Error("pubkeyCache required for indexed signature set");
87
90
  }
88
- return verify(signatureSet.signingRoot, index2pubkey[signatureSet.index], signature);
91
+ const pubkey = pubkeyCache.getOrThrow(signatureSet.index);
92
+ return verify(signatureSet.signingRoot, pubkey, signature);
89
93
  }
90
94
 
91
95
  case SignatureSetType.aggregate: {
92
- if (!index2pubkey) {
93
- throw Error("index2pubkey required for aggregate signature set");
96
+ if (!pubkeyCache) {
97
+ throw Error("pubkeyCache required for aggregate signature set");
94
98
  }
95
- const pubkeys = signatureSet.indices.map((i) => index2pubkey[i]);
99
+ const pubkeys = signatureSet.indices.map((i) => {
100
+ return pubkeyCache.getOrThrow(i);
101
+ });
96
102
  return fastAggregateVerify(signatureSet.signingRoot, pubkeys, signature);
97
103
  }
98
104
 
@@ -47,7 +47,7 @@ export function computeWeakSubjectivityPeriodCachedState(
47
47
  state: CachedBeaconStateAllForks
48
48
  ): number {
49
49
  const activeValidatorCount = state.epochCtx.currentShuffling.activeIndices.length;
50
- const fork = state.config.getForkName(state.slot);
50
+ const fork = config.getForkName(state.slot);
51
51
 
52
52
  return isForkPostElectra(fork)
53
53
  ? computeWeakSubjectivityPeriodFromConstituentsElectra(