@lodestar/state-transition 1.47.0 → 1.48.0-dev.042c96183c
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.
- package/lib/block/index.d.ts.map +1 -1
- package/lib/block/index.js +44 -13
- package/lib/block/index.js.map +1 -1
- package/lib/block/processExecutionPayloadBid.d.ts +2 -2
- package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
- package/lib/block/processExecutionPayloadBid.js +3 -2
- package/lib/block/processExecutionPayloadBid.js.map +1 -1
- package/lib/block/processOperations.d.ts.map +1 -1
- package/lib/block/processOperations.js +62 -33
- package/lib/block/processOperations.js.map +1 -1
- package/lib/block/processParentExecutionPayload.d.ts.map +1 -1
- package/lib/block/processParentExecutionPayload.js +2 -16
- package/lib/block/processParentExecutionPayload.js.map +1 -1
- package/lib/block/processPayloadAttestation.d.ts +1 -1
- package/lib/block/processPayloadAttestation.d.ts.map +1 -1
- package/lib/block/processPayloadAttestation.js +2 -2
- package/lib/block/processPayloadAttestation.js.map +1 -1
- package/lib/block/processWithdrawals.d.ts.map +1 -1
- package/lib/block/processWithdrawals.js +8 -2
- package/lib/block/processWithdrawals.js.map +1 -1
- package/lib/block/types.d.ts +30 -0
- package/lib/block/types.d.ts.map +1 -1
- package/lib/block/types.js +32 -0
- package/lib/block/types.js.map +1 -1
- package/lib/epoch/processInactivityUpdates.d.ts.map +1 -1
- package/lib/epoch/processInactivityUpdates.js +7 -1
- package/lib/epoch/processInactivityUpdates.js.map +1 -1
- package/lib/epoch/processPtcWindow.d.ts.map +1 -1
- package/lib/epoch/processPtcWindow.js +12 -9
- package/lib/epoch/processPtcWindow.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/lightClient/spec/index.d.ts +1 -1
- package/lib/lightClient/spec/index.d.ts.map +1 -1
- package/lib/lightClient/spec/index.js +1 -1
- package/lib/lightClient/spec/index.js.map +1 -1
- package/lib/lightClient/spec/utils.d.ts +13 -3
- package/lib/lightClient/spec/utils.d.ts.map +1 -1
- package/lib/lightClient/spec/utils.js +108 -34
- package/lib/lightClient/spec/utils.js.map +1 -1
- package/lib/metrics.d.ts +7 -1
- package/lib/metrics.d.ts.map +1 -1
- package/lib/metrics.js +12 -1
- package/lib/metrics.js.map +1 -1
- package/lib/rewards/blockRewards.d.ts.map +1 -1
- package/lib/rewards/blockRewards.js +1 -3
- package/lib/rewards/blockRewards.js.map +1 -1
- package/lib/signatureSets/index.d.ts.map +1 -1
- package/lib/signatureSets/index.js +8 -0
- package/lib/signatureSets/index.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +1 -1
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateView/beaconStateView.d.ts +8 -2
- package/lib/stateView/beaconStateView.d.ts.map +1 -1
- package/lib/stateView/beaconStateView.js +18 -1
- package/lib/stateView/beaconStateView.js.map +1 -1
- package/lib/stateView/computeNewStateRoot.d.ts +6 -0
- package/lib/stateView/computeNewStateRoot.d.ts.map +1 -0
- package/lib/stateView/computeNewStateRoot.js +28 -0
- package/lib/stateView/computeNewStateRoot.js.map +1 -0
- package/lib/stateView/interface.d.ts +20 -3
- package/lib/stateView/interface.d.ts.map +1 -1
- package/lib/stateView/interface.js.map +1 -1
- package/lib/stateView/nativeBeaconStateView.d.ts +6 -2
- package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -1
- package/lib/stateView/nativeBeaconStateView.js +15 -0
- package/lib/stateView/nativeBeaconStateView.js.map +1 -1
- package/lib/util/gloas.d.ts.map +1 -1
- package/lib/util/gloas.js +1 -1
- package/lib/util/gloas.js.map +1 -1
- package/lib/util/seed.d.ts +1 -1
- package/lib/util/seed.d.ts.map +1 -1
- package/lib/util/seed.js +14 -11
- package/lib/util/seed.js.map +1 -1
- package/package.json +8 -8
- package/src/block/index.ts +53 -17
- package/src/block/processExecutionPayloadBid.ts +6 -5
- package/src/block/processOperations.ts +63 -45
- package/src/block/processParentExecutionPayload.ts +2 -27
- package/src/block/processPayloadAttestation.ts +3 -2
- package/src/block/processWithdrawals.ts +12 -2
- package/src/block/types.ts +32 -0
- package/src/epoch/processInactivityUpdates.ts +8 -1
- package/src/epoch/processPtcWindow.ts +15 -8
- package/src/index.ts +3 -1
- package/src/lightClient/spec/index.ts +2 -0
- package/src/lightClient/spec/utils.ts +142 -48
- package/src/metrics.ts +13 -2
- package/src/rewards/blockRewards.ts +1 -3
- package/src/signatureSets/index.ts +17 -3
- package/src/slot/upgradeStateToGloas.ts +1 -1
- package/src/stateView/beaconStateView.ts +32 -1
- package/src/stateView/computeNewStateRoot.ts +35 -0
- package/src/stateView/interface.ts +27 -1
- package/src/stateView/nativeBeaconStateView.ts +23 -1
- package/src/util/gloas.ts +1 -0
- package/src/util/seed.ts +25 -13
|
@@ -23,18 +23,18 @@ import {
|
|
|
23
23
|
} from "@lodestar/params";
|
|
24
24
|
import {
|
|
25
25
|
BeaconBlockHeader,
|
|
26
|
+
LightClientBootstrap,
|
|
26
27
|
LightClientFinalityUpdate,
|
|
27
28
|
LightClientHeader,
|
|
28
29
|
LightClientOptimisticUpdate,
|
|
29
30
|
LightClientUpdate,
|
|
30
|
-
Slot,
|
|
31
31
|
SyncCommittee,
|
|
32
32
|
isElectraLightClientUpdate,
|
|
33
33
|
ssz,
|
|
34
34
|
} from "@lodestar/types";
|
|
35
|
-
import {byteArrayEquals, verifyMerkleBranch} from "@lodestar/utils";
|
|
36
|
-
import {computeEpochAtSlot
|
|
37
|
-
import type {
|
|
35
|
+
import {byteArrayEquals, hash, verifyMerkleBranch} from "@lodestar/utils";
|
|
36
|
+
import {computeEpochAtSlot} from "../../util/epoch.js";
|
|
37
|
+
import type {ILightClientStore, SyncCommitteeFast} from "./store.js";
|
|
38
38
|
|
|
39
39
|
export const GENESIS_SLOT = 0;
|
|
40
40
|
export const ZERO_HASH = new Uint8Array(32);
|
|
@@ -201,6 +201,76 @@ function getGindexIndex(gindex: number): number {
|
|
|
201
201
|
return gindex - 2 ** getGindexDepth(gindex);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Returns the execution root represented by a light-client header.
|
|
206
|
+
*
|
|
207
|
+
* Gloas headers removed `execution` so Pre-Gloas headers upgraded to the Gloas format
|
|
208
|
+
* require reconstruction of the original execution payload header root.
|
|
209
|
+
*
|
|
210
|
+
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.14/specs/gloas/light-client/sync-protocol.md#modified-get_lc_execution_root
|
|
211
|
+
*/
|
|
212
|
+
export function getLightClientExecutionRoot(config: ChainForkConfig, header: LightClientHeader): Uint8Array {
|
|
213
|
+
const epoch = computeEpochAtSlot(header.beacon.slot);
|
|
214
|
+
|
|
215
|
+
if (!isGloasLightClientHeader(header)) {
|
|
216
|
+
if (epoch < config.CAPELLA_FORK_EPOCH) {
|
|
217
|
+
return ZERO_HASH;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return config
|
|
221
|
+
.getPostBellatrixForkTypes(header.beacon.slot)
|
|
222
|
+
.ExecutionPayloadHeader.hashTreeRoot((header as LightClientHeader<ForkName.capella>).execution);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (epoch >= config.GLOAS_FORK_EPOCH) {
|
|
226
|
+
return header.executionBlockHash;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Reconstruct the pre-Gloas execution payload root for a header upgraded to the Gloas format
|
|
230
|
+
// because the structure is different, `execution` is removed
|
|
231
|
+
const executionPayloadDepth = getGindexDepth(BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX);
|
|
232
|
+
const innerBranch = header.executionBranch.slice(0, -executionPayloadDepth);
|
|
233
|
+
|
|
234
|
+
if (epoch >= config.DENEB_FORK_EPOCH) {
|
|
235
|
+
if (header.beacon.slot === GENESIS_SLOT) {
|
|
236
|
+
return ssz.deneb.ExecutionPayloadHeader.hashTreeRoot(ssz.deneb.ExecutionPayloadHeader.defaultValue());
|
|
237
|
+
}
|
|
238
|
+
return computeBranchRoot(
|
|
239
|
+
header.executionBlockHash,
|
|
240
|
+
innerBranch,
|
|
241
|
+
ssz.deneb.ExecutionPayloadHeader.getPathInfo(["blockHash"]).gindex
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (epoch >= config.CAPELLA_FORK_EPOCH) {
|
|
246
|
+
if (header.beacon.slot === GENESIS_SLOT) {
|
|
247
|
+
return ssz.capella.ExecutionPayloadHeader.hashTreeRoot(ssz.capella.ExecutionPayloadHeader.defaultValue());
|
|
248
|
+
}
|
|
249
|
+
return computeBranchRoot(
|
|
250
|
+
header.executionBlockHash,
|
|
251
|
+
innerBranch,
|
|
252
|
+
ssz.capella.ExecutionPayloadHeader.getPathInfo(["blockHash"]).gindex
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return ZERO_HASH;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function upgradeLightClientBootstrap(
|
|
260
|
+
config: ChainForkConfig,
|
|
261
|
+
targetFork: ForkName,
|
|
262
|
+
bootstrap: LightClientBootstrap
|
|
263
|
+
): LightClientBootstrap {
|
|
264
|
+
return {
|
|
265
|
+
...bootstrap,
|
|
266
|
+
header: upgradeLightClientHeader(config, targetFork, bootstrap.header),
|
|
267
|
+
currentSyncCommitteeBranch: normalizeMerkleBranch(
|
|
268
|
+
bootstrap.currentSyncCommitteeBranch,
|
|
269
|
+
currentSyncCommitteeGindexAtFork(targetFork)
|
|
270
|
+
),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
204
274
|
export function upgradeLightClientHeader(
|
|
205
275
|
config: ChainForkConfig,
|
|
206
276
|
targetFork: ForkName,
|
|
@@ -211,9 +281,7 @@ export function upgradeLightClientHeader(
|
|
|
211
281
|
throw Error(`Invalid upgrade request from headerFork=${headerFork} to targetFork=${targetFork}`);
|
|
212
282
|
}
|
|
213
283
|
|
|
214
|
-
|
|
215
|
-
// not required as of now
|
|
216
|
-
let upgradedHeader = header;
|
|
284
|
+
let upgradedHeader = {...header} as LightClientHeader;
|
|
217
285
|
const startUpgradeFromFork = Object.values(ForkName)[ForkSeq[headerFork] + 1];
|
|
218
286
|
|
|
219
287
|
switch (startUpgradeFromFork) {
|
|
@@ -231,23 +299,30 @@ export function upgradeLightClientHeader(
|
|
|
231
299
|
|
|
232
300
|
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
233
301
|
case ForkName.capella:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
ssz.capella.LightClientHeader.fields.executionBranch.defaultValue()
|
|
302
|
+
upgradedHeader = {
|
|
303
|
+
...upgradedHeader,
|
|
304
|
+
execution: ssz.capella.LightClientHeader.fields.execution.defaultValue(),
|
|
305
|
+
executionBranch: ssz.capella.LightClientHeader.fields.executionBranch.defaultValue(),
|
|
306
|
+
} as LightClientHeader<ForkName.capella>;
|
|
238
307
|
|
|
239
308
|
// Break if no further upgradation is required else fall through
|
|
240
309
|
if (ForkSeq[targetFork] <= ForkSeq.capella) break;
|
|
241
310
|
|
|
242
311
|
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
243
|
-
case ForkName.deneb:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
312
|
+
case ForkName.deneb: {
|
|
313
|
+
const capellaHeader = upgradedHeader as LightClientHeader<ForkName.capella>;
|
|
314
|
+
upgradedHeader = {
|
|
315
|
+
...capellaHeader,
|
|
316
|
+
execution: {
|
|
317
|
+
...capellaHeader.execution,
|
|
318
|
+
blobGasUsed: ssz.deneb.LightClientHeader.fields.execution.fields.blobGasUsed.defaultValue(),
|
|
319
|
+
excessBlobGas: ssz.deneb.LightClientHeader.fields.execution.fields.excessBlobGas.defaultValue(),
|
|
320
|
+
},
|
|
321
|
+
} as LightClientHeader<ForkName.deneb>;
|
|
248
322
|
|
|
249
323
|
// Break if no further upgradation is required else fall through
|
|
250
324
|
if (ForkSeq[targetFork] <= ForkSeq.deneb) break;
|
|
325
|
+
}
|
|
251
326
|
|
|
252
327
|
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
253
328
|
case ForkName.electra:
|
|
@@ -361,15 +436,16 @@ export function upgradeLightClientUpdate(
|
|
|
361
436
|
targetFork: ForkName,
|
|
362
437
|
update: LightClientUpdate
|
|
363
438
|
): LightClientUpdate {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
update.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
439
|
+
return {
|
|
440
|
+
...update,
|
|
441
|
+
attestedHeader: upgradeLightClientHeader(config, targetFork, update.attestedHeader),
|
|
442
|
+
finalizedHeader: upgradeLightClientHeader(config, targetFork, update.finalizedHeader),
|
|
443
|
+
nextSyncCommitteeBranch: normalizeMerkleBranch(
|
|
444
|
+
update.nextSyncCommitteeBranch,
|
|
445
|
+
nextSyncCommitteeGindexAtFork(targetFork)
|
|
446
|
+
),
|
|
447
|
+
finalityBranch: normalizeMerkleBranch(update.finalityBranch, finalizedRootGindexAtFork(targetFork)),
|
|
448
|
+
};
|
|
373
449
|
}
|
|
374
450
|
|
|
375
451
|
export function upgradeLightClientFinalityUpdate(
|
|
@@ -377,14 +453,12 @@ export function upgradeLightClientFinalityUpdate(
|
|
|
377
453
|
targetFork: ForkName,
|
|
378
454
|
finalityUpdate: LightClientFinalityUpdate
|
|
379
455
|
): LightClientFinalityUpdate {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
finalityUpdate.
|
|
384
|
-
finalizedRootGindexAtFork(targetFork)
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
return finalityUpdate;
|
|
456
|
+
return {
|
|
457
|
+
...finalityUpdate,
|
|
458
|
+
attestedHeader: upgradeLightClientHeader(config, targetFork, finalityUpdate.attestedHeader),
|
|
459
|
+
finalizedHeader: upgradeLightClientHeader(config, targetFork, finalityUpdate.finalizedHeader),
|
|
460
|
+
finalityBranch: normalizeMerkleBranch(finalityUpdate.finalityBranch, finalizedRootGindexAtFork(targetFork)),
|
|
461
|
+
};
|
|
388
462
|
}
|
|
389
463
|
|
|
390
464
|
export function upgradeLightClientOptimisticUpdate(
|
|
@@ -392,9 +466,10 @@ export function upgradeLightClientOptimisticUpdate(
|
|
|
392
466
|
targetFork: ForkName,
|
|
393
467
|
optimisticUpdate: LightClientOptimisticUpdate
|
|
394
468
|
): LightClientOptimisticUpdate {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
469
|
+
return {
|
|
470
|
+
...optimisticUpdate,
|
|
471
|
+
attestedHeader: upgradeLightClientHeader(config, targetFork, optimisticUpdate.attestedHeader),
|
|
472
|
+
};
|
|
398
473
|
}
|
|
399
474
|
|
|
400
475
|
/**
|
|
@@ -405,25 +480,44 @@ export function upgradeLightClientOptimisticUpdate(
|
|
|
405
480
|
export function upgradeLightClientStore(
|
|
406
481
|
config: ChainForkConfig,
|
|
407
482
|
targetFork: ForkName,
|
|
408
|
-
store:
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
update: upgradeLightClientUpdate(config, targetFork, bestValidUpdate.update),
|
|
417
|
-
summary: bestValidUpdate.summary,
|
|
483
|
+
store: ILightClientStore
|
|
484
|
+
): ILightClientStore {
|
|
485
|
+
const bestValidUpdates = new Map();
|
|
486
|
+
|
|
487
|
+
for (const [period, entry] of store.bestValidUpdates) {
|
|
488
|
+
bestValidUpdates.set(period, {
|
|
489
|
+
...entry,
|
|
490
|
+
update: upgradeLightClientUpdate(config, targetFork, entry.update),
|
|
418
491
|
});
|
|
419
492
|
}
|
|
493
|
+
const finalizedHeader = upgradeLightClientHeader(config, targetFork, store.finalizedHeader);
|
|
494
|
+
const optimisticHeader = upgradeLightClientHeader(config, targetFork, store.optimisticHeader);
|
|
420
495
|
|
|
421
|
-
|
|
422
|
-
|
|
496
|
+
for (const [period, bestValidUpdate] of bestValidUpdates) {
|
|
497
|
+
store.bestValidUpdates.set(period, bestValidUpdate);
|
|
498
|
+
}
|
|
499
|
+
store.finalizedHeader = finalizedHeader;
|
|
500
|
+
store.optimisticHeader = optimisticHeader;
|
|
423
501
|
|
|
424
502
|
return store;
|
|
425
503
|
}
|
|
426
504
|
|
|
505
|
+
function computeBranchRoot(leaf: Uint8Array, branch: Uint8Array[], gindex: bigint): Uint8Array {
|
|
506
|
+
const depth = getGindexDepth(Number(gindex));
|
|
507
|
+
if (branch.length < depth) {
|
|
508
|
+
throw Error(`Invalid Merkle branch length ${branch.length}, expected at least ${depth}`);
|
|
509
|
+
}
|
|
510
|
+
const proof = branch.slice(-depth);
|
|
511
|
+
const index = getGindexIndex(Number(gindex));
|
|
512
|
+
let root = leaf;
|
|
513
|
+
|
|
514
|
+
for (let i = 0; i < depth; i++) {
|
|
515
|
+
root = Math.floor(index / 2 ** i) % 2 === 1 ? hash(proof[i], root) : hash(root, proof[i]);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return root;
|
|
519
|
+
}
|
|
520
|
+
|
|
427
521
|
function isGloasLightClientHeader(header: LightClientHeader): header is LightClientHeader<ForkName.gloas> {
|
|
428
522
|
return (header as LightClientHeader<ForkName.gloas>).executionBlockHash !== undefined;
|
|
429
523
|
}
|
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",
|
|
@@ -42,9 +42,7 @@ export async function computeBlockRewards(
|
|
|
42
42
|
let syncAggregateReward = cachedSyncAggregateReward;
|
|
43
43
|
|
|
44
44
|
if (blockAttestationReward === 0) {
|
|
45
|
-
const parentSlot = isForkPostGloas(fork)
|
|
46
|
-
? (preState as CachedBeaconStateGloas).latestExecutionPayloadBid.slot
|
|
47
|
-
: null;
|
|
45
|
+
const parentSlot = isForkPostGloas(fork) ? (preState as CachedBeaconStateGloas).latestBlockHeader.slot : null;
|
|
48
46
|
blockAttestationReward =
|
|
49
47
|
fork === ForkName.phase0
|
|
50
48
|
? computeBlockAttestationRewardPhase0(block as phase0.BeaconBlock, preState as CachedBeaconStatePhase0)
|
|
@@ -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
|
}
|
|
@@ -110,7 +110,7 @@ export function upgradeStateToGloas(
|
|
|
110
110
|
ssz.gloas.PendingConsolidations
|
|
111
111
|
);
|
|
112
112
|
stateGloasView.proposerLookahead = stateGloasCloned.proposerLookahead;
|
|
113
|
-
stateGloasView.ptcWindow = ssz.gloas.
|
|
113
|
+
stateGloasView.ptcWindow = ssz.gloas.PayloadTimelinessCommitteeWindow.toViewDU(initializePtcWindow(stateFulu));
|
|
114
114
|
|
|
115
115
|
for (let i = 0; i < SLOTS_PER_HISTORICAL_ROOT; i++) {
|
|
116
116
|
stateGloasView.executionPayloadAvailability.set(i, true);
|
|
@@ -70,7 +70,15 @@ import {loadState} from "../util/loadState/loadState.js";
|
|
|
70
70
|
import {PreVerifyBuilderDepositsResult, preVerifyBuilderDepositsPreGloas} from "../util/preVerifyBuilderDeposits.js";
|
|
71
71
|
import {getRandaoMix} from "../util/seed.js";
|
|
72
72
|
import {getLatestWeakSubjectivityCheckpointEpoch} from "../util/weakSubjectivity.js";
|
|
73
|
-
import {
|
|
73
|
+
import {computeNewStateRootStateTransitionOpts, getComputeNewStateRootResult} from "./computeNewStateRoot.js";
|
|
74
|
+
import {
|
|
75
|
+
ComputeNewStateRootInput,
|
|
76
|
+
ComputeNewStateRootResult,
|
|
77
|
+
IBeaconStateView,
|
|
78
|
+
IBeaconStateViewGloas,
|
|
79
|
+
IBeaconStateViewLatestFork,
|
|
80
|
+
isStatePostGloas,
|
|
81
|
+
} from "./interface.js";
|
|
74
82
|
|
|
75
83
|
export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
76
84
|
private readonly config: BeaconConfig;
|
|
@@ -108,6 +116,10 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
108
116
|
return this.config.getForkName(this.cachedState.slot);
|
|
109
117
|
}
|
|
110
118
|
|
|
119
|
+
get forkSeq(): ForkSeq {
|
|
120
|
+
return this.config.getForkSeq(this.cachedState.slot);
|
|
121
|
+
}
|
|
122
|
+
|
|
111
123
|
get slot(): number {
|
|
112
124
|
return this.cachedState.slot;
|
|
113
125
|
}
|
|
@@ -444,6 +456,18 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
444
456
|
}
|
|
445
457
|
throw new Error(`PTC committees are not available for epoch=${epoch}`);
|
|
446
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
|
+
|
|
447
471
|
/**
|
|
448
472
|
* Return all positions of the validator in the PTC committee for the given slot.
|
|
449
473
|
*
|
|
@@ -823,6 +847,13 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
823
847
|
|
|
824
848
|
// State transition
|
|
825
849
|
|
|
850
|
+
computeNewStateRoot({block}: ComputeNewStateRootInput, modules: StateTransitionModules): ComputeNewStateRootResult {
|
|
851
|
+
const postState = new BeaconStateView(
|
|
852
|
+
stateTransition(this.cachedState, block, computeNewStateRootStateTransitionOpts, modules)
|
|
853
|
+
);
|
|
854
|
+
return getComputeNewStateRootResult(postState, modules);
|
|
855
|
+
}
|
|
856
|
+
|
|
826
857
|
stateTransition(
|
|
827
858
|
signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock,
|
|
828
859
|
options: StateTransitionOpts,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {DataAvailabilityStatus, ExecutionPayloadStatus} from "../block/externalData.js";
|
|
2
|
+
import {StateHashTreeRootSource, StateTransitionModules, StateTransitionOpts} from "../stateTransition.js";
|
|
3
|
+
import {ComputeNewStateRootResult, IBeaconStateView} from "./interface.js";
|
|
4
|
+
|
|
5
|
+
/** State transition options for computing the state root of a locally produced block. */
|
|
6
|
+
export const computeNewStateRootStateTransitionOpts: StateTransitionOpts = {
|
|
7
|
+
// ExecutionPayloadStatus.valid: Assume payload valid, it has been produced by a trusted EL
|
|
8
|
+
executionPayloadStatus: ExecutionPayloadStatus.valid,
|
|
9
|
+
// DataAvailabilityStatus.available: Assume the blobs to be available, have just been produced by trusted EL
|
|
10
|
+
dataAvailabilityStatus: DataAvailabilityStatus.Available,
|
|
11
|
+
// verifyStateRoot: false | the root in the block is zero-ed, it's being computed here
|
|
12
|
+
verifyStateRoot: false,
|
|
13
|
+
// verifyProposer: false | as the block signature is zero-ed
|
|
14
|
+
verifyProposer: false,
|
|
15
|
+
// verifySignatures: false | since the data to assemble the block is trusted
|
|
16
|
+
verifySignatures: false,
|
|
17
|
+
// Preserve cache in source state, since the resulting state is not added to the state cache
|
|
18
|
+
dontTransferCache: true,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function getComputeNewStateRootResult(
|
|
22
|
+
postState: IBeaconStateView,
|
|
23
|
+
{metrics}: StateTransitionModules
|
|
24
|
+
): ComputeNewStateRootResult {
|
|
25
|
+
const {attestations, syncAggregate, slashing} = postState.proposerRewards;
|
|
26
|
+
const proposerReward = BigInt(attestations + syncAggregate + slashing);
|
|
27
|
+
|
|
28
|
+
const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
|
|
29
|
+
source: StateHashTreeRootSource.computeNewStateRoot,
|
|
30
|
+
});
|
|
31
|
+
const newStateRoot = postState.hashTreeRoot();
|
|
32
|
+
hashTreeRootTimer?.();
|
|
33
|
+
|
|
34
|
+
return {newStateRoot, proposerReward, postState};
|
|
35
|
+
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ForkPostFulu,
|
|
11
11
|
ForkPostGloas,
|
|
12
12
|
ForkPostHeze,
|
|
13
|
+
ForkSeq,
|
|
13
14
|
isForkPostAltair,
|
|
14
15
|
isForkPostBellatrix,
|
|
15
16
|
isForkPostCapella,
|
|
@@ -29,6 +30,7 @@ import {
|
|
|
29
30
|
Epoch,
|
|
30
31
|
ExecutionPayloadBid,
|
|
31
32
|
ExecutionPayloadHeader,
|
|
33
|
+
Gwei,
|
|
32
34
|
Root,
|
|
33
35
|
RootHex,
|
|
34
36
|
SignedBeaconBlock,
|
|
@@ -54,6 +56,20 @@ import {StateTransitionModules, StateTransitionOpts} from "../stateTransition.js
|
|
|
54
56
|
import {EpochShuffling} from "../util/epochShuffling.js";
|
|
55
57
|
import {PreVerifyBuilderDepositsResult} from "../util/preVerifyBuilderDeposits.js";
|
|
56
58
|
|
|
59
|
+
/** Inputs for computing the state root of a locally produced block. */
|
|
60
|
+
export type ComputeNewStateRootInput = {
|
|
61
|
+
block: SignedBeaconBlock | SignedBlindedBeaconBlock;
|
|
62
|
+
/** Pre-serialized block bytes for native implementations. */
|
|
63
|
+
ssz?: Uint8Array;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** State root computation result. Includes data derived from the post-state. */
|
|
67
|
+
export type ComputeNewStateRootResult = {
|
|
68
|
+
newStateRoot: Root;
|
|
69
|
+
proposerReward: Gwei;
|
|
70
|
+
postState: IBeaconStateView;
|
|
71
|
+
};
|
|
72
|
+
|
|
57
73
|
/**
|
|
58
74
|
* A read-only view of the BeaconState.
|
|
59
75
|
*/
|
|
@@ -62,6 +78,7 @@ export interface IBeaconStateView {
|
|
|
62
78
|
|
|
63
79
|
// phase0
|
|
64
80
|
forkName: ForkName;
|
|
81
|
+
forkSeq: ForkSeq;
|
|
65
82
|
slot: Slot;
|
|
66
83
|
fork: Fork;
|
|
67
84
|
epoch: Epoch;
|
|
@@ -161,6 +178,7 @@ export interface IBeaconStateView {
|
|
|
161
178
|
hashTreeRoot(): Uint8Array;
|
|
162
179
|
|
|
163
180
|
// State transition
|
|
181
|
+
computeNewStateRoot(input: ComputeNewStateRootInput, modules: StateTransitionModules): ComputeNewStateRootResult;
|
|
164
182
|
stateTransition(
|
|
165
183
|
signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock,
|
|
166
184
|
options: StateTransitionOpts,
|
|
@@ -267,6 +285,7 @@ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
|
|
|
267
285
|
getBuildersLength(): number;
|
|
268
286
|
canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
|
|
269
287
|
getEpochPTCs(epoch: Epoch): Uint32Array[];
|
|
288
|
+
getPayloadTimelinessCommittee(slot: Slot): Uint32Array;
|
|
270
289
|
getIndicesInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number[];
|
|
271
290
|
/**
|
|
272
291
|
* Clone the state and apply parent execution payload effects.
|
|
@@ -308,13 +327,20 @@ export type IBeaconStateViewLatestFork = Omit<
|
|
|
308
327
|
* `loadOtherState`, `withParentPayloadApplied`) return `IBeaconStateViewNative`
|
|
309
328
|
* so callers can re-wrap without an `as unknown` cast. Param lists are reused
|
|
310
329
|
* via `Parameters<...>` to avoid duplicating signatures.
|
|
330
|
+
* - `computeNewStateRoot` is implemented by the wrapper because its inputs differ
|
|
331
|
+
* between the TypeScript and Zig implementations.
|
|
311
332
|
*
|
|
312
333
|
* The TS-side `BeaconStateView` also structurally satisfies this contract since
|
|
313
334
|
* `BitArray` exposes `uint8Array` and `bitLen`.
|
|
314
335
|
*/
|
|
315
336
|
export type IBeaconStateViewNative = Omit<
|
|
316
337
|
IBeaconStateViewLatestFork,
|
|
317
|
-
|
|
338
|
+
| "computeNewStateRoot"
|
|
339
|
+
| "executionPayloadAvailability"
|
|
340
|
+
| "loadOtherState"
|
|
341
|
+
| "stateTransition"
|
|
342
|
+
| "processSlots"
|
|
343
|
+
| "withParentPayloadApplied"
|
|
318
344
|
> & {
|
|
319
345
|
executionPayloadAvailability: {uint8Array: Uint8Array; bitLen: number};
|
|
320
346
|
loadOtherState(...args: Parameters<IBeaconStateViewLatestFork["loadOtherState"]>): IBeaconStateViewNative;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {CompactMultiProof} from "@chainsafe/persistent-merkle-tree";
|
|
2
2
|
import {BitArray, ByteViews} from "@chainsafe/ssz";
|
|
3
|
-
import {ForkName} from "@lodestar/params";
|
|
3
|
+
import {ForkName, ForkSeq} from "@lodestar/params";
|
|
4
4
|
import {
|
|
5
5
|
BeaconBlock,
|
|
6
6
|
BeaconState,
|
|
@@ -36,7 +36,10 @@ import {SyncCommitteeWitness} from "../lightClient/types.js";
|
|
|
36
36
|
import {StateTransitionModules, StateTransitionOpts} from "../stateTransition.js";
|
|
37
37
|
import {EpochShuffling} from "../util/epochShuffling.js";
|
|
38
38
|
import {PreVerifyBuilderDepositsResult} from "../util/preVerifyBuilderDeposits.js";
|
|
39
|
+
import {computeNewStateRootStateTransitionOpts, getComputeNewStateRootResult} from "./computeNewStateRoot.js";
|
|
39
40
|
import {
|
|
41
|
+
ComputeNewStateRootInput,
|
|
42
|
+
ComputeNewStateRootResult,
|
|
40
43
|
IBeaconStateView,
|
|
41
44
|
IBeaconStateViewGloas,
|
|
42
45
|
IBeaconStateViewLatestFork,
|
|
@@ -63,6 +66,7 @@ import {
|
|
|
63
66
|
export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
|
|
64
67
|
// phase0
|
|
65
68
|
private _forkName: ForkName | null = null;
|
|
69
|
+
private _forkSeq: ForkSeq | null = null;
|
|
66
70
|
private _slot: Slot | null = null;
|
|
67
71
|
private _fork: Fork | null = null;
|
|
68
72
|
private _epoch: Epoch | null = null;
|
|
@@ -191,6 +195,13 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
191
195
|
return this._forkName;
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
get forkSeq(): ForkSeq {
|
|
199
|
+
if (this._forkSeq === null) {
|
|
200
|
+
this._forkSeq = this.binding.forkSeq;
|
|
201
|
+
}
|
|
202
|
+
return this._forkSeq;
|
|
203
|
+
}
|
|
204
|
+
|
|
194
205
|
get slot(): Slot {
|
|
195
206
|
if (this._slot === null) {
|
|
196
207
|
this._slot = this.binding.slot;
|
|
@@ -637,6 +648,13 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
637
648
|
|
|
638
649
|
// State transition
|
|
639
650
|
|
|
651
|
+
computeNewStateRoot(input: ComputeNewStateRootInput, modules: StateTransitionModules): ComputeNewStateRootResult {
|
|
652
|
+
const postState = new NativeBeaconStateView(
|
|
653
|
+
this.binding.stateTransition(input.block, computeNewStateRootStateTransitionOpts, modules)
|
|
654
|
+
);
|
|
655
|
+
return getComputeNewStateRootResult(postState, modules);
|
|
656
|
+
}
|
|
657
|
+
|
|
640
658
|
stateTransition(
|
|
641
659
|
signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock,
|
|
642
660
|
options: StateTransitionOpts,
|
|
@@ -908,6 +926,10 @@ export class NativeBeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
908
926
|
return cached;
|
|
909
927
|
}
|
|
910
928
|
|
|
929
|
+
getPayloadTimelinessCommittee(slot: Slot): Uint32Array {
|
|
930
|
+
return this.binding.getPayloadTimelinessCommittee(slot);
|
|
931
|
+
}
|
|
932
|
+
|
|
911
933
|
getIndicesInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number[] {
|
|
912
934
|
return this.binding.getIndicesInPayloadTimelinessCommittee(validatorIndex, slot);
|
|
913
935
|
}
|
package/src/util/gloas.ts
CHANGED
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
|
-
|
|
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
|
}
|