@lodestar/beacon-node 1.39.0-dev.075956b855 → 1.39.0-dev.0e7901d6a2

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 (180) hide show
  1. package/README.md +1 -1
  2. package/lib/api/impl/beacon/state/utils.d.ts +2 -7
  3. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  4. package/lib/api/impl/beacon/state/utils.js +0 -12
  5. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  6. package/lib/api/impl/lodestar/index.js +1 -1
  7. package/lib/api/impl/lodestar/index.js.map +1 -1
  8. package/lib/api/impl/proof/index.d.ts.map +1 -1
  9. package/lib/api/impl/proof/index.js +1 -2
  10. package/lib/api/impl/proof/index.js.map +1 -1
  11. package/lib/api/impl/validator/index.d.ts.map +1 -1
  12. package/lib/api/impl/validator/index.js +1 -3
  13. package/lib/api/impl/validator/index.js.map +1 -1
  14. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  15. package/lib/chain/blocks/importBlock.js +12 -3
  16. package/lib/chain/blocks/importBlock.js.map +1 -1
  17. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  18. package/lib/chain/blocks/verifyBlock.js +8 -1
  19. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  20. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
  21. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +1 -0
  22. package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
  23. package/lib/chain/chain.d.ts +7 -9
  24. package/lib/chain/chain.d.ts.map +1 -1
  25. package/lib/chain/chain.js +17 -10
  26. package/lib/chain/chain.js.map +1 -1
  27. package/lib/chain/interface.d.ts +6 -9
  28. package/lib/chain/interface.d.ts.map +1 -1
  29. package/lib/chain/interface.js.map +1 -1
  30. package/lib/chain/lightClient/proofs.d.ts.map +1 -1
  31. package/lib/chain/lightClient/proofs.js +0 -2
  32. package/lib/chain/lightClient/proofs.js.map +1 -1
  33. package/lib/chain/opPools/aggregatedAttestationPool.d.ts +5 -9
  34. package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
  35. package/lib/chain/opPools/aggregatedAttestationPool.js +12 -141
  36. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  37. package/lib/chain/opPools/opPool.js +5 -8
  38. package/lib/chain/opPools/opPool.js.map +1 -1
  39. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  40. package/lib/chain/prepareNextSlot.js +4 -7
  41. package/lib/chain/prepareNextSlot.js.map +1 -1
  42. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  43. package/lib/chain/produceBlock/produceBlockBody.js +2 -2
  44. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  45. package/lib/chain/regen/interface.d.ts +1 -5
  46. package/lib/chain/regen/interface.d.ts.map +1 -1
  47. package/lib/chain/regen/queued.d.ts +4 -7
  48. package/lib/chain/regen/queued.d.ts.map +1 -1
  49. package/lib/chain/regen/queued.js +15 -25
  50. package/lib/chain/regen/queued.js.map +1 -1
  51. package/lib/chain/regen/regen.d.ts +1 -1
  52. package/lib/chain/regen/regen.d.ts.map +1 -1
  53. package/lib/chain/regen/regen.js +13 -17
  54. package/lib/chain/regen/regen.js.map +1 -1
  55. package/lib/chain/shufflingCache.d.ts +16 -11
  56. package/lib/chain/shufflingCache.d.ts.map +1 -1
  57. package/lib/chain/shufflingCache.js +47 -41
  58. package/lib/chain/shufflingCache.js.map +1 -1
  59. package/lib/chain/stateCache/blockStateCacheImpl.d.ts +1 -2
  60. package/lib/chain/stateCache/blockStateCacheImpl.d.ts.map +1 -1
  61. package/lib/chain/stateCache/blockStateCacheImpl.js +2 -2
  62. package/lib/chain/stateCache/blockStateCacheImpl.js.map +1 -1
  63. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +1 -2
  64. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  65. package/lib/chain/stateCache/fifoBlockStateCache.js +4 -4
  66. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  67. package/lib/chain/stateCache/inMemoryCheckpointsCache.d.ts +4 -5
  68. package/lib/chain/stateCache/inMemoryCheckpointsCache.d.ts.map +1 -1
  69. package/lib/chain/stateCache/inMemoryCheckpointsCache.js +9 -10
  70. package/lib/chain/stateCache/inMemoryCheckpointsCache.js.map +1 -1
  71. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +5 -6
  72. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  73. package/lib/chain/stateCache/persistentCheckpointsCache.js +17 -17
  74. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  75. package/lib/chain/stateCache/types.d.ts +5 -6
  76. package/lib/chain/stateCache/types.d.ts.map +1 -1
  77. package/lib/chain/stateCache/types.js.map +1 -1
  78. package/lib/chain/validation/attestation.d.ts.map +1 -1
  79. package/lib/chain/validation/attestation.js +2 -2
  80. package/lib/chain/validation/attestation.js.map +1 -1
  81. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  82. package/lib/chain/validation/attesterSlashing.js +1 -1
  83. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  84. package/lib/chain/validation/blobSidecar.d.ts.map +1 -1
  85. package/lib/chain/validation/blobSidecar.js +2 -3
  86. package/lib/chain/validation/blobSidecar.js.map +1 -1
  87. package/lib/chain/validation/block.d.ts.map +1 -1
  88. package/lib/chain/validation/block.js +2 -0
  89. package/lib/chain/validation/block.js.map +1 -1
  90. package/lib/chain/validation/blsToExecutionChange.d.ts.map +1 -1
  91. package/lib/chain/validation/blsToExecutionChange.js +9 -2
  92. package/lib/chain/validation/blsToExecutionChange.js.map +1 -1
  93. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  94. package/lib/chain/validation/dataColumnSidecar.js +2 -3
  95. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  96. package/lib/chain/validation/proposerSlashing.js +2 -1
  97. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  98. package/lib/db/repositories/stateArchive.d.ts +1 -1
  99. package/lib/db/repositories/stateArchive.d.ts.map +1 -1
  100. package/lib/db/repositories/stateArchive.js +2 -2
  101. package/lib/db/repositories/stateArchive.js.map +1 -1
  102. package/lib/execution/engine/mock.d.ts +9 -6
  103. package/lib/execution/engine/mock.d.ts.map +1 -1
  104. package/lib/execution/engine/mock.js +34 -7
  105. package/lib/execution/engine/mock.js.map +1 -1
  106. package/lib/metrics/metrics/lodestar.d.ts +1 -6
  107. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  108. package/lib/metrics/metrics/lodestar.js +3 -17
  109. package/lib/metrics/metrics/lodestar.js.map +1 -1
  110. package/lib/network/peers/discover.d.ts.map +1 -1
  111. package/lib/network/peers/discover.js.map +1 -1
  112. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +1 -1
  113. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  114. package/lib/node/nodejs.d.ts.map +1 -1
  115. package/lib/node/nodejs.js +17 -2
  116. package/lib/node/nodejs.js.map +1 -1
  117. package/lib/sync/backfill/backfill.d.ts.map +1 -1
  118. package/lib/sync/backfill/backfill.js +2 -2
  119. package/lib/sync/backfill/backfill.js.map +1 -1
  120. package/lib/sync/backfill/verify.d.ts +2 -2
  121. package/lib/sync/backfill/verify.d.ts.map +1 -1
  122. package/lib/sync/backfill/verify.js +2 -2
  123. package/lib/sync/backfill/verify.js.map +1 -1
  124. package/lib/util/sszBytes.js +1 -1
  125. package/lib/util/sszBytes.js.map +1 -1
  126. package/package.json +28 -20
  127. package/src/api/impl/beacon/state/utils.ts +2 -22
  128. package/src/api/impl/lodestar/index.ts +1 -1
  129. package/src/api/impl/proof/index.ts +1 -2
  130. package/src/api/impl/validator/index.ts +1 -3
  131. package/src/chain/blocks/importBlock.ts +13 -3
  132. package/src/chain/blocks/verifyBlock.ts +9 -3
  133. package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +1 -0
  134. package/src/chain/chain.ts +27 -15
  135. package/src/chain/interface.ts +6 -8
  136. package/src/chain/lightClient/proofs.ts +0 -2
  137. package/src/chain/opPools/aggregatedAttestationPool.ts +19 -191
  138. package/src/chain/opPools/opPool.ts +5 -7
  139. package/src/chain/prepareNextSlot.ts +2 -6
  140. package/src/chain/produceBlock/produceBlockBody.ts +7 -2
  141. package/src/chain/regen/interface.ts +1 -5
  142. package/src/chain/regen/queued.ts +15 -34
  143. package/src/chain/regen/regen.ts +12 -18
  144. package/src/chain/shufflingCache.ts +67 -50
  145. package/src/chain/stateCache/blockStateCacheImpl.ts +2 -3
  146. package/src/chain/stateCache/fifoBlockStateCache.ts +4 -5
  147. package/src/chain/stateCache/inMemoryCheckpointsCache.ts +9 -15
  148. package/src/chain/stateCache/persistentCheckpointsCache.ts +17 -25
  149. package/src/chain/stateCache/types.ts +5 -10
  150. package/src/chain/validation/attestation.ts +3 -3
  151. package/src/chain/validation/attesterSlashing.ts +8 -1
  152. package/src/chain/validation/blobSidecar.ts +3 -3
  153. package/src/chain/validation/block.ts +3 -0
  154. package/src/chain/validation/blsToExecutionChange.ts +9 -7
  155. package/src/chain/validation/dataColumnSidecar.ts +3 -3
  156. package/src/chain/validation/proposerSlashing.ts +2 -1
  157. package/src/db/repositories/stateArchive.ts +2 -2
  158. package/src/execution/engine/mock.ts +40 -13
  159. package/src/metrics/metrics/lodestar.ts +3 -17
  160. package/src/network/peers/discover.ts +3 -3
  161. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +1 -1
  162. package/src/node/nodejs.ts +18 -3
  163. package/src/sync/backfill/backfill.ts +2 -14
  164. package/src/sync/backfill/verify.ts +1 -7
  165. package/src/util/sszBytes.ts +1 -1
  166. package/lib/chain/rewards/attestationsRewards.d.ts +0 -8
  167. package/lib/chain/rewards/attestationsRewards.d.ts.map +0 -1
  168. package/lib/chain/rewards/attestationsRewards.js +0 -112
  169. package/lib/chain/rewards/attestationsRewards.js.map +0 -1
  170. package/lib/chain/rewards/blockRewards.d.ts +0 -15
  171. package/lib/chain/rewards/blockRewards.d.ts.map +0 -1
  172. package/lib/chain/rewards/blockRewards.js +0 -94
  173. package/lib/chain/rewards/blockRewards.js.map +0 -1
  174. package/lib/chain/rewards/syncCommitteeRewards.d.ts +0 -7
  175. package/lib/chain/rewards/syncCommitteeRewards.d.ts.map +0 -1
  176. package/lib/chain/rewards/syncCommitteeRewards.js +0 -36
  177. package/lib/chain/rewards/syncCommitteeRewards.js.map +0 -1
  178. package/src/chain/rewards/attestationsRewards.ts +0 -206
  179. package/src/chain/rewards/blockRewards.ts +0 -153
  180. package/src/chain/rewards/syncCommitteeRewards.ts +0 -60
@@ -14,9 +14,12 @@ import {
14
14
  EpochShuffling,
15
15
  Index2PubkeyCache,
16
16
  computeAnchorCheckpoint,
17
+ computeAttestationsRewards,
18
+ computeBlockRewards,
17
19
  computeEndSlotAtEpoch,
18
20
  computeEpochAtSlot,
19
21
  computeStartSlotAtEpoch,
22
+ computeSyncCommitteeRewards,
20
23
  getEffectiveBalanceIncrementsZeroInactive,
21
24
  getEffectiveBalancesFromStateBytes,
22
25
  processSlots,
@@ -36,6 +39,7 @@ import {
36
39
  Wei,
37
40
  isBlindedBeaconBlock,
38
41
  phase0,
42
+ rewards,
39
43
  } from "@lodestar/types";
40
44
  import {Logger, fromHex, gweiToWei, isErrorAborted, pruneSetToMax, sleep, toRootHex} from "@lodestar/utils";
41
45
  import {ProcessShutdownCallback} from "@lodestar/validator";
@@ -48,6 +52,7 @@ import {computeNodeIdFromPrivateKey} from "../network/subnets/interface.js";
48
52
  import {BufferPool} from "../util/bufferPool.js";
49
53
  import {Clock, ClockEvent, IClock} from "../util/clock.js";
50
54
  import {CustodyConfig, getValidatorsCustodyRequirement} from "../util/dataColumns.js";
55
+ import {callInNextEventLoop} from "../util/eventLoop.js";
51
56
  import {ensureDir, writeIfNotExist} from "../util/file.js";
52
57
  import {isOptimisticBlock} from "../util/forkChoice.js";
53
58
  import {SerializedCache} from "../util/serializedCache.js";
@@ -77,9 +82,6 @@ import {AssembledBlockType, BlockType, ProduceResult} from "./produceBlock/index
77
82
  import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js";
78
83
  import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
79
84
  import {ReprocessController} from "./reprocess.js";
80
- import {AttestationsRewards, computeAttestationsRewards} from "./rewards/attestationsRewards.js";
81
- import {BlockRewards, computeBlockRewards} from "./rewards/blockRewards.js";
82
- import {SyncCommitteeRewards, computeSyncCommitteeRewards} from "./rewards/syncCommitteeRewards.js";
83
85
  import {
84
86
  SeenAggregators,
85
87
  SeenAttesters,
@@ -290,7 +292,8 @@ export class BeaconChain implements IBeaconChain {
290
292
  });
291
293
 
292
294
  this._earliestAvailableSlot = anchorState.slot;
293
- this.shufflingCache = anchorState.epochCtx.shufflingCache = new ShufflingCache(metrics, logger, this.opts, [
295
+
296
+ this.shufflingCache = new ShufflingCache(metrics, logger, this.opts, [
294
297
  {
295
298
  shuffling: anchorState.epochCtx.previousShuffling,
296
299
  decisionRoot: anchorState.epochCtx.previousDecisionRoot,
@@ -416,6 +419,7 @@ export class BeaconChain implements IBeaconChain {
416
419
  clock.addListener(ClockEvent.epoch, this.onClockEpoch.bind(this));
417
420
  emitter.addListener(ChainEvent.forkChoiceFinalized, this.onForkChoiceFinalized.bind(this));
418
421
  emitter.addListener(ChainEvent.forkChoiceJustified, this.onForkChoiceJustified.bind(this));
422
+ emitter.addListener(ChainEvent.checkpoint, this.onCheckpoint.bind(this));
419
423
  }
420
424
 
421
425
  async init(): Promise<void> {
@@ -503,7 +507,7 @@ export class BeaconChain implements IBeaconChain {
503
507
  async getStateBySlot(
504
508
  slot: Slot,
505
509
  opts?: StateGetOpts
506
- ): Promise<{state: BeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null> {
510
+ ): Promise<{state: CachedBeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null> {
507
511
  const finalizedBlock = this.forkChoice.getFinalizedBlock();
508
512
 
509
513
  if (slot < finalizedBlock.slot) {
@@ -558,7 +562,7 @@ export class BeaconChain implements IBeaconChain {
558
562
  async getStateByStateRoot(
559
563
  stateRoot: RootHex,
560
564
  opts?: StateGetOpts
561
- ): Promise<{state: BeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null> {
565
+ ): Promise<{state: CachedBeaconStateAllForks | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
562
566
  if (opts?.allowRegen) {
563
567
  const state = await this.regen.getState(stateRoot, RegenCaller.restApi);
564
568
  const block = this.forkChoice.getBlock(state.latestBlockHeader.hashTreeRoot());
@@ -586,7 +590,8 @@ export class BeaconChain implements IBeaconChain {
586
590
  };
587
591
  }
588
592
 
589
- const data = await this.db.stateArchive.getByRoot(fromHex(stateRoot));
593
+ // this is mostly useful for a node with `--chain.archiveStateEpochFrequency 1`
594
+ const data = await this.db.stateArchive.getBinaryByRoot(fromHex(stateRoot));
590
595
  return data && {state: data, executionOptimistic: false, finalized: true};
591
596
  }
592
597
 
@@ -979,8 +984,8 @@ export class BeaconChain implements IBeaconChain {
979
984
  this.metrics?.gossipAttestation.useHeadBlockState.inc({caller: regenCaller});
980
985
  state = await this.regen.getState(attHeadBlock.stateRoot, regenCaller);
981
986
  }
982
-
983
- // should always be the current epoch of the active context so no need to await a result from the ShufflingCache
987
+ // resolve the promise to unblock other calls of the same epoch and dependent root
988
+ this.shufflingCache.processState(state);
984
989
  return state.epochCtx.getShufflingAtEpoch(attEpoch);
985
990
  }
986
991
 
@@ -1164,6 +1169,13 @@ export class BeaconChain implements IBeaconChain {
1164
1169
  this.logger.verbose("Fork choice justified", {epoch: cp.epoch, root: cp.rootHex});
1165
1170
  }
1166
1171
 
1172
+ private onCheckpoint(this: BeaconChain, _checkpoint: phase0.Checkpoint, state: CachedBeaconStateAllForks): void {
1173
+ // Defer to not block other checkpoint event handlers, which can cause lightclient update delays
1174
+ callInNextEventLoop(() => {
1175
+ this.shufflingCache.processState(state);
1176
+ });
1177
+ }
1178
+
1167
1179
  private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
1168
1180
  this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
1169
1181
  this.seenBlockProposers.prune(computeStartSlotAtEpoch(cp.epoch));
@@ -1285,7 +1297,7 @@ export class BeaconChain implements IBeaconChain {
1285
1297
  }
1286
1298
  }
1287
1299
 
1288
- async getBlockRewards(block: BeaconBlock | BlindedBeaconBlock): Promise<BlockRewards> {
1300
+ async getBlockRewards(block: BeaconBlock | BlindedBeaconBlock): Promise<rewards.BlockRewards> {
1289
1301
  let preState = this.regen.getPreStateSync(block);
1290
1302
 
1291
1303
  if (preState === null) {
@@ -1294,15 +1306,15 @@ export class BeaconChain implements IBeaconChain {
1294
1306
 
1295
1307
  preState = processSlots(preState, block.slot); // Dial preState's slot to block.slot
1296
1308
 
1297
- const postState = this.regen.getStateSync(toRootHex(block.stateRoot)) ?? undefined;
1309
+ const proposerRewards = this.regen.getStateSync(toRootHex(block.stateRoot))?.proposerRewards ?? undefined;
1298
1310
 
1299
- return computeBlockRewards(this.config, block, preState.clone(), postState?.clone());
1311
+ return computeBlockRewards(this.config, block, preState, proposerRewards);
1300
1312
  }
1301
1313
 
1302
1314
  async getAttestationsRewards(
1303
1315
  epoch: Epoch,
1304
1316
  validatorIds?: (ValidatorIndex | string)[]
1305
- ): Promise<{rewards: AttestationsRewards; executionOptimistic: boolean; finalized: boolean}> {
1317
+ ): Promise<{rewards: rewards.AttestationsRewards; executionOptimistic: boolean; finalized: boolean}> {
1306
1318
  // We use end slot of (epoch + 1) to ensure we have seen all attestations. On-time or late. Any late attestation beyond this slot is not considered
1307
1319
  const slot = computeEndSlotAtEpoch(epoch + 1);
1308
1320
  const stateResult = await this.getStateBySlot(slot, {allowRegen: false}); // No regen if state not in cache
@@ -1328,7 +1340,7 @@ export class BeaconChain implements IBeaconChain {
1328
1340
  async getSyncCommitteeRewards(
1329
1341
  block: BeaconBlock | BlindedBeaconBlock,
1330
1342
  validatorIds?: (ValidatorIndex | string)[]
1331
- ): Promise<SyncCommitteeRewards> {
1343
+ ): Promise<rewards.SyncCommitteeRewards> {
1332
1344
  let preState = this.regen.getPreStateSync(block);
1333
1345
 
1334
1346
  if (preState === null) {
@@ -1337,6 +1349,6 @@ export class BeaconChain implements IBeaconChain {
1337
1349
 
1338
1350
  preState = processSlots(preState, block.slot); // Dial preState's slot to block.slot
1339
1351
 
1340
- return computeSyncCommitteeRewards(this.config, this.index2pubkey, block, preState.clone(), validatorIds);
1352
+ return computeSyncCommitteeRewards(this.config, this.index2pubkey, block, preState, validatorIds);
1341
1353
  }
1342
1354
  }
@@ -23,6 +23,7 @@ import {
23
23
  altair,
24
24
  capella,
25
25
  phase0,
26
+ rewards,
26
27
  } from "@lodestar/types";
27
28
  import {Logger} from "@lodestar/utils";
28
29
  import {IExecutionBuilder, IExecutionEngine} from "../execution/index.js";
@@ -48,9 +49,6 @@ import {IChainOptions} from "./options.js";
48
49
  import {AssembledBlockType, BlockAttributes, BlockType, ProduceResult} from "./produceBlock/produceBlockBody.js";
49
50
  import {IStateRegenerator, RegenCaller} from "./regen/index.js";
50
51
  import {ReprocessController} from "./reprocess.js";
51
- import {AttestationsRewards} from "./rewards/attestationsRewards.js";
52
- import {BlockRewards} from "./rewards/blockRewards.js";
53
- import {SyncCommitteeRewards} from "./rewards/syncCommitteeRewards.js";
54
52
  import {
55
53
  SeenAggregators,
56
54
  SeenAttesters,
@@ -170,12 +168,12 @@ export interface IBeaconChain {
170
168
  getStateBySlot(
171
169
  slot: Slot,
172
170
  opts?: StateGetOpts
173
- ): Promise<{state: BeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null>;
171
+ ): Promise<{state: CachedBeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null>;
174
172
  /** Returns a local state by state root */
175
173
  getStateByStateRoot(
176
174
  stateRoot: RootHex,
177
175
  opts?: StateGetOpts
178
- ): Promise<{state: BeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null>;
176
+ ): Promise<{state: CachedBeaconStateAllForks | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null>;
179
177
  /** Return serialized bytes of a persisted checkpoint state */
180
178
  getPersistedCheckpointState(checkpoint?: phase0.Checkpoint): Promise<Uint8Array | null>;
181
179
  /** Returns a cached state by checkpoint */
@@ -255,15 +253,15 @@ export interface IBeaconChain {
255
253
  regenCanAcceptWork(): boolean;
256
254
  blsThreadPoolCanAcceptWork(): boolean;
257
255
 
258
- getBlockRewards(blockRef: BeaconBlock | BlindedBeaconBlock): Promise<BlockRewards>;
256
+ getBlockRewards(blockRef: BeaconBlock | BlindedBeaconBlock): Promise<rewards.BlockRewards>;
259
257
  getAttestationsRewards(
260
258
  epoch: Epoch,
261
259
  validatorIds?: (ValidatorIndex | string)[]
262
- ): Promise<{rewards: AttestationsRewards; executionOptimistic: boolean; finalized: boolean}>;
260
+ ): Promise<{rewards: rewards.AttestationsRewards; executionOptimistic: boolean; finalized: boolean}>;
263
261
  getSyncCommitteeRewards(
264
262
  blockRef: BeaconBlock | BlindedBeaconBlock,
265
263
  validatorIds?: (ValidatorIndex | string)[]
266
- ): Promise<SyncCommitteeRewards>;
264
+ ): Promise<rewards.SyncCommitteeRewards>;
267
265
  }
268
266
 
269
267
  export type SSZObjectType =
@@ -12,7 +12,6 @@ import {BeaconBlockBody, SSZTypesFor, ssz} from "@lodestar/types";
12
12
  import {SyncCommitteeWitness} from "./types.js";
13
13
 
14
14
  export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllForks): SyncCommitteeWitness {
15
- state.commit();
16
15
  const n1 = state.node;
17
16
  let witness: Uint8Array[];
18
17
  let currentSyncCommitteeRoot: Uint8Array;
@@ -71,7 +70,6 @@ export function getCurrentSyncCommitteeBranch(syncCommitteesWitness: SyncCommitt
71
70
  }
72
71
 
73
72
  export function getFinalizedRootProof(state: CachedBeaconStateAllForks): Uint8Array[] {
74
- state.commit();
75
73
  const finalizedRootGindex = state.epochCtx.isPostElectra() ? FINALIZED_ROOT_GINDEX_ELECTRA : FINALIZED_ROOT_GINDEX;
76
74
  return new Tree(state.node).getSingleProof(BigInt(finalizedRootGindex));
77
75
  }
@@ -5,7 +5,6 @@ import {IForkChoice} from "@lodestar/fork-choice";
5
5
  import {
6
6
  ForkName,
7
7
  ForkSeq,
8
- MAX_ATTESTATIONS,
9
8
  MAX_ATTESTATIONS_ELECTRA,
10
9
  MAX_COMMITTEES_PER_SLOT,
11
10
  MIN_ATTESTATION_INCLUSION_DELAY,
@@ -23,7 +22,6 @@ import {
23
22
  CachedBeaconStateAllForks,
24
23
  CachedBeaconStateAltair,
25
24
  CachedBeaconStateGloas,
26
- CachedBeaconStatePhase0,
27
25
  EffectiveBalanceIncrements,
28
26
  RootCache,
29
27
  computeEpochAtSlot,
@@ -32,21 +30,12 @@ import {
32
30
  getAttestationParticipationStatus,
33
31
  getBlockRootAtSlot,
34
32
  } from "@lodestar/state-transition";
35
- import {
36
- Attestation,
37
- Epoch,
38
- RootHex,
39
- Slot,
40
- ValidatorIndex,
41
- electra,
42
- isElectraAttestation,
43
- phase0,
44
- ssz,
45
- } from "@lodestar/types";
33
+ import {Attestation, Epoch, RootHex, Slot, electra, isElectraAttestation, phase0, ssz} from "@lodestar/types";
46
34
  import {MapDef, assert, toRootHex} from "@lodestar/utils";
47
35
  import {Metrics} from "../../metrics/metrics.js";
48
36
  import {IntersectResult, intersectUint8Arrays} from "../../util/bitArray.js";
49
37
  import {getShufflingDependentRoot} from "../../util/dependentRoot.js";
38
+ import {ShufflingCache} from "../shufflingCache.js";
50
39
  import {InsertOutcome} from "./types.js";
51
40
  import {pruneBySlot, signatureFromBytesNoCheck} from "./utils.js";
52
41
 
@@ -54,8 +43,6 @@ type DataRootHex = string;
54
43
 
55
44
  type CommitteeIndex = number;
56
45
 
57
- // for pre-electra
58
- type AttestationWithScore = {attestation: Attestation; score: number};
59
46
  /**
60
47
  * for electra, this is to consolidate aggregated attestations of the same attestation data into a single attestation to be included in block
61
48
  * note that this is local definition in this file and it's NOT validator consolidation
@@ -110,15 +97,6 @@ const MAX_RETAINED_ATTESTATIONS_PER_GROUP = 4;
110
97
  */
111
98
  const MAX_RETAINED_ATTESTATIONS_PER_GROUP_ELECTRA = 8;
112
99
 
113
- /**
114
- * Pre-electra, each slot has 64 committees, and each block has 128 attestations max so in average
115
- * we get 2 attestation per groups.
116
- * Starting from Jan 2024, we have a performance issue getting attestations for a block. Based on the
117
- * fact that lot of groups will have only 1 full participation attestation, increase this number
118
- * a bit higher than average. This also help decrease number of slots to search for attestations.
119
- */
120
- const MAX_ATTESTATIONS_PER_GROUP = 3;
121
-
122
100
  /**
123
101
  * For electra, there is on chain aggregation of attestations across committees, so we can just pick up to 8
124
102
  * attestations per group, sort by scores to get first 8.
@@ -230,123 +208,18 @@ export class AggregatedAttestationPool {
230
208
  this.lowestPermissibleSlot = Math.max(clockSlot - slotsToRetain, 0);
231
209
  }
232
210
 
233
- getAttestationsForBlock(fork: ForkName, forkChoice: IForkChoice, state: CachedBeaconStateAllForks): Attestation[] {
234
- const forkSeq = ForkSeq[fork];
235
- return forkSeq >= ForkSeq.electra
236
- ? this.getAttestationsForBlockElectra(fork, forkChoice, state)
237
- : this.getAttestationsForBlockPreElectra(fork, forkChoice, state);
238
- }
239
-
240
- /**
241
- * Get attestations to be included in a block pre-electra. Returns up to $MAX_ATTESTATIONS items
242
- */
243
- getAttestationsForBlockPreElectra(
211
+ getAttestationsForBlock(
244
212
  fork: ForkName,
245
213
  forkChoice: IForkChoice,
214
+ shufflingCache: ShufflingCache,
246
215
  state: CachedBeaconStateAllForks
247
- ): phase0.Attestation[] {
248
- const stateSlot = state.slot;
249
- const stateEpoch = state.epochCtx.epoch;
250
- const statePrevEpoch = stateEpoch - 1;
251
-
252
- const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, state);
253
- const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);
254
-
255
- const attestationsByScore: AttestationWithScore[] = [];
256
-
257
- const slots = Array.from(this.attestationGroupByIndexByDataHexBySlot.keys()).sort((a, b) => b - a);
258
- let minScore = Number.MAX_SAFE_INTEGER;
259
- let slotCount = 0;
260
- slot: for (const slot of slots) {
261
- slotCount++;
262
- const attestationGroupByIndexByDataHash = this.attestationGroupByIndexByDataHexBySlot.get(slot);
263
- // should not happen
264
- if (!attestationGroupByIndexByDataHash) {
265
- throw Error(`No aggregated attestation pool for slot=${slot}`);
266
- }
267
-
268
- const epoch = computeEpochAtSlot(slot);
269
- // validateAttestation condition: Attestation target epoch not in previous or current epoch
270
- if (!(epoch === stateEpoch || epoch === statePrevEpoch)) {
271
- continue; // Invalid attestations
272
- }
273
- // validateAttestation condition: Attestation slot not within inclusion window
274
- if (
275
- !(
276
- slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot &&
277
- // Post deneb, attestations are valid for current and previous epoch
278
- (ForkSeq[fork] >= ForkSeq.deneb || stateSlot <= slot + SLOTS_PER_EPOCH)
279
- )
280
- ) {
281
- continue; // Invalid attestations
282
- }
283
-
284
- const inclusionDistance = stateSlot - slot;
285
- for (const attestationGroupByIndex of attestationGroupByIndexByDataHash.values()) {
286
- for (const [committeeIndex, attestationGroup] of attestationGroupByIndex.entries()) {
287
- const notSeenCommitteeMembers = notSeenValidatorsFn(epoch, slot, committeeIndex);
288
- if (notSeenCommitteeMembers === null || notSeenCommitteeMembers.size === 0) {
289
- continue;
290
- }
291
-
292
- if (
293
- slotCount > 2 &&
294
- attestationsByScore.length >= MAX_ATTESTATIONS &&
295
- notSeenCommitteeMembers.size / inclusionDistance < minScore
296
- ) {
297
- // after 2 slots, there are a good chance that we have 2 * MAX_ATTESTATIONS attestations and break the for loop early
298
- // if not, we may have to scan all slots in the pool
299
- // if we have enough attestations and the max possible score is lower than scores of `attestationsByScore`, we should skip
300
- // otherwise it takes time to check attestation, add it and remove it later after the sort by score
301
- continue;
302
- }
303
-
304
- if (validateAttestationDataFn(attestationGroup.data) !== null) {
305
- continue;
306
- }
307
-
308
- // TODO: Is it necessary to validateAttestation for:
309
- // - Attestation committee index not within current committee count
310
- // - Attestation aggregation bits length does not match committee length
311
- //
312
- // These properties should not change after being validate in gossip
313
- // IF they have to be validated, do it only with one attestation per group since same data
314
- // The committeeCountPerSlot can be precomputed once per slot
315
- const getAttestationsResult = attestationGroup.getAttestationsForBlock(
316
- fork,
317
- state.epochCtx.effectiveBalanceIncrements,
318
- notSeenCommitteeMembers,
319
- MAX_ATTESTATIONS_PER_GROUP
320
- );
321
- for (const {attestation, newSeenEffectiveBalance} of getAttestationsResult.result) {
322
- const score = newSeenEffectiveBalance / inclusionDistance;
323
- if (score < minScore) {
324
- minScore = score;
325
- }
326
- attestationsByScore.push({
327
- attestation,
328
- score,
329
- });
330
- }
331
-
332
- // Stop accumulating attestations there are enough that may have good scoring
333
- if (attestationsByScore.length >= MAX_ATTESTATIONS * 2) {
334
- break slot;
335
- }
336
- }
337
- }
216
+ ): Attestation[] {
217
+ const forkSeq = ForkSeq[fork];
218
+ if (forkSeq < ForkSeq.electra) {
219
+ throw new Error("Does not support producing blocks for pre-electra forks anymore");
338
220
  }
339
221
 
340
- const sortedAttestationsByScore = attestationsByScore.sort((a, b) => b.score - a.score);
341
- const attestationsForBlock: phase0.Attestation[] = [];
342
- for (const [i, attestationWithScore] of sortedAttestationsByScore.entries()) {
343
- if (i >= MAX_ATTESTATIONS) {
344
- break;
345
- }
346
- // attestations could be modified in this op pool, so we need to clone for block
347
- attestationsForBlock.push(ssz.phase0.Attestation.clone(attestationWithScore.attestation));
348
- }
349
- return attestationsForBlock;
222
+ return this.getAttestationsForBlockElectra(fork, forkChoice, shufflingCache, state);
350
223
  }
351
224
 
352
225
  /**
@@ -355,6 +228,7 @@ export class AggregatedAttestationPool {
355
228
  getAttestationsForBlockElectra(
356
229
  fork: ForkName,
357
230
  forkChoice: IForkChoice,
231
+ shufflingCache: ShufflingCache,
358
232
  state: CachedBeaconStateAllForks
359
233
  ): electra.Attestation[] {
360
234
  const stateSlot = state.slot;
@@ -362,7 +236,7 @@ export class AggregatedAttestationPool {
362
236
  const statePrevEpoch = stateEpoch - 1;
363
237
  const rootCache = new RootCache(state);
364
238
 
365
- const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, state);
239
+ const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, shufflingCache, state);
366
240
  const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);
367
241
 
368
242
  const slots = Array.from(this.attestationGroupByIndexByDataHexBySlot.keys()).sort((a, b) => b - a);
@@ -864,41 +738,14 @@ export function aggregateConsolidation({byCommittee, attData}: AttestationsConso
864
738
  * Pre-compute participation from a CachedBeaconStateAllForks, for use to check if an attestation's committee
865
739
  * has already attested or not.
866
740
  */
867
- export function getNotSeenValidatorsFn(config: BeaconConfig, state: CachedBeaconStateAllForks): GetNotSeenValidatorsFn {
741
+ export function getNotSeenValidatorsFn(
742
+ config: BeaconConfig,
743
+ shufflingCache: ShufflingCache,
744
+ state: CachedBeaconStateAllForks
745
+ ): GetNotSeenValidatorsFn {
868
746
  const stateSlot = state.slot;
869
747
  if (config.getForkName(stateSlot) === ForkName.phase0) {
870
- // Get attestations to be included in a phase0 block.
871
- // As we are close to altair, this is not really important, it's mainly for e2e.
872
- // The performance is not great due to the different BeaconState data structure to altair.
873
- // check for phase0 block already
874
- const phase0State = state as CachedBeaconStatePhase0;
875
- const stateEpoch = computeEpochAtSlot(stateSlot);
876
-
877
- const previousEpochParticipants = extractParticipationPhase0(
878
- phase0State.previousEpochAttestations.getAllReadonly(),
879
- state
880
- );
881
- const currentEpochParticipants = extractParticipationPhase0(
882
- phase0State.currentEpochAttestations.getAllReadonly(),
883
- state
884
- );
885
-
886
- return (epoch: Epoch, slot: Slot, committeeIndex: number) => {
887
- const participants =
888
- epoch === stateEpoch ? currentEpochParticipants : epoch === stateEpoch - 1 ? previousEpochParticipants : null;
889
- if (participants === null) {
890
- return null;
891
- }
892
- const committee = state.epochCtx.getBeaconCommittee(slot, committeeIndex);
893
-
894
- const notSeenCommitteeMembers = new Set<number>();
895
- for (const [i, validatorIndex] of committee.entries()) {
896
- if (!participants.has(validatorIndex)) {
897
- notSeenCommitteeMembers.add(i);
898
- }
899
- }
900
- return notSeenCommitteeMembers.size === 0 ? null : notSeenCommitteeMembers;
901
- };
748
+ throw new Error("getNotSeenValidatorsFn is not supported phase0 state");
902
749
  }
903
750
 
904
751
  // altair and future forks
@@ -927,7 +774,8 @@ export function getNotSeenValidatorsFn(config: BeaconConfig, state: CachedBeacon
927
774
  return notSeenCommitteeMembers.size === 0 ? null : notSeenCommitteeMembers;
928
775
  }
929
776
 
930
- const committee = state.epochCtx.getBeaconCommittee(slot, committeeIndex);
777
+ const decisionRoot = state.epochCtx.getShufflingDecisionRoot(computeEpochAtSlot(slot));
778
+ const committee = shufflingCache.getBeaconCommittee(epoch, decisionRoot, slot, committeeIndex);
931
779
  notSeenCommitteeMembers = new Set<number>();
932
780
  for (const [i, validatorIndex] of committee.entries()) {
933
781
  // no need to check flagIsTimelySource as if validator is not seen, it's participation status is 0
@@ -942,26 +790,6 @@ export function getNotSeenValidatorsFn(config: BeaconConfig, state: CachedBeacon
942
790
  };
943
791
  }
944
792
 
945
- export function extractParticipationPhase0(
946
- attestations: phase0.PendingAttestation[],
947
- state: CachedBeaconStateAllForks
948
- ): Set<ValidatorIndex> {
949
- const {epochCtx} = state;
950
- const allParticipants = new Set<ValidatorIndex>();
951
- for (const att of attestations) {
952
- const aggregationBits = att.aggregationBits;
953
- const attData = att.data;
954
- const attSlot = attData.slot;
955
- const committeeIndex = attData.index;
956
- const committee = epochCtx.getBeaconCommittee(attSlot, committeeIndex);
957
- const participants = aggregationBits.intersectValues(committee);
958
- for (const participant of participants) {
959
- allParticipants.add(participant);
960
- }
961
- }
962
- return allParticipants;
963
- }
964
-
965
793
  /**
966
794
  * This returns a function to validate if an attestation data is compatible to a state.
967
795
  *
@@ -1,5 +1,5 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
- import {Id, Repository} from "@lodestar/db";
2
+ import {DbBatch, Id, Repository} from "@lodestar/db";
3
3
  import {
4
4
  BLS_WITHDRAWAL_PREFIX,
5
5
  ForkName,
@@ -440,23 +440,21 @@ async function persistDiff<K extends Id, V>(
440
440
  serializeKey: (key: K) => number | string
441
441
  ): Promise<void> {
442
442
  const persistedKeys = await dbRepo.keys();
443
- const itemsToPut: {key: K; value: V}[] = [];
444
- const keysToDelete: K[] = [];
443
+ const batch: DbBatch<K, V> = [];
445
444
 
446
445
  const persistedKeysSerialized = new Set(persistedKeys.map(serializeKey));
447
446
  for (const item of items) {
448
447
  if (!persistedKeysSerialized.has(serializeKey(item.key))) {
449
- itemsToPut.push(item);
448
+ batch.push({type: "put", key: item.key, value: item.value});
450
449
  }
451
450
  }
452
451
 
453
452
  const targetKeysSerialized = new Set(items.map((item) => serializeKey(item.key)));
454
453
  for (const persistedKey of persistedKeys) {
455
454
  if (!targetKeysSerialized.has(serializeKey(persistedKey))) {
456
- keysToDelete.push(persistedKey);
455
+ batch.push({type: "del", key: persistedKey});
457
456
  }
458
457
  }
459
458
 
460
- if (itemsToPut.length > 0) await dbRepo.batchPut(itemsToPut);
461
- if (keysToDelete.length > 0) await dbRepo.batchDelete(keysToDelete);
459
+ if (batch.length > 0) await dbRepo.batch(batch);
462
460
  }
@@ -117,12 +117,7 @@ export class PrepareNextSlotScheduler {
117
117
  // the slot 0 of next epoch will likely use this Previous Root Checkpoint state for state transition so we transfer cache here
118
118
  // the resulting state with cache will be cached in Checkpoint State Cache which is used for the upcoming block processing
119
119
  // for other slots dontTransferCached=true because we don't run state transition on this state
120
- //
121
- // Shuffling calculation will be done asynchronously when passing asyncShufflingCalculation=true. Shuffling will be queued in
122
- // beforeProcessEpoch and should theoretically be ready immediately after the synchronous epoch transition finished and the
123
- // event loop is free. In long periods of non-finality too many forks will cause the shufflingCache to throw an error for
124
- // too many queued shufflings so only run async during normal epoch transition. See issue ChainSafe/lodestar#7244
125
- {dontTransferCache: !isEpochTransition, asyncShufflingCalculation: true},
120
+ {dontTransferCache: !isEpochTransition},
126
121
  RegenCaller.precomputeEpoch
127
122
  );
128
123
 
@@ -148,6 +143,7 @@ export class PrepareNextSlotScheduler {
148
143
  updatedPrepareState = (await this.chain.regen.getBlockSlotState(
149
144
  proposerHeadRoot,
150
145
  prepareSlot,
146
+ // only transfer cache if epoch transition because that's the state we will use to stateTransition() the 1st block of epoch
151
147
  {dontTransferCache: !isEpochTransition},
152
148
  RegenCaller.predictProposerHead
153
149
  )) as CachedBeaconStateExecutions;
@@ -623,7 +623,7 @@ function preparePayloadAttributes(
623
623
  (payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals = getExpectedWithdrawals(
624
624
  ForkSeq[fork],
625
625
  prepareState as CachedBeaconStateCapella
626
- ).withdrawals;
626
+ ).expectedWithdrawals;
627
627
  }
628
628
 
629
629
  if (ForkSeq[fork] >= ForkSeq.deneb) {
@@ -661,7 +661,12 @@ export async function produceCommonBlockBody<T extends BlockType>(
661
661
  this.opPool.getSlashingsAndExits(currentState, blockType, this.metrics);
662
662
 
663
663
  const endAttestations = stepsMetrics?.startTimer();
664
- const attestations = this.aggregatedAttestationPool.getAttestationsForBlock(fork, this.forkChoice, currentState);
664
+ const attestations = this.aggregatedAttestationPool.getAttestationsForBlock(
665
+ fork,
666
+ this.forkChoice,
667
+ this.shufflingCache,
668
+ currentState
669
+ );
665
670
  endAttestations?.({
666
671
  step: BlockProductionStep.attestations,
667
672
  });
@@ -31,10 +31,6 @@ export enum RegenFnName {
31
31
 
32
32
  export type StateRegenerationOpts = {
33
33
  dontTransferCache: boolean;
34
- /**
35
- * Do not queue shuffling calculation async. Forces sync JIT calculation in afterProcessEpoch if not passed as `true`
36
- */
37
- asyncShufflingCalculation?: boolean;
38
34
  };
39
35
 
40
36
  export interface IStateRegenerator extends IStateRegeneratorInternal {
@@ -90,5 +86,5 @@ export interface IStateRegeneratorInternal {
90
86
  /**
91
87
  * Return the exact state with `stateRoot`
92
88
  */
93
- getState(stateRoot: RootHex, rCaller: RegenCaller, opts?: StateRegenerationOpts): Promise<CachedBeaconStateAllForks>;
89
+ getState(stateRoot: RootHex, rCaller: RegenCaller): Promise<CachedBeaconStateAllForks>;
94
90
  }