@lodestar/state-transition 1.48.0-dev.fd70e5fc15 → 1.48.0-rc.0

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 (60) hide show
  1. package/lib/block/index.d.ts.map +1 -1
  2. package/lib/block/index.js +41 -11
  3. package/lib/block/index.js.map +1 -1
  4. package/lib/block/processOperations.d.ts.map +1 -1
  5. package/lib/block/processOperations.js +61 -16
  6. package/lib/block/processOperations.js.map +1 -1
  7. package/lib/block/processPayloadAttestation.d.ts +1 -1
  8. package/lib/block/processPayloadAttestation.d.ts.map +1 -1
  9. package/lib/block/processPayloadAttestation.js +2 -2
  10. package/lib/block/processPayloadAttestation.js.map +1 -1
  11. package/lib/block/types.d.ts +30 -0
  12. package/lib/block/types.d.ts.map +1 -1
  13. package/lib/block/types.js +32 -0
  14. package/lib/block/types.js.map +1 -1
  15. package/lib/epoch/processPtcWindow.d.ts.map +1 -1
  16. package/lib/epoch/processPtcWindow.js +12 -9
  17. package/lib/epoch/processPtcWindow.js.map +1 -1
  18. package/lib/index.d.ts +1 -1
  19. package/lib/index.d.ts.map +1 -1
  20. package/lib/index.js +1 -1
  21. package/lib/index.js.map +1 -1
  22. package/lib/metrics.d.ts +7 -1
  23. package/lib/metrics.d.ts.map +1 -1
  24. package/lib/metrics.js +12 -1
  25. package/lib/metrics.js.map +1 -1
  26. package/lib/signatureSets/index.d.ts.map +1 -1
  27. package/lib/signatureSets/index.js +8 -0
  28. package/lib/signatureSets/index.js.map +1 -1
  29. package/lib/stateView/beaconStateView.d.ts +4 -0
  30. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  31. package/lib/stateView/beaconStateView.js +9 -0
  32. package/lib/stateView/beaconStateView.js.map +1 -1
  33. package/lib/stateView/interface.d.ts +1 -0
  34. package/lib/stateView/interface.d.ts.map +1 -1
  35. package/lib/stateView/interface.js.map +1 -1
  36. package/lib/stateView/nativeBeaconStateView.d.ts +1 -0
  37. package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -1
  38. package/lib/stateView/nativeBeaconStateView.js +3 -0
  39. package/lib/stateView/nativeBeaconStateView.js.map +1 -1
  40. package/lib/util/gloas.d.ts.map +1 -1
  41. package/lib/util/gloas.js +1 -1
  42. package/lib/util/gloas.js.map +1 -1
  43. package/lib/util/seed.d.ts +1 -1
  44. package/lib/util/seed.d.ts.map +1 -1
  45. package/lib/util/seed.js +14 -11
  46. package/lib/util/seed.js.map +1 -1
  47. package/package.json +7 -7
  48. package/src/block/index.ts +48 -15
  49. package/src/block/processOperations.ts +62 -17
  50. package/src/block/processPayloadAttestation.ts +3 -2
  51. package/src/block/types.ts +32 -0
  52. package/src/epoch/processPtcWindow.ts +15 -8
  53. package/src/index.ts +1 -1
  54. package/src/metrics.ts +13 -2
  55. package/src/signatureSets/index.ts +17 -3
  56. package/src/stateView/beaconStateView.ts +12 -0
  57. package/src/stateView/interface.ts +1 -0
  58. package/src/stateView/nativeBeaconStateView.ts +4 -0
  59. package/src/util/gloas.ts +1 -0
  60. package/src/util/seed.ts +25 -13
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/ChainSafe/lodestar/issues"
13
13
  },
14
- "version": "1.48.0-dev.fd70e5fc15",
14
+ "version": "1.48.0-rc.0",
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
@@ -70,14 +70,14 @@
70
70
  "@chainsafe/persistent-merkle-tree": "^1.3.1",
71
71
  "@chainsafe/ssz": "^1.7.0",
72
72
  "@chainsafe/swap-or-not-shuffle": "^1.2.1",
73
- "@lodestar/config": "^1.48.0-dev.fd70e5fc15",
74
- "@lodestar/params": "^1.48.0-dev.fd70e5fc15",
75
- "@lodestar/types": "^1.48.0-dev.fd70e5fc15",
76
- "@lodestar/utils": "^1.48.0-dev.fd70e5fc15",
73
+ "@lodestar/config": "^1.48.0-rc.0",
74
+ "@lodestar/params": "^1.48.0-rc.0",
75
+ "@lodestar/types": "^1.48.0-rc.0",
76
+ "@lodestar/utils": "^1.48.0-rc.0",
77
77
  "@vekexasia/bigint-buffer2": "^1.1.1"
78
78
  },
79
79
  "devDependencies": {
80
- "@lodestar/api": "^1.48.0-dev.fd70e5fc15"
80
+ "@lodestar/api": "^1.48.0-rc.0"
81
81
  },
82
82
  "keywords": [
83
83
  "ethereum",
@@ -85,5 +85,5 @@
85
85
  "beacon",
86
86
  "blockchain"
87
87
  ],
88
- "gitHead": "dbc49c8fa4a2870bc2b4b102fe61a4d2909621fa"
88
+ "gitHead": "ee1c2b21489c8e76dc3f609baee79d31695be0f8"
89
89
  }
@@ -20,7 +20,7 @@ import {processPayloadAttestation} from "./processPayloadAttestation.js";
20
20
  import {processRandao} from "./processRandao.js";
21
21
  import {processSyncAggregate} from "./processSyncCommittee.js";
22
22
  import {processWithdrawals} from "./processWithdrawals.js";
23
- import {ProcessBlockOpts, ProposerRewardType} from "./types.js";
23
+ import {BlockProcessStep, ProcessBlockOpts, ProposerRewardType} from "./types.js";
24
24
 
25
25
  // Spec tests
26
26
  export {
@@ -60,21 +60,31 @@ export function processBlock(
60
60
  // Apply the parent's deferred payload effects before everything else. Must run before
61
61
  // processBlockHeader and processExecutionPayloadBid so subsequent steps see the updated state.
62
62
  if (fork >= ForkSeq.gloas) {
63
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processParentExecutionPayload});
63
64
  processParentExecutionPayload(state as CachedBeaconStateGloas, block as BeaconBlock<ForkPostGloas>);
65
+ timer?.();
64
66
  }
65
67
 
66
- processBlockHeader(state, block);
68
+ {
69
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processBlockHeader});
70
+ processBlockHeader(state, block);
71
+ timer?.();
72
+ }
67
73
 
68
- if (fork >= ForkSeq.gloas) {
69
- // Parent payload's execution requests were already applied by processParentExecutionPayload above
70
- processWithdrawals(fork, state as CachedBeaconStateGloas);
71
- } else if (fork >= ForkSeq.capella) {
72
- const fullOrBlindedPayload = getFullOrBlindedPayload(block);
73
- processWithdrawals(
74
- fork,
75
- state as CachedBeaconStateCapella,
76
- fullOrBlindedPayload as capella.FullOrBlindedExecutionPayload
77
- );
74
+ if (fork >= ForkSeq.capella) {
75
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processWithdrawals});
76
+ if (fork >= ForkSeq.gloas) {
77
+ // Parent payload's execution requests were already applied by processParentExecutionPayload above
78
+ processWithdrawals(fork, state as CachedBeaconStateGloas);
79
+ } else {
80
+ const fullOrBlindedPayload = getFullOrBlindedPayload(block);
81
+ processWithdrawals(
82
+ fork,
83
+ state as CachedBeaconStateCapella,
84
+ fullOrBlindedPayload as capella.FullOrBlindedExecutionPayload
85
+ );
86
+ }
87
+ timer?.();
78
88
  }
79
89
 
80
90
  // The call to the process_execution_payload must happen before the call to the process_randao as the former depends
@@ -87,25 +97,48 @@ export function processBlock(
87
97
  fork >= ForkSeq.bellatrix &&
88
98
  isExecutionEnabled(state as CachedBeaconStateBellatrix, block)
89
99
  ) {
100
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processExecutionPayload});
90
101
  processExecutionPayload(fork, state as CachedBeaconStateBellatrix, block.body, externalData);
102
+ timer?.();
91
103
  }
92
104
 
93
105
  if (fork >= ForkSeq.gloas) {
106
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processExecutionPayloadBid});
94
107
  processExecutionPayloadBid(
95
108
  state as CachedBeaconStateGloas,
96
109
  (block as BeaconBlock<ForkPostGloas>).body.signedExecutionPayloadBid
97
110
  );
111
+ timer?.();
112
+ }
113
+
114
+ {
115
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processRandao});
116
+ processRandao(state, block, verifySignatures);
117
+ timer?.();
118
+ }
119
+
120
+ {
121
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processEth1Data});
122
+ processEth1Data(state, block.body.eth1Data);
123
+ timer?.();
124
+ }
125
+
126
+ {
127
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processOperations});
128
+ processOperations(fork, state, block.body, parentSlot, opts, metrics);
129
+ timer?.();
98
130
  }
99
131
 
100
- processRandao(state, block, verifySignatures);
101
- processEth1Data(state, block.body.eth1Data);
102
- processOperations(fork, state, block.body, parentSlot, opts, metrics);
103
132
  if (fork >= ForkSeq.altair) {
133
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processSyncAggregate});
104
134
  processSyncAggregate(state, block as altair.BeaconBlock, verifySignatures);
135
+ timer?.();
105
136
  }
106
137
 
107
138
  if (fork >= ForkSeq.deneb) {
139
+ const timer = metrics?.processBlockStepTime.startTimer({step: BlockProcessStep.processBlobKzgCommitments});
108
140
  processBlobKzgCommitments(externalData);
141
+ timer?.();
109
142
  // Only throw PreData so beacon can also sync/process blocks optimistically
110
143
  // and let forkChoice handle it
111
144
  if (externalData.dataAvailabilityStatus === DataAvailabilityStatus.PreData) {
@@ -18,7 +18,7 @@ import {processPayloadAttestation} from "./processPayloadAttestation.js";
18
18
  import {processProposerSlashing} from "./processProposerSlashing.js";
19
19
  import {processVoluntaryExit} from "./processVoluntaryExit.js";
20
20
  import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
21
- import {ProcessBlockOpts} from "./types.js";
21
+ import {ProcessBlockOpts, ProcessOperationsStep} from "./types.js";
22
22
 
23
23
  export {
24
24
  processProposerSlashing,
@@ -49,49 +49,94 @@ export function processOperations(
49
49
  );
50
50
  }
51
51
 
52
- for (const proposerSlashing of body.proposerSlashings) {
53
- processProposerSlashing(fork, state, proposerSlashing, opts.verifySignatures);
52
+ {
53
+ const timer = metrics?.processOperationsStepTime.startTimer({step: ProcessOperationsStep.processProposerSlashing});
54
+ for (const proposerSlashing of body.proposerSlashings) {
55
+ processProposerSlashing(fork, state, proposerSlashing, opts.verifySignatures);
56
+ }
57
+ timer?.();
54
58
  }
55
- for (const attesterSlashing of body.attesterSlashings) {
56
- processAttesterSlashing(fork, state, attesterSlashing, opts.verifySignatures);
59
+
60
+ {
61
+ const timer = metrics?.processOperationsStepTime.startTimer({step: ProcessOperationsStep.processAttesterSlashing});
62
+ for (const attesterSlashing of body.attesterSlashings) {
63
+ processAttesterSlashing(fork, state, attesterSlashing, opts.verifySignatures);
64
+ }
65
+ timer?.();
57
66
  }
58
67
 
59
- processAttestations(fork, state, body.attestations, parentSlot, opts.verifySignatures, metrics);
68
+ {
69
+ const timer = metrics?.processOperationsStepTime.startTimer({step: ProcessOperationsStep.processAttestations});
70
+ processAttestations(fork, state, body.attestations, parentSlot, opts.verifySignatures, metrics);
71
+ timer?.();
72
+ }
60
73
 
61
- for (const deposit of body.deposits) {
62
- processDeposit(fork, state, deposit);
74
+ {
75
+ const timer = metrics?.processOperationsStepTime.startTimer({step: ProcessOperationsStep.processDeposit});
76
+ for (const deposit of body.deposits) {
77
+ processDeposit(fork, state, deposit);
78
+ }
79
+ timer?.();
63
80
  }
64
81
 
65
- for (const voluntaryExit of body.voluntaryExits) {
66
- processVoluntaryExit(fork, state, voluntaryExit, opts.verifySignatures);
82
+ {
83
+ const timer = metrics?.processOperationsStepTime.startTimer({step: ProcessOperationsStep.processVoluntaryExit});
84
+ for (const voluntaryExit of body.voluntaryExits) {
85
+ processVoluntaryExit(fork, state, voluntaryExit, opts.verifySignatures);
86
+ }
87
+ timer?.();
67
88
  }
68
89
 
69
90
  if (fork >= ForkSeq.capella) {
91
+ const timer = metrics?.processOperationsStepTime.startTimer({
92
+ step: ProcessOperationsStep.processBlsToExecutionChange,
93
+ });
70
94
  for (const blsToExecutionChange of (body as capella.BeaconBlockBody).blsToExecutionChanges) {
71
95
  processBlsToExecutionChange(state as CachedBeaconStateCapella, blsToExecutionChange);
72
96
  }
97
+ timer?.();
73
98
  }
74
99
 
75
100
  if (fork >= ForkSeq.electra && fork < ForkSeq.gloas) {
76
101
  const stateElectra = state as CachedBeaconStateElectra;
77
102
  const bodyElectra = body as electra.BeaconBlockBody;
78
103
 
79
- for (const depositRequest of bodyElectra.executionRequests.deposits) {
80
- processDepositRequest(fork, stateElectra, depositRequest);
104
+ {
105
+ const timer = metrics?.processOperationsStepTime.startTimer({step: ProcessOperationsStep.processDepositRequest});
106
+ for (const depositRequest of bodyElectra.executionRequests.deposits) {
107
+ processDepositRequest(fork, stateElectra, depositRequest);
108
+ }
109
+ timer?.();
81
110
  }
82
111
 
83
- for (const elWithdrawalRequest of bodyElectra.executionRequests.withdrawals) {
84
- processWithdrawalRequest(fork, stateElectra, elWithdrawalRequest);
112
+ {
113
+ const timer = metrics?.processOperationsStepTime.startTimer({
114
+ step: ProcessOperationsStep.processWithdrawalRequest,
115
+ });
116
+ for (const elWithdrawalRequest of bodyElectra.executionRequests.withdrawals) {
117
+ processWithdrawalRequest(fork, stateElectra, elWithdrawalRequest);
118
+ }
119
+ timer?.();
85
120
  }
86
121
 
87
- for (const elConsolidationRequest of bodyElectra.executionRequests.consolidations) {
88
- processConsolidationRequest(stateElectra, elConsolidationRequest);
122
+ {
123
+ const timer = metrics?.processOperationsStepTime.startTimer({
124
+ step: ProcessOperationsStep.processConsolidationRequest,
125
+ });
126
+ for (const elConsolidationRequest of bodyElectra.executionRequests.consolidations) {
127
+ processConsolidationRequest(stateElectra, elConsolidationRequest);
128
+ }
129
+ timer?.();
89
130
  }
90
131
  }
91
132
 
92
133
  if (fork >= ForkSeq.gloas) {
134
+ const timer = metrics?.processOperationsStepTime.startTimer({
135
+ step: ProcessOperationsStep.processPayloadAttestation,
136
+ });
93
137
  for (const payloadAttestation of (body as gloas.BeaconBlockBody).payloadAttestations) {
94
- processPayloadAttestation(state as CachedBeaconStateGloas, payloadAttestation);
138
+ processPayloadAttestation(state as CachedBeaconStateGloas, payloadAttestation, opts.verifySignatures);
95
139
  }
140
+ timer?.();
96
141
  }
97
142
  }
@@ -5,7 +5,8 @@ import {isValidIndexedPayloadAttestation} from "./isValidIndexedPayloadAttestati
5
5
 
6
6
  export function processPayloadAttestation(
7
7
  state: CachedBeaconStateGloas,
8
- payloadAttestation: gloas.PayloadAttestation
8
+ payloadAttestation: gloas.PayloadAttestation,
9
+ verifySignature = true
9
10
  ): void {
10
11
  const data = payloadAttestation.data;
11
12
 
@@ -19,7 +20,7 @@ export function processPayloadAttestation(
19
20
 
20
21
  const indexedPayloadAttestation = state.epochCtx.getIndexedPayloadAttestation(data.slot, payloadAttestation);
21
22
 
22
- if (!isValidIndexedPayloadAttestation(state, indexedPayloadAttestation, true)) {
23
+ if (!isValidIndexedPayloadAttestation(state, indexedPayloadAttestation, verifySignature)) {
23
24
  throw Error("Invalid payload attestation");
24
25
  }
25
26
  }
@@ -7,3 +7,35 @@ export enum ProposerRewardType {
7
7
  syncAggregate = "sync_aggregate",
8
8
  slashing = "slashing",
9
9
  }
10
+
11
+ /**
12
+ * Steps of `processBlock()` tracked in metrics
13
+ */
14
+ export enum BlockProcessStep {
15
+ processParentExecutionPayload = "processParentExecutionPayload",
16
+ processBlockHeader = "processBlockHeader",
17
+ processWithdrawals = "processWithdrawals",
18
+ processExecutionPayload = "processExecutionPayload",
19
+ processExecutionPayloadBid = "processExecutionPayloadBid",
20
+ processRandao = "processRandao",
21
+ processEth1Data = "processEth1Data",
22
+ processOperations = "processOperations",
23
+ processSyncAggregate = "processSyncAggregate",
24
+ processBlobKzgCommitments = "processBlobKzgCommitments",
25
+ }
26
+
27
+ /**
28
+ * Steps of `processOperations()` tracked in metrics
29
+ */
30
+ export enum ProcessOperationsStep {
31
+ processProposerSlashing = "processProposerSlashing",
32
+ processAttesterSlashing = "processAttesterSlashing",
33
+ processAttestations = "processAttestations",
34
+ processDeposit = "processDeposit",
35
+ processVoluntaryExit = "processVoluntaryExit",
36
+ processBlsToExecutionChange = "processBlsToExecutionChange",
37
+ processDepositRequest = "processDepositRequest",
38
+ processWithdrawalRequest = "processWithdrawalRequest",
39
+ processConsolidationRequest = "processConsolidationRequest",
40
+ processPayloadAttestation = "processPayloadAttestation",
41
+ }
@@ -1,4 +1,4 @@
1
- import {MIN_SEED_LOOKAHEAD} from "@lodestar/params";
1
+ import {MIN_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
2
2
  import {ssz} from "@lodestar/types";
3
3
  import {CachedBeaconStateGloas, EpochTransitionCache} from "../types.js";
4
4
  import {computeEpochShuffling} from "../util/epochShuffling.js";
@@ -22,17 +22,24 @@ export function processPtcWindow(state: CachedBeaconStateGloas, cache: EpochTran
22
22
  state,
23
23
  nextEpoch,
24
24
  nextEpochShuffling.committees,
25
+ nextEpochShuffling.shuffling,
25
26
  state.epochCtx.effectiveBalanceIncrements
26
27
  );
27
28
 
28
29
  // Stash for finalProcessEpoch to shift into epoch cache
29
30
  cache.nextEpochPayloadTimelinessCommittees = newNextPayloadTimelinessCommittees;
30
31
 
31
- // Write shifted window to state: current(N) + next(N+1) + newlyComputed(N+2)
32
- // From the perspective of upcoming epoch N+1, this is previous + current + next
33
- state.ptcWindow = ssz.gloas.PayloadTimelinessCommitteeWindow.toViewDU([
34
- ...state.epochCtx.payloadTimelinessCommittees,
35
- ...state.epochCtx.nextPayloadTimelinessCommittees,
36
- ...newNextPayloadTimelinessCommittees,
37
- ]);
32
+ // Shift the current and next epochs forward by reusing their SSZ subtrees and cached hashes.
33
+ // Only the newly computed epoch needs new subtrees.
34
+ const ptcWindow = state.ptcWindow;
35
+ const retainedLength = ptcWindow.length - SLOTS_PER_EPOCH;
36
+ for (let i = 0; i < retainedLength; i++) {
37
+ ptcWindow.set(i, ptcWindow.getReadonly(i + SLOTS_PER_EPOCH));
38
+ }
39
+ for (let i = 0; i < SLOTS_PER_EPOCH; i++) {
40
+ ptcWindow.set(
41
+ retainedLength + i,
42
+ ssz.gloas.PayloadTimelinessCommittee.toViewDU(newNextPayloadTimelinessCommittees[i])
43
+ );
44
+ }
38
45
  }
package/src/index.ts CHANGED
@@ -13,7 +13,7 @@ export {
13
13
  } from "./block/processVoluntaryExit.js";
14
14
  // Withdrawals for new blocks
15
15
  export {getExpectedWithdrawals} from "./block/processWithdrawals.js";
16
- export {ProposerRewardType} from "./block/types.js";
16
+ export {BlockProcessStep, ProcessOperationsStep, ProposerRewardType} from "./block/types.js";
17
17
  export {
18
18
  type EffectiveBalanceIncrements,
19
19
  getEffectiveBalanceIncrementsWithLen,
package/src/metrics.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import {ForkName} from "@lodestar/params";
2
2
  import {MetricsRegister} from "@lodestar/utils";
3
- import {ProposerRewardType} from "./block/types.js";
3
+ import {BlockProcessStep, ProcessOperationsStep, ProposerRewardType} from "./block/types.js";
4
4
  import {EpochTransitionStep} from "./epoch/index.js";
5
5
  import {StateCloneSource, StateHashTreeRootSource} from "./stateTransition.js";
6
6
  import {CachedBeaconStateAllForks} from "./types.js";
@@ -57,10 +57,21 @@ export function getMetrics(register: MetricsRegister) {
57
57
  processBlockTime: register.histogram({
58
58
  name: "lodestar_stfn_process_block_seconds",
59
59
  help: "Time to process a single block in seconds",
60
- // TODO: Add metrics for each step
61
60
  // Block processing can take 5-40ms, 100ms max
62
61
  buckets: [0.005, 0.01, 0.02, 0.05, 0.1, 1],
63
62
  }),
63
+ processBlockStepTime: register.histogram<{step: BlockProcessStep}>({
64
+ name: "lodestar_stfn_process_block_step_seconds",
65
+ help: "Time to call each step of process block in seconds",
66
+ labelNames: ["step"],
67
+ buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1],
68
+ }),
69
+ processOperationsStepTime: register.histogram<{step: ProcessOperationsStep}>({
70
+ name: "lodestar_stfn_process_operations_step_seconds",
71
+ help: "Time to call each step of process operations in seconds",
72
+ labelNames: ["step"],
73
+ buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1],
74
+ }),
64
75
  processBlockCommitTime: register.histogram({
65
76
  name: "lodestar_stfn_process_block_commit_seconds",
66
77
  help: "Time to call commit after process a single block in seconds",
@@ -1,13 +1,14 @@
1
1
  import {BeaconConfig} from "@lodestar/config";
2
2
  import {ForkSeq} from "@lodestar/params";
3
- import {IndexedAttestation, SignedBeaconBlock, altair, capella} from "@lodestar/types";
3
+ import {IndexedAttestation, SignedBeaconBlock, altair, capella, gloas} from "@lodestar/types";
4
4
  import {getSyncCommitteeSignatureSet} from "../block/processSyncCommittee.js";
5
5
  import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
6
- import {IBeaconStateView} from "../stateView/interface.js";
7
- import {ISignatureSet} from "../util/index.js";
6
+ import {IBeaconStateView, IBeaconStateViewGloas} from "../stateView/interface.js";
7
+ import {ISignatureSet, createAggregateSignatureSetFromComponents} from "../util/index.js";
8
8
  import {getAttesterSlashingsSignatureSets} from "./attesterSlashings.js";
9
9
  import {getBlsToExecutionChangeSignatureSets} from "./blsToExecutionChange.js";
10
10
  import {getAttestationsSignatureSets} from "./indexedAttestation.js";
11
+ import {getPayloadAttestationDataSigningRoot} from "./indexedPayloadAttestation.js";
11
12
  import {getBlockProposerSignatureSet} from "./proposer.js";
12
13
  import {getProposerSlashingsSignatureSets} from "./proposerSlashings.js";
13
14
  import {getRandaoRevealSignatureSet} from "./randao.js";
@@ -79,5 +80,18 @@ export function getBlockSignatureSets(
79
80
  }
80
81
  }
81
82
 
83
+ if (fork >= ForkSeq.gloas) {
84
+ for (const payloadAttestation of (signedBlock as gloas.SignedBeaconBlock).message.body.payloadAttestations) {
85
+ const ptc = (state as IBeaconStateViewGloas).getPayloadTimelinessCommittee(payloadAttestation.data.slot);
86
+ signatureSets.push(
87
+ createAggregateSignatureSetFromComponents(
88
+ payloadAttestation.aggregationBits.intersectValues(ptc),
89
+ getPayloadAttestationDataSigningRoot(config, payloadAttestation.data),
90
+ payloadAttestation.signature
91
+ )
92
+ );
93
+ }
94
+ }
95
+
82
96
  return signatureSets;
83
97
  }
@@ -456,6 +456,18 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
456
456
  }
457
457
  throw new Error(`PTC committees are not available for epoch=${epoch}`);
458
458
  }
459
+
460
+ /**
461
+ * Return the PTC for a slot in the previous, current or next epoch
462
+ */
463
+ getPayloadTimelinessCommittee(slot: Slot): Uint32Array {
464
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
465
+ throw new Error("PTC committees are not supported before Gloas");
466
+ }
467
+
468
+ return (this.cachedState as CachedBeaconStateGloas).epochCtx.getPayloadTimelinessCommittee(slot);
469
+ }
470
+
459
471
  /**
460
472
  * Return all positions of the validator in the PTC committee for the given slot.
461
473
  *
@@ -285,6 +285,7 @@ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
285
285
  getBuildersLength(): number;
286
286
  canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
287
287
  getEpochPTCs(epoch: Epoch): Uint32Array[];
288
+ getPayloadTimelinessCommittee(slot: Slot): Uint32Array;
288
289
  getIndicesInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number[];
289
290
  /**
290
291
  * Clone the state and apply parent execution payload effects.
@@ -926,6 +926,10 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
926
926
  return cached;
927
927
  }
928
928
 
929
+ getPayloadTimelinessCommittee(slot: Slot): Uint32Array {
930
+ return this.binding.getPayloadTimelinessCommittee(slot);
931
+ }
932
+
929
933
  getIndicesInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number[] {
930
934
  return this.binding.getIndicesInPayloadTimelinessCommittee(validatorIndex, slot);
931
935
  }
package/src/util/gloas.ts CHANGED
@@ -220,6 +220,7 @@ export function initializePtcWindow(state: CachedBeaconStateFulu): Uint32Array[]
220
220
  state,
221
221
  epoch,
222
222
  shuffling.committees,
223
+ shuffling.shuffling,
223
224
  state.epochCtx.effectiveBalanceIncrements
224
225
  )
225
226
  );
package/src/util/seed.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import {digest} from "@chainsafe/as-sha256";
2
+ import {computePtcIndicesForEpoch} from "@chainsafe/lodestar-z/shuffle";
2
3
  import {
3
4
  computeProposerIndex as nativeComputeProposerIndex,
4
5
  computeSyncCommitteeIndices as nativeComputeSyncCommitteeIndices,
@@ -275,25 +276,36 @@ export function computePayloadTimelinessCommitteesForEpoch(
275
276
  state: BeaconStateAllForks,
276
277
  epoch: number,
277
278
  committees: Uint32Array[][],
279
+ shuffling: Uint32Array,
278
280
  effectiveBalanceIncrements: EffectiveBalanceIncrements
279
281
  ): Uint32Array[] {
280
282
  const epochSeed = getSeed(state, epoch, DOMAIN_PTC_ATTESTER);
281
283
  const startSlot = epoch * SLOTS_PER_EPOCH;
284
+ const slotOffsets = new Uint32Array(SLOTS_PER_EPOCH + 1);
285
+ let shufflingLength = 0;
286
+ for (let i = 0; i < SLOTS_PER_EPOCH; i++) {
287
+ slotOffsets[i] = shufflingLength;
288
+ for (const committee of committees[i]) {
289
+ shufflingLength += committee.length;
290
+ }
291
+ }
292
+ slotOffsets[SLOTS_PER_EPOCH] = shufflingLength;
293
+
294
+ // Native sampling returns all committees concatenated in slot order, with PTC_SIZE indices per slot.
295
+ const flatPtcIndices = computePtcIndicesForEpoch(
296
+ epochSeed,
297
+ startSlot,
298
+ SLOTS_PER_EPOCH,
299
+ shuffling,
300
+ slotOffsets,
301
+ effectiveBalanceIncrements,
302
+ PTC_SIZE,
303
+ MAX_EFFECTIVE_BALANCE_ELECTRA,
304
+ EFFECTIVE_BALANCE_INCREMENT
305
+ );
282
306
  const result: Uint32Array[] = new Array(SLOTS_PER_EPOCH);
283
-
284
- // Pre-allocate slot seed buffer once, reuse across all slots
285
- const slotSeedInput = new Uint8Array(epochSeed.length + 8);
286
- slotSeedInput.set(epochSeed, 0);
287
- const slotSeedView = new DataView(slotSeedInput.buffer, slotSeedInput.byteOffset, slotSeedInput.byteLength);
288
-
289
307
  for (let i = 0; i < SLOTS_PER_EPOCH; i++) {
290
- const slot = startSlot + i;
291
- // Write slot as little-endian uint64 (fits in uint32 range)
292
- slotSeedView.setUint32(epochSeed.length, slot, true);
293
- slotSeedView.setUint32(epochSeed.length + 4, 0, true);
294
- const slotSeed = digest(slotSeedInput);
295
-
296
- result[i] = computePayloadTimelinessCommitteeForSlot(slotSeed, committees[i], effectiveBalanceIncrements);
308
+ result[i] = flatPtcIndices.subarray(i * PTC_SIZE, (i + 1) * PTC_SIZE);
297
309
  }
298
310
  return result;
299
311
  }