@lodestar/state-transition 1.51.0-dev.2593063a82 → 1.51.0-dev.2aff7aaeb7

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 (173) hide show
  1. package/lib/block/index.d.ts +1 -1
  2. package/lib/block/index.d.ts.map +1 -1
  3. package/lib/block/index.js +2 -2
  4. package/lib/block/index.js.map +1 -1
  5. package/lib/block/processAttestationsAltair.d.ts.map +1 -1
  6. package/lib/block/processAttestationsAltair.js +3 -1
  7. package/lib/block/processAttestationsAltair.js.map +1 -1
  8. package/lib/block/processDeposit.d.ts.map +1 -1
  9. package/lib/block/processDeposit.js +1 -1
  10. package/lib/block/processDeposit.js.map +1 -1
  11. package/lib/block/processOperations.d.ts +1 -1
  12. package/lib/block/processOperations.d.ts.map +1 -1
  13. package/lib/block/processOperations.js +1 -1
  14. package/lib/block/processOperations.js.map +1 -1
  15. package/lib/cache/epochCache.d.ts +5 -2
  16. package/lib/cache/epochCache.d.ts.map +1 -1
  17. package/lib/cache/epochCache.js +12 -5
  18. package/lib/cache/epochCache.js.map +1 -1
  19. package/lib/cache/epochTransitionCache.d.ts.map +1 -1
  20. package/lib/cache/epochTransitionCache.js +2 -1
  21. package/lib/cache/epochTransitionCache.js.map +1 -1
  22. package/lib/cache/syncCommitteeCache.d.ts +1 -1
  23. package/lib/cache/syncCommitteeCache.d.ts.map +1 -1
  24. package/lib/epoch/getRewardsAndPenalties.d.ts.map +1 -1
  25. package/lib/epoch/getRewardsAndPenalties.js +9 -2
  26. package/lib/epoch/getRewardsAndPenalties.js.map +1 -1
  27. package/lib/epoch/index.d.ts +2 -2
  28. package/lib/epoch/index.d.ts.map +1 -1
  29. package/lib/epoch/index.js +2 -2
  30. package/lib/epoch/index.js.map +1 -1
  31. package/lib/epoch/processSyncCommitteeUpdates.js +1 -1
  32. package/lib/epoch/processSyncCommitteeUpdates.js.map +1 -1
  33. package/lib/lightClient/spec/store.d.ts +1 -1
  34. package/lib/lightClient/spec/store.d.ts.map +1 -1
  35. package/lib/lightClient/spec/utils.d.ts.map +1 -1
  36. package/lib/lightClient/spec/utils.js +1 -1
  37. package/lib/lightClient/spec/utils.js.map +1 -1
  38. package/lib/lightClient/spec/validateLightClientUpdate.d.ts.map +1 -1
  39. package/lib/lightClient/spec/validateLightClientUpdate.js +1 -1
  40. package/lib/lightClient/spec/validateLightClientUpdate.js.map +1 -1
  41. package/lib/metrics.d.ts +1 -1
  42. package/lib/metrics.d.ts.map +1 -1
  43. package/lib/metrics.js +4 -4
  44. package/lib/metrics.js.map +1 -1
  45. package/lib/rewards/attestationsRewards.d.ts +1 -1
  46. package/lib/rewards/attestationsRewards.d.ts.map +1 -1
  47. package/lib/rewards/attestationsRewards.js +10 -3
  48. package/lib/rewards/attestationsRewards.js.map +1 -1
  49. package/lib/rewards/syncCommitteeRewards.d.ts +1 -1
  50. package/lib/rewards/syncCommitteeRewards.d.ts.map +1 -1
  51. package/lib/signatureSets/inclusionList.d.ts +5 -0
  52. package/lib/signatureSets/inclusionList.d.ts.map +1 -0
  53. package/lib/signatureSets/inclusionList.js +14 -0
  54. package/lib/signatureSets/inclusionList.js.map +1 -0
  55. package/lib/signatureSets/index.d.ts +1 -0
  56. package/lib/signatureSets/index.d.ts.map +1 -1
  57. package/lib/signatureSets/index.js +1 -0
  58. package/lib/signatureSets/index.js.map +1 -1
  59. package/lib/stateTransition.js +2 -2
  60. package/lib/stateTransition.js.map +1 -1
  61. package/lib/stateView/beaconStateView.d.ts +1 -0
  62. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  63. package/lib/stateView/beaconStateView.js +5 -1
  64. package/lib/stateView/beaconStateView.js.map +1 -1
  65. package/lib/stateView/interface.d.ts +2 -4
  66. package/lib/stateView/interface.d.ts.map +1 -1
  67. package/lib/stateView/interface.js.map +1 -1
  68. package/lib/stateView/nativeBeaconStateView.d.ts +15 -7
  69. package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -1
  70. package/lib/stateView/nativeBeaconStateView.js +76 -26
  71. package/lib/stateView/nativeBeaconStateView.js.map +1 -1
  72. package/lib/stateView/stateViewFactory.d.ts.map +1 -1
  73. package/lib/stateView/stateViewFactory.js +5 -4
  74. package/lib/stateView/stateViewFactory.js.map +1 -1
  75. package/lib/testUtils/interopPubkeyCache.js +1 -1
  76. package/lib/testUtils/interopPubkeyCache.js.map +1 -1
  77. package/lib/testUtils/state.d.ts.map +1 -1
  78. package/lib/testUtils/state.js +1 -1
  79. package/lib/testUtils/state.js.map +1 -1
  80. package/lib/testUtils/util.d.ts +1 -1
  81. package/lib/testUtils/util.d.ts.map +1 -1
  82. package/lib/testUtils/util.js +2 -2
  83. package/lib/testUtils/util.js.map +1 -1
  84. package/lib/util/altair.d.ts +3 -1
  85. package/lib/util/altair.d.ts.map +1 -1
  86. package/lib/util/altair.js +7 -4
  87. package/lib/util/altair.js.map +1 -1
  88. package/lib/util/blindedBlock.d.ts.map +1 -1
  89. package/lib/util/blindedBlock.js +4 -12
  90. package/lib/util/blindedBlock.js.map +1 -1
  91. package/lib/util/clock.js +1 -1
  92. package/lib/util/clock.js.map +1 -1
  93. package/lib/util/epochShuffling.d.ts.map +1 -1
  94. package/lib/util/epochShuffling.js +1 -1
  95. package/lib/util/epochShuffling.js.map +1 -1
  96. package/lib/util/inclusionListCommittee.d.ts +9 -0
  97. package/lib/util/inclusionListCommittee.d.ts.map +1 -0
  98. package/lib/util/inclusionListCommittee.js +18 -0
  99. package/lib/util/inclusionListCommittee.js.map +1 -0
  100. package/lib/util/index.d.ts +1 -0
  101. package/lib/util/index.d.ts.map +1 -1
  102. package/lib/util/index.js +1 -0
  103. package/lib/util/index.js.map +1 -1
  104. package/lib/util/interop.d.ts +1 -1
  105. package/lib/util/interop.d.ts.map +1 -1
  106. package/lib/util/interop.js +1 -1
  107. package/lib/util/interop.js.map +1 -1
  108. package/lib/util/seed.js +1 -1
  109. package/lib/util/seed.js.map +1 -1
  110. package/lib/util/shuffling.d.ts +3 -0
  111. package/lib/util/shuffling.d.ts.map +1 -1
  112. package/lib/util/shuffling.js +41 -1
  113. package/lib/util/shuffling.js.map +1 -1
  114. package/lib/util/signatureSets.d.ts +1 -1
  115. package/lib/util/signatureSets.d.ts.map +1 -1
  116. package/lib/util/signatureSets.js +1 -1
  117. package/lib/util/signatureSets.js.map +1 -1
  118. package/lib/util/slot.d.ts +15 -1
  119. package/lib/util/slot.d.ts.map +1 -1
  120. package/lib/util/slot.js +66 -6
  121. package/lib/util/slot.js.map +1 -1
  122. package/lib/util/syncCommittee.d.ts +4 -3
  123. package/lib/util/syncCommittee.d.ts.map +1 -1
  124. package/lib/util/syncCommittee.js +10 -8
  125. package/lib/util/syncCommittee.js.map +1 -1
  126. package/lib/util/validator.d.ts +1 -1
  127. package/lib/util/validator.d.ts.map +1 -1
  128. package/lib/util/validator.js +32 -11
  129. package/lib/util/validator.js.map +1 -1
  130. package/lib/util/weakSubjectivity.d.ts.map +1 -1
  131. package/lib/util/weakSubjectivity.js +4 -3
  132. package/lib/util/weakSubjectivity.js.map +1 -1
  133. package/package.json +9 -9
  134. package/src/block/index.ts +11 -12
  135. package/src/block/processAttestationsAltair.ts +4 -1
  136. package/src/block/processDeposit.ts +2 -2
  137. package/src/block/processOperations.ts +5 -5
  138. package/src/cache/epochCache.ts +30 -6
  139. package/src/cache/epochTransitionCache.ts +2 -1
  140. package/src/cache/syncCommitteeCache.ts +1 -1
  141. package/src/epoch/getRewardsAndPenalties.ts +11 -1
  142. package/src/epoch/index.ts +14 -15
  143. package/src/epoch/processSyncCommitteeUpdates.ts +1 -1
  144. package/src/lightClient/spec/store.ts +1 -1
  145. package/src/lightClient/spec/utils.ts +1 -1
  146. package/src/lightClient/spec/validateLightClientUpdate.ts +1 -1
  147. package/src/metrics.ts +5 -4
  148. package/src/rewards/attestationsRewards.ts +13 -2
  149. package/src/rewards/syncCommitteeRewards.ts +1 -1
  150. package/src/signatureSets/inclusionList.ts +20 -0
  151. package/src/signatureSets/index.ts +1 -0
  152. package/src/stateTransition.ts +2 -2
  153. package/src/stateView/beaconStateView.ts +5 -1
  154. package/src/stateView/interface.ts +1 -12
  155. package/src/stateView/nativeBeaconStateView.ts +77 -33
  156. package/src/stateView/stateViewFactory.ts +5 -4
  157. package/src/testUtils/interopPubkeyCache.ts +1 -1
  158. package/src/testUtils/state.ts +1 -1
  159. package/src/testUtils/util.ts +2 -2
  160. package/src/util/altair.ts +15 -4
  161. package/src/util/blindedBlock.ts +4 -12
  162. package/src/util/clock.ts +1 -1
  163. package/src/util/epochShuffling.ts +1 -1
  164. package/src/util/inclusionListCommittee.ts +17 -0
  165. package/src/util/index.ts +1 -0
  166. package/src/util/interop.ts +1 -1
  167. package/src/util/seed.ts +1 -1
  168. package/src/util/shuffling.ts +46 -1
  169. package/src/util/signatureSets.ts +1 -1
  170. package/src/util/slot.ts +72 -7
  171. package/src/util/syncCommittee.ts +22 -10
  172. package/src/util/validator.ts +47 -20
  173. package/src/util/weakSubjectivity.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  import {CompactMultiProof} from "@chainsafe/persistent-merkle-tree";
2
2
  import {BitArray, ByteViews} from "@chainsafe/ssz";
3
3
  import type {BeaconConfig} from "@lodestar/config";
4
- import {ForkName, ForkSeq} from "@lodestar/params";
4
+ import {ForkName, ForkPostElectra, ForkSeq} from "@lodestar/params";
5
5
  import {
6
6
  BeaconBlock,
7
7
  BeaconState,
@@ -28,6 +28,7 @@ import {
28
28
  phase0,
29
29
  rewards,
30
30
  ssz,
31
+ sszTypesFor,
31
32
  } from "@lodestar/types";
32
33
  import {Checkpoint, Fork} from "@lodestar/types/phase0";
33
34
  import {VoluntaryExitValidity} from "../block/processVoluntaryExit.js";
@@ -39,6 +40,7 @@ import {SyncCommitteeWitness} from "../lightClient/types.js";
39
40
  import {StateTransitionModules, StateTransitionOpts} from "../stateTransition.js";
40
41
  import {EpochShuffling} from "../util/epochShuffling.js";
41
42
  import {PreVerifyBuilderDepositsResult} from "../util/preVerifyBuilderDeposits.js";
43
+ import {getStateSlotFromBytes} from "../util/sszBytes.js";
42
44
  import {computeNewStateRootStateTransitionOpts, getComputeNewStateRootResult} from "./computeNewStateRoot.js";
43
45
  import {
44
46
  BlockSTFInput,
@@ -47,8 +49,15 @@ import {
47
49
  IBeaconStateViewGloas,
48
50
  IBeaconStateViewLatestFork,
49
51
  IBeaconStateViewNative,
52
+ isStatePostGloas,
50
53
  } from "./interface.js";
51
54
 
55
+ export function assertNativeForkSupported(config: BeaconConfig, slot: Slot): void {
56
+ if (config.getForkSeq(slot) > ForkSeq.gloas) {
57
+ throw Error(`Native state transition does not support ${config.getForkName(slot)}`);
58
+ }
59
+ }
60
+
52
61
  /**
53
62
  * Wraps a native binding (the auto-generated JS interface produced by a `.node`
54
63
  * file) and exposes it as a fully-conformant `IBeaconStateViewLatestFork`.
@@ -115,6 +124,14 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
115
124
  private cachedPendingConsolidationsCount: number | null = null;
116
125
  // fulu
117
126
  private cachedProposerLookahead: fulu.ProposerLookahead | null = null;
127
+ // gloas
128
+ private cachedLatestBlockHash: Bytes32 | null = null;
129
+ private cachedExecutionPayloadAvailability: BitArray | null = null;
130
+ private cachedLatestExecutionPayloadBid: ExecutionPayloadBid | null = null;
131
+ private cachedPayloadExpectedWithdrawals: capella.Withdrawal[] | null = null;
132
+ private cachedBuilderPendingPayments: gloas.BuilderPendingPayments | null = null;
133
+ private cachedBuilderPendingWithdrawals: gloas.BuilderPendingWithdrawals | null = null;
134
+ private cachedBuildersLength: number | null = null;
118
135
  // Per-argument caches for argument-taking methods. The binding is treated as
119
136
  // immutable for the view's lifetime, so a given argument always yields the
120
137
  // same result. Maps grow only with touched arguments — typical call patterns
@@ -174,7 +191,11 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
174
191
  }
175
192
 
176
193
  get executionPayloadAvailability(): BitArray {
177
- throw new Error("NativeBeaconStateView does not support Gloas");
194
+ if (this.cachedExecutionPayloadAvailability === null) {
195
+ const {uint8Array, bitLen} = this.binding.executionPayloadAvailability;
196
+ this.cachedExecutionPayloadAvailability = new BitArray(uint8Array, bitLen);
197
+ }
198
+ return this.cachedExecutionPayloadAvailability;
178
199
  }
179
200
 
180
201
  // ─── phase0 ──────────────────────────────────────────────────────────────
@@ -598,6 +619,7 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
598
619
  seedValidatorsBytes?: Uint8Array,
599
620
  opts?: {preloadValidatorsAndBalances?: boolean}
600
621
  ): IBeaconStateView {
622
+ assertNativeForkSupported(this.config, getStateSlotFromBytes(stateBytes));
601
623
  return new NativeBeaconStateView(this.config, this.binding.loadOtherState(stateBytes, seedValidatorsBytes, opts));
602
624
  }
603
625
 
@@ -663,6 +685,7 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
663
685
  options: StateTransitionOpts,
664
686
  _modules: StateTransitionModules
665
687
  ): IBeaconStateView {
688
+ assertNativeForkSupported(this.config, block.message.slot);
666
689
  const isBlinded = isBlindedBeaconBlock(block.message);
667
690
  const signedBlockBytes =
668
691
  ssz ??
@@ -675,6 +698,7 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
675
698
  }
676
699
 
677
700
  processSlots(slot: Slot, opts?: {dontTransferCache?: boolean}, _modules?: StateTransitionModules): IBeaconStateView {
701
+ assertNativeForkSupported(this.config, slot);
678
702
  return new NativeBeaconStateView(this.config, this.binding.processSlots(slot, opts));
679
703
  }
680
704
 
@@ -832,7 +856,8 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
832
856
 
833
857
  get pendingDeposits(): electra.PendingDeposits {
834
858
  if (this.cachedPendingDeposits === null) {
835
- this.cachedPendingDeposits = ssz.electra.PendingDeposits.deserialize(this.binding.pendingDeposits);
859
+ const type = sszTypesFor(this.forkName as ForkPostElectra).PendingDeposits;
860
+ this.cachedPendingDeposits = type.deserialize(this.binding.pendingDeposits);
836
861
  }
837
862
  return this.cachedPendingDeposits;
838
863
  }
@@ -846,9 +871,8 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
846
871
 
847
872
  get pendingPartialWithdrawals(): electra.PendingPartialWithdrawals {
848
873
  if (this.cachedPendingPartialWithdrawals === null) {
849
- this.cachedPendingPartialWithdrawals = ssz.electra.PendingPartialWithdrawals.deserialize(
850
- this.binding.pendingPartialWithdrawals
851
- );
874
+ const type = sszTypesFor(this.forkName as ForkPostElectra).PendingPartialWithdrawals;
875
+ this.cachedPendingPartialWithdrawals = type.deserialize(this.binding.pendingPartialWithdrawals);
852
876
  }
853
877
  return this.cachedPendingPartialWithdrawals;
854
878
  }
@@ -862,9 +886,8 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
862
886
 
863
887
  get pendingConsolidations(): electra.PendingConsolidations {
864
888
  if (this.cachedPendingConsolidations === null) {
865
- this.cachedPendingConsolidations = ssz.electra.PendingConsolidations.deserialize(
866
- this.binding.pendingConsolidations
867
- );
889
+ const type = sszTypesFor(this.forkName as ForkPostElectra).PendingConsolidations;
890
+ this.cachedPendingConsolidations = type.deserialize(this.binding.pendingConsolidations);
868
891
  }
869
892
  return this.cachedPendingConsolidations;
870
893
  }
@@ -885,64 +908,85 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
885
908
  return this.cachedProposerLookahead;
886
909
  }
887
910
 
888
- preVerifyBuilderDepositsPreGloas(
889
- _maxBuilderDeposits: number,
890
- _maxDurationMs: number
891
- ): PreVerifyBuilderDepositsResult {
892
- throw new Error("NativeBeaconStateView does not support Gloas");
911
+ preVerifyBuilderDepositsPreGloas(maxBuilderDeposits: number, maxDurationMs: number): PreVerifyBuilderDepositsResult {
912
+ return this.binding.preVerifyBuilderDepositsPreGloas(maxBuilderDeposits, maxDurationMs);
893
913
  }
894
914
 
895
915
  clearPreGloasBuilderDepositCache(): void {
896
- throw new Error("NativeBeaconStateView does not support Gloas");
916
+ this.binding.clearPreGloasBuilderDepositCache();
897
917
  }
898
918
 
899
919
  // ─── gloas ───────────────────────────────────────────────────────────────
900
920
 
901
921
  get latestBlockHash(): Bytes32 {
902
- throw new Error("NativeBeaconStateView does not support Gloas");
922
+ if (this.cachedLatestBlockHash === null) {
923
+ this.cachedLatestBlockHash = this.binding.latestBlockHash;
924
+ }
925
+ return this.cachedLatestBlockHash;
903
926
  }
904
927
 
905
928
  get latestExecutionPayloadBid(): ExecutionPayloadBid {
906
- throw new Error("NativeBeaconStateView does not support Gloas");
929
+ if (this.cachedLatestExecutionPayloadBid === null) {
930
+ this.cachedLatestExecutionPayloadBid = this.binding.latestExecutionPayloadBid;
931
+ }
932
+ return this.cachedLatestExecutionPayloadBid;
907
933
  }
908
934
 
909
935
  get payloadExpectedWithdrawals(): capella.Withdrawal[] {
910
- throw new Error("NativeBeaconStateView does not support Gloas");
936
+ if (this.cachedPayloadExpectedWithdrawals === null) {
937
+ this.cachedPayloadExpectedWithdrawals = this.binding.payloadExpectedWithdrawals;
938
+ }
939
+ return this.cachedPayloadExpectedWithdrawals;
911
940
  }
912
941
 
913
942
  get builderPendingPayments(): gloas.BuilderPendingPayments {
914
- throw new Error("NativeBeaconStateView does not support Gloas");
943
+ if (this.cachedBuilderPendingPayments === null) {
944
+ this.cachedBuilderPendingPayments = this.binding.builderPendingPayments;
945
+ }
946
+ return this.cachedBuilderPendingPayments;
915
947
  }
916
948
 
917
949
  get builderPendingWithdrawals(): gloas.BuilderPendingWithdrawals {
918
- throw new Error("NativeBeaconStateView does not support Gloas");
950
+ if (this.cachedBuilderPendingWithdrawals === null) {
951
+ this.cachedBuilderPendingWithdrawals = this.binding.builderPendingWithdrawals;
952
+ }
953
+ return this.cachedBuilderPendingWithdrawals;
919
954
  }
920
955
 
921
- getBuilder(_index: BuilderIndex): gloas.Builder {
922
- throw new Error("NativeBeaconStateView does not support Gloas");
956
+ getBuilder(index: BuilderIndex): gloas.Builder {
957
+ return this.binding.getBuilder(index);
923
958
  }
924
959
 
925
960
  getBuildersLength(): number {
926
- throw new Error("NativeBeaconStateView does not support Gloas");
961
+ if (this.cachedBuildersLength === null) {
962
+ this.cachedBuildersLength = this.binding.getBuildersLength();
963
+ }
964
+ return this.cachedBuildersLength;
927
965
  }
928
966
 
929
- canBuilderCoverBid(_builderIndex: BuilderIndex, _bidAmount: number): boolean {
930
- throw new Error("NativeBeaconStateView does not support Gloas");
967
+ canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean {
968
+ return this.binding.canBuilderCoverBid(builderIndex, bidAmount);
931
969
  }
932
970
 
933
- getEpochPTCs(_epoch: Epoch): Uint32Array[] {
934
- throw new Error("NativeBeaconStateView does not support Gloas");
971
+ getEpochPTCs(epoch: Epoch): Uint32Array[] {
972
+ return this.binding.getEpochPTCs(epoch);
935
973
  }
936
974
 
937
- getPayloadTimelinessCommittee(_slot: Slot): Uint32Array {
938
- throw new Error("NativeBeaconStateView does not support Gloas");
975
+ getPayloadTimelinessCommittee(slot: Slot): Uint32Array {
976
+ return this.binding.getPayloadTimelinessCommittee(slot);
939
977
  }
940
978
 
941
- getIndicesInPayloadTimelinessCommittee(_validatorIndex: ValidatorIndex, _slot: Slot): number[] {
942
- throw new Error("NativeBeaconStateView does not support Gloas");
979
+ getIndicesInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number[] {
980
+ return this.binding.getIndicesInPayloadTimelinessCommittee(validatorIndex, slot);
943
981
  }
944
982
 
945
- withParentPayloadApplied(_executionRequests: gloas.ExecutionRequests): IBeaconStateViewGloas {
946
- throw new Error("NativeBeaconStateView does not support Gloas");
983
+ withParentPayloadApplied(executionRequests: gloas.ExecutionRequests): IBeaconStateViewGloas {
984
+ const bytes = ssz.gloas.ExecutionRequests.serialize(executionRequests);
985
+ const state = new NativeBeaconStateView(this.config, this.binding.withParentPayloadApplied(bytes));
986
+ if (!isStatePostGloas(state)) {
987
+ state.release();
988
+ throw Error("Expected Gloas state after applying the parent payload");
989
+ }
990
+ return state;
947
991
  }
948
992
  }
@@ -1,11 +1,11 @@
1
- import bindings from "@chainsafe/lodestar-z";
2
- import {pubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
3
1
  import {BeaconConfig} from "@lodestar/config";
2
+ import bindings from "@lodestar/native";
3
+ import {pubkeyCache} from "@lodestar/native/pubkeys";
4
4
  import {createCachedBeaconState} from "../cache/stateCache.js";
5
- import {getStateTypeFromBytes} from "../util/sszBytes.js";
5
+ import {getStateSlotFromBytes, getStateTypeFromBytes} from "../util/sszBytes.js";
6
6
  import {BeaconStateView} from "./beaconStateView.js";
7
7
  import {IBeaconStateView, IBeaconStateViewNative} from "./interface.js";
8
- import {NativeBeaconStateView} from "./nativeBeaconStateView.js";
8
+ import {NativeBeaconStateView, assertNativeForkSupported} from "./nativeBeaconStateView.js";
9
9
 
10
10
  // ---- createBeaconStateView (startup path) ----
11
11
 
@@ -62,6 +62,7 @@ export function createBeaconStateViewForHistoricalRegen(opts: RegenNodeJSOpts |
62
62
  }
63
63
 
64
64
  function createNativeBeaconStateView(config: BeaconConfig, stateBytes: Uint8Array): IBeaconStateView {
65
+ assertNativeForkSupported(config, getStateSlotFromBytes(stateBytes));
65
66
  const nativeConfig = new bindings.BeaconConfig(config, config.genesisValidatorsRoot);
66
67
  return new NativeBeaconStateView(
67
68
  config,
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import {pubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
3
+ import {pubkeyCache} from "@lodestar/native/pubkeys";
4
4
  import {interopSecretKey} from "../util/interop.js";
5
5
  import {testCachePath} from "./cache.js";
6
6
 
@@ -1,6 +1,6 @@
1
- import {pubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
2
1
  import {ChainForkConfig, createBeaconConfig} from "@lodestar/config";
3
2
  import {config, config as minimalConfig} from "@lodestar/config/default";
3
+ import {pubkeyCache} from "@lodestar/native/pubkeys";
4
4
  import {
5
5
  EPOCHS_PER_HISTORICAL_VECTOR,
6
6
  EPOCHS_PER_SLASHINGS_VECTOR,
@@ -1,8 +1,8 @@
1
- import {PublicKey, SecretKey} from "@chainsafe/lodestar-z/blst";
2
- import {pubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
3
1
  import {BitArray, fromHexString} from "@chainsafe/ssz";
4
2
  import {BeaconConfig, createBeaconConfig, createChainForkConfig} from "@lodestar/config";
5
3
  import {config} from "@lodestar/config/default";
4
+ import {PublicKey, SecretKey} from "@lodestar/native/blst";
5
+ import {pubkeyCache} from "@lodestar/native/pubkeys";
6
6
  import {
7
7
  BUILDER_WITHDRAWAL_PREFIX,
8
8
  DOMAIN_DEPOSIT,
@@ -1,13 +1,24 @@
1
- import {BASE_REWARD_FACTOR, EFFECTIVE_BALANCE_INCREMENT} from "@lodestar/params";
1
+ import {ChainConfig} from "@lodestar/config";
2
+ import {BASE_REWARD_FACTOR, EFFECTIVE_BALANCE_INCREMENT, GENESIS_EPOCH} from "@lodestar/params";
3
+ import {Epoch} from "@lodestar/types";
2
4
  import {bigIntSqrt, bnToNum} from "@lodestar/utils";
5
+ import {getSlotDurationMsAtEpoch} from "./slot.js";
3
6
 
4
7
  /**
5
8
  * Before we manage bigIntSqrt(totalActiveStake) as BigInt and return BigInt.
6
9
  * bigIntSqrt(totalActiveStake) should fit a number (2 ** 53 -1 max)
7
10
  **/
8
- export function computeBaseRewardPerIncrement(totalActiveStakeByIncrement: number): number {
11
+ export function computeBaseRewardPerIncrement(
12
+ config: ChainConfig,
13
+ epoch: Epoch,
14
+ totalActiveStakeByIncrement: number
15
+ ): number {
16
+ // Divide last so the exact slot duration ratio applies
17
+ const numerator = Math.floor(
18
+ (EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR * getSlotDurationMsAtEpoch(config, epoch)) /
19
+ getSlotDurationMsAtEpoch(config, GENESIS_EPOCH)
20
+ );
9
21
  return Math.floor(
10
- (EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR) /
11
- bnToNum(bigIntSqrt(BigInt(totalActiveStakeByIncrement) * BigInt(EFFECTIVE_BALANCE_INCREMENT)))
22
+ numerator / bnToNum(bigIntSqrt(BigInt(totalActiveStakeByIncrement) * BigInt(EFFECTIVE_BALANCE_INCREMENT)))
12
23
  );
13
24
  }
@@ -33,13 +33,9 @@ export function blindedOrFullBlockHashTreeRoot(
33
33
  ): Root {
34
34
  return isBlindedBeaconBlock(blindedOrFull)
35
35
  ? // Blinded
36
- config
37
- .getPostBellatrixForkTypes(blindedOrFull.slot)
38
- .BlindedBeaconBlock.hashTreeRoot(blindedOrFull)
36
+ config.getPostBellatrixForkTypes(blindedOrFull.slot).BlindedBeaconBlock.hashTreeRoot(blindedOrFull)
39
37
  : // Full
40
- config
41
- .getForkTypes(blindedOrFull.slot)
42
- .BeaconBlock.hashTreeRoot(blindedOrFull);
38
+ config.getForkTypes(blindedOrFull.slot).BeaconBlock.hashTreeRoot(blindedOrFull);
43
39
  }
44
40
 
45
41
  export function blindedOrFullBlockToHeader(
@@ -48,13 +44,9 @@ export function blindedOrFullBlockToHeader(
48
44
  ): BeaconBlockHeader {
49
45
  const bodyRoot = isBlindedBeaconBlock(blindedOrFull)
50
46
  ? // Blinded
51
- config
52
- .getPostBellatrixForkTypes(blindedOrFull.slot)
53
- .BlindedBeaconBlockBody.hashTreeRoot(blindedOrFull.body)
47
+ config.getPostBellatrixForkTypes(blindedOrFull.slot).BlindedBeaconBlockBody.hashTreeRoot(blindedOrFull.body)
54
48
  : // Full
55
- config
56
- .getForkTypes(blindedOrFull.slot)
57
- .BeaconBlockBody.hashTreeRoot(blindedOrFull.body);
49
+ config.getForkTypes(blindedOrFull.slot).BeaconBlockBody.hashTreeRoot(blindedOrFull.body);
58
50
 
59
51
  return {
60
52
  slot: blindedOrFull.slot,
package/src/util/clock.ts CHANGED
@@ -133,7 +133,7 @@ export class Clock implements IClock {
133
133
  if (timeItem === TimeItem.Slot) {
134
134
  if (nextSlot > slot + 1) {
135
135
  // It's not very likely that we skip more than one slot as HTTP timeout is set
136
- // to SLOT_DURATION_MS so we will fail task before skipping another slot.
136
+ // to at most one slot duration so we will fail task before skipping another slot.
137
137
  this.logger.warn("Skipped slot due to task taking more than one slot to run", {
138
138
  skippedSlot: slot + 1,
139
139
  });
@@ -1,5 +1,5 @@
1
- import {unshuffleList} from "@chainsafe/lodestar-z/shuffle";
2
1
  import {BeaconConfig} from "@lodestar/config";
2
+ import {unshuffleList} from "@lodestar/native/shuffle";
3
3
  import {
4
4
  DOMAIN_BEACON_ATTESTER,
5
5
  GENESIS_SLOT,
@@ -0,0 +1,17 @@
1
+ import {ValidatorIndex} from "@lodestar/types";
2
+
3
+ /** Spec `InclusionListCommittee` of one slot */
4
+ export class InclusionListCommittee {
5
+ constructor(readonly validatorIndices: Uint32Array) {}
6
+
7
+ /** Positions of `validatorIndex`, several when the committee cycles over a slot with fewer validators than its size */
8
+ getCommitteeIndices(validatorIndex: ValidatorIndex): number[] {
9
+ const committeeIndices: number[] = [];
10
+ for (let i = 0; i < this.validatorIndices.length; i++) {
11
+ if (this.validatorIndices[i] === validatorIndex) {
12
+ committeeIndices.push(i);
13
+ }
14
+ }
15
+ return committeeIndices;
16
+ }
17
+ }
package/src/util/index.ts CHANGED
@@ -17,6 +17,7 @@ export * from "./execution.js";
17
17
  export * from "./finality.js";
18
18
  export * from "./genesis.js";
19
19
  export * from "./gloas.js";
20
+ export * from "./inclusionListCommittee.js";
20
21
  export * from "./interop.js";
21
22
  export * from "./loadState/index.js";
22
23
  export * from "./pendingDepositsLookup.js";
@@ -1,6 +1,6 @@
1
1
  import {toBufferBE} from "@vekexasia/bigint-buffer2";
2
2
  import {digest} from "@chainsafe/as-sha256";
3
- import {SecretKey} from "@chainsafe/lodestar-z/blst";
3
+ import {SecretKey} from "@lodestar/native/blst";
4
4
  import {bytesToBigInt, intToBytes} from "@lodestar/utils";
5
5
 
6
6
  let curveOrder: bigint;
package/src/util/seed.ts CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  computePtcIndicesForEpoch,
4
4
  computeProposerIndex as nativeComputeProposerIndex,
5
5
  computeSyncCommitteeIndices as nativeComputeSyncCommitteeIndices,
6
- } from "@chainsafe/lodestar-z/shuffle";
6
+ } from "@lodestar/native/shuffle";
7
7
  import {
8
8
  DOMAIN_BEACON_PROPOSER,
9
9
  DOMAIN_PTC_ATTESTER,
@@ -1,4 +1,4 @@
1
- import {ForkName, ForkSeq, SLOTS_PER_EPOCH, isForkPostFulu} from "@lodestar/params";
1
+ import {ForkName, ForkSeq, INCLUSION_LIST_COMMITTEE_SIZE, SLOTS_PER_EPOCH, isForkPostFulu} from "@lodestar/params";
2
2
  import {
3
3
  Attestation,
4
4
  CommitteeIndex,
@@ -15,6 +15,7 @@ import {IBeaconStateView} from "../stateView/interface.js";
15
15
  import {getBlockRootAtSlot} from "./blockRoot.js";
16
16
  import {computeStartSlotAtEpoch} from "./epoch.js";
17
17
  import {EpochShuffling} from "./epochShuffling.js";
18
+ import {InclusionListCommittee} from "./inclusionListCommittee.js";
18
19
 
19
20
  /**
20
21
  * Block root that decided the proposer shuffling for `proposalEpoch` (keys that shuffling).
@@ -196,6 +197,50 @@ export function getBeaconCommittee(epochShuffling: EpochShuffling, slot: Slot, i
196
197
  return getBeaconCommittees(epochShuffling, slot, [index])[0];
197
198
  }
198
199
 
200
+ /** Inclusion list committees of a shuffling by slot index, computed on first use and freed with the shuffling */
201
+ // TODO HEZE: replace this module-level map with an extended EpochShuffling tracked by the beacon-node ShufflingCache
202
+ const inclusionListCommitteesByShuffling = new WeakMap<EpochShuffling, InclusionListCommittee[]>();
203
+
204
+ /** Spec get_inclusion_list_committee: cycle over the slot's concatenated beacon committees */
205
+ export function getInclusionListCommittee(epochShuffling: EpochShuffling, slot: Slot): InclusionListCommittee {
206
+ let committees = inclusionListCommitteesByShuffling.get(epochShuffling);
207
+ if (committees === undefined) {
208
+ committees = [];
209
+ inclusionListCommitteesByShuffling.set(epochShuffling, committees);
210
+ }
211
+ const slotIndex = slot % SLOTS_PER_EPOCH;
212
+ let committee = committees[slotIndex];
213
+ if (committee === undefined) {
214
+ committee = new InclusionListCommittee(computeInclusionListCommittee(epochShuffling.committees[slotIndex]));
215
+ committees[slotIndex] = committee;
216
+ }
217
+ return committee;
218
+ }
219
+
220
+ function computeInclusionListCommittee(slotCommittees: Uint32Array[]): Uint32Array {
221
+ let totalLen = 0;
222
+ for (const committee of slotCommittees) {
223
+ totalLen += committee.length;
224
+ }
225
+
226
+ const result = new Uint32Array(INCLUSION_LIST_COMMITTEE_SIZE);
227
+ if (totalLen === 0) {
228
+ return result;
229
+ }
230
+
231
+ for (let i = 0; i < INCLUSION_LIST_COMMITTEE_SIZE; i++) {
232
+ let offset = i % totalLen;
233
+ for (const committee of slotCommittees) {
234
+ if (offset < committee.length) {
235
+ result[i] = committee[offset];
236
+ break;
237
+ }
238
+ offset -= committee.length;
239
+ }
240
+ }
241
+ return result;
242
+ }
243
+
199
244
  /**
200
245
  * Return a Uint32Array[] representing committees validator indices
201
246
  */
@@ -1,4 +1,4 @@
1
- import {BLS_VERIFIER_SET_TYPE, type BlsSignatureSet, verifySignatureSets} from "@chainsafe/lodestar-z/bls-verifier";
1
+ import {BLS_VERIFIER_SET_TYPE, type BlsSignatureSet, verifySignatureSets} from "@lodestar/native/bls-verifier";
2
2
  import {Root} from "@lodestar/types";
3
3
 
4
4
  export enum SignatureSetType {
package/src/util/slot.ts CHANGED
@@ -1,27 +1,92 @@
1
1
  import {ChainConfig, ChainForkConfig} from "@lodestar/config";
2
- import {GENESIS_SLOT} from "@lodestar/params";
2
+ import {GENESIS_EPOCH, GENESIS_SLOT} from "@lodestar/params";
3
3
  import {Epoch, Slot, TimeSeconds} from "@lodestar/types";
4
4
  import {computeEpochAtSlot, computeStartSlotAtEpoch} from "./epoch.js";
5
5
 
6
+ /**
7
+ * Slot durations with their activation epochs, derived from the configuration.
8
+ * A fork that changes the slot duration must add an entry here and in `ForkConfig.getSlotDurationMs`.
9
+ */
10
+ export function getSlotDurations(config: ChainConfig): [Epoch, number][] {
11
+ const durations: [Epoch, number][] = [[GENESIS_EPOCH, config.SLOT_DURATION_MS]];
12
+ if (config.HEZE_FORK_EPOCH !== Infinity) {
13
+ durations.push([config.HEZE_FORK_EPOCH, config.SLOT_DURATION_MS_HEZE]);
14
+ }
15
+ return durations;
16
+ }
17
+
18
+ /** Slot duration in ms in effect at a given epoch */
19
+ export function getSlotDurationMsAtEpoch(config: ChainConfig, epoch: Epoch): number {
20
+ let slotDurationMs = config.SLOT_DURATION_MS;
21
+ for (const [forkEpoch, forkSlotDurationMs] of getSlotDurations(config)) {
22
+ if (epoch >= forkEpoch) {
23
+ slotDurationMs = forkSlotDurationMs;
24
+ }
25
+ }
26
+ return slotDurationMs;
27
+ }
28
+
6
29
  /** Slot duration in ms for a given slot */
7
30
  export function getSlotDurationMs(config: ChainForkConfig, slot: Slot): number {
8
31
  return config.getSlotDurationMs(config.getForkName(slot));
9
32
  }
10
33
 
34
+ export function computeTimeAtSlotMs(config: ChainConfig, slot: Slot, genesisTimeMs: number): number {
35
+ let endSlot = slot;
36
+ let timeMs = genesisTimeMs;
37
+ for (const [forkEpoch, slotDurationMs] of getSlotDurations(config).reverse()) {
38
+ const forkSlot = computeStartSlotAtEpoch(forkEpoch);
39
+ if (forkSlot < endSlot) {
40
+ timeMs += (endSlot - forkSlot) * slotDurationMs;
41
+ endSlot = forkSlot;
42
+ }
43
+ }
44
+ // Pre-genesis slots extrapolate linearly so the clock keeps ticking before genesis
45
+ return timeMs + (endSlot - GENESIS_SLOT) * getSlotDurationMsAtEpoch(config, GENESIS_EPOCH);
46
+ }
47
+
48
+ export function computeTimeAtSlot(config: ChainConfig, slot: Slot, genesisTime: TimeSeconds): TimeSeconds {
49
+ return computeTimeAtSlotMs(config, slot, genesisTime * 1000) / 1000;
50
+ }
51
+
52
+ export function computeSlotAtTimeMs(config: ChainConfig, timeMs: number, genesisTimeMs: number): Slot {
53
+ let startSlot = GENESIS_SLOT;
54
+ let startTimeMs = genesisTimeMs;
55
+ let slotDurationMs = getSlotDurationMsAtEpoch(config, GENESIS_EPOCH);
56
+ for (const [forkEpoch, forkSlotDurationMs] of getSlotDurations(config)) {
57
+ const forkSlot = computeStartSlotAtEpoch(forkEpoch);
58
+ const forkTimeMs = computeTimeAtSlotMs(config, forkSlot, genesisTimeMs);
59
+ if (timeMs >= forkTimeMs) {
60
+ startSlot = forkSlot;
61
+ startTimeMs = forkTimeMs;
62
+ slotDurationMs = forkSlotDurationMs;
63
+ }
64
+ }
65
+ return startSlot + Math.floor((timeMs - startTimeMs) / slotDurationMs);
66
+ }
67
+
11
68
  export function getSlotsSinceGenesis(config: ChainConfig, genesisTime: TimeSeconds): Slot {
12
- const diffInSeconds = Date.now() / 1000 - genesisTime;
13
- return Math.floor(diffInSeconds / (config.SLOT_DURATION_MS / 1000));
69
+ return computeSlotAtTimeMs(config, Date.now(), genesisTime * 1000) - GENESIS_SLOT;
14
70
  }
15
71
 
16
72
  export function getCurrentSlot(config: ChainConfig, genesisTime: TimeSeconds): Slot {
17
73
  return GENESIS_SLOT + getSlotsSinceGenesis(config, genesisTime);
18
74
  }
19
75
 
76
+ /**
77
+ * Start epoch of the blob data retention window, preserving its wall-clock length
78
+ * across slot duration changes
79
+ */
80
+ export function getBlobDataRetentionStartEpoch(config: ChainConfig, epoch: Epoch): Epoch {
81
+ const windowMs = config.MIN_BLOB_DATA_RETENTION_MS;
82
+ const currentStartMs = computeTimeAtSlotMs(config, computeStartSlotAtEpoch(epoch), 0);
83
+ if (currentStartMs < windowMs) {
84
+ return GENESIS_EPOCH;
85
+ }
86
+ return computeEpochAtSlot(computeSlotAtTimeMs(config, currentStartMs - windowMs, 0));
87
+ }
88
+
20
89
  export function computeSlotsSinceEpochStart(slot: Slot, epoch?: Epoch): Slot {
21
90
  const computeEpoch = epoch ?? computeEpochAtSlot(slot);
22
91
  return slot - computeStartSlotAtEpoch(computeEpoch);
23
92
  }
24
-
25
- export function computeTimeAtSlot(config: ChainConfig, slot: Slot, genesisTime: TimeSeconds): TimeSeconds {
26
- return genesisTime + slot * (config.SLOT_DURATION_MS / 1000);
27
- }
@@ -1,18 +1,21 @@
1
- import {aggregateSerializedPublicKeys} from "@chainsafe/lodestar-z/blst";
1
+ import {ChainConfig} from "@lodestar/config";
2
+ import {aggregateSerializedPublicKeys} from "@lodestar/native/blst";
2
3
  import {
3
4
  BASE_REWARD_FACTOR,
4
5
  EFFECTIVE_BALANCE_INCREMENT,
5
6
  ForkSeq,
7
+ GENESIS_EPOCH,
6
8
  SLOTS_PER_EPOCH,
7
9
  SYNC_COMMITTEE_SIZE,
8
10
  SYNC_REWARD_WEIGHT,
9
11
  WEIGHT_DENOMINATOR,
10
12
  } from "@lodestar/params";
11
- import {altair} from "@lodestar/types";
13
+ import {Epoch, altair} from "@lodestar/types";
12
14
  import {bigIntSqrt} from "@lodestar/utils";
13
15
  import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
14
16
  import {BeaconStateAllForks} from "../types.js";
15
17
  import {getNextSyncCommitteeIndices} from "./seed.js";
18
+ import {getSlotDurationMsAtEpoch} from "./slot.js";
16
19
 
17
20
  /**
18
21
  * Return the sync committee for a given state and epoch.
@@ -45,11 +48,12 @@ export function getNextSyncCommittee(
45
48
  /**
46
49
  * Same logic in https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.5/specs/altair/beacon-chain.md#sync-committee-processing
47
50
  */
48
- export function computeSyncParticipantReward(totalActiveBalanceIncrements: number): number {
49
- const totalActiveBalance = BigInt(totalActiveBalanceIncrements) * BigInt(EFFECTIVE_BALANCE_INCREMENT);
50
- const baseRewardPerIncrement = Math.floor(
51
- (EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR) / Number(bigIntSqrt(totalActiveBalance))
52
- );
51
+ export function computeSyncParticipantReward(
52
+ config: ChainConfig,
53
+ epoch: Epoch,
54
+ totalActiveBalanceIncrements: number
55
+ ): number {
56
+ const baseRewardPerIncrement = computeBaseRewardPerIncrement(config, epoch, totalActiveBalanceIncrements);
53
57
  const totalBaseRewards = baseRewardPerIncrement * totalActiveBalanceIncrements;
54
58
  const maxParticipantRewards = Math.floor(
55
59
  Math.floor((totalBaseRewards * SYNC_REWARD_WEIGHT) / WEIGHT_DENOMINATOR) / SLOTS_PER_EPOCH
@@ -61,9 +65,17 @@ export function computeSyncParticipantReward(totalActiveBalanceIncrements: numbe
61
65
  * Before we manage bigIntSqrt(totalActiveStake) as BigInt and return BigInt.
62
66
  * bigIntSqrt(totalActiveStake) should fit a number (2 ** 53 -1 max)
63
67
  **/
64
- export function computeBaseRewardPerIncrement(totalActiveStakeByIncrement: number): number {
68
+ export function computeBaseRewardPerIncrement(
69
+ config: ChainConfig,
70
+ epoch: Epoch,
71
+ totalActiveStakeByIncrement: number
72
+ ): number {
73
+ // Divide last so the exact slot duration ratio applies
74
+ const numerator = Math.floor(
75
+ (EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR * getSlotDurationMsAtEpoch(config, epoch)) /
76
+ getSlotDurationMsAtEpoch(config, GENESIS_EPOCH)
77
+ );
65
78
  return Math.floor(
66
- (EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR) /
67
- Number(bigIntSqrt(BigInt(totalActiveStakeByIncrement) * BigInt(EFFECTIVE_BALANCE_INCREMENT)))
79
+ numerator / Number(bigIntSqrt(BigInt(totalActiveStakeByIncrement) * BigInt(EFFECTIVE_BALANCE_INCREMENT)))
68
80
  );
69
81
  }