@lodestar/beacon-node 1.39.0 → 1.40.0-dev.3715a82f3f
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/api/impl/beacon/state/utils.d.ts +2 -7
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +0 -12
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +3 -0
- package/lib/api/impl/config/constants.d.ts.map +1 -1
- package/lib/api/impl/config/constants.js +5 -1
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/proof/index.d.ts.map +1 -1
- package/lib/api/impl/proof/index.js +1 -2
- package/lib/api/impl/proof/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +1 -3
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -3
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +19 -14
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +12 -3
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +8 -1
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +1 -0
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/chain.d.ts +3 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +16 -6
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/initState.d.ts +1 -1
- package/lib/chain/initState.d.ts.map +1 -1
- package/lib/chain/initState.js +5 -3
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +0 -2
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +5 -9
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +12 -141
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/opPool.js +5 -8
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +4 -7
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -5
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/queued.d.ts +4 -7
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +15 -25
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +13 -17
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +16 -11
- package/lib/chain/shufflingCache.d.ts.map +1 -1
- package/lib/chain/shufflingCache.js +47 -41
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/chain/stateCache/blockStateCacheImpl.d.ts +1 -2
- package/lib/chain/stateCache/blockStateCacheImpl.d.ts.map +1 -1
- package/lib/chain/stateCache/blockStateCacheImpl.js +2 -2
- package/lib/chain/stateCache/blockStateCacheImpl.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +1 -2
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +4 -4
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/inMemoryCheckpointsCache.d.ts +4 -5
- package/lib/chain/stateCache/inMemoryCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/inMemoryCheckpointsCache.js +9 -10
- package/lib/chain/stateCache/inMemoryCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +5 -6
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +17 -17
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +5 -6
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +2 -2
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -1
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +2 -0
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/blsToExecutionChange.d.ts.map +1 -1
- package/lib/chain/validation/blsToExecutionChange.js +9 -2
- package/lib/chain/validation/blsToExecutionChange.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +2 -1
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/db/repositories/checkpointState.d.ts +2 -6
- package/lib/db/repositories/checkpointState.d.ts.map +1 -1
- package/lib/db/repositories/checkpointState.js +3 -15
- package/lib/db/repositories/checkpointState.js.map +1 -1
- package/lib/db/repositories/stateArchive.d.ts +9 -9
- package/lib/db/repositories/stateArchive.d.ts.map +1 -1
- package/lib/db/repositories/stateArchive.js +6 -21
- package/lib/db/repositories/stateArchive.js.map +1 -1
- package/lib/execution/engine/mock.d.ts +9 -6
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +34 -7
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -6
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +3 -17
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +17 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/state/utils.ts +2 -22
- package/src/api/impl/config/constants.ts +8 -0
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/proof/index.ts +1 -2
- package/src/api/impl/validator/index.ts +1 -3
- package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -3
- package/src/chain/archiveStore/utils/archiveBlocks.ts +21 -14
- package/src/chain/blocks/importBlock.ts +13 -3
- package/src/chain/blocks/verifyBlock.ts +9 -3
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +1 -0
- package/src/chain/chain.ts +20 -9
- package/src/chain/initState.ts +5 -3
- package/src/chain/interface.ts +2 -2
- package/src/chain/lightClient/proofs.ts +0 -2
- package/src/chain/opPools/aggregatedAttestationPool.ts +19 -191
- package/src/chain/opPools/opPool.ts +5 -7
- package/src/chain/prepareNextSlot.ts +2 -6
- package/src/chain/produceBlock/produceBlockBody.ts +6 -1
- package/src/chain/regen/interface.ts +1 -5
- package/src/chain/regen/queued.ts +15 -34
- package/src/chain/regen/regen.ts +12 -18
- package/src/chain/shufflingCache.ts +67 -50
- package/src/chain/stateCache/blockStateCacheImpl.ts +2 -3
- package/src/chain/stateCache/fifoBlockStateCache.ts +4 -5
- package/src/chain/stateCache/inMemoryCheckpointsCache.ts +9 -15
- package/src/chain/stateCache/persistentCheckpointsCache.ts +17 -25
- package/src/chain/stateCache/types.ts +5 -10
- package/src/chain/validation/attestation.ts +3 -3
- package/src/chain/validation/attesterSlashing.ts +8 -1
- package/src/chain/validation/block.ts +3 -0
- package/src/chain/validation/blsToExecutionChange.ts +9 -7
- package/src/chain/validation/proposerSlashing.ts +2 -1
- package/src/db/repositories/checkpointState.ts +3 -19
- package/src/db/repositories/stateArchive.ts +13 -27
- package/src/execution/engine/mock.ts +40 -13
- package/src/index.ts +1 -1
- package/src/metrics/metrics/lodestar.ts +3 -17
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +1 -1
- package/src/node/nodejs.ts +18 -3
- package/src/util/sszBytes.ts +1 -1
|
@@ -73,9 +73,12 @@ export async function archiveBlocks(
|
|
|
73
73
|
root: fromHex(block.blockRoot),
|
|
74
74
|
}));
|
|
75
75
|
|
|
76
|
+
const logCtx = {currentEpoch, finalizedEpoch: finalizedCheckpoint.epoch, finalizedRoot: finalizedCheckpoint.rootHex};
|
|
77
|
+
|
|
76
78
|
if (finalizedCanonicalBlockRoots.length > 0) {
|
|
77
79
|
await migrateBlocksFromHotToColdDb(db, finalizedCanonicalBlockRoots);
|
|
78
80
|
logger.verbose("Migrated blocks from hot DB to cold DB", {
|
|
81
|
+
...logCtx,
|
|
79
82
|
fromSlot: finalizedCanonicalBlockRoots[0].slot,
|
|
80
83
|
toSlot: finalizedCanonicalBlockRoots.at(-1)?.slot,
|
|
81
84
|
size: finalizedCanonicalBlockRoots.length,
|
|
@@ -88,7 +91,7 @@ export async function archiveBlocks(
|
|
|
88
91
|
finalizedCanonicalBlockRoots,
|
|
89
92
|
currentEpoch
|
|
90
93
|
);
|
|
91
|
-
logger.verbose("Migrated blobSidecars from hot DB to cold DB", {migratedEntries});
|
|
94
|
+
logger.verbose("Migrated blobSidecars from hot DB to cold DB", {...logCtx, migratedEntries});
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
if (finalizedPostFulu) {
|
|
@@ -99,7 +102,7 @@ export async function archiveBlocks(
|
|
|
99
102
|
finalizedCanonicalBlockRoots,
|
|
100
103
|
currentEpoch
|
|
101
104
|
);
|
|
102
|
-
logger.verbose("Migrated dataColumnSidecars from hot DB to cold DB", {migratedEntries});
|
|
105
|
+
logger.verbose("Migrated dataColumnSidecars from hot DB to cold DB", {...logCtx, migratedEntries});
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
|
|
@@ -114,14 +117,14 @@ export async function archiveBlocks(
|
|
|
114
117
|
nonCanonicalBlockRoots.map(async (root, index) => {
|
|
115
118
|
const block = finalizedNonCanonicalBlocks[index];
|
|
116
119
|
const blockBytes = await db.block.getBinary(root);
|
|
117
|
-
const
|
|
120
|
+
const blockLogCtx = {slot: block.slot, root: block.blockRoot};
|
|
118
121
|
if (blockBytes) {
|
|
119
122
|
await persistOrphanedBlock(block.slot, block.blockRoot, blockBytes, {
|
|
120
123
|
persistOrphanedBlocksDir: persistOrphanedBlocksDir ?? "orphaned_blocks",
|
|
121
124
|
});
|
|
122
|
-
logger.verbose("Persisted orphaned block", logCtx);
|
|
125
|
+
logger.verbose("Persisted orphaned block", {...logCtx, ...blockLogCtx});
|
|
123
126
|
} else {
|
|
124
|
-
logger.warn("Tried to persist orphaned block but no block found", logCtx);
|
|
127
|
+
logger.warn("Tried to persist orphaned block but no block found", {...logCtx, ...blockLogCtx});
|
|
125
128
|
}
|
|
126
129
|
})
|
|
127
130
|
);
|
|
@@ -129,17 +132,18 @@ export async function archiveBlocks(
|
|
|
129
132
|
|
|
130
133
|
await db.block.batchDelete(nonCanonicalBlockRoots);
|
|
131
134
|
logger.verbose("Deleted non canonical blocks from hot DB", {
|
|
135
|
+
...logCtx,
|
|
132
136
|
slots: finalizedNonCanonicalBlocks.map((summary) => summary.slot).join(","),
|
|
133
137
|
});
|
|
134
138
|
|
|
135
139
|
if (finalizedPostDeneb) {
|
|
136
140
|
await db.blobSidecars.batchDelete(nonCanonicalBlockRoots);
|
|
137
|
-
logger.verbose("Deleted non canonical blobSidecars from hot DB");
|
|
141
|
+
logger.verbose("Deleted non canonical blobSidecars from hot DB", logCtx);
|
|
138
142
|
}
|
|
139
143
|
|
|
140
144
|
if (finalizedPostFulu) {
|
|
141
145
|
await db.dataColumnSidecar.deleteMany(nonCanonicalBlockRoots);
|
|
142
|
-
logger.verbose("Deleted non canonical dataColumnSidecars from hot DB");
|
|
146
|
+
logger.verbose("Deleted non canonical dataColumnSidecars from hot DB", logCtx);
|
|
143
147
|
}
|
|
144
148
|
}
|
|
145
149
|
|
|
@@ -154,13 +158,13 @@ export async function archiveBlocks(
|
|
|
154
158
|
const slotsToDelete = await db.blobSidecarsArchive.keys({lt: computeStartSlotAtEpoch(blobSidecarsMinEpoch)});
|
|
155
159
|
if (slotsToDelete.length > 0) {
|
|
156
160
|
await db.blobSidecarsArchive.batchDelete(slotsToDelete);
|
|
157
|
-
logger.verbose(`blobSidecars prune: batchDelete range ${slotsToDelete[0]}..${slotsToDelete.at(-1)}
|
|
161
|
+
logger.verbose(`blobSidecars prune: batchDelete range ${slotsToDelete[0]}..${slotsToDelete.at(-1)}`, logCtx);
|
|
158
162
|
} else {
|
|
159
|
-
logger.verbose(`blobSidecars prune: no entries before epoch ${blobSidecarsMinEpoch}
|
|
163
|
+
logger.verbose(`blobSidecars prune: no entries before epoch ${blobSidecarsMinEpoch}`, logCtx);
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
} else {
|
|
163
|
-
logger.verbose("blobSidecars pruning skipped: archiveDataEpochs set to Infinity");
|
|
167
|
+
logger.verbose("blobSidecars pruning skipped: archiveDataEpochs set to Infinity", logCtx);
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
|
|
@@ -184,20 +188,22 @@ export async function archiveBlocks(
|
|
|
184
188
|
if (slotsToDelete.length > 0) {
|
|
185
189
|
await db.dataColumnSidecarArchive.deleteMany(slotsToDelete);
|
|
186
190
|
logger.verbose("dataColumnSidecars prune", {
|
|
191
|
+
...logCtx,
|
|
187
192
|
slotRange: prettyPrintIndices(slotsToDelete),
|
|
188
193
|
numOfSlots: slotsToDelete.length,
|
|
189
194
|
totalNumOfSidecars: prefixedKeys.length,
|
|
190
195
|
});
|
|
191
196
|
} else {
|
|
192
|
-
logger.verbose(`dataColumnSidecars prune: no entries before epoch ${dataColumnSidecarsMinEpoch}
|
|
197
|
+
logger.verbose(`dataColumnSidecars prune: no entries before epoch ${dataColumnSidecarsMinEpoch}`, logCtx);
|
|
193
198
|
}
|
|
194
199
|
} else {
|
|
195
200
|
logger.verbose(
|
|
196
|
-
`dataColumnSidecars pruning skipped: ${dataColumnSidecarsMinEpoch} is before fulu fork epoch ${config.FULU_FORK_EPOCH}
|
|
201
|
+
`dataColumnSidecars pruning skipped: ${dataColumnSidecarsMinEpoch} is before fulu fork epoch ${config.FULU_FORK_EPOCH}`,
|
|
202
|
+
logCtx
|
|
197
203
|
);
|
|
198
204
|
}
|
|
199
205
|
} else {
|
|
200
|
-
logger.verbose("dataColumnSidecars pruning skipped: archiveDataEpochs set to Infinity");
|
|
206
|
+
logger.verbose("dataColumnSidecars pruning skipped: archiveDataEpochs set to Infinity", logCtx);
|
|
201
207
|
}
|
|
202
208
|
}
|
|
203
209
|
|
|
@@ -213,8 +219,8 @@ export async function archiveBlocks(
|
|
|
213
219
|
}
|
|
214
220
|
|
|
215
221
|
logger.verbose("Archiving of finalized blocks complete", {
|
|
222
|
+
...logCtx,
|
|
216
223
|
totalArchived: finalizedCanonicalBlocks.length,
|
|
217
|
-
finalizedEpoch: finalizedCheckpoint.epoch,
|
|
218
224
|
});
|
|
219
225
|
}
|
|
220
226
|
|
|
@@ -340,6 +346,7 @@ async function migrateDataColumnSidecarsFromHotToColdDb(
|
|
|
340
346
|
const dataColumnSidecarBytes = await fromAsync(db.dataColumnSidecar.valuesStreamBinary(block.root));
|
|
341
347
|
// there could be 0 dataColumnSidecarBytes if block has no blob
|
|
342
348
|
logger.verbose("migrateDataColumnSidecarsFromHotToColdDb", {
|
|
349
|
+
currentEpoch,
|
|
343
350
|
slot: block.slot,
|
|
344
351
|
root: toRootHex(block.root),
|
|
345
352
|
numSidecars: dataColumnSidecarBytes.length,
|
|
@@ -418,13 +418,20 @@ export async function importBlock(
|
|
|
418
418
|
this.logger.verbose("After importBlock caching postState without SSZ cache", {slot: postState.slot});
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
+
// Cache shufflings when crossing an epoch boundary
|
|
422
|
+
const parentEpoch = computeEpochAtSlot(parentBlockSlot);
|
|
423
|
+
if (parentEpoch < blockEpoch) {
|
|
424
|
+
this.shufflingCache.processState(postState);
|
|
425
|
+
this.logger.verbose("Processed shuffling for next epoch", {parentEpoch, blockEpoch, slot: blockSlot});
|
|
426
|
+
}
|
|
427
|
+
|
|
421
428
|
if (blockSlot % SLOTS_PER_EPOCH === 0) {
|
|
422
429
|
// Cache state to preserve epoch transition work
|
|
423
430
|
const checkpointState = postState;
|
|
424
431
|
const cp = getCheckpointFromState(checkpointState);
|
|
425
432
|
this.regen.addCheckpointState(cp, checkpointState);
|
|
426
|
-
// consumers should not mutate
|
|
427
|
-
this.emitter.emit(ChainEvent.checkpoint, cp, checkpointState
|
|
433
|
+
// consumers should not mutate state ever
|
|
434
|
+
this.emitter.emit(ChainEvent.checkpoint, cp, checkpointState);
|
|
428
435
|
|
|
429
436
|
// Note: in-lined code from previos handler of ChainEvent.checkpoint
|
|
430
437
|
this.logger.verbose("Checkpoint processed", toCheckpointHex(cp));
|
|
@@ -584,7 +591,10 @@ export function addAttestationPostElectra(
|
|
|
584
591
|
true
|
|
585
592
|
);
|
|
586
593
|
} else {
|
|
587
|
-
const
|
|
594
|
+
const attSlot = attestation.data.slot;
|
|
595
|
+
const attEpoch = computeEpochAtSlot(attSlot);
|
|
596
|
+
const decisionRoot = epochCtx.getShufflingDecisionRoot(attEpoch);
|
|
597
|
+
const committees = this.shufflingCache.getBeaconCommittees(attEpoch, decisionRoot, attSlot, committeeIndices);
|
|
588
598
|
const aggregationBools = attestation.aggregationBits.toBoolArray();
|
|
589
599
|
let offset = 0;
|
|
590
600
|
for (let i = 0; i < committees.length; i++) {
|
|
@@ -75,6 +75,10 @@ export async function verifyBlocksInEpoch(
|
|
|
75
75
|
throw new BlockError(block0, {code: BlockErrorCode.PRESTATE_MISSING, error: e as Error});
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
+
// in forky condition, make sure to populate ShufflingCache with regened state
|
|
79
|
+
// otherwise it may fail to get indexed attestations from shuffling cache later
|
|
80
|
+
this.shufflingCache.processState(preState0);
|
|
81
|
+
|
|
78
82
|
if (!isStateValidatorsNodesPopulated(preState0)) {
|
|
79
83
|
this.logger.verbose("verifyBlocksInEpoch preState0 SSZ cache stats", {
|
|
80
84
|
slot: preState0.slot,
|
|
@@ -105,9 +109,11 @@ export async function verifyBlocksInEpoch(
|
|
|
105
109
|
// Store indexed attestations for each block to avoid recomputing them during import
|
|
106
110
|
const indexedAttestationsByBlock: IndexedAttestation[][] = [];
|
|
107
111
|
for (const [i, block] of blocks.entries()) {
|
|
108
|
-
indexedAttestationsByBlock[i] = block.message.body.attestations.map((attestation) =>
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
indexedAttestationsByBlock[i] = block.message.body.attestations.map((attestation) => {
|
|
113
|
+
const attEpoch = computeEpochAtSlot(attestation.data.slot);
|
|
114
|
+
const decisionRoot = preState0.epochCtx.getShufflingDecisionRoot(attEpoch);
|
|
115
|
+
return this.shufflingCache.getIndexedAttestation(attEpoch, decisionRoot, fork, attestation);
|
|
116
|
+
});
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
// batch all I/O operations to reduce overhead
|
|
@@ -59,6 +59,7 @@ export async function verifyBlocksStateTransitionOnly(
|
|
|
59
59
|
// if block is trusted don't verify proposer or op signature
|
|
60
60
|
verifyProposer: !useBlsBatchVerify && !validSignatures && !validProposerSignature,
|
|
61
61
|
verifySignatures: !useBlsBatchVerify && !validSignatures,
|
|
62
|
+
dontTransferCache: false,
|
|
62
63
|
},
|
|
63
64
|
{metrics, validatorMonitor}
|
|
64
65
|
);
|
package/src/chain/chain.ts
CHANGED
|
@@ -52,6 +52,7 @@ import {computeNodeIdFromPrivateKey} from "../network/subnets/interface.js";
|
|
|
52
52
|
import {BufferPool} from "../util/bufferPool.js";
|
|
53
53
|
import {Clock, ClockEvent, IClock} from "../util/clock.js";
|
|
54
54
|
import {CustodyConfig, getValidatorsCustodyRequirement} from "../util/dataColumns.js";
|
|
55
|
+
import {callInNextEventLoop} from "../util/eventLoop.js";
|
|
55
56
|
import {ensureDir, writeIfNotExist} from "../util/file.js";
|
|
56
57
|
import {isOptimisticBlock} from "../util/forkChoice.js";
|
|
57
58
|
import {SerializedCache} from "../util/serializedCache.js";
|
|
@@ -291,7 +292,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
291
292
|
});
|
|
292
293
|
|
|
293
294
|
this._earliestAvailableSlot = anchorState.slot;
|
|
294
|
-
|
|
295
|
+
|
|
296
|
+
this.shufflingCache = new ShufflingCache(metrics, logger, this.opts, [
|
|
295
297
|
{
|
|
296
298
|
shuffling: anchorState.epochCtx.previousShuffling,
|
|
297
299
|
decisionRoot: anchorState.epochCtx.previousDecisionRoot,
|
|
@@ -417,6 +419,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
417
419
|
clock.addListener(ClockEvent.epoch, this.onClockEpoch.bind(this));
|
|
418
420
|
emitter.addListener(ChainEvent.forkChoiceFinalized, this.onForkChoiceFinalized.bind(this));
|
|
419
421
|
emitter.addListener(ChainEvent.forkChoiceJustified, this.onForkChoiceJustified.bind(this));
|
|
422
|
+
emitter.addListener(ChainEvent.checkpoint, this.onCheckpoint.bind(this));
|
|
420
423
|
}
|
|
421
424
|
|
|
422
425
|
async init(): Promise<void> {
|
|
@@ -504,7 +507,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
504
507
|
async getStateBySlot(
|
|
505
508
|
slot: Slot,
|
|
506
509
|
opts?: StateGetOpts
|
|
507
|
-
): Promise<{state:
|
|
510
|
+
): Promise<{state: CachedBeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
508
511
|
const finalizedBlock = this.forkChoice.getFinalizedBlock();
|
|
509
512
|
|
|
510
513
|
if (slot < finalizedBlock.slot) {
|
|
@@ -559,7 +562,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
559
562
|
async getStateByStateRoot(
|
|
560
563
|
stateRoot: RootHex,
|
|
561
564
|
opts?: StateGetOpts
|
|
562
|
-
): Promise<{state:
|
|
565
|
+
): Promise<{state: CachedBeaconStateAllForks | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
563
566
|
if (opts?.allowRegen) {
|
|
564
567
|
const state = await this.regen.getState(stateRoot, RegenCaller.restApi);
|
|
565
568
|
const block = this.forkChoice.getBlock(state.latestBlockHeader.hashTreeRoot());
|
|
@@ -587,7 +590,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
587
590
|
};
|
|
588
591
|
}
|
|
589
592
|
|
|
590
|
-
|
|
593
|
+
// this is mostly useful for a node with `--chain.archiveStateEpochFrequency 1`
|
|
594
|
+
const data = await this.db.stateArchive.getBinaryByRoot(fromHex(stateRoot));
|
|
591
595
|
return data && {state: data, executionOptimistic: false, finalized: true};
|
|
592
596
|
}
|
|
593
597
|
|
|
@@ -980,8 +984,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
980
984
|
this.metrics?.gossipAttestation.useHeadBlockState.inc({caller: regenCaller});
|
|
981
985
|
state = await this.regen.getState(attHeadBlock.stateRoot, regenCaller);
|
|
982
986
|
}
|
|
983
|
-
|
|
984
|
-
|
|
987
|
+
// resolve the promise to unblock other calls of the same epoch and dependent root
|
|
988
|
+
this.shufflingCache.processState(state);
|
|
985
989
|
return state.epochCtx.getShufflingAtEpoch(attEpoch);
|
|
986
990
|
}
|
|
987
991
|
|
|
@@ -1165,6 +1169,13 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1165
1169
|
this.logger.verbose("Fork choice justified", {epoch: cp.epoch, root: cp.rootHex});
|
|
1166
1170
|
}
|
|
1167
1171
|
|
|
1172
|
+
private onCheckpoint(this: BeaconChain, _checkpoint: phase0.Checkpoint, state: CachedBeaconStateAllForks): void {
|
|
1173
|
+
// Defer to not block other checkpoint event handlers, which can cause lightclient update delays
|
|
1174
|
+
callInNextEventLoop(() => {
|
|
1175
|
+
this.shufflingCache.processState(state);
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1168
1179
|
private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
|
|
1169
1180
|
this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
|
|
1170
1181
|
this.seenBlockProposers.prune(computeStartSlotAtEpoch(cp.epoch));
|
|
@@ -1295,9 +1306,9 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1295
1306
|
|
|
1296
1307
|
preState = processSlots(preState, block.slot); // Dial preState's slot to block.slot
|
|
1297
1308
|
|
|
1298
|
-
const
|
|
1309
|
+
const proposerRewards = this.regen.getStateSync(toRootHex(block.stateRoot))?.proposerRewards ?? undefined;
|
|
1299
1310
|
|
|
1300
|
-
return computeBlockRewards(this.config, block, preState
|
|
1311
|
+
return computeBlockRewards(this.config, block, preState, proposerRewards);
|
|
1301
1312
|
}
|
|
1302
1313
|
|
|
1303
1314
|
async getAttestationsRewards(
|
|
@@ -1338,6 +1349,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1338
1349
|
|
|
1339
1350
|
preState = processSlots(preState, block.slot); // Dial preState's slot to block.slot
|
|
1340
1351
|
|
|
1341
|
-
return computeSyncCommitteeRewards(this.config, this.index2pubkey, block, preState
|
|
1352
|
+
return computeSyncCommitteeRewards(this.config, this.index2pubkey, block, preState, validatorIds);
|
|
1342
1353
|
}
|
|
1343
1354
|
}
|
package/src/chain/initState.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {Logger, toHex, toRootHex} from "@lodestar/utils";
|
|
|
6
6
|
import {GENESIS_SLOT} from "../constants/index.js";
|
|
7
7
|
import {IBeaconDb} from "../db/index.js";
|
|
8
8
|
import {Metrics} from "../metrics/index.js";
|
|
9
|
+
import {getStateTypeFromBytes} from "../util/multifork.js";
|
|
9
10
|
|
|
10
11
|
export async function persistAnchorState(
|
|
11
12
|
config: ChainForkConfig,
|
|
@@ -53,14 +54,15 @@ export function createGenesisBlock(config: ChainForkConfig, genesisState: Beacon
|
|
|
53
54
|
* Restore the latest beacon state from db
|
|
54
55
|
*/
|
|
55
56
|
export async function initStateFromDb(
|
|
56
|
-
|
|
57
|
+
config: ChainForkConfig,
|
|
57
58
|
db: IBeaconDb,
|
|
58
59
|
logger: Logger
|
|
59
60
|
): Promise<BeaconStateAllForks> {
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
61
|
+
const stateBytes = await db.stateArchive.lastBinary();
|
|
62
|
+
if (stateBytes == null) {
|
|
62
63
|
throw new Error("No state exists in database");
|
|
63
64
|
}
|
|
65
|
+
const state = getStateTypeFromBytes(config, stateBytes).deserializeToViewDU(stateBytes);
|
|
64
66
|
|
|
65
67
|
logger.info("Initializing beacon state from db", {
|
|
66
68
|
slot: state.slot,
|
package/src/chain/interface.ts
CHANGED
|
@@ -168,12 +168,12 @@ export interface IBeaconChain {
|
|
|
168
168
|
getStateBySlot(
|
|
169
169
|
slot: Slot,
|
|
170
170
|
opts?: StateGetOpts
|
|
171
|
-
): Promise<{state:
|
|
171
|
+
): Promise<{state: CachedBeaconStateAllForks; executionOptimistic: boolean; finalized: boolean} | null>;
|
|
172
172
|
/** Returns a local state by state root */
|
|
173
173
|
getStateByStateRoot(
|
|
174
174
|
stateRoot: RootHex,
|
|
175
175
|
opts?: StateGetOpts
|
|
176
|
-
): Promise<{state:
|
|
176
|
+
): Promise<{state: CachedBeaconStateAllForks | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null>;
|
|
177
177
|
/** Return serialized bytes of a persisted checkpoint state */
|
|
178
178
|
getPersistedCheckpointState(checkpoint?: phase0.Checkpoint): Promise<Uint8Array | null>;
|
|
179
179
|
/** Returns a cached state by checkpoint */
|
|
@@ -12,7 +12,6 @@ import {BeaconBlockBody, SSZTypesFor, ssz} from "@lodestar/types";
|
|
|
12
12
|
import {SyncCommitteeWitness} from "./types.js";
|
|
13
13
|
|
|
14
14
|
export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllForks): SyncCommitteeWitness {
|
|
15
|
-
state.commit();
|
|
16
15
|
const n1 = state.node;
|
|
17
16
|
let witness: Uint8Array[];
|
|
18
17
|
let currentSyncCommitteeRoot: Uint8Array;
|
|
@@ -71,7 +70,6 @@ export function getCurrentSyncCommitteeBranch(syncCommitteesWitness: SyncCommitt
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
export function getFinalizedRootProof(state: CachedBeaconStateAllForks): Uint8Array[] {
|
|
74
|
-
state.commit();
|
|
75
73
|
const finalizedRootGindex = state.epochCtx.isPostElectra() ? FINALIZED_ROOT_GINDEX_ELECTRA : FINALIZED_ROOT_GINDEX;
|
|
76
74
|
return new Tree(state.node).getSingleProof(BigInt(finalizedRootGindex));
|
|
77
75
|
}
|
|
@@ -5,7 +5,6 @@ import {IForkChoice} from "@lodestar/fork-choice";
|
|
|
5
5
|
import {
|
|
6
6
|
ForkName,
|
|
7
7
|
ForkSeq,
|
|
8
|
-
MAX_ATTESTATIONS,
|
|
9
8
|
MAX_ATTESTATIONS_ELECTRA,
|
|
10
9
|
MAX_COMMITTEES_PER_SLOT,
|
|
11
10
|
MIN_ATTESTATION_INCLUSION_DELAY,
|
|
@@ -23,7 +22,6 @@ import {
|
|
|
23
22
|
CachedBeaconStateAllForks,
|
|
24
23
|
CachedBeaconStateAltair,
|
|
25
24
|
CachedBeaconStateGloas,
|
|
26
|
-
CachedBeaconStatePhase0,
|
|
27
25
|
EffectiveBalanceIncrements,
|
|
28
26
|
RootCache,
|
|
29
27
|
computeEpochAtSlot,
|
|
@@ -32,21 +30,12 @@ import {
|
|
|
32
30
|
getAttestationParticipationStatus,
|
|
33
31
|
getBlockRootAtSlot,
|
|
34
32
|
} from "@lodestar/state-transition";
|
|
35
|
-
import {
|
|
36
|
-
Attestation,
|
|
37
|
-
Epoch,
|
|
38
|
-
RootHex,
|
|
39
|
-
Slot,
|
|
40
|
-
ValidatorIndex,
|
|
41
|
-
electra,
|
|
42
|
-
isElectraAttestation,
|
|
43
|
-
phase0,
|
|
44
|
-
ssz,
|
|
45
|
-
} from "@lodestar/types";
|
|
33
|
+
import {Attestation, Epoch, RootHex, Slot, electra, isElectraAttestation, phase0, ssz} from "@lodestar/types";
|
|
46
34
|
import {MapDef, assert, toRootHex} from "@lodestar/utils";
|
|
47
35
|
import {Metrics} from "../../metrics/metrics.js";
|
|
48
36
|
import {IntersectResult, intersectUint8Arrays} from "../../util/bitArray.js";
|
|
49
37
|
import {getShufflingDependentRoot} from "../../util/dependentRoot.js";
|
|
38
|
+
import {ShufflingCache} from "../shufflingCache.js";
|
|
50
39
|
import {InsertOutcome} from "./types.js";
|
|
51
40
|
import {pruneBySlot, signatureFromBytesNoCheck} from "./utils.js";
|
|
52
41
|
|
|
@@ -54,8 +43,6 @@ type DataRootHex = string;
|
|
|
54
43
|
|
|
55
44
|
type CommitteeIndex = number;
|
|
56
45
|
|
|
57
|
-
// for pre-electra
|
|
58
|
-
type AttestationWithScore = {attestation: Attestation; score: number};
|
|
59
46
|
/**
|
|
60
47
|
* for electra, this is to consolidate aggregated attestations of the same attestation data into a single attestation to be included in block
|
|
61
48
|
* note that this is local definition in this file and it's NOT validator consolidation
|
|
@@ -110,15 +97,6 @@ const MAX_RETAINED_ATTESTATIONS_PER_GROUP = 4;
|
|
|
110
97
|
*/
|
|
111
98
|
const MAX_RETAINED_ATTESTATIONS_PER_GROUP_ELECTRA = 8;
|
|
112
99
|
|
|
113
|
-
/**
|
|
114
|
-
* Pre-electra, each slot has 64 committees, and each block has 128 attestations max so in average
|
|
115
|
-
* we get 2 attestation per groups.
|
|
116
|
-
* Starting from Jan 2024, we have a performance issue getting attestations for a block. Based on the
|
|
117
|
-
* fact that lot of groups will have only 1 full participation attestation, increase this number
|
|
118
|
-
* a bit higher than average. This also help decrease number of slots to search for attestations.
|
|
119
|
-
*/
|
|
120
|
-
const MAX_ATTESTATIONS_PER_GROUP = 3;
|
|
121
|
-
|
|
122
100
|
/**
|
|
123
101
|
* For electra, there is on chain aggregation of attestations across committees, so we can just pick up to 8
|
|
124
102
|
* attestations per group, sort by scores to get first 8.
|
|
@@ -230,123 +208,18 @@ export class AggregatedAttestationPool {
|
|
|
230
208
|
this.lowestPermissibleSlot = Math.max(clockSlot - slotsToRetain, 0);
|
|
231
209
|
}
|
|
232
210
|
|
|
233
|
-
getAttestationsForBlock(
|
|
234
|
-
const forkSeq = ForkSeq[fork];
|
|
235
|
-
return forkSeq >= ForkSeq.electra
|
|
236
|
-
? this.getAttestationsForBlockElectra(fork, forkChoice, state)
|
|
237
|
-
: this.getAttestationsForBlockPreElectra(fork, forkChoice, state);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Get attestations to be included in a block pre-electra. Returns up to $MAX_ATTESTATIONS items
|
|
242
|
-
*/
|
|
243
|
-
getAttestationsForBlockPreElectra(
|
|
211
|
+
getAttestationsForBlock(
|
|
244
212
|
fork: ForkName,
|
|
245
213
|
forkChoice: IForkChoice,
|
|
214
|
+
shufflingCache: ShufflingCache,
|
|
246
215
|
state: CachedBeaconStateAllForks
|
|
247
|
-
):
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, state);
|
|
253
|
-
const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);
|
|
254
|
-
|
|
255
|
-
const attestationsByScore: AttestationWithScore[] = [];
|
|
256
|
-
|
|
257
|
-
const slots = Array.from(this.attestationGroupByIndexByDataHexBySlot.keys()).sort((a, b) => b - a);
|
|
258
|
-
let minScore = Number.MAX_SAFE_INTEGER;
|
|
259
|
-
let slotCount = 0;
|
|
260
|
-
slot: for (const slot of slots) {
|
|
261
|
-
slotCount++;
|
|
262
|
-
const attestationGroupByIndexByDataHash = this.attestationGroupByIndexByDataHexBySlot.get(slot);
|
|
263
|
-
// should not happen
|
|
264
|
-
if (!attestationGroupByIndexByDataHash) {
|
|
265
|
-
throw Error(`No aggregated attestation pool for slot=${slot}`);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const epoch = computeEpochAtSlot(slot);
|
|
269
|
-
// validateAttestation condition: Attestation target epoch not in previous or current epoch
|
|
270
|
-
if (!(epoch === stateEpoch || epoch === statePrevEpoch)) {
|
|
271
|
-
continue; // Invalid attestations
|
|
272
|
-
}
|
|
273
|
-
// validateAttestation condition: Attestation slot not within inclusion window
|
|
274
|
-
if (
|
|
275
|
-
!(
|
|
276
|
-
slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot &&
|
|
277
|
-
// Post deneb, attestations are valid for current and previous epoch
|
|
278
|
-
(ForkSeq[fork] >= ForkSeq.deneb || stateSlot <= slot + SLOTS_PER_EPOCH)
|
|
279
|
-
)
|
|
280
|
-
) {
|
|
281
|
-
continue; // Invalid attestations
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const inclusionDistance = stateSlot - slot;
|
|
285
|
-
for (const attestationGroupByIndex of attestationGroupByIndexByDataHash.values()) {
|
|
286
|
-
for (const [committeeIndex, attestationGroup] of attestationGroupByIndex.entries()) {
|
|
287
|
-
const notSeenCommitteeMembers = notSeenValidatorsFn(epoch, slot, committeeIndex);
|
|
288
|
-
if (notSeenCommitteeMembers === null || notSeenCommitteeMembers.size === 0) {
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (
|
|
293
|
-
slotCount > 2 &&
|
|
294
|
-
attestationsByScore.length >= MAX_ATTESTATIONS &&
|
|
295
|
-
notSeenCommitteeMembers.size / inclusionDistance < minScore
|
|
296
|
-
) {
|
|
297
|
-
// after 2 slots, there are a good chance that we have 2 * MAX_ATTESTATIONS attestations and break the for loop early
|
|
298
|
-
// if not, we may have to scan all slots in the pool
|
|
299
|
-
// if we have enough attestations and the max possible score is lower than scores of `attestationsByScore`, we should skip
|
|
300
|
-
// otherwise it takes time to check attestation, add it and remove it later after the sort by score
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (validateAttestationDataFn(attestationGroup.data) !== null) {
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// TODO: Is it necessary to validateAttestation for:
|
|
309
|
-
// - Attestation committee index not within current committee count
|
|
310
|
-
// - Attestation aggregation bits length does not match committee length
|
|
311
|
-
//
|
|
312
|
-
// These properties should not change after being validate in gossip
|
|
313
|
-
// IF they have to be validated, do it only with one attestation per group since same data
|
|
314
|
-
// The committeeCountPerSlot can be precomputed once per slot
|
|
315
|
-
const getAttestationsResult = attestationGroup.getAttestationsForBlock(
|
|
316
|
-
fork,
|
|
317
|
-
state.epochCtx.effectiveBalanceIncrements,
|
|
318
|
-
notSeenCommitteeMembers,
|
|
319
|
-
MAX_ATTESTATIONS_PER_GROUP
|
|
320
|
-
);
|
|
321
|
-
for (const {attestation, newSeenEffectiveBalance} of getAttestationsResult.result) {
|
|
322
|
-
const score = newSeenEffectiveBalance / inclusionDistance;
|
|
323
|
-
if (score < minScore) {
|
|
324
|
-
minScore = score;
|
|
325
|
-
}
|
|
326
|
-
attestationsByScore.push({
|
|
327
|
-
attestation,
|
|
328
|
-
score,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Stop accumulating attestations there are enough that may have good scoring
|
|
333
|
-
if (attestationsByScore.length >= MAX_ATTESTATIONS * 2) {
|
|
334
|
-
break slot;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
216
|
+
): Attestation[] {
|
|
217
|
+
const forkSeq = ForkSeq[fork];
|
|
218
|
+
if (forkSeq < ForkSeq.electra) {
|
|
219
|
+
throw new Error("Does not support producing blocks for pre-electra forks anymore");
|
|
338
220
|
}
|
|
339
221
|
|
|
340
|
-
|
|
341
|
-
const attestationsForBlock: phase0.Attestation[] = [];
|
|
342
|
-
for (const [i, attestationWithScore] of sortedAttestationsByScore.entries()) {
|
|
343
|
-
if (i >= MAX_ATTESTATIONS) {
|
|
344
|
-
break;
|
|
345
|
-
}
|
|
346
|
-
// attestations could be modified in this op pool, so we need to clone for block
|
|
347
|
-
attestationsForBlock.push(ssz.phase0.Attestation.clone(attestationWithScore.attestation));
|
|
348
|
-
}
|
|
349
|
-
return attestationsForBlock;
|
|
222
|
+
return this.getAttestationsForBlockElectra(fork, forkChoice, shufflingCache, state);
|
|
350
223
|
}
|
|
351
224
|
|
|
352
225
|
/**
|
|
@@ -355,6 +228,7 @@ export class AggregatedAttestationPool {
|
|
|
355
228
|
getAttestationsForBlockElectra(
|
|
356
229
|
fork: ForkName,
|
|
357
230
|
forkChoice: IForkChoice,
|
|
231
|
+
shufflingCache: ShufflingCache,
|
|
358
232
|
state: CachedBeaconStateAllForks
|
|
359
233
|
): electra.Attestation[] {
|
|
360
234
|
const stateSlot = state.slot;
|
|
@@ -362,7 +236,7 @@ export class AggregatedAttestationPool {
|
|
|
362
236
|
const statePrevEpoch = stateEpoch - 1;
|
|
363
237
|
const rootCache = new RootCache(state);
|
|
364
238
|
|
|
365
|
-
const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, state);
|
|
239
|
+
const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, shufflingCache, state);
|
|
366
240
|
const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);
|
|
367
241
|
|
|
368
242
|
const slots = Array.from(this.attestationGroupByIndexByDataHexBySlot.keys()).sort((a, b) => b - a);
|
|
@@ -864,41 +738,14 @@ export function aggregateConsolidation({byCommittee, attData}: AttestationsConso
|
|
|
864
738
|
* Pre-compute participation from a CachedBeaconStateAllForks, for use to check if an attestation's committee
|
|
865
739
|
* has already attested or not.
|
|
866
740
|
*/
|
|
867
|
-
export function getNotSeenValidatorsFn(
|
|
741
|
+
export function getNotSeenValidatorsFn(
|
|
742
|
+
config: BeaconConfig,
|
|
743
|
+
shufflingCache: ShufflingCache,
|
|
744
|
+
state: CachedBeaconStateAllForks
|
|
745
|
+
): GetNotSeenValidatorsFn {
|
|
868
746
|
const stateSlot = state.slot;
|
|
869
747
|
if (config.getForkName(stateSlot) === ForkName.phase0) {
|
|
870
|
-
|
|
871
|
-
// As we are close to altair, this is not really important, it's mainly for e2e.
|
|
872
|
-
// The performance is not great due to the different BeaconState data structure to altair.
|
|
873
|
-
// check for phase0 block already
|
|
874
|
-
const phase0State = state as CachedBeaconStatePhase0;
|
|
875
|
-
const stateEpoch = computeEpochAtSlot(stateSlot);
|
|
876
|
-
|
|
877
|
-
const previousEpochParticipants = extractParticipationPhase0(
|
|
878
|
-
phase0State.previousEpochAttestations.getAllReadonly(),
|
|
879
|
-
state
|
|
880
|
-
);
|
|
881
|
-
const currentEpochParticipants = extractParticipationPhase0(
|
|
882
|
-
phase0State.currentEpochAttestations.getAllReadonly(),
|
|
883
|
-
state
|
|
884
|
-
);
|
|
885
|
-
|
|
886
|
-
return (epoch: Epoch, slot: Slot, committeeIndex: number) => {
|
|
887
|
-
const participants =
|
|
888
|
-
epoch === stateEpoch ? currentEpochParticipants : epoch === stateEpoch - 1 ? previousEpochParticipants : null;
|
|
889
|
-
if (participants === null) {
|
|
890
|
-
return null;
|
|
891
|
-
}
|
|
892
|
-
const committee = state.epochCtx.getBeaconCommittee(slot, committeeIndex);
|
|
893
|
-
|
|
894
|
-
const notSeenCommitteeMembers = new Set<number>();
|
|
895
|
-
for (const [i, validatorIndex] of committee.entries()) {
|
|
896
|
-
if (!participants.has(validatorIndex)) {
|
|
897
|
-
notSeenCommitteeMembers.add(i);
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
return notSeenCommitteeMembers.size === 0 ? null : notSeenCommitteeMembers;
|
|
901
|
-
};
|
|
748
|
+
throw new Error("getNotSeenValidatorsFn is not supported phase0 state");
|
|
902
749
|
}
|
|
903
750
|
|
|
904
751
|
// altair and future forks
|
|
@@ -927,7 +774,8 @@ export function getNotSeenValidatorsFn(config: BeaconConfig, state: CachedBeacon
|
|
|
927
774
|
return notSeenCommitteeMembers.size === 0 ? null : notSeenCommitteeMembers;
|
|
928
775
|
}
|
|
929
776
|
|
|
930
|
-
const
|
|
777
|
+
const decisionRoot = state.epochCtx.getShufflingDecisionRoot(computeEpochAtSlot(slot));
|
|
778
|
+
const committee = shufflingCache.getBeaconCommittee(epoch, decisionRoot, slot, committeeIndex);
|
|
931
779
|
notSeenCommitteeMembers = new Set<number>();
|
|
932
780
|
for (const [i, validatorIndex] of committee.entries()) {
|
|
933
781
|
// no need to check flagIsTimelySource as if validator is not seen, it's participation status is 0
|
|
@@ -942,26 +790,6 @@ export function getNotSeenValidatorsFn(config: BeaconConfig, state: CachedBeacon
|
|
|
942
790
|
};
|
|
943
791
|
}
|
|
944
792
|
|
|
945
|
-
export function extractParticipationPhase0(
|
|
946
|
-
attestations: phase0.PendingAttestation[],
|
|
947
|
-
state: CachedBeaconStateAllForks
|
|
948
|
-
): Set<ValidatorIndex> {
|
|
949
|
-
const {epochCtx} = state;
|
|
950
|
-
const allParticipants = new Set<ValidatorIndex>();
|
|
951
|
-
for (const att of attestations) {
|
|
952
|
-
const aggregationBits = att.aggregationBits;
|
|
953
|
-
const attData = att.data;
|
|
954
|
-
const attSlot = attData.slot;
|
|
955
|
-
const committeeIndex = attData.index;
|
|
956
|
-
const committee = epochCtx.getBeaconCommittee(attSlot, committeeIndex);
|
|
957
|
-
const participants = aggregationBits.intersectValues(committee);
|
|
958
|
-
for (const participant of participants) {
|
|
959
|
-
allParticipants.add(participant);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
return allParticipants;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
793
|
/**
|
|
966
794
|
* This returns a function to validate if an attestation data is compatible to a state.
|
|
967
795
|
*
|