@lodestar/beacon-node 1.48.0-dev.b71ab45af3 → 1.48.0-dev.bb3ffba9e9
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/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +4 -4
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +39 -39
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +1 -0
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -0
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +12 -1
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.d.ts +2 -0
- package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js +4 -1
- package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +31 -34
- 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 +0 -13
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +3 -2
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.js +9 -16
- package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +9 -3
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +127 -73
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +1 -1
- package/lib/chain/emitter.js +1 -1
- package/lib/chain/errors/blockError.d.ts +19 -7
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -2
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.d.ts +15 -2
- package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +10 -11
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +0 -2
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +13 -13
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +46 -64
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +35 -54
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +38 -13
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +10 -29
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +7 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +22 -5
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/beacon.d.ts +14 -5
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +17 -3
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/dataColumnStore.d.ts +39 -0
- package/lib/db/dataColumnStore.d.ts.map +1 -0
- package/lib/db/dataColumnStore.js +81 -0
- package/lib/db/dataColumnStore.js.map +1 -0
- package/lib/db/flatFileStore/atomicWrite.d.ts +8 -0
- package/lib/db/flatFileStore/atomicWrite.d.ts.map +1 -0
- package/lib/db/flatFileStore/atomicWrite.js +61 -0
- package/lib/db/flatFileStore/atomicWrite.js.map +1 -0
- package/lib/db/flatFileStore/dcolFormat.d.ts +90 -0
- package/lib/db/flatFileStore/dcolFormat.d.ts.map +1 -0
- package/lib/db/flatFileStore/dcolFormat.js +286 -0
- package/lib/db/flatFileStore/dcolFormat.js.map +1 -0
- package/lib/db/flatFileStore/errors.d.ts +82 -0
- package/lib/db/flatFileStore/errors.d.ts.map +1 -0
- package/lib/db/flatFileStore/errors.js +32 -0
- package/lib/db/flatFileStore/errors.js.map +1 -0
- package/lib/db/flatFileStore/flatFileStore.d.ts +41 -0
- package/lib/db/flatFileStore/flatFileStore.d.ts.map +1 -0
- package/lib/db/flatFileStore/flatFileStore.js +251 -0
- package/lib/db/flatFileStore/flatFileStore.js.map +1 -0
- package/lib/db/flatFileStore/index.d.ts +4 -0
- package/lib/db/flatFileStore/index.d.ts.map +1 -0
- package/lib/db/flatFileStore/index.js +2 -0
- package/lib/db/flatFileStore/index.js.map +1 -0
- package/lib/db/flatFileStore/interface.d.ts +25 -0
- package/lib/db/flatFileStore/interface.d.ts.map +1 -0
- package/lib/db/flatFileStore/interface.js +2 -0
- package/lib/db/flatFileStore/interface.js.map +1 -0
- package/lib/db/flatFileStore/metrics.d.ts +24 -0
- package/lib/db/flatFileStore/metrics.d.ts.map +1 -0
- package/lib/db/flatFileStore/metrics.js +25 -0
- package/lib/db/flatFileStore/metrics.js.map +1 -0
- package/lib/db/flatFileStore/path.d.ts +9 -0
- package/lib/db/flatFileStore/path.d.ts.map +1 -0
- package/lib/db/flatFileStore/path.js +21 -0
- package/lib/db/flatFileStore/path.js.map +1 -0
- package/lib/db/flatFileStore/slotIndex.d.ts +14 -0
- package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -0
- package/lib/db/flatFileStore/slotIndex.js +46 -0
- package/lib/db/flatFileStore/slotIndex.js.map +1 -0
- package/lib/db/flatFileStore/snappy.d.ts +3 -0
- package/lib/db/flatFileStore/snappy.d.ts.map +1 -0
- package/lib/db/flatFileStore/snappy.js +12 -0
- package/lib/db/flatFileStore/snappy.js.map +1 -0
- package/lib/db/index.d.ts +1 -0
- package/lib/db/index.d.ts.map +1 -1
- package/lib/db/interface.d.ts +8 -1
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.d.ts +8 -1
- package/lib/db/repositories/blockArchive.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.js +65 -28
- package/lib/db/repositories/blockArchive.js.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +1 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.js +3 -7
- package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +4 -1
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +18 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +55 -1
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/monitoring/clientStats.js +1 -1
- package/lib/monitoring/clientStats.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +0 -7
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +2 -1
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +4 -20
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +6 -7
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +49 -3
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +232 -98
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +112 -91
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +2 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +6 -22
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/network/reqresp/rateLimit.js +2 -1
- package/lib/network/reqresp/rateLimit.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +2 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +14 -9
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/node/nodejs.d.ts +2 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +4 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/util/sszBytes.d.ts +6 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +18 -19
- package/src/api/impl/beacon/blocks/index.ts +8 -4
- package/src/api/impl/validator/index.ts +44 -43
- package/src/chain/archiveStore/archiveStore.ts +6 -1
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -0
- package/src/chain/archiveStore/historicalState/metrics.ts +14 -1
- package/src/chain/archiveStore/historicalState/types.ts +2 -0
- package/src/chain/archiveStore/historicalState/worker.ts +4 -1
- package/src/chain/archiveStore/utils/archiveBlocks.ts +34 -39
- package/src/chain/blocks/importBlock.ts +0 -13
- package/src/chain/blocks/index.ts +13 -2
- package/src/chain/blocks/writeBlockInputToDb.ts +10 -17
- package/src/chain/chain.ts +149 -80
- package/src/chain/emitter.ts +1 -1
- package/src/chain/errors/blockError.ts +22 -10
- package/src/chain/errors/dataColumnSidecarError.ts +24 -8
- package/src/chain/errors/executionPayloadEnvelope.ts +14 -11
- package/src/chain/interface.ts +2 -2
- package/src/chain/prepareNextSlot.ts +16 -16
- package/src/chain/produceBlock/produceBlockBody.ts +50 -71
- package/src/chain/validation/block.ts +35 -70
- package/src/chain/validation/dataColumnSidecar.ts +47 -14
- package/src/chain/validation/executionPayloadEnvelope.ts +10 -37
- package/src/chain/validatorMonitor.ts +24 -7
- package/src/db/beacon.ts +33 -6
- package/src/db/dataColumnStore.ts +113 -0
- package/src/db/flatFileStore/atomicWrite.ts +65 -0
- package/src/db/flatFileStore/dcolFormat.ts +389 -0
- package/src/db/flatFileStore/errors.ts +53 -0
- package/src/db/flatFileStore/flatFileStore.ts +310 -0
- package/src/db/flatFileStore/index.ts +3 -0
- package/src/db/flatFileStore/interface.ts +21 -0
- package/src/db/flatFileStore/metrics.ts +48 -0
- package/src/db/flatFileStore/path.ts +32 -0
- package/src/db/flatFileStore/slotIndex.ts +56 -0
- package/src/db/flatFileStore/snappy.ts +14 -0
- package/src/db/index.ts +1 -0
- package/src/db/interface.ts +9 -1
- package/src/db/repositories/blockArchive.ts +89 -30
- package/src/db/repositories/blockArchiveIndex.ts +4 -9
- package/src/execution/engine/mock.ts +4 -1
- package/src/metrics/metrics/lodestar.ts +57 -1
- package/src/monitoring/clientStats.ts +1 -1
- package/src/network/gossip/encoding.ts +0 -7
- package/src/network/gossip/topic.ts +3 -25
- package/src/network/processor/gossipHandlers.ts +11 -11
- package/src/network/processor/index.ts +263 -148
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +139 -105
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +2 -0
- package/src/network/reqresp/protocols.ts +7 -24
- package/src/network/reqresp/rateLimit.ts +2 -1
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +15 -7
- package/src/node/nodejs.ts +6 -1
- package/src/util/sszBytes.ts +1 -1
|
@@ -25,7 +25,10 @@ logger.info("Historical state worker started");
|
|
|
25
25
|
|
|
26
26
|
const config = createBeaconConfig(chainConfigFromJson(workerData.chainConfigJson), workerData.genesisValidatorsRoot);
|
|
27
27
|
|
|
28
|
-
const db = new BeaconDb(config, await LevelDbController.create({name: workerData.dbLocation}, {logger})
|
|
28
|
+
const db = new BeaconDb(config, await LevelDbController.create({name: workerData.dbLocation}, {logger}), {
|
|
29
|
+
dataColumnDir: workerData.dataColumnDir,
|
|
30
|
+
logger,
|
|
31
|
+
});
|
|
29
32
|
|
|
30
33
|
const abortController = new AbortController();
|
|
31
34
|
|
|
@@ -134,6 +134,8 @@ export async function archiveBlocks(
|
|
|
134
134
|
logger.verbose("Migrated blobSidecars from hot DB to cold DB", {...logCtx, migratedEntries});
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// Keep the normal LevelDB hot-to-cold flow for legacy columns. New columns are written
|
|
138
|
+
// directly to flat files, so this only moves fallback data left by older versions.
|
|
137
139
|
if (finalizedPostFulu) {
|
|
138
140
|
const migratedSlots = await migrateDataColumnSidecarsFromHotToColdDb(
|
|
139
141
|
config,
|
|
@@ -142,7 +144,7 @@ export async function archiveBlocks(
|
|
|
142
144
|
finalizedCanonicalBlocks,
|
|
143
145
|
currentEpoch
|
|
144
146
|
);
|
|
145
|
-
logger.verbose("Migrated dataColumnSidecars from hot DB to cold DB", {
|
|
147
|
+
logger.verbose("Migrated legacy dataColumnSidecars from hot DB to cold DB", {
|
|
146
148
|
...logCtx,
|
|
147
149
|
migratedEntries: migratedSlots.length,
|
|
148
150
|
slotRange: prettyPrintIndices(migratedSlots),
|
|
@@ -195,6 +197,23 @@ export async function archiveBlocks(
|
|
|
195
197
|
slotRange: prettyPrintIndices(nonCanonicalSlots),
|
|
196
198
|
};
|
|
197
199
|
|
|
200
|
+
const columnItems = finalizedNonCanonicalBlocks
|
|
201
|
+
// Gloas EMPTY and FULL variants share a block root. EMPTY has no sidecars, so deleting by its root could
|
|
202
|
+
// remove the canonical FULL variant's columns. Pre-Gloas blocks are always FULL.
|
|
203
|
+
.filter(
|
|
204
|
+
(summary) => config.getForkSeq(summary.slot) >= ForkSeq.fulu && summary.payloadStatus === PayloadStatus.FULL
|
|
205
|
+
)
|
|
206
|
+
.map((summary) => ({slot: summary.slot, blockRoot: summary.blockRoot}));
|
|
207
|
+
if (columnItems.length > 0) {
|
|
208
|
+
// Delete sidecars first so their block roots remain available to retry cleanup after a failure or crash.
|
|
209
|
+
await db.dataColumns.deleteMany(columnItems);
|
|
210
|
+
logger.verbose("Deleted non canonical data columns of blocks", {
|
|
211
|
+
...logCtx,
|
|
212
|
+
blocks: columnItems.length,
|
|
213
|
+
slotRange: prettyPrintIndices(columnItems.map(({slot}) => slot).sort((a, b) => a - b)),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
198
217
|
await db.block.batchDelete(nonCanonicalBlockRoots);
|
|
199
218
|
logger.verbose("Deleted non canonical blocks from hot DB", nonCanonicalLogCtx);
|
|
200
219
|
|
|
@@ -203,11 +222,6 @@ export async function archiveBlocks(
|
|
|
203
222
|
logger.verbose("Deleted non canonical blobSidecars from hot DB", nonCanonicalLogCtx);
|
|
204
223
|
}
|
|
205
224
|
|
|
206
|
-
if (finalizedPostFulu) {
|
|
207
|
-
await db.dataColumnSidecar.deleteMany(nonCanonicalBlockRoots);
|
|
208
|
-
logger.verbose("Deleted non canonical dataColumnSidecars from hot DB", nonCanonicalLogCtx);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
225
|
if (finalizedPostGloas) {
|
|
212
226
|
await db.executionPayloadEnvelope.batchDelete(nonCanonicalBlockRoots);
|
|
213
227
|
logger.verbose("Deleted non canonical executionPayloadEnvelopes from hot DB", nonCanonicalLogCtx);
|
|
@@ -245,23 +259,16 @@ export async function archiveBlocks(
|
|
|
245
259
|
);
|
|
246
260
|
const dataColumnSidecarsMinEpoch = currentEpoch - dataColumnSidecarsArchiveWindow;
|
|
247
261
|
if (dataColumnSidecarsMinEpoch >= config.FULU_FORK_EPOCH) {
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
});
|
|
252
|
-
// for each slot there could be multiple dataColumnSidecar, so we need to deduplicate it
|
|
253
|
-
const slotsToDelete = [...new Set(prefixedKeys.map(({prefix}) => prefix))].sort((a, b) => a - b);
|
|
254
|
-
|
|
255
|
-
if (slotsToDelete.length > 0) {
|
|
256
|
-
await db.dataColumnSidecarArchive.deleteMany(slotsToDelete);
|
|
262
|
+
const columnsPruneSlot = computeStartSlotAtEpoch(dataColumnSidecarsMinEpoch);
|
|
263
|
+
const prunedColumnSlots = await db.dataColumns.pruneBefore(columnsPruneSlot);
|
|
264
|
+
if (prunedColumnSlots.length > 0) {
|
|
257
265
|
logger.verbose("dataColumnSidecars prune", {
|
|
258
266
|
...logCtx,
|
|
259
|
-
slotRange: prettyPrintIndices(
|
|
260
|
-
numOfSlots:
|
|
261
|
-
totalNumOfSidecars: prefixedKeys.length,
|
|
267
|
+
slotRange: prettyPrintIndices(prunedColumnSlots),
|
|
268
|
+
numOfSlots: prunedColumnSlots.length,
|
|
262
269
|
});
|
|
263
270
|
} else {
|
|
264
|
-
logger.verbose(
|
|
271
|
+
logger.verbose("dataColumnSidecars prune: no entries before slot", {...logCtx, slot: columnsPruneSlot});
|
|
265
272
|
}
|
|
266
273
|
} else {
|
|
267
274
|
logger.verbose(
|
|
@@ -389,7 +396,6 @@ async function migrateBlobSidecarsFromHotToColdDb(
|
|
|
389
396
|
)
|
|
390
397
|
).filter((e): e is KeyValue<Slot, Uint8Array> => e !== null);
|
|
391
398
|
|
|
392
|
-
// put to blockArchive db and delete block db
|
|
393
399
|
await Promise.all([
|
|
394
400
|
db.blobSidecarsArchive.batchPutBinary(canonicalBlobSidecarsEntries),
|
|
395
401
|
db.blobSidecars.batchDelete(canonicalBlocks.map((block) => block.root)),
|
|
@@ -402,11 +408,9 @@ async function migrateBlobSidecarsFromHotToColdDb(
|
|
|
402
408
|
|
|
403
409
|
// TODO: This function can be simplified further by reducing layers of promises in a loop
|
|
404
410
|
/**
|
|
405
|
-
* Post-gloas the data columns of a Gloas block are tied to its execution payload envelope
|
|
406
|
-
*
|
|
407
|
-
* blocks only have a FULL variant, so the
|
|
408
|
-
* Blocks whose canonical variant is PENDING/EMPTY are skipped here — their columns will be picked
|
|
409
|
-
* up on a later run once the FULL variant appears in the ancestor walk.
|
|
411
|
+
* Post-gloas the data columns of a Gloas block are tied to its execution payload envelope.
|
|
412
|
+
* Columns only exist once the FULL variant of the block is in the proto-array. Pre-Gloas
|
|
413
|
+
* blocks only have a FULL variant, so the payload-status filter passes them all.
|
|
410
414
|
*/
|
|
411
415
|
async function migrateDataColumnSidecarsFromHotToColdDb(
|
|
412
416
|
config: ChainForkConfig,
|
|
@@ -428,32 +432,24 @@ async function migrateDataColumnSidecarsFromHotToColdDb(
|
|
|
428
432
|
if (batch.length === 0) break;
|
|
429
433
|
|
|
430
434
|
const promises: Promise<void>[] = [];
|
|
431
|
-
|
|
432
|
-
// load Buffer instead of ssz deserialized to improve performance
|
|
433
435
|
for (const block of batch) {
|
|
434
|
-
const
|
|
435
|
-
const blockEpoch = computeEpochAtSlot(blockSlot);
|
|
436
|
-
|
|
436
|
+
const blockEpoch = computeEpochAtSlot(block.slot);
|
|
437
437
|
if (
|
|
438
|
-
config.getForkSeq(
|
|
439
|
-
// if block is out of ${config.MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS}, skip this step
|
|
438
|
+
config.getForkSeq(block.slot) < ForkSeq.fulu ||
|
|
440
439
|
blockEpoch < currentEpoch - config.MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS
|
|
441
440
|
) {
|
|
442
441
|
continue;
|
|
443
442
|
}
|
|
444
443
|
|
|
445
|
-
// Here we assume the data column sidecars are already in the hot db
|
|
446
444
|
const dataColumnSidecarBytes = await fromAsync(db.dataColumnSidecar.valuesStreamBinary(block.root));
|
|
447
445
|
if (dataColumnSidecarBytes.length === 0) {
|
|
448
|
-
// Empty stream: either the block has no blobs, or columns were already archived on a
|
|
449
|
-
// previous run (boundary block). Nothing to migrate.
|
|
450
446
|
logger.debug("DataColumnSidecars in forkchoice but missing in hot db, could be already archived", {
|
|
451
447
|
slot: block.slot,
|
|
452
448
|
root: toRootHex(block.root),
|
|
453
449
|
});
|
|
454
450
|
continue;
|
|
455
451
|
}
|
|
456
|
-
logger.verbose("Migrated dataColumnSidecars for block", {
|
|
452
|
+
logger.verbose("Migrated legacy dataColumnSidecars for block", {
|
|
457
453
|
currentEpoch,
|
|
458
454
|
slot: block.slot,
|
|
459
455
|
root: toRootHex(block.root),
|
|
@@ -462,15 +458,14 @@ async function migrateDataColumnSidecarsFromHotToColdDb(
|
|
|
462
458
|
promises.push(
|
|
463
459
|
db.dataColumnSidecarArchive.putManyBinary(
|
|
464
460
|
block.slot,
|
|
465
|
-
dataColumnSidecarBytes.map((
|
|
461
|
+
dataColumnSidecarBytes.map((entry) => ({key: entry.id, value: entry.value}))
|
|
466
462
|
)
|
|
467
463
|
);
|
|
468
464
|
migratedSlots.push(block.slot);
|
|
469
465
|
}
|
|
470
466
|
|
|
471
|
-
promises.push(db.dataColumnSidecar.deleteMany(batch.map((block) => block.root)));
|
|
472
|
-
|
|
473
467
|
await Promise.all(promises);
|
|
468
|
+
await db.dataColumnSidecar.deleteMany(batch.map((block) => block.root));
|
|
474
469
|
}
|
|
475
470
|
|
|
476
471
|
// Ancestor walk is newest to oldest; sort ascending so `prettyPrintIndices` renders cleanly.
|
|
@@ -524,19 +524,6 @@ export async function importBlock(
|
|
|
524
524
|
this.metrics?.previousJustifiedEpoch.set(checkpointState.previousJustifiedCheckpoint.epoch);
|
|
525
525
|
this.metrics?.currentJustifiedEpoch.set(justifiedCheckpoint.epoch);
|
|
526
526
|
}
|
|
527
|
-
const finalizedCheckpoint = checkpointState.finalizedCheckpoint;
|
|
528
|
-
const finalizedEpoch = finalizedCheckpoint.epoch;
|
|
529
|
-
const preFinalizedEpoch = parentBlockSummary.finalizedEpoch;
|
|
530
|
-
if (finalizedEpoch > preFinalizedEpoch) {
|
|
531
|
-
this.emitter.emit(routes.events.EventType.finalizedCheckpoint, {
|
|
532
|
-
block: toRootHex(finalizedCheckpoint.root),
|
|
533
|
-
epoch: finalizedCheckpoint.epoch,
|
|
534
|
-
state: toRootHex(checkpointState.hashTreeRoot()),
|
|
535
|
-
executionOptimistic: false,
|
|
536
|
-
});
|
|
537
|
-
this.logger.verbose("Checkpoint finalized", toCheckpointHex(finalizedCheckpoint));
|
|
538
|
-
this.metrics?.finalizedEpoch.set(finalizedCheckpoint.epoch);
|
|
539
|
-
}
|
|
540
527
|
}
|
|
541
528
|
}
|
|
542
529
|
|
|
@@ -209,8 +209,19 @@ export async function processBlocks(
|
|
|
209
209
|
const blockSlot = signedBlock.message.slot;
|
|
210
210
|
const {state} = err.type;
|
|
211
211
|
const forkTypes = this.config.getForkTypes(blockSlot);
|
|
212
|
-
|
|
213
|
-
this.
|
|
212
|
+
const blockRootHex = toRootHex(forkTypes.BeaconBlock.hashTreeRoot(signedBlock.message));
|
|
213
|
+
this.persistInvalidSszValue(
|
|
214
|
+
forkTypes.SignedBeaconBlock,
|
|
215
|
+
signedBlock,
|
|
216
|
+
`${blockSlot}_invalid_signature`,
|
|
217
|
+
blockRootHex
|
|
218
|
+
);
|
|
219
|
+
this.persistInvalidSszBytes(
|
|
220
|
+
"BeaconState",
|
|
221
|
+
state.serialize(),
|
|
222
|
+
toRootHex(state.hashTreeRoot()),
|
|
223
|
+
`${state.slot}_invalid_signature`
|
|
224
|
+
);
|
|
214
225
|
} else if (err.type.code === BlockErrorCode.INVALID_STATE_ROOT) {
|
|
215
226
|
const {signedBlock} = err;
|
|
216
227
|
const blockSlot = signedBlock.message.slot;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {ForkPostDeneb, isForkPostDeneb} from "@lodestar/params";
|
|
1
|
+
import {ForkPostDeneb, ForkPostFulu, isForkPostDeneb} from "@lodestar/params";
|
|
2
2
|
import {SignedBeaconBlock} from "@lodestar/types";
|
|
3
|
-
import {
|
|
3
|
+
import {toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {getBlobKzgCommitments} from "../../util/dataColumns.js";
|
|
5
5
|
import {BeaconChain} from "../chain.js";
|
|
6
6
|
import {IBlockInput, IDataColumnsInput, isBlockInputBlobs, isBlockInputColumns} from "./blockInput/index.js";
|
|
@@ -76,7 +76,6 @@ async function writeBlockAndBlobsToDb(this: BeaconChain, blockInput: IBlockInput
|
|
|
76
76
|
*/
|
|
77
77
|
export async function writeDataColumnsToDb(this: BeaconChain, blockInput: IDataColumnsInput): Promise<void> {
|
|
78
78
|
const {slot, blockRootHex} = blockInput;
|
|
79
|
-
const blockRoot = fromHex(blockRootHex);
|
|
80
79
|
|
|
81
80
|
if (!blockInput.hasComputedAllData()) {
|
|
82
81
|
// Supernodes may only have a subset of the data columns by the time the block begins to be imported
|
|
@@ -89,25 +88,19 @@ export async function writeDataColumnsToDb(this: BeaconChain, blockInput: IDataC
|
|
|
89
88
|
|
|
90
89
|
const {custodyColumns} = this.custodyConfig;
|
|
91
90
|
const dataColumnSidecars = blockInput.getCustodyColumns();
|
|
91
|
+
const dataColumnSidecarType = this.config.getForkTypes<ForkPostFulu>(slot).DataColumnSidecar;
|
|
92
92
|
|
|
93
|
-
const
|
|
94
|
-
const nonbinaryPuts = [];
|
|
93
|
+
const binaryColumns: {index: number; data: Uint8Array}[] = [];
|
|
95
94
|
for (const dataColumnSidecar of dataColumnSidecars) {
|
|
96
|
-
// skip reserializing column if we already have it
|
|
97
95
|
const serialized = this.serializedCache.get(dataColumnSidecar);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
96
|
+
binaryColumns.push({
|
|
97
|
+
index: dataColumnSidecar.index,
|
|
98
|
+
data: serialized ?? dataColumnSidecarType.serialize(dataColumnSidecar),
|
|
99
|
+
});
|
|
103
100
|
}
|
|
101
|
+
await this.db.dataColumns.putManyBinary({slot, blockRoot: blockRootHex}, binaryColumns);
|
|
104
102
|
|
|
105
|
-
|
|
106
|
-
this.db.dataColumnSidecar.putManyBinary(blockRoot, binaryPuts),
|
|
107
|
-
this.db.dataColumnSidecar.putMany(blockRoot, nonbinaryPuts),
|
|
108
|
-
]);
|
|
109
|
-
|
|
110
|
-
this.logger.debug("Persisted dataColumnSidecars to hot DB", {
|
|
103
|
+
this.logger.debug("Persisted dataColumnSidecars", {
|
|
111
104
|
slot,
|
|
112
105
|
root: blockRootHex,
|
|
113
106
|
dataColumnSidecars: dataColumnSidecars.length,
|
package/src/chain/chain.ts
CHANGED
|
@@ -268,6 +268,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
268
268
|
db,
|
|
269
269
|
dbName,
|
|
270
270
|
dataDir,
|
|
271
|
+
dataColumnDir,
|
|
271
272
|
logger,
|
|
272
273
|
processShutdownCallback,
|
|
273
274
|
clock,
|
|
@@ -285,6 +286,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
285
286
|
db: IBeaconDb;
|
|
286
287
|
dbName: string;
|
|
287
288
|
dataDir: string;
|
|
289
|
+
dataColumnDir?: string;
|
|
288
290
|
logger: Logger;
|
|
289
291
|
processShutdownCallback: ProcessShutdownCallback;
|
|
290
292
|
/** Used for testing to supply fake clock */
|
|
@@ -505,7 +507,12 @@ export class BeaconChain implements IBeaconChain {
|
|
|
505
507
|
|
|
506
508
|
this.archiveStore = new ArchiveStore(
|
|
507
509
|
{db, chain: this, logger: logger as LoggerNode, metrics},
|
|
508
|
-
{
|
|
510
|
+
{
|
|
511
|
+
...opts,
|
|
512
|
+
dbName,
|
|
513
|
+
dataColumnDir: dataColumnDir ?? path.join(dataDir, "data_columns"),
|
|
514
|
+
anchorState: {finalizedCheckpoint: anchorState.finalizedCheckpoint},
|
|
515
|
+
},
|
|
509
516
|
signal
|
|
510
517
|
);
|
|
511
518
|
|
|
@@ -969,12 +976,15 @@ export class BeaconChain implements IBeaconChain {
|
|
|
969
976
|
|
|
970
977
|
async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecar[]> {
|
|
971
978
|
const fork = this.config.getForkName(blockSlot);
|
|
979
|
+
const sidecarsByIndex = new Map<number, DataColumnSidecar>();
|
|
972
980
|
|
|
973
981
|
if (isForkPostGloas(fork)) {
|
|
974
982
|
// After gloas, columns are tracked in PayloadEnvelopeInput
|
|
975
983
|
const payloadInput = this.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
976
984
|
if (payloadInput) {
|
|
977
|
-
|
|
985
|
+
for (const sidecar of payloadInput.getAllColumns()) {
|
|
986
|
+
sidecarsByIndex.set(sidecar.index, sidecar);
|
|
987
|
+
}
|
|
978
988
|
}
|
|
979
989
|
} else {
|
|
980
990
|
// Before gloas, columns are tracked in BlockInput
|
|
@@ -983,16 +993,17 @@ export class BeaconChain implements IBeaconChain {
|
|
|
983
993
|
if (!isBlockInputColumns(blockInput)) {
|
|
984
994
|
throw new Error(`Expected block input to have columns: slot=${blockSlot} root=${blockRootHex}`);
|
|
985
995
|
}
|
|
986
|
-
|
|
996
|
+
for (const sidecar of blockInput.getAllColumns()) {
|
|
997
|
+
sidecarsByIndex.set(sidecar.index, sidecar);
|
|
998
|
+
}
|
|
987
999
|
}
|
|
988
1000
|
}
|
|
989
1001
|
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
return sidecarsUnfinalized;
|
|
1002
|
+
for (const sidecar of await this.db.dataColumns.getAll({slot: blockSlot, blockRoot: blockRootHex})) {
|
|
1003
|
+
if (!sidecarsByIndex.has(sidecar.index)) sidecarsByIndex.set(sidecar.index, sidecar);
|
|
993
1004
|
}
|
|
994
|
-
|
|
995
|
-
return
|
|
1005
|
+
|
|
1006
|
+
return [...sidecarsByIndex.values()].sort((a, b) => a.index - b.index);
|
|
996
1007
|
}
|
|
997
1008
|
|
|
998
1009
|
async getSerializedDataColumnSidecars(
|
|
@@ -1001,22 +1012,25 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1001
1012
|
indices: number[]
|
|
1002
1013
|
): Promise<(Uint8Array | undefined)[]> {
|
|
1003
1014
|
const fork = this.config.getForkName(blockSlot);
|
|
1015
|
+
const dataColumnSidecars: (Uint8Array | undefined)[] = indices.map(() => undefined);
|
|
1004
1016
|
|
|
1005
1017
|
if (isForkPostGloas(fork)) {
|
|
1006
1018
|
// After gloas, columns are tracked in PayloadEnvelopeInput
|
|
1007
1019
|
const payloadInput = this.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
1008
1020
|
if (payloadInput) {
|
|
1009
|
-
|
|
1021
|
+
for (let i = 0; i < indices.length; i++) {
|
|
1022
|
+
const index = indices[i];
|
|
1010
1023
|
const sidecar = payloadInput.getColumn(index);
|
|
1011
1024
|
if (!sidecar) {
|
|
1012
|
-
|
|
1025
|
+
continue;
|
|
1013
1026
|
}
|
|
1014
1027
|
const serialized = this.serializedCache.get(sidecar);
|
|
1015
1028
|
if (serialized) {
|
|
1016
|
-
|
|
1029
|
+
dataColumnSidecars[i] = serialized;
|
|
1030
|
+
} else {
|
|
1031
|
+
dataColumnSidecars[i] = sszTypesFor(fork as ForkPostGloas).DataColumnSidecar.serialize(sidecar);
|
|
1017
1032
|
}
|
|
1018
|
-
|
|
1019
|
-
});
|
|
1033
|
+
}
|
|
1020
1034
|
}
|
|
1021
1035
|
} else {
|
|
1022
1036
|
// Before gloas, columns are tracked in BlockInput
|
|
@@ -1025,26 +1039,42 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1025
1039
|
if (!isBlockInputColumns(blockInput)) {
|
|
1026
1040
|
throw new Error(`Expected block input to have columns: slot=${blockSlot} root=${blockRootHex}`);
|
|
1027
1041
|
}
|
|
1028
|
-
|
|
1042
|
+
for (let i = 0; i < indices.length; i++) {
|
|
1043
|
+
const index = indices[i];
|
|
1029
1044
|
const sidecar = blockInput.getColumn(index);
|
|
1030
1045
|
if (!sidecar) {
|
|
1031
|
-
|
|
1046
|
+
continue;
|
|
1032
1047
|
}
|
|
1033
1048
|
const serialized = this.serializedCache.get(sidecar);
|
|
1034
1049
|
if (serialized) {
|
|
1035
|
-
|
|
1050
|
+
dataColumnSidecars[i] = serialized;
|
|
1051
|
+
} else {
|
|
1052
|
+
dataColumnSidecars[i] = sszTypesFor(blockInput.forkName as ForkPostFulu).DataColumnSidecar.serialize(
|
|
1053
|
+
sidecar
|
|
1054
|
+
);
|
|
1036
1055
|
}
|
|
1037
|
-
|
|
1038
|
-
});
|
|
1056
|
+
}
|
|
1039
1057
|
}
|
|
1040
1058
|
}
|
|
1041
1059
|
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1060
|
+
const missingPositions = dataColumnSidecars
|
|
1061
|
+
.map((sidecar, position) => (sidecar === undefined ? position : -1))
|
|
1062
|
+
.filter((position) => position !== -1);
|
|
1063
|
+
|
|
1064
|
+
if (missingPositions.length > 0) {
|
|
1065
|
+
const persistedSidecars = await this.db.dataColumns.getManyBinary(
|
|
1066
|
+
{slot: blockSlot, blockRoot: blockRootHex},
|
|
1067
|
+
missingPositions.map((position) => indices[position])
|
|
1068
|
+
);
|
|
1069
|
+
for (let i = 0; i < missingPositions.length; i++) {
|
|
1070
|
+
const sidecar = persistedSidecars[i];
|
|
1071
|
+
if (sidecar !== undefined) {
|
|
1072
|
+
dataColumnSidecars[missingPositions[i]] = sidecar;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1045
1075
|
}
|
|
1046
|
-
|
|
1047
|
-
return
|
|
1076
|
+
|
|
1077
|
+
return dataColumnSidecars;
|
|
1048
1078
|
}
|
|
1049
1079
|
|
|
1050
1080
|
async produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody> {
|
|
@@ -1271,59 +1301,65 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1271
1301
|
try {
|
|
1272
1302
|
const prevHead = this.forkChoice.getHead();
|
|
1273
1303
|
const head = this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head;
|
|
1304
|
+
this.emitHeadEvents(prevHead, head);
|
|
1305
|
+
return head;
|
|
1306
|
+
} catch (e) {
|
|
1307
|
+
this.metrics?.forkChoice.errors.inc({entrypoint: UpdateHeadOpt.GetCanonicalHead});
|
|
1308
|
+
throw e;
|
|
1309
|
+
} finally {
|
|
1310
|
+
timer?.();
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1274
1313
|
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1314
|
+
/**
|
|
1315
|
+
* Emit `head` and `head_v2` when the fork choice head differs between two observations. Every caller that
|
|
1316
|
+
* can move the head, `updateAndGetHead()` and `updateTime()`, must capture the head before and pass both.
|
|
1317
|
+
*/
|
|
1318
|
+
private emitHeadEvents(prevHead: ProtoBlock, head: ProtoBlock): void {
|
|
1319
|
+
const headRootChanged = head.blockRoot !== prevHead.blockRoot;
|
|
1280
1320
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
const epochTransition = computeStartSlotAtEpoch(computeEpochAtSlot(head.slot)) === head.slot;
|
|
1285
|
-
const executionOptimistic = isOptimisticBlock(head);
|
|
1286
|
-
|
|
1287
|
-
if (headRootChanged) {
|
|
1288
|
-
this.emitter.emit(routes.events.EventType.head, {
|
|
1289
|
-
block: head.blockRoot,
|
|
1290
|
-
epochTransition,
|
|
1291
|
-
slot: head.slot,
|
|
1292
|
-
state: head.stateRoot,
|
|
1293
|
-
previousDutyDependentRoot,
|
|
1294
|
-
currentDutyDependentRoot,
|
|
1295
|
-
executionOptimistic,
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1321
|
+
if (!headRootChanged && prevHead.payloadStatus === head.payloadStatus) {
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1298
1324
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1325
|
+
try {
|
|
1326
|
+
const previousDutyDependentRoot = this.forkChoice.getDependentRoot(head, EpochDifference.previous);
|
|
1327
|
+
const currentDutyDependentRoot = this.forkChoice.getDependentRoot(head, EpochDifference.current);
|
|
1328
|
+
const epochTransition = computeStartSlotAtEpoch(computeEpochAtSlot(head.slot)) === head.slot;
|
|
1329
|
+
const executionOptimistic = isOptimisticBlock(head);
|
|
1330
|
+
|
|
1331
|
+
if (headRootChanged) {
|
|
1332
|
+
this.emitter.emit(routes.events.EventType.head, {
|
|
1333
|
+
block: head.blockRoot,
|
|
1334
|
+
epochTransition,
|
|
1335
|
+
slot: head.slot,
|
|
1336
|
+
state: head.stateRoot,
|
|
1337
|
+
previousDutyDependentRoot,
|
|
1338
|
+
currentDutyDependentRoot,
|
|
1339
|
+
executionOptimistic,
|
|
1311
1340
|
});
|
|
1312
|
-
} catch (e) {
|
|
1313
|
-
// getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue
|
|
1314
|
-
this.logger.debug(
|
|
1315
|
-
"Error emitting head/head_v2 event",
|
|
1316
|
-
{slot: head.slot, root: head.blockRoot, headRootChanged},
|
|
1317
|
-
e as Error
|
|
1318
|
-
);
|
|
1319
1341
|
}
|
|
1320
1342
|
|
|
1321
|
-
|
|
1343
|
+
this.emitter.emit(routes.events.EventType.headV2, {
|
|
1344
|
+
version: this.config.getForkName(head.slot),
|
|
1345
|
+
data: {
|
|
1346
|
+
slot: head.slot,
|
|
1347
|
+
block: head.blockRoot,
|
|
1348
|
+
state: head.stateRoot,
|
|
1349
|
+
payloadStatus: head.payloadStatus === PayloadStatus.FULL ? "full" : "empty",
|
|
1350
|
+
epochTransition,
|
|
1351
|
+
currentEpochDependentRoot: previousDutyDependentRoot,
|
|
1352
|
+
nextEpochDependentRoot: currentDutyDependentRoot,
|
|
1353
|
+
executionOptimistic,
|
|
1354
|
+
},
|
|
1355
|
+
});
|
|
1322
1356
|
} catch (e) {
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1357
|
+
// getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue
|
|
1358
|
+
this.logger.debug(
|
|
1359
|
+
"Error emitting head/head_v2 event",
|
|
1360
|
+
{slot: head.slot, root: head.blockRoot, headRootChanged},
|
|
1361
|
+
e as Error
|
|
1362
|
+
);
|
|
1327
1363
|
}
|
|
1328
1364
|
}
|
|
1329
1365
|
|
|
@@ -1381,10 +1417,15 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1381
1417
|
const slot = data.slot;
|
|
1382
1418
|
if (isBlindedBeaconBlock(data)) {
|
|
1383
1419
|
const sszType = this.config.getPostBellatrixForkTypes(slot).BlindedBeaconBlock;
|
|
1384
|
-
void this.persistSszObject(
|
|
1420
|
+
void this.persistSszObject(
|
|
1421
|
+
"BlindedBeaconBlock",
|
|
1422
|
+
sszType.serialize(data),
|
|
1423
|
+
toRootHex(sszType.hashTreeRoot(data)),
|
|
1424
|
+
suffix
|
|
1425
|
+
);
|
|
1385
1426
|
} else {
|
|
1386
1427
|
const sszType = this.config.getForkTypes(slot).BeaconBlock;
|
|
1387
|
-
void this.persistSszObject("BeaconBlock", sszType.serialize(data), sszType.hashTreeRoot(data), suffix);
|
|
1428
|
+
void this.persistSszObject("BeaconBlock", sszType.serialize(data), toRootHex(sszType.hashTreeRoot(data)), suffix);
|
|
1388
1429
|
}
|
|
1389
1430
|
}
|
|
1390
1431
|
|
|
@@ -1405,33 +1446,39 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1405
1446
|
this.persistSszObject(
|
|
1406
1447
|
`SignedBeaconBlock_slot_${blockSlot}`,
|
|
1407
1448
|
blockType.serialize(block),
|
|
1408
|
-
|
|
1449
|
+
toRootHex(this.config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block.message)),
|
|
1409
1450
|
`${logStr}_block`
|
|
1410
1451
|
),
|
|
1411
1452
|
this.persistSszObject(
|
|
1412
1453
|
`preState_slot_${preState.slot}_BeaconState`,
|
|
1413
1454
|
preState.serialize(),
|
|
1414
|
-
preState.hashTreeRoot(),
|
|
1455
|
+
toRootHex(preState.hashTreeRoot()),
|
|
1415
1456
|
`${logStr}_pre_state`
|
|
1416
1457
|
),
|
|
1417
1458
|
this.persistSszObject(
|
|
1418
1459
|
`postState_slot_${postState.slot}_BeaconState`,
|
|
1419
1460
|
postState.serialize(),
|
|
1420
|
-
|
|
1461
|
+
toRootHex(postStateRoot),
|
|
1421
1462
|
`${logStr}_post_state`
|
|
1422
1463
|
),
|
|
1423
1464
|
]);
|
|
1424
1465
|
}
|
|
1425
1466
|
|
|
1426
|
-
persistInvalidSszValue<T>(type: Type<T>, sszObject: T, suffix?: string): void {
|
|
1467
|
+
persistInvalidSszValue<T>(type: Type<T>, sszObject: T, suffix?: string, rootHex?: RootHex): void {
|
|
1427
1468
|
if (this.opts.persistInvalidSszObjects) {
|
|
1428
|
-
void this.persistSszObject(
|
|
1469
|
+
void this.persistSszObject(
|
|
1470
|
+
type.typeName,
|
|
1471
|
+
type.serialize(sszObject),
|
|
1472
|
+
// in SignedBeaconBlock case, we want to use BeaconBlock root instead
|
|
1473
|
+
rootHex ?? toRootHex(type.hashTreeRoot(sszObject)),
|
|
1474
|
+
suffix
|
|
1475
|
+
);
|
|
1429
1476
|
}
|
|
1430
1477
|
}
|
|
1431
1478
|
|
|
1432
|
-
persistInvalidSszBytes(typeName: string, sszBytes: Uint8Array, suffix?: string): void {
|
|
1479
|
+
persistInvalidSszBytes(typeName: string, sszBytes: Uint8Array, rootHex: RootHex, suffix?: string): void {
|
|
1433
1480
|
if (this.opts.persistInvalidSszObjects) {
|
|
1434
|
-
void this.persistSszObject(typeName, sszBytes,
|
|
1481
|
+
void this.persistSszObject(typeName, sszBytes, rootHex, suffix);
|
|
1435
1482
|
}
|
|
1436
1483
|
}
|
|
1437
1484
|
|
|
@@ -1562,14 +1609,14 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1562
1609
|
return {state: blockState, stateId: "block_state_any_epoch", shouldWarn: true};
|
|
1563
1610
|
}
|
|
1564
1611
|
|
|
1565
|
-
private async persistSszObject(prefix: string, bytes: Uint8Array,
|
|
1612
|
+
private async persistSszObject(prefix: string, bytes: Uint8Array, rootHex: RootHex, logStr?: string): Promise<void> {
|
|
1566
1613
|
const now = new Date();
|
|
1567
1614
|
// yyyy-MM-dd
|
|
1568
1615
|
const dateStr = now.toISOString().split("T")[0];
|
|
1569
1616
|
|
|
1570
1617
|
// by default store to lodestar_archive of current dir
|
|
1571
1618
|
const dirpath = path.join(this.opts.persistInvalidSszObjectsDir ?? "invalid_ssz_objects", dateStr);
|
|
1572
|
-
const filepath = path.join(dirpath, `${prefix}_${
|
|
1619
|
+
const filepath = path.join(dirpath, `${prefix}_${rootHex}.ssz`);
|
|
1573
1620
|
|
|
1574
1621
|
await ensureDir(dirpath);
|
|
1575
1622
|
|
|
@@ -1612,7 +1659,10 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1612
1659
|
this.processShutdownCallback(this.forkChoice.irrecoverableError);
|
|
1613
1660
|
}
|
|
1614
1661
|
|
|
1662
|
+
// updateTime() recomputes the head on epoch-boundary checkpoint pull-up and under fast confirmation
|
|
1663
|
+
const prevHead = this.forkChoice.getHead();
|
|
1615
1664
|
this.forkChoice.updateTime(slot);
|
|
1665
|
+
this.emitHeadEvents(prevHead, this.forkChoice.getHead());
|
|
1616
1666
|
this.metrics?.clockSlot.set(slot);
|
|
1617
1667
|
|
|
1618
1668
|
this.attestationPool.prune(slot);
|
|
@@ -1670,6 +1720,25 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1670
1720
|
|
|
1671
1721
|
private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
|
|
1672
1722
|
this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
|
|
1723
|
+
this.metrics?.finalizedEpoch.set(cp.epoch);
|
|
1724
|
+
const finalizedBlock = this.forkChoice.getBlockHexDefaultStatus(cp.rootHex);
|
|
1725
|
+
if (finalizedBlock) {
|
|
1726
|
+
// The callback runs synchronously inside fork choice checkpoint updates, defer writing to subscribers
|
|
1727
|
+
callInNextEventLoop(() => {
|
|
1728
|
+
this.emitter.emit(routes.events.EventType.finalizedCheckpoint, {
|
|
1729
|
+
block: cp.rootHex,
|
|
1730
|
+
epoch: cp.epoch,
|
|
1731
|
+
state: finalizedBlock.stateRoot,
|
|
1732
|
+
executionOptimistic: isOptimisticBlock(finalizedBlock),
|
|
1733
|
+
});
|
|
1734
|
+
});
|
|
1735
|
+
} else {
|
|
1736
|
+
this.logger.debug("Finalized block not found in fork choice, skip finalized_checkpoint event", {
|
|
1737
|
+
epoch: cp.epoch,
|
|
1738
|
+
root: cp.rootHex,
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1673
1742
|
const finalizedSlot = computeStartSlotAtEpoch(cp.epoch);
|
|
1674
1743
|
this.seenBlockProposers.prune(finalizedSlot);
|
|
1675
1744
|
|
package/src/chain/emitter.ts
CHANGED
|
@@ -33,7 +33,7 @@ export enum ChainEvent {
|
|
|
33
33
|
/**
|
|
34
34
|
* This event signals that the fork choice store has been updated.
|
|
35
35
|
*
|
|
36
|
-
* This event is guaranteed to be triggered whenever the fork choice
|
|
36
|
+
* This event is guaranteed to be triggered whenever the fork choice finalized checkpoint is updated. This is either in response to a newly processed block or a new clock tick.
|
|
37
37
|
*/
|
|
38
38
|
forkChoiceFinalized = "forkChoice:finalized",
|
|
39
39
|
/**
|