@lodestar/state-transition 1.40.0 → 1.41.0-dev.0087e7a664

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 (216) hide show
  1. package/lib/block/externalData.d.ts +2 -1
  2. package/lib/block/externalData.d.ts.map +1 -1
  3. package/lib/block/externalData.js +2 -0
  4. package/lib/block/externalData.js.map +1 -1
  5. package/lib/block/index.d.ts +3 -3
  6. package/lib/block/isValidIndexedAttestation.d.ts +3 -3
  7. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
  8. package/lib/block/isValidIndexedAttestation.js +4 -4
  9. package/lib/block/isValidIndexedAttestation.js.map +1 -1
  10. package/lib/block/isValidIndexedPayloadAttestation.js +1 -1
  11. package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -1
  12. package/lib/block/processAttestationPhase0.js +1 -1
  13. package/lib/block/processAttestationPhase0.js.map +1 -1
  14. package/lib/block/processAttestationsAltair.js +1 -1
  15. package/lib/block/processAttestationsAltair.js.map +1 -1
  16. package/lib/block/processAttesterSlashing.d.ts +2 -2
  17. package/lib/block/processAttesterSlashing.d.ts.map +1 -1
  18. package/lib/block/processAttesterSlashing.js +3 -3
  19. package/lib/block/processAttesterSlashing.js.map +1 -1
  20. package/lib/block/processDepositRequest.d.ts +1 -1
  21. package/lib/block/processDepositRequest.d.ts.map +1 -1
  22. package/lib/block/processDepositRequest.js +6 -5
  23. package/lib/block/processDepositRequest.js.map +1 -1
  24. package/lib/block/processExecutionPayloadBid.d.ts +1 -1
  25. package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
  26. package/lib/block/processExecutionPayloadBid.js +5 -0
  27. package/lib/block/processExecutionPayloadBid.js.map +1 -1
  28. package/lib/block/processExecutionPayloadEnvelope.d.ts +5 -2
  29. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -1
  30. package/lib/block/processExecutionPayloadEnvelope.js +25 -24
  31. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -1
  32. package/lib/block/processPayloadAttestation.d.ts +1 -1
  33. package/lib/block/processProposerSlashing.d.ts +2 -2
  34. package/lib/block/processProposerSlashing.d.ts.map +1 -1
  35. package/lib/block/processProposerSlashing.js +3 -3
  36. package/lib/block/processProposerSlashing.js.map +1 -1
  37. package/lib/block/processRandao.js +1 -1
  38. package/lib/block/processRandao.js.map +1 -1
  39. package/lib/block/processSyncCommittee.js +1 -1
  40. package/lib/block/processSyncCommittee.js.map +1 -1
  41. package/lib/block/processVoluntaryExit.js +1 -1
  42. package/lib/block/processVoluntaryExit.js.map +1 -1
  43. package/lib/block/processWithdrawalRequest.js +2 -2
  44. package/lib/block/processWithdrawalRequest.js.map +1 -1
  45. package/lib/block/processWithdrawals.d.ts.map +1 -1
  46. package/lib/block/processWithdrawals.js +9 -1
  47. package/lib/block/processWithdrawals.js.map +1 -1
  48. package/lib/cache/epochCache.d.ts +11 -18
  49. package/lib/cache/epochCache.d.ts.map +1 -1
  50. package/lib/cache/epochCache.js +41 -40
  51. package/lib/cache/epochCache.js.map +1 -1
  52. package/lib/cache/pubkeyCache.d.ts +21 -6
  53. package/lib/cache/pubkeyCache.d.ts.map +1 -1
  54. package/lib/cache/pubkeyCache.js +39 -14
  55. package/lib/cache/pubkeyCache.js.map +1 -1
  56. package/lib/cache/stateCache.d.ts +1 -1
  57. package/lib/cache/stateCache.d.ts.map +1 -1
  58. package/lib/cache/stateCache.js +3 -7
  59. package/lib/cache/stateCache.js.map +1 -1
  60. package/lib/cache/syncCommitteeCache.d.ts +3 -2
  61. package/lib/cache/syncCommitteeCache.d.ts.map +1 -1
  62. package/lib/cache/syncCommitteeCache.js +4 -4
  63. package/lib/cache/syncCommitteeCache.js.map +1 -1
  64. package/lib/epoch/index.d.ts +1 -1
  65. package/lib/epoch/processBuilderPendingPayments.d.ts +1 -1
  66. package/lib/index.d.ts +3 -1
  67. package/lib/index.d.ts.map +1 -1
  68. package/lib/index.js +2 -1
  69. package/lib/index.js.map +1 -1
  70. package/lib/lightClient/proofs.d.ts +10 -0
  71. package/lib/lightClient/proofs.d.ts.map +1 -0
  72. package/lib/lightClient/proofs.js +63 -0
  73. package/lib/lightClient/proofs.js.map +1 -0
  74. package/lib/lightClient/types.d.ts +34 -0
  75. package/lib/lightClient/types.d.ts.map +1 -0
  76. package/lib/lightClient/types.js +2 -0
  77. package/lib/lightClient/types.js.map +1 -0
  78. package/lib/rewards/attestationsRewards.d.ts +2 -2
  79. package/lib/rewards/attestationsRewards.d.ts.map +1 -1
  80. package/lib/rewards/attestationsRewards.js +4 -4
  81. package/lib/rewards/attestationsRewards.js.map +1 -1
  82. package/lib/rewards/syncCommitteeRewards.d.ts +2 -2
  83. package/lib/rewards/syncCommitteeRewards.d.ts.map +1 -1
  84. package/lib/rewards/syncCommitteeRewards.js +5 -2
  85. package/lib/rewards/syncCommitteeRewards.js.map +1 -1
  86. package/lib/signatureSets/indexedPayloadAttestation.d.ts +3 -4
  87. package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -1
  88. package/lib/signatureSets/indexedPayloadAttestation.js +4 -4
  89. package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -1
  90. package/lib/signatureSets/proposer.d.ts +2 -2
  91. package/lib/signatureSets/proposer.d.ts.map +1 -1
  92. package/lib/signatureSets/proposer.js +2 -2
  93. package/lib/signatureSets/proposer.js.map +1 -1
  94. package/lib/signatureSets/randao.d.ts +2 -2
  95. package/lib/signatureSets/randao.d.ts.map +1 -1
  96. package/lib/signatureSets/randao.js +2 -2
  97. package/lib/signatureSets/randao.js.map +1 -1
  98. package/lib/signatureSets/voluntaryExits.d.ts +2 -2
  99. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  100. package/lib/signatureSets/voluntaryExits.js +2 -2
  101. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  102. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  103. package/lib/slot/upgradeStateToGloas.js +50 -0
  104. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  105. package/lib/stateTransition.d.ts +2 -1
  106. package/lib/stateTransition.d.ts.map +1 -1
  107. package/lib/stateTransition.js +2 -1
  108. package/lib/stateTransition.js.map +1 -1
  109. package/lib/stateView/beaconStateView.d.ts +144 -0
  110. package/lib/stateView/beaconStateView.d.ts.map +1 -0
  111. package/lib/stateView/beaconStateView.js +496 -0
  112. package/lib/stateView/beaconStateView.js.map +1 -0
  113. package/lib/stateView/index.d.ts +3 -0
  114. package/lib/stateView/index.d.ts.map +1 -0
  115. package/lib/stateView/index.js +3 -0
  116. package/lib/stateView/index.js.map +1 -0
  117. package/lib/stateView/interface.d.ts +118 -0
  118. package/lib/stateView/interface.d.ts.map +1 -0
  119. package/lib/stateView/interface.js +2 -0
  120. package/lib/stateView/interface.js.map +1 -0
  121. package/lib/testUtils/cache.d.ts +2 -0
  122. package/lib/testUtils/cache.d.ts.map +1 -0
  123. package/lib/testUtils/cache.js +7 -0
  124. package/lib/testUtils/cache.js.map +1 -0
  125. package/lib/testUtils/index.d.ts +6 -0
  126. package/lib/testUtils/index.d.ts.map +1 -0
  127. package/lib/testUtils/index.js +6 -0
  128. package/lib/testUtils/index.js.map +1 -0
  129. package/lib/testUtils/infura.d.ts +3 -0
  130. package/lib/testUtils/infura.d.ts.map +1 -0
  131. package/lib/testUtils/infura.js +8 -0
  132. package/lib/testUtils/infura.js.map +1 -0
  133. package/lib/testUtils/interop.d.ts +2 -0
  134. package/lib/testUtils/interop.d.ts.map +1 -0
  135. package/lib/testUtils/interop.js +24 -0
  136. package/lib/testUtils/interop.js.map +1 -0
  137. package/lib/testUtils/params.d.ts +18 -0
  138. package/lib/testUtils/params.d.ts.map +1 -0
  139. package/lib/testUtils/params.js +20 -0
  140. package/lib/testUtils/params.js.map +1 -0
  141. package/lib/testUtils/state.d.ts +20 -0
  142. package/lib/testUtils/state.d.ts.map +1 -0
  143. package/lib/testUtils/state.js +78 -0
  144. package/lib/testUtils/state.js.map +1 -0
  145. package/lib/testUtils/testFileCache.d.ts +17 -0
  146. package/lib/testUtils/testFileCache.d.ts.map +1 -0
  147. package/lib/testUtils/testFileCache.js +96 -0
  148. package/lib/testUtils/testFileCache.js.map +1 -0
  149. package/lib/testUtils/util.d.ts +50 -0
  150. package/lib/testUtils/util.d.ts.map +1 -0
  151. package/lib/testUtils/util.js +329 -0
  152. package/lib/testUtils/util.js.map +1 -0
  153. package/lib/util/gloas.d.ts.map +1 -1
  154. package/lib/util/gloas.js +10 -3
  155. package/lib/util/gloas.js.map +1 -1
  156. package/lib/util/seed.d.ts +20 -12
  157. package/lib/util/seed.d.ts.map +1 -1
  158. package/lib/util/seed.js +80 -72
  159. package/lib/util/seed.js.map +1 -1
  160. package/lib/util/signatureSets.d.ts +7 -7
  161. package/lib/util/signatureSets.d.ts.map +1 -1
  162. package/lib/util/signatureSets.js +18 -12
  163. package/lib/util/signatureSets.js.map +1 -1
  164. package/lib/util/weakSubjectivity.js +1 -1
  165. package/lib/util/weakSubjectivity.js.map +1 -1
  166. package/package.json +12 -7
  167. package/src/block/externalData.ts +2 -0
  168. package/src/block/index.ts +3 -3
  169. package/src/block/isValidIndexedAttestation.ts +5 -5
  170. package/src/block/isValidIndexedPayloadAttestation.ts +4 -4
  171. package/src/block/processAttestationPhase0.ts +1 -1
  172. package/src/block/processAttestationsAltair.ts +2 -2
  173. package/src/block/processAttesterSlashing.ts +4 -4
  174. package/src/block/processDepositRequest.ts +8 -5
  175. package/src/block/processExecutionPayloadBid.ts +12 -4
  176. package/src/block/processExecutionPayloadEnvelope.ts +40 -37
  177. package/src/block/processOperations.ts +1 -1
  178. package/src/block/processPayloadAttestation.ts +2 -2
  179. package/src/block/processProposerSlashing.ts +4 -4
  180. package/src/block/processRandao.ts +1 -1
  181. package/src/block/processSyncCommittee.ts +1 -1
  182. package/src/block/processVoluntaryExit.ts +1 -1
  183. package/src/block/processWithdrawalRequest.ts +2 -2
  184. package/src/block/processWithdrawals.ts +11 -2
  185. package/src/cache/epochCache.ts +62 -54
  186. package/src/cache/pubkeyCache.ts +62 -21
  187. package/src/cache/stateCache.ts +4 -8
  188. package/src/cache/syncCommitteeCache.ts +4 -5
  189. package/src/epoch/index.ts +1 -1
  190. package/src/epoch/processBuilderPendingPayments.ts +2 -2
  191. package/src/index.ts +3 -1
  192. package/src/lightClient/proofs.ts +83 -0
  193. package/src/lightClient/types.ts +33 -0
  194. package/src/rewards/attestationsRewards.ts +5 -5
  195. package/src/rewards/syncCommitteeRewards.ts +6 -5
  196. package/src/signatureSets/indexedPayloadAttestation.ts +4 -6
  197. package/src/signatureSets/proposer.ts +3 -3
  198. package/src/signatureSets/randao.ts +3 -7
  199. package/src/signatureSets/voluntaryExits.ts +3 -3
  200. package/src/slot/upgradeStateToGloas.ts +74 -0
  201. package/src/stateTransition.ts +2 -1
  202. package/src/stateView/beaconStateView.ts +744 -0
  203. package/src/stateView/index.ts +2 -0
  204. package/src/stateView/interface.ts +196 -0
  205. package/src/testUtils/cache.ts +8 -0
  206. package/src/testUtils/index.ts +5 -0
  207. package/src/testUtils/infura.ts +10 -0
  208. package/src/testUtils/interop.ts +29 -0
  209. package/src/testUtils/params.ts +23 -0
  210. package/src/testUtils/state.ts +110 -0
  211. package/src/testUtils/testFileCache.ts +127 -0
  212. package/src/testUtils/util.ts +429 -0
  213. package/src/util/gloas.ts +11 -3
  214. package/src/util/seed.ts +106 -89
  215. package/src/util/signatureSets.ts +23 -17
  216. package/src/util/weakSubjectivity.ts +1 -1
@@ -14,7 +14,8 @@ export function applyDepositForBuilder(
14
14
  pubkey: BLSPubkey,
15
15
  withdrawalCredentials: Bytes32,
16
16
  amount: UintNum64,
17
- signature: Bytes32
17
+ signature: Bytes32,
18
+ slot: UintNum64
18
19
  ): void {
19
20
  const builderIndex = findBuilderIndexByPubkey(state, pubkey);
20
21
 
@@ -25,7 +26,7 @@ export function applyDepositForBuilder(
25
26
  } else {
26
27
  // New builder - verify signature and add to registry
27
28
  if (isValidDepositSignature(state.config, pubkey, withdrawalCredentials, amount, signature)) {
28
- addBuilderToRegistry(state, pubkey, withdrawalCredentials, amount);
29
+ addBuilderToRegistry(state, pubkey, withdrawalCredentials, amount, slot);
29
30
  }
30
31
  }
31
32
  }
@@ -38,9 +39,11 @@ function addBuilderToRegistry(
38
39
  state: CachedBeaconStateGloas,
39
40
  pubkey: BLSPubkey,
40
41
  withdrawalCredentials: Bytes32,
41
- amount: UintNum64
42
+ amount: UintNum64,
43
+ slot: UintNum64
42
44
  ): void {
43
45
  const currentEpoch = computeEpochAtSlot(state.slot);
46
+ const depositEpoch = computeEpochAtSlot(slot);
44
47
 
45
48
  // Try to find a reusable slot from an exited builder with zero balance
46
49
  let builderIndex = state.builders.length;
@@ -58,7 +61,7 @@ function addBuilderToRegistry(
58
61
  version: withdrawalCredentials[0],
59
62
  executionAddress: withdrawalCredentials.subarray(12),
60
63
  balance: amount,
61
- depositEpoch: currentEpoch,
64
+ depositEpoch: depositEpoch,
62
65
  withdrawableEpoch: FAR_FUTURE_EPOCH,
63
66
  });
64
67
 
@@ -93,7 +96,7 @@ export function processDepositRequest(
93
96
  // Route to builder if it's an existing builder OR has builder prefix and is not a validator
94
97
  if (isBuilder || (isBuilderPrefix && !isValidator)) {
95
98
  // Apply builder deposits immediately
96
- applyDepositForBuilder(stateGloas, pubkey, withdrawalCredentials, amount, signature);
99
+ applyDepositForBuilder(stateGloas, pubkey, withdrawalCredentials, amount, signature, state.slot);
97
100
  return;
98
101
  }
99
102
  }
@@ -2,11 +2,11 @@ import {PublicKey, Signature, verify} from "@chainsafe/blst";
2
2
  import {BUILDER_INDEX_SELF_BUILD, ForkPostGloas, SLOTS_PER_EPOCH} from "@lodestar/params";
3
3
  import {BeaconBlock, gloas, ssz} from "@lodestar/types";
4
4
  import {byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
5
- import {G2_POINT_AT_INFINITY} from "../constants/constants.ts";
5
+ import {G2_POINT_AT_INFINITY} from "../constants/constants.js";
6
6
  import {getExecutionPayloadBidSigningRoot} from "../signatureSets/executionPayloadBid.js";
7
- import {CachedBeaconStateGloas} from "../types.ts";
8
- import {canBuilderCoverBid, isActiveBuilder} from "../util/gloas.ts";
9
- import {getCurrentEpoch, getRandaoMix} from "../util/index.ts";
7
+ import {CachedBeaconStateGloas} from "../types.js";
8
+ import {canBuilderCoverBid, isActiveBuilder} from "../util/gloas.js";
9
+ import {getCurrentEpoch, getRandaoMix} from "../util/index.js";
10
10
 
11
11
  export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block: BeaconBlock<ForkPostGloas>): void {
12
12
  const signedBid = block.body.signedExecutionPayloadBid;
@@ -63,6 +63,14 @@ export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block:
63
63
  throw Error(`Prev randao ${toHex(bid.prevRandao)} of bid does not match state's randao mix ${toHex(stateRandao)}`);
64
64
  }
65
65
 
66
+ // Verify commitments are under limit
67
+ const maxBlobsPerBlock = state.config.getMaxBlobsPerBlock(state.epochCtx.epoch);
68
+ if (bid.blobKzgCommitments.length > maxBlobsPerBlock) {
69
+ throw Error(
70
+ `Kzg commitments exceed limit commitments.length=${bid.blobKzgCommitments.length} limit=${maxBlobsPerBlock}`
71
+ );
72
+ }
73
+
66
74
  if (amount > 0) {
67
75
  const pendingPaymentView = ssz.gloas.BuilderPendingPayment.toViewDU({
68
76
  weight: 0,
@@ -7,18 +7,25 @@ import {
7
7
  } from "@lodestar/params";
8
8
  import {gloas, ssz} from "@lodestar/types";
9
9
  import {byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
10
- import {CachedBeaconStateGloas} from "../types.ts";
11
- import {computeSigningRoot, computeTimeAtSlot} from "../util/index.ts";
12
- import {processConsolidationRequest} from "./processConsolidationRequest.ts";
13
- import {processDepositRequest} from "./processDepositRequest.ts";
14
- import {processWithdrawalRequest} from "./processWithdrawalRequest.ts";
15
-
16
- // This function does not call execution engine to verify payload. Need to call it from other place
10
+ import {CachedBeaconStateGloas} from "../types.js";
11
+ import {computeSigningRoot, computeTimeAtSlot} from "../util/index.js";
12
+ import {processConsolidationRequest} from "./processConsolidationRequest.js";
13
+ import {processDepositRequest} from "./processDepositRequest.js";
14
+ import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
15
+
16
+ export type ProcessExecutionPayloadEnvelopeOpts = {
17
+ dontTransferCache?: boolean;
18
+ };
19
+
20
+ // Unlike other block processing functions which mutate state in-place, this function
21
+ // clones the state and returns the post-state, similar to stateTransition().
22
+ // This function does not call execution engine to verify payload. Need to call it from other place.
17
23
  export function processExecutionPayloadEnvelope(
18
24
  state: CachedBeaconStateGloas,
19
25
  signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
20
- verify: boolean
21
- ): void {
26
+ verify: boolean,
27
+ opts?: ProcessExecutionPayloadEnvelopeOpts
28
+ ): CachedBeaconStateGloas {
22
29
  const envelope = signedEnvelope.message;
23
30
  const payload = envelope.payload;
24
31
  const fork = state.config.getForkSeq(envelope.slot);
@@ -27,42 +34,49 @@ export function processExecutionPayloadEnvelope(
27
34
  throw Error(`Execution payload envelope has invalid signature builderIndex=${envelope.builderIndex}`);
28
35
  }
29
36
 
30
- validateExecutionPayloadEnvelope(state, envelope);
37
+ // .clone() before mutating state, similar to stateTransition()
38
+ const postState = state.clone(opts?.dontTransferCache) as CachedBeaconStateGloas;
39
+
40
+ validateExecutionPayloadEnvelope(postState, envelope);
31
41
 
32
42
  const requests = envelope.executionRequests;
33
43
 
34
44
  for (const deposit of requests.deposits) {
35
- processDepositRequest(fork, state, deposit);
45
+ processDepositRequest(fork, postState, deposit);
36
46
  }
37
47
 
38
48
  for (const withdrawal of requests.withdrawals) {
39
- processWithdrawalRequest(fork, state, withdrawal);
49
+ processWithdrawalRequest(fork, postState, withdrawal);
40
50
  }
41
51
 
42
52
  for (const consolidation of requests.consolidations) {
43
- processConsolidationRequest(state, consolidation);
53
+ processConsolidationRequest(postState, consolidation);
44
54
  }
45
55
 
46
56
  // Queue the builder payment
47
- const paymentIndex = SLOTS_PER_EPOCH + (state.slot % SLOTS_PER_EPOCH);
48
- const payment = state.builderPendingPayments.get(paymentIndex).clone();
57
+ const paymentIndex = SLOTS_PER_EPOCH + (postState.slot % SLOTS_PER_EPOCH);
58
+ const payment = postState.builderPendingPayments.get(paymentIndex).clone();
49
59
  const amount = payment.withdrawal.amount;
50
60
 
51
61
  if (amount > 0) {
52
- state.builderPendingWithdrawals.push(payment.withdrawal);
62
+ postState.builderPendingWithdrawals.push(payment.withdrawal);
53
63
  }
54
64
 
55
- state.builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
65
+ postState.builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
56
66
 
57
67
  // Cache the execution payload hash
58
- state.executionPayloadAvailability.set(state.slot % SLOTS_PER_HISTORICAL_ROOT, true);
59
- state.latestBlockHash = payload.blockHash;
68
+ postState.executionPayloadAvailability.set(postState.slot % SLOTS_PER_HISTORICAL_ROOT, true);
69
+ postState.latestBlockHash = payload.blockHash;
70
+
71
+ postState.commit();
60
72
 
61
- if (verify && !byteArrayEquals(envelope.stateRoot, state.hashTreeRoot())) {
73
+ if (verify && !byteArrayEquals(envelope.stateRoot, postState.hashTreeRoot())) {
62
74
  throw new Error(
63
- `Envelope's state root does not match state envelope=${toRootHex(envelope.stateRoot)} state=${toRootHex(state.hashTreeRoot())}`
75
+ `Envelope's state root does not match state envelope=${toRootHex(envelope.stateRoot)} state=${toRootHex(postState.hashTreeRoot())}`
64
76
  );
65
77
  }
78
+
79
+ return postState;
66
80
  }
67
81
 
68
82
  function validateExecutionPayloadEnvelope(
@@ -96,13 +110,6 @@ function validateExecutionPayloadEnvelope(
96
110
  );
97
111
  }
98
112
 
99
- const envelopeKzgRoot = ssz.deneb.BlobKzgCommitments.hashTreeRoot(envelope.blobKzgCommitments);
100
- if (!byteArrayEquals(committedBid.blobKzgCommitmentsRoot, envelopeKzgRoot)) {
101
- throw new Error(
102
- `Kzg commitment root mismatch between envelope and committed bid envelope=${toRootHex(envelopeKzgRoot)} committedBid=${toRootHex(committedBid.blobKzgCommitmentsRoot)}`
103
- );
104
- }
105
-
106
113
  if (!byteArrayEquals(committedBid.prevRandao, payload.prevRandao)) {
107
114
  throw new Error(
108
115
  `Prev randao mismatch between committed bid and payload committedBid=${toHex(committedBid.prevRandao)} payload=${toHex(payload.prevRandao)}`
@@ -146,14 +153,6 @@ function validateExecutionPayloadEnvelope(
146
153
  );
147
154
  }
148
155
 
149
- // Verify commitments are under limit
150
- const maxBlobsPerBlock = state.config.getMaxBlobsPerBlock(state.epochCtx.epoch);
151
- if (envelope.blobKzgCommitments.length > maxBlobsPerBlock) {
152
- throw new Error(
153
- `Kzg commitments exceed limit commitment.length=${envelope.blobKzgCommitments.length} limit=${maxBlobsPerBlock}`
154
- );
155
- }
156
-
157
156
  // Skipped: Verify the execution payload is valid
158
157
  }
159
158
 
@@ -171,7 +170,11 @@ function verifyExecutionPayloadEnvelopeSignature(
171
170
 
172
171
  if (builderIndex === BUILDER_INDEX_SELF_BUILD) {
173
172
  const validatorIndex = state.latestBlockHeader.proposerIndex;
174
- publicKey = state.epochCtx.index2pubkey[validatorIndex];
173
+ const proposerPubkey = state.epochCtx.pubkeyCache.get(validatorIndex);
174
+ if (!proposerPubkey) {
175
+ return false;
176
+ }
177
+ publicKey = proposerPubkey;
175
178
  } else {
176
179
  publicKey = PublicKey.fromBytes(state.builders.getReadonly(builderIndex).pubkey);
177
180
  }
@@ -14,7 +14,7 @@ import {processBlsToExecutionChange} from "./processBlsToExecutionChange.js";
14
14
  import {processConsolidationRequest} from "./processConsolidationRequest.js";
15
15
  import {processDeposit} from "./processDeposit.js";
16
16
  import {processDepositRequest} from "./processDepositRequest.js";
17
- import {processPayloadAttestation} from "./processPayloadAttestation.ts";
17
+ import {processPayloadAttestation} from "./processPayloadAttestation.js";
18
18
  import {processProposerSlashing} from "./processProposerSlashing.js";
19
19
  import {processVoluntaryExit} from "./processVoluntaryExit.js";
20
20
  import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
@@ -1,7 +1,7 @@
1
1
  import {gloas} from "@lodestar/types";
2
2
  import {byteArrayEquals} from "@lodestar/utils";
3
- import {CachedBeaconStateGloas} from "../types.ts";
4
- import {isValidIndexedPayloadAttestation} from "./isValidIndexedPayloadAttestation.ts";
3
+ import {CachedBeaconStateGloas} from "../types.js";
4
+ import {isValidIndexedPayloadAttestation} from "./isValidIndexedPayloadAttestation.js";
5
5
 
6
6
  export function processPayloadAttestation(
7
7
  state: CachedBeaconStateGloas,
@@ -2,7 +2,7 @@ import {BeaconConfig} from "@lodestar/config";
2
2
  import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
3
3
  import {Slot, phase0, ssz} from "@lodestar/types";
4
4
  import {Validator} from "@lodestar/types/phase0";
5
- import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
5
+ import {PubkeyCache} from "../cache/pubkeyCache.js";
6
6
  import {getProposerSlashingSignatureSets} from "../signatureSets/index.js";
7
7
  import {CachedBeaconStateAllForks, CachedBeaconStateGloas} from "../types.js";
8
8
  import {computeEpochAtSlot, isSlashableValidator} from "../util/index.js";
@@ -24,7 +24,7 @@ export function processProposerSlashing(
24
24
  const proposer = state.validators.getReadonly(proposerSlashing.signedHeader1.message.proposerIndex);
25
25
  assertValidProposerSlashing(
26
26
  state.config,
27
- state.epochCtx.index2pubkey,
27
+ state.epochCtx.pubkeyCache,
28
28
  state.slot,
29
29
  proposerSlashing,
30
30
  proposer,
@@ -57,7 +57,7 @@ export function processProposerSlashing(
57
57
 
58
58
  export function assertValidProposerSlashing(
59
59
  config: BeaconConfig,
60
- index2pubkey: Index2PubkeyCache,
60
+ pubkeyCache: PubkeyCache,
61
61
  stateSlot: Slot,
62
62
  proposerSlashing: phase0.ProposerSlashing,
63
63
  proposer: Validator,
@@ -94,7 +94,7 @@ export function assertValidProposerSlashing(
94
94
  if (verifySignatures) {
95
95
  const signatureSets = getProposerSlashingSignatureSets(config, stateSlot, proposerSlashing);
96
96
  for (let i = 0; i < signatureSets.length; i++) {
97
- if (!verifySignatureSet(signatureSets[i], index2pubkey)) {
97
+ if (!verifySignatureSet(signatureSets[i], pubkeyCache)) {
98
98
  throw new Error(`ProposerSlashing header${i + 1} signature invalid`);
99
99
  }
100
100
  }
@@ -17,7 +17,7 @@ export function processRandao(state: CachedBeaconStateAllForks, block: BeaconBlo
17
17
  const randaoReveal = block.body.randaoReveal;
18
18
 
19
19
  // verify RANDAO reveal
20
- if (verifySignature && !verifyRandaoSignature(config, epochCtx.index2pubkey, block)) {
20
+ if (verifySignature && !verifyRandaoSignature(config, epochCtx.pubkeyCache, block)) {
21
21
  throw new Error("RANDAO reveal is an invalid signature");
22
22
  }
23
23
 
@@ -32,7 +32,7 @@ export function processSyncAggregate(
32
32
  participantIndices
33
33
  );
34
34
  // When there's no participation we consider the signature valid and just ignore it
35
- if (signatureSet !== null && !verifySignatureSet(signatureSet, state.epochCtx.index2pubkey)) {
35
+ if (signatureSet !== null && !verifySignatureSet(signatureSet, state.epochCtx.pubkeyCache)) {
36
36
  throw Error("Sync committee signature invalid");
37
37
  }
38
38
  }
@@ -131,7 +131,7 @@ export function getVoluntaryExitValidity(
131
131
 
132
132
  if (
133
133
  verifySignature &&
134
- !verifyVoluntaryExitSignature(state.config, epochCtx.index2pubkey, state.slot, signedVoluntaryExit)
134
+ !verifyVoluntaryExitSignature(state.config, epochCtx.pubkeyCache, state.slot, signedVoluntaryExit)
135
135
  ) {
136
136
  return VoluntaryExitValidity.invalidSignature;
137
137
  }
@@ -21,7 +21,7 @@ export function processWithdrawalRequest(
21
21
  const amount = Number(withdrawalRequest.amount);
22
22
  const {pendingPartialWithdrawals, validators, epochCtx} = state;
23
23
  // no need to use unfinalized pubkey cache from 6110 as validator won't be active anyway
24
- const {pubkey2index, config} = epochCtx;
24
+ const {pubkeyCache, config} = epochCtx;
25
25
  const isFullExitRequest = amount === FULL_EXIT_REQUEST_AMOUNT;
26
26
 
27
27
  // If partial withdrawal queue is full, only full exits are processed
@@ -31,7 +31,7 @@ export function processWithdrawalRequest(
31
31
 
32
32
  // bail out if validator is not in beacon state
33
33
  // note that we don't need to check for 6110 unfinalized vals as they won't be eligible for withdraw/exit anyway
34
- const validatorIndex = pubkey2index.get(withdrawalRequest.validatorPubkey);
34
+ const validatorIndex = pubkeyCache.getIndex(withdrawalRequest.validatorPubkey);
35
35
  if (validatorIndex === null) {
36
36
  return;
37
37
  }
@@ -16,7 +16,7 @@ import {
16
16
  convertValidatorIndexToBuilderIndex,
17
17
  isBuilderIndex,
18
18
  isParentBlockFull,
19
- } from "../util/gloas.ts";
19
+ } from "../util/gloas.js";
20
20
  import {
21
21
  decreaseBalance,
22
22
  getMaxEffectiveBalance,
@@ -249,6 +249,10 @@ function getPendingPartialWithdrawals(
249
249
  numPriorWithdrawal + MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
250
250
  MAX_WITHDRAWALS_PER_PAYLOAD - 1
251
251
  );
252
+ // There must be at least one space reserved for validator sweep withdrawals
253
+ if (numPriorWithdrawal > partialWithdrawalBound) {
254
+ throw Error(`Prior withdrawals exceed limit: ${numPriorWithdrawal} > ${partialWithdrawalBound}`);
255
+ }
252
256
 
253
257
  // MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP = 8, PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728 so we should only call getAllReadonly() if it makes sense
254
258
  // pendingPartialWithdrawals comes from EIP-7002 smart contract where it takes fee so it's more likely than not validator is in correct condition to withdraw
@@ -309,6 +313,11 @@ function getValidatorsSweepWithdrawals(
309
313
  numPriorWithdrawal: number,
310
314
  validatorBalanceAfterWithdrawals: Map<ValidatorIndex, number>
311
315
  ): {sweepWithdrawals: capella.Withdrawal[]; processedCount: number} {
316
+ // There must be at least one space reserved for validator sweep withdrawals
317
+ if (numPriorWithdrawal >= MAX_WITHDRAWALS_PER_PAYLOAD) {
318
+ throw Error(`Prior withdrawals exceed limit: ${numPriorWithdrawal} >= ${MAX_WITHDRAWALS_PER_PAYLOAD}`);
319
+ }
320
+
312
321
  const sweepWithdrawals: capella.Withdrawal[] = [];
313
322
  const epoch = state.epochCtx.epoch;
314
323
  const {validators, balances, nextWithdrawalValidatorIndex} = state;
@@ -319,7 +328,7 @@ function getValidatorsSweepWithdrawals(
319
328
  // Just run a bounded loop max iterating over all withdrawals
320
329
  // however breaks out once we have MAX_WITHDRAWALS_PER_PAYLOAD
321
330
  for (let n = 0; n < validatorsLimit; n++) {
322
- if (sweepWithdrawals.length + numPriorWithdrawal === MAX_WITHDRAWALS_PER_PAYLOAD) {
331
+ if (sweepWithdrawals.length + numPriorWithdrawal >= MAX_WITHDRAWALS_PER_PAYLOAD) {
323
332
  break;
324
333
  }
325
334
 
@@ -1,5 +1,4 @@
1
1
  import {PublicKey} from "@chainsafe/blst";
2
- import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
3
2
  import {BeaconConfig, ChainConfig, createBeaconConfig} from "@lodestar/config";
4
3
  import {
5
4
  ATTESTATION_SUBNET_COUNT,
@@ -36,6 +35,7 @@ import {
36
35
  import {
37
36
  computeActivationExitEpoch,
38
37
  computeEpochAtSlot,
38
+ computePayloadTimelinessCommitteesForEpoch,
39
39
  computeProposers,
40
40
  computeSyncPeriodAtEpoch,
41
41
  getActivationChurnLimit,
@@ -43,7 +43,6 @@ import {
43
43
  getSeed,
44
44
  isActiveValidator,
45
45
  isAggregatorFromCommitteeLength,
46
- naiveGetPayloadTimlinessCommitteeIndices,
47
46
  } from "../util/index.js";
48
47
  import {
49
48
  AttesterDuty,
@@ -56,7 +55,7 @@ import {computeBaseRewardPerIncrement, computeSyncParticipantReward} from "../ut
56
55
  import {sumTargetUnslashedBalanceIncrements} from "../util/targetUnslashedBalance.js";
57
56
  import {EffectiveBalanceIncrements, getEffectiveBalanceIncrementsWithLen} from "./effectiveBalanceIncrements.js";
58
57
  import {EpochTransitionCache} from "./epochTransitionCache.js";
59
- import {Index2PubkeyCache, syncPubkeys} from "./pubkeyCache.js";
58
+ import {PubkeyCache, createPubkeyCache, syncPubkeys} from "./pubkeyCache.js";
60
59
  import {CachedBeaconStateAllForks, CachedBeaconStateFulu} from "./stateCache.js";
61
60
  import {
62
61
  SyncCommitteeCache,
@@ -64,15 +63,14 @@ import {
64
63
  computeSyncCommitteeCache,
65
64
  getSyncCommitteeCache,
66
65
  } from "./syncCommitteeCache.js";
67
- import {BeaconStateAllForks, BeaconStateAltair, BeaconStateGloas, ShufflingGetter} from "./types.js";
66
+ import {BeaconStateAllForks, BeaconStateAltair, ShufflingGetter} from "./types.js";
68
67
 
69
68
  /** `= PROPOSER_WEIGHT / (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)` */
70
69
  export const PROPOSER_WEIGHT_FACTOR = PROPOSER_WEIGHT / (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT);
71
70
 
72
71
  export type EpochCacheImmutableData = {
73
72
  config: BeaconConfig;
74
- pubkey2index: PubkeyIndexMap;
75
- index2pubkey: Index2PubkeyCache;
73
+ pubkeyCache: PubkeyCache;
76
74
  };
77
75
 
78
76
  export type EpochCacheOpts = {
@@ -111,15 +109,9 @@ export class EpochCache {
111
109
  /**
112
110
  * Unique globally shared pubkey registry. There should only exist one for the entire application.
113
111
  *
114
- * $VALIDATOR_COUNT x 192 char String -> Number Map
112
+ * Couples both index→pubkey and pubkey→index lookups, keeping them in sync atomically.
115
113
  */
116
- pubkey2index: PubkeyIndexMap;
117
- /**
118
- * Unique globally shared pubkey registry. There should only exist one for the entire application.
119
- *
120
- * $VALIDATOR_COUNT x BLST deserialized pubkey (Jacobian coordinates)
121
- */
122
- index2pubkey: Index2PubkeyCache;
114
+ pubkeyCache: PubkeyCache;
123
115
  /**
124
116
  * Indexes of the block proposers for the current epoch.
125
117
  * For pre-fulu, this is computed and cached from the current shuffling.
@@ -234,9 +226,11 @@ export class EpochCache {
234
226
  /** TODO: Indexed SyncCommitteeCache */
235
227
  nextSyncCommitteeIndexed: SyncCommitteeCache;
236
228
 
237
- // TODO GLOAS: See if we need to cached PTC for prev/next epoch
238
- // PTC for current epoch
239
- payloadTimelinessCommittee: ValidatorIndex[][];
229
+ // TODO GLOAS: See if we need to cache PTC for next epoch
230
+ // PTC for previous epoch, required for slot N block validating slot N-1 attestations
231
+ previousPayloadTimelinessCommittees: Uint32Array[];
232
+ // PTC for current epoch, computed eagerly at epoch transition
233
+ payloadTimelinessCommittees: Uint32Array[];
240
234
 
241
235
  // TODO: Helper stats
242
236
  syncPeriod: SyncPeriod;
@@ -249,8 +243,7 @@ export class EpochCache {
249
243
 
250
244
  constructor(data: {
251
245
  config: BeaconConfig;
252
- pubkey2index: PubkeyIndexMap;
253
- index2pubkey: Index2PubkeyCache;
246
+ pubkeyCache: PubkeyCache;
254
247
  proposers: number[];
255
248
  proposersPrevEpoch: number[] | null;
256
249
  proposersNextEpoch: ProposersDeferred;
@@ -275,13 +268,13 @@ export class EpochCache {
275
268
  previousTargetUnslashedBalanceIncrements: number;
276
269
  currentSyncCommitteeIndexed: SyncCommitteeCache;
277
270
  nextSyncCommitteeIndexed: SyncCommitteeCache;
278
- payloadTimelinessCommittee: ValidatorIndex[][];
271
+ previousPayloadTimelinessCommittees: Uint32Array[];
272
+ payloadTimelinessCommittees: Uint32Array[];
279
273
  epoch: Epoch;
280
274
  syncPeriod: SyncPeriod;
281
275
  }) {
282
276
  this.config = data.config;
283
- this.pubkey2index = data.pubkey2index;
284
- this.index2pubkey = data.index2pubkey;
277
+ this.pubkeyCache = data.pubkeyCache;
285
278
  this.proposers = data.proposers;
286
279
  this.proposersPrevEpoch = data.proposersPrevEpoch;
287
280
  this.proposersNextEpoch = data.proposersNextEpoch;
@@ -306,7 +299,8 @@ export class EpochCache {
306
299
  this.previousTargetUnslashedBalanceIncrements = data.previousTargetUnslashedBalanceIncrements;
307
300
  this.currentSyncCommitteeIndexed = data.currentSyncCommitteeIndexed;
308
301
  this.nextSyncCommitteeIndexed = data.nextSyncCommitteeIndexed;
309
- this.payloadTimelinessCommittee = data.payloadTimelinessCommittee;
302
+ this.previousPayloadTimelinessCommittees = data.previousPayloadTimelinessCommittees;
303
+ this.payloadTimelinessCommittees = data.payloadTimelinessCommittees;
310
304
  this.epoch = data.epoch;
311
305
  this.syncPeriod = data.syncPeriod;
312
306
  }
@@ -319,7 +313,7 @@ export class EpochCache {
319
313
  */
320
314
  static createFromState(
321
315
  state: BeaconStateAllForks,
322
- {config, pubkey2index, index2pubkey}: EpochCacheImmutableData,
316
+ {config, pubkeyCache}: EpochCacheImmutableData,
323
317
  opts?: EpochCacheOpts
324
318
  ): EpochCache {
325
319
  const currentEpoch = computeEpochAtSlot(state.slot);
@@ -335,9 +329,9 @@ export class EpochCache {
335
329
  const validatorCount = validators.length;
336
330
 
337
331
  // syncPubkeys here to ensure EpochCacheImmutableData is popualted before computing the rest of caches
338
- // - computeSyncCommitteeCache() needs a fully populated pubkey2index cache
332
+ // - computeSyncCommitteeCache() needs a fully populated pubkeyCache
339
333
  if (!opts?.skipSyncPubkeys) {
340
- syncPubkeys(validators, pubkey2index, index2pubkey);
334
+ syncPubkeys(pubkeyCache, validators);
341
335
  }
342
336
 
343
337
  const effectiveBalanceIncrements = getEffectiveBalanceIncrementsWithLen(validatorCount);
@@ -450,22 +444,32 @@ export class EpochCache {
450
444
  // Allow to skip populating sync committee for initializeBeaconStateFromEth1()
451
445
  if (afterAltairFork && !opts?.skipSyncCommitteeCache) {
452
446
  const altairState = state as BeaconStateAltair;
453
- currentSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.currentSyncCommittee, pubkey2index);
454
- nextSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.nextSyncCommittee, pubkey2index);
447
+ currentSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.currentSyncCommittee, pubkeyCache);
448
+ nextSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.nextSyncCommittee, pubkeyCache);
455
449
  } else {
456
450
  currentSyncCommitteeIndexed = new SyncCommitteeCacheEmpty();
457
451
  nextSyncCommitteeIndexed = new SyncCommitteeCacheEmpty();
458
452
  }
459
453
 
460
- // Compute PTC for this epoch
461
- let payloadTimelinessCommittee: ValidatorIndex[][] = [];
454
+ // Compute PTC for all slots in the prev/current epoch
455
+ let previousPayloadTimelinessCommittees: Uint32Array[] = [];
456
+ let payloadTimelinessCommittees: Uint32Array[] = [];
462
457
  if (currentEpoch >= config.GLOAS_FORK_EPOCH) {
463
- payloadTimelinessCommittee = naiveGetPayloadTimlinessCommitteeIndices(
464
- state as BeaconStateGloas,
465
- currentShuffling,
466
- effectiveBalanceIncrements,
467
- currentEpoch
458
+ payloadTimelinessCommittees = computePayloadTimelinessCommitteesForEpoch(
459
+ state,
460
+ currentEpoch,
461
+ currentShuffling.committees,
462
+ effectiveBalanceIncrements
468
463
  );
464
+
465
+ if (!isGenesis && previousEpoch >= config.GLOAS_FORK_EPOCH) {
466
+ previousPayloadTimelinessCommittees = computePayloadTimelinessCommitteesForEpoch(
467
+ state,
468
+ previousEpoch,
469
+ previousShuffling.committees,
470
+ effectiveBalanceIncrements
471
+ );
472
+ }
469
473
  }
470
474
 
471
475
  // Precompute churnLimit for efficient initiateValidatorExit() during block proposing MUST be recompute everytime the
@@ -514,8 +518,7 @@ export class EpochCache {
514
518
 
515
519
  return new EpochCache({
516
520
  config,
517
- pubkey2index,
518
- index2pubkey,
521
+ pubkeyCache,
519
522
  proposers,
520
523
  // On first epoch, set to null to prevent unnecessary work since this is only used for metrics
521
524
  proposersPrevEpoch: null,
@@ -541,7 +544,8 @@ export class EpochCache {
541
544
  currentTargetUnslashedBalanceIncrements,
542
545
  currentSyncCommitteeIndexed,
543
546
  nextSyncCommitteeIndexed,
544
- payloadTimelinessCommittee: payloadTimelinessCommittee,
547
+ previousPayloadTimelinessCommittees,
548
+ payloadTimelinessCommittees,
545
549
  epoch: currentEpoch,
546
550
  syncPeriod: computeSyncPeriodAtEpoch(currentEpoch),
547
551
  });
@@ -557,8 +561,7 @@ export class EpochCache {
557
561
  return new EpochCache({
558
562
  config: this.config,
559
563
  // Common append-only structures shared with all states, no need to clone
560
- pubkey2index: this.pubkey2index,
561
- index2pubkey: this.index2pubkey,
564
+ pubkeyCache: this.pubkeyCache,
562
565
  // Immutable data
563
566
  proposers: this.proposers,
564
567
  proposersPrevEpoch: this.proposersPrevEpoch,
@@ -587,7 +590,8 @@ export class EpochCache {
587
590
  currentTargetUnslashedBalanceIncrements: this.currentTargetUnslashedBalanceIncrements,
588
591
  currentSyncCommitteeIndexed: this.currentSyncCommitteeIndexed,
589
592
  nextSyncCommitteeIndexed: this.nextSyncCommitteeIndexed,
590
- payloadTimelinessCommittee: this.payloadTimelinessCommittee,
593
+ previousPayloadTimelinessCommittees: this.previousPayloadTimelinessCommittees,
594
+ payloadTimelinessCommittees: this.payloadTimelinessCommittees,
591
595
  epoch: this.epoch,
592
596
  syncPeriod: this.syncPeriod,
593
597
  });
@@ -698,11 +702,13 @@ export class EpochCache {
698
702
 
699
703
  this.proposersPrevEpoch = this.proposers;
700
704
  if (upcomingEpoch >= this.config.GLOAS_FORK_EPOCH) {
701
- this.payloadTimelinessCommittee = naiveGetPayloadTimlinessCommitteeIndices(
702
- state as BeaconStateGloas,
703
- this.currentShuffling,
704
- this.effectiveBalanceIncrements,
705
- upcomingEpoch
705
+ // Shift and compute current epoch PTC eagerly for all slots
706
+ this.previousPayloadTimelinessCommittees = this.payloadTimelinessCommittees;
707
+ this.payloadTimelinessCommittees = computePayloadTimelinessCommitteesForEpoch(
708
+ state,
709
+ upcomingEpoch,
710
+ this.currentShuffling.committees,
711
+ this.effectiveBalanceIncrements
706
712
  );
707
713
  }
708
714
  if (upcomingEpoch >= this.config.FULU_FORK_EPOCH) {
@@ -882,16 +888,15 @@ export class EpochCache {
882
888
  * Return pubkey given the validator index.
883
889
  */
884
890
  getPubkey(index: ValidatorIndex): PublicKey | undefined {
885
- return this.index2pubkey[index];
891
+ return this.pubkeyCache.get(index);
886
892
  }
887
893
 
888
894
  getValidatorIndex(pubkey: Uint8Array): ValidatorIndex | null {
889
- return this.pubkey2index.get(pubkey);
895
+ return this.pubkeyCache.getIndex(pubkey);
890
896
  }
891
897
 
892
898
  addPubkey(index: ValidatorIndex, pubkey: Uint8Array): void {
893
- this.pubkey2index.set(pubkey, index);
894
- this.index2pubkey[index] = PublicKey.fromBytes(pubkey); // Optimize for aggregation
899
+ this.pubkeyCache.set(index, pubkey);
895
900
  }
896
901
 
897
902
  getShufflingAtSlot(slot: Slot): EpochShuffling {
@@ -1022,7 +1027,7 @@ export class EpochCache {
1022
1027
  return this.epoch >= this.config.ELECTRA_FORK_EPOCH;
1023
1028
  }
1024
1029
 
1025
- getPayloadTimelinessCommittee(slot: Slot): ValidatorIndex[] {
1030
+ getPayloadTimelinessCommittee(slot: Slot): Uint32Array {
1026
1031
  const epoch = computeEpochAtSlot(slot);
1027
1032
 
1028
1033
  if (epoch < this.config.GLOAS_FORK_EPOCH) {
@@ -1030,7 +1035,11 @@ export class EpochCache {
1030
1035
  }
1031
1036
 
1032
1037
  if (epoch === this.epoch) {
1033
- return this.payloadTimelinessCommittee[slot % SLOTS_PER_EPOCH];
1038
+ return this.payloadTimelinessCommittees[slot % SLOTS_PER_EPOCH];
1039
+ }
1040
+
1041
+ if (epoch === this.epoch - 1 && this.previousPayloadTimelinessCommittees.length > 0) {
1042
+ return this.previousPayloadTimelinessCommittees[slot % SLOTS_PER_EPOCH];
1034
1043
  }
1035
1044
 
1036
1045
  throw new Error(`Payload Timeliness Committee is not available for slot=${slot}`);
@@ -1099,7 +1108,6 @@ export function createEmptyEpochCacheImmutableData(
1099
1108
  return {
1100
1109
  config: createBeaconConfig(chainConfig, state.genesisValidatorsRoot),
1101
1110
  // This is a test state, there's no need to have a global shared cache of keys
1102
- pubkey2index: new PubkeyIndexMap(),
1103
- index2pubkey: [],
1111
+ pubkeyCache: createPubkeyCache(),
1104
1112
  };
1105
1113
  }