@lodestar/state-transition 1.45.0-dev.b94232ff23 → 1.45.0-dev.b98df0df3d

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 (100) hide show
  1. package/lib/block/processAttestationsAltair.d.ts +4 -1
  2. package/lib/block/processAttestationsAltair.d.ts.map +1 -1
  3. package/lib/block/processAttestationsAltair.js +7 -5
  4. package/lib/block/processAttestationsAltair.js.map +1 -1
  5. package/lib/block/processBuilderDepositRequest.d.ts.map +1 -1
  6. package/lib/block/processBuilderDepositRequest.js +13 -7
  7. package/lib/block/processBuilderDepositRequest.js.map +1 -1
  8. package/lib/block/processOperations.d.ts.map +1 -1
  9. package/lib/block/processOperations.js +17 -1
  10. package/lib/block/processOperations.js.map +1 -1
  11. package/lib/block/processParentExecutionPayload.d.ts.map +1 -1
  12. package/lib/block/processParentExecutionPayload.js +15 -1
  13. package/lib/block/processParentExecutionPayload.js.map +1 -1
  14. package/lib/block/processWithdrawals.js +1 -1
  15. package/lib/block/processWithdrawals.js.map +1 -1
  16. package/lib/cache/stateCache.d.ts.map +1 -1
  17. package/lib/cache/stateCache.js +2 -4
  18. package/lib/cache/stateCache.js.map +1 -1
  19. package/lib/epoch/processParticipationFlagUpdates.d.ts +2 -2
  20. package/lib/epoch/processParticipationFlagUpdates.d.ts.map +1 -1
  21. package/lib/epoch/processParticipationFlagUpdates.js +13 -0
  22. package/lib/epoch/processParticipationFlagUpdates.js.map +1 -1
  23. package/lib/epoch/processRewardsAndPenalties.d.ts.map +1 -1
  24. package/lib/epoch/processRewardsAndPenalties.js +1 -2
  25. package/lib/epoch/processRewardsAndPenalties.js.map +1 -1
  26. package/lib/lightClient/proofs.d.ts.map +1 -1
  27. package/lib/lightClient/proofs.js +28 -2
  28. package/lib/lightClient/proofs.js.map +1 -1
  29. package/lib/lightClient/spec/index.d.ts.map +1 -1
  30. package/lib/lightClient/spec/index.js +7 -4
  31. package/lib/lightClient/spec/index.js.map +1 -1
  32. package/lib/lightClient/spec/utils.d.ts +7 -1
  33. package/lib/lightClient/spec/utils.d.ts.map +1 -1
  34. package/lib/lightClient/spec/utils.js +123 -14
  35. package/lib/lightClient/spec/utils.js.map +1 -1
  36. package/lib/lightClient/spec/validateLightClientBootstrap.d.ts.map +1 -1
  37. package/lib/lightClient/spec/validateLightClientBootstrap.js +2 -7
  38. package/lib/lightClient/spec/validateLightClientBootstrap.js.map +1 -1
  39. package/lib/lightClient/spec/validateLightClientUpdate.d.ts.map +1 -1
  40. package/lib/lightClient/spec/validateLightClientUpdate.js +6 -5
  41. package/lib/lightClient/spec/validateLightClientUpdate.js.map +1 -1
  42. package/lib/lightClient/types.d.ts +3 -1
  43. package/lib/lightClient/types.d.ts.map +1 -1
  44. package/lib/metrics.js +2 -4
  45. package/lib/metrics.js.map +1 -1
  46. package/lib/slot/upgradeStateToAltair.js +1 -1
  47. package/lib/slot/upgradeStateToAltair.js.map +1 -1
  48. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  49. package/lib/slot/upgradeStateToGloas.js +48 -9
  50. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  51. package/lib/stateView/beaconStateView.d.ts +1 -1
  52. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  53. package/lib/stateView/beaconStateView.js +6 -8
  54. package/lib/stateView/beaconStateView.js.map +1 -1
  55. package/lib/stateView/interface.d.ts +1 -1
  56. package/lib/stateView/interface.d.ts.map +1 -1
  57. package/lib/stateView/nativeBeaconStateView.d.ts +2 -2
  58. package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -1
  59. package/lib/stateView/nativeBeaconStateView.js +7 -9
  60. package/lib/stateView/nativeBeaconStateView.js.map +1 -1
  61. package/lib/util/execution.d.ts.map +1 -1
  62. package/lib/util/execution.js +7 -2
  63. package/lib/util/execution.js.map +1 -1
  64. package/lib/util/gloas.d.ts +3 -1
  65. package/lib/util/gloas.d.ts.map +1 -1
  66. package/lib/util/gloas.js +8 -11
  67. package/lib/util/gloas.js.map +1 -1
  68. package/lib/util/index.d.ts +1 -0
  69. package/lib/util/index.d.ts.map +1 -1
  70. package/lib/util/index.js +1 -0
  71. package/lib/util/index.js.map +1 -1
  72. package/lib/util/ssz.d.ts +9 -0
  73. package/lib/util/ssz.d.ts.map +1 -0
  74. package/lib/util/ssz.js +39 -0
  75. package/lib/util/ssz.js.map +1 -0
  76. package/package.json +11 -11
  77. package/src/block/processAttestationsAltair.ts +9 -6
  78. package/src/block/processBuilderDepositRequest.ts +21 -8
  79. package/src/block/processOperations.ts +28 -1
  80. package/src/block/processParentExecutionPayload.ts +26 -1
  81. package/src/block/processWithdrawals.ts +1 -1
  82. package/src/cache/stateCache.ts +2 -4
  83. package/src/epoch/processParticipationFlagUpdates.ts +20 -2
  84. package/src/epoch/processRewardsAndPenalties.ts +1 -2
  85. package/src/lightClient/proofs.ts +36 -1
  86. package/src/lightClient/spec/index.ts +8 -4
  87. package/src/lightClient/spec/utils.ts +193 -25
  88. package/src/lightClient/spec/validateLightClientBootstrap.ts +3 -10
  89. package/src/lightClient/spec/validateLightClientUpdate.ts +11 -21
  90. package/src/lightClient/types.ts +3 -1
  91. package/src/metrics.ts +2 -4
  92. package/src/slot/upgradeStateToAltair.ts +1 -1
  93. package/src/slot/upgradeStateToGloas.ts +84 -9
  94. package/src/stateView/beaconStateView.ts +8 -8
  95. package/src/stateView/interface.ts +1 -1
  96. package/src/stateView/nativeBeaconStateView.ts +8 -10
  97. package/src/util/execution.ts +9 -4
  98. package/src/util/gloas.ts +9 -14
  99. package/src/util/index.ts +1 -0
  100. package/src/util/ssz.ts +42 -0
@@ -398,6 +398,14 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
398
398
  return (this.cachedState as CachedBeaconStateGloas).builders.getReadonly(index);
399
399
  }
400
400
 
401
+ getBuildersLength(): number {
402
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
403
+ throw new Error("Builders are not supported before Gloas");
404
+ }
405
+
406
+ return (this.cachedState as CachedBeaconStateGloas).builders.length;
407
+ }
408
+
401
409
  canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean {
402
410
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
403
411
  throw new Error("Builders are not supported before Gloas");
@@ -507,14 +515,6 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
507
515
  return this.cachedState.epochCtx.getBeaconProposer(slot);
508
516
  }
509
517
 
510
- getBeaconProposerOrNull(slot: Slot): ValidatorIndex | null {
511
- try {
512
- return this.cachedState.epochCtx.getBeaconProposer(slot);
513
- } catch {
514
- return null;
515
- }
516
- }
517
-
518
518
  computeAnchorCheckpoint(): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader} {
519
519
  return computeAnchorCheckpoint(this.config, this.cachedState);
520
520
  }
@@ -92,7 +92,6 @@ export interface IBeaconStateView {
92
92
  currentProposers: ValidatorIndex[];
93
93
  nextProposers: ValidatorIndex[];
94
94
  getBeaconProposer(slot: Slot): ValidatorIndex;
95
- getBeaconProposerOrNull(slot: Slot): ValidatorIndex | null;
96
95
 
97
96
  // Validators and balances
98
97
  effectiveBalanceIncrements: EffectiveBalanceIncrements;
@@ -255,6 +254,7 @@ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
255
254
  latestExecutionPayloadBid: ExecutionPayloadBid;
256
255
  payloadExpectedWithdrawals: capella.Withdrawal[];
257
256
  getBuilder(index: BuilderIndex): gloas.Builder;
257
+ getBuildersLength(): number;
258
258
  canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
259
259
  getEpochPTCs(epoch: Epoch): Uint32Array[];
260
260
  getIndicesInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number[];
@@ -128,8 +128,6 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
128
128
  private readonly _getBeaconCommitteeCountPerSlot = new Map<Epoch, number>();
129
129
  private readonly _getShufflingDecisionRoot = new Map<Epoch, RootHex>();
130
130
  private readonly _getBeaconProposer = new Map<Slot, ValidatorIndex>();
131
- // getBeaconProposerOrNull can return null, so use .has() to distinguish "not cached" from "cached null"
132
- private readonly _getBeaconProposerOrNull = new Map<Slot, ValidatorIndex | null>();
133
131
  private readonly _getValidator = new Map<ValidatorIndex, phase0.Validator>();
134
132
  private readonly _getBalance = new Map<number, number>();
135
133
  private readonly _getIndexedSyncCommitteeAtEpoch = new Map<Epoch, SyncCommitteeCache>();
@@ -144,6 +142,7 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
144
142
  private _getNextShuffling: EpochShuffling | null = null;
145
143
  private _getEffectiveBalanceIncrementsZeroInactive: EffectiveBalanceIncrements | null = null;
146
144
  private _getAllValidators: phase0.Validator[] | null = null;
145
+ private _getBuildersLength: number | null = null;
147
146
  private _getAllBalances: number[] | null = null;
148
147
  private _getLatestWeakSubjectivityCheckpointEpoch: Epoch | null = null;
149
148
  private _getFinalizedRootProof: Uint8Array[] | null = null;
@@ -410,14 +409,6 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
410
409
  return cached;
411
410
  }
412
411
 
413
- getBeaconProposerOrNull(slot: Slot): ValidatorIndex | null {
414
- if (!this._getBeaconProposerOrNull.has(slot)) {
415
- this._getBeaconProposerOrNull.set(slot, this.binding.getBeaconProposerOrNull(slot));
416
- }
417
- // biome-ignore lint/style/noNonNullAssertion: has() check guarantees a value
418
- return this._getBeaconProposerOrNull.get(slot)!;
419
- }
420
-
421
412
  // Validators and balances
422
413
 
423
414
  get effectiveBalanceIncrements(): EffectiveBalanceIncrements {
@@ -888,6 +879,13 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
888
879
  return cached;
889
880
  }
890
881
 
882
+ getBuildersLength(): number {
883
+ if (this._getBuildersLength === null) {
884
+ this._getBuildersLength = this.binding.getBuildersLength();
885
+ }
886
+ return this._getBuildersLength;
887
+ }
888
+
891
889
  canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean {
892
890
  return this.binding.canBuilderCoverBid(builderIndex, bidAmount);
893
891
  }
@@ -9,6 +9,7 @@ import {
9
9
  bellatrix,
10
10
  capella,
11
11
  deneb,
12
+ gloas,
12
13
  isBlindedBeaconBlockBody,
13
14
  isExecutionPayload,
14
15
  ssz,
@@ -127,7 +128,10 @@ export function isCapellaPayloadHeader(
127
128
  }
128
129
 
129
130
  export function executionPayloadToPayloadHeader(fork: ForkSeq, payload: ExecutionPayload): ExecutionPayloadHeader {
130
- const transactionsRoot = ssz.bellatrix.Transactions.hashTreeRoot(payload.transactions);
131
+ const transactionsRoot =
132
+ fork >= ForkSeq.gloas
133
+ ? ssz.gloas.Transactions.hashTreeRoot((payload as gloas.ExecutionPayload).transactions)
134
+ : ssz.bellatrix.Transactions.hashTreeRoot(payload.transactions);
131
135
 
132
136
  const bellatrixPayloadFields: ExecutionPayloadHeader = {
133
137
  parentHash: payload.parentHash,
@@ -147,9 +151,10 @@ export function executionPayloadToPayloadHeader(fork: ForkSeq, payload: Executio
147
151
  };
148
152
 
149
153
  if (fork >= ForkSeq.capella) {
150
- (bellatrixPayloadFields as capella.ExecutionPayloadHeader).withdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(
151
- (payload as capella.ExecutionPayload).withdrawals
152
- );
154
+ (bellatrixPayloadFields as capella.ExecutionPayloadHeader).withdrawalsRoot =
155
+ fork >= ForkSeq.gloas
156
+ ? ssz.gloas.Withdrawals.hashTreeRoot((payload as gloas.ExecutionPayload).withdrawals)
157
+ : ssz.capella.Withdrawals.hashTreeRoot((payload as capella.ExecutionPayload).withdrawals);
153
158
  }
154
159
 
155
160
  if (fork >= ForkSeq.deneb) {
package/src/util/gloas.ts CHANGED
@@ -18,7 +18,6 @@ import {AttestationData} from "@lodestar/types/phase0";
18
18
  import {byteArrayEquals} from "@lodestar/utils";
19
19
  import {ZERO_HASH} from "../constants/index.js";
20
20
  import {CachedBeaconStateFulu, CachedBeaconStateGloas} from "../types.js";
21
- import {getBlockRootAtSlot} from "./blockRoot.js";
22
21
  import {computeDomain} from "./domain.js";
23
22
  import {computeEpochAtSlot} from "./epoch.js";
24
23
  import {computeEpochShuffling} from "./epochShuffling.js";
@@ -32,11 +31,13 @@ export function isBuilderWithdrawalCredential(withdrawalCredentials: Uint8Array)
32
31
  }
33
32
 
34
33
  export function getBuilderPaymentQuorumThreshold(state: CachedBeaconStateGloas): number {
35
- const quorum =
36
- Math.floor((state.epochCtx.totalActiveBalanceIncrements * EFFECTIVE_BALANCE_INCREMENT) / SLOTS_PER_EPOCH) *
37
- BUILDER_PAYMENT_THRESHOLD_NUMERATOR;
34
+ // total active balance exceeds Number.MAX_SAFE_INTEGER at mainnet scale, keep the intermediate math in bigint
35
+ const perSlotBalance =
36
+ (BigInt(state.epochCtx.totalActiveBalanceIncrements) * BigInt(EFFECTIVE_BALANCE_INCREMENT)) /
37
+ BigInt(SLOTS_PER_EPOCH);
38
+ const quorum = perSlotBalance * BigInt(BUILDER_PAYMENT_THRESHOLD_NUMERATOR);
38
39
 
39
- return Math.floor(quorum / BUILDER_PAYMENT_THRESHOLD_DENOMINATOR);
40
+ return Number(quorum / BigInt(BUILDER_PAYMENT_THRESHOLD_DENOMINATOR));
40
41
  }
41
42
 
42
43
  function hasBuilderIndexFlag(index: number): boolean {
@@ -188,15 +189,9 @@ export function findBuilderIndexByPubkey(state: CachedBeaconStateGloas, pubkey:
188
189
  return null;
189
190
  }
190
191
 
191
- export function isAttestationSameSlot(state: CachedBeaconStateGloas, data: AttestationData): boolean {
192
- if (data.slot === 0) return true;
193
-
194
- const isMatchingBlockRoot = byteArrayEquals(data.beaconBlockRoot, getBlockRootAtSlot(state, data.slot));
195
- const isCurrentBlockRoot = !byteArrayEquals(data.beaconBlockRoot, getBlockRootAtSlot(state, data.slot - 1));
196
-
197
- return isMatchingBlockRoot && isCurrentBlockRoot;
198
- }
199
-
192
+ /**
193
+ * Use cached block roots to avoid repeated state root lookups while matching the spec's is_attestation_same_slot behavior.
194
+ */
200
195
  export function isAttestationSameSlotRootCache(rootCache: RootCache, data: AttestationData): boolean {
201
196
  if (data.slot === 0) return true;
202
197
 
package/src/util/index.ts CHANGED
@@ -25,6 +25,7 @@ export * from "./shuffling.js";
25
25
  export * from "./signatureSets.js";
26
26
  export * from "./signingRoot.js";
27
27
  export * from "./slot.js";
28
+ export * from "./ssz.js";
28
29
  export * from "./syncCommittee.js";
29
30
  export * from "./validator.js";
30
31
  export * from "./weakSubjectivity.js";
@@ -0,0 +1,42 @@
1
+ import {BranchNode, LeafNode, Node, zeroNode} from "@chainsafe/persistent-merkle-tree";
2
+ import {progressiveSubtreeFillToContents} from "@chainsafe/ssz";
3
+
4
+ // TODO: move these utils to @chainsafe/ssz (progressive.ts, next to progressiveSubtreeFillToContents)
5
+
6
+ /** Root node (chunks + length mix-in) of a zero-filled ProgressiveListBasicType of `length` items */
7
+ export function zeroProgressiveListBasicRootNode(itemsPerChunk: number, length: number): Node {
8
+ const chunkCount = Math.ceil(length / itemsPerChunk);
9
+ // mirrors ssz's progressiveSubtreeCount (subtree capacities 1, 4, 16, ...)
10
+ let numSubtrees = 0;
11
+ for (let remaining = chunkCount, subtreeLength = 1; remaining > 0; subtreeLength *= 4) {
12
+ remaining -= Math.min(remaining, subtreeLength);
13
+ numSubtrees++;
14
+ }
15
+ return new BranchNode(zeroProgressiveNode(numSubtrees), LeafNode.fromUint32(length));
16
+ }
17
+
18
+ /**
19
+ * Root node of a progressive list from its chunk/element nodes + length mix-in.
20
+ * `nodes` are packed 32-byte chunk leaves for basic lists, or element root nodes for composite lists.
21
+ */
22
+ export function progressiveListRootNode(nodes: Node[], length: number): Node {
23
+ return new BranchNode(progressiveSubtreeFillToContents(nodes), LeafNode.fromUint32(length));
24
+ }
25
+
26
+ /**
27
+ * Return the chunks node of a zero-filled progressive merkle list spanning `numSubtrees`
28
+ * balanced subtrees (chunk capacities 1, 4, 16, ... = 4^i; depths 0, 2, 4, ... = 2i).
29
+ *
30
+ * Not memoized: the zero data is already fully shared via the cached zeroNode(2i) subtrees;
31
+ * only the O(numSubtrees) spine BranchNodes (~10 at 2M validators) are allocated per call.
32
+ */
33
+ function zeroProgressiveNode(numSubtrees: number): Node {
34
+ // chain(k) = B(zeroNode(0), B(zeroNode(2), ... B(zeroNode(2(k-1)), zeroNode(0))))
35
+ let node: Node = zeroNode(0); // terminator
36
+ for (let i = numSubtrees - 1; i >= 0; i--) {
37
+ // BranchNode(left: balanced subtree i, right: rest of chain) — chain grows to the right,
38
+ // same as ssz progressiveSubtreeFillToContents: `root = new BranchNode(subtreeRoots[i], root)`
39
+ node = new BranchNode(zeroNode(2 * i), node);
40
+ }
41
+ return node;
42
+ }