@lodestar/beacon-node 1.36.0-dev.f259361847 → 1.36.0-dev.f3703b7882
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 +41 -22
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts +5 -0
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +35 -10
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/node/utils.js +1 -1
- package/lib/api/impl/node/utils.js.map +1 -1
- package/lib/chain/chain.d.ts +5 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +32 -16
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blobSidecarError.d.ts +5 -0
- package/lib/chain/errors/blobSidecarError.d.ts.map +1 -1
- package/lib/chain/errors/blobSidecarError.js.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.d.ts +21 -14
- package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.js +4 -0
- package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts +9 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +109 -4
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -0
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/options.d.ts +0 -2
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +2 -2
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/stateCache/datastore/db.d.ts +12 -0
- package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
- package/lib/chain/stateCache/datastore/db.js +70 -0
- package/lib/chain/stateCache/datastore/db.js.map +1 -1
- package/lib/chain/stateCache/datastore/file.d.ts +1 -0
- package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
- package/lib/chain/stateCache/datastore/file.js +7 -0
- package/lib/chain/stateCache/datastore/file.js.map +1 -1
- package/lib/chain/stateCache/datastore/types.d.ts +1 -0
- package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +16 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +31 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/validation/blobSidecar.d.ts +4 -1
- package/lib/chain/validation/blobSidecar.d.ts.map +1 -1
- package/lib/chain/validation/blobSidecar.js +46 -11
- package/lib/chain/validation/blobSidecar.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts +4 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +64 -19
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/metrics/metrics/lodestar.js +1 -1
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/core/networkCore.d.ts.map +1 -1
- package/lib/network/core/networkCore.js +5 -1
- package/lib/network/core/networkCore.js.map +1 -1
- package/lib/network/core/networkCoreWorker.js +8 -8
- package/lib/network/core/networkCoreWorker.js.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.js +1 -1
- package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
- package/lib/network/discv5/worker.js +2 -7
- package/lib/network/discv5/worker.js.map +1 -1
- package/lib/network/events.d.ts +1 -0
- package/lib/network/events.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +1 -1
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +6 -1
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/interface.d.ts +2 -0
- package/lib/network/gossip/interface.d.ts.map +1 -1
- package/lib/network/gossip/snappy_bun.d.ts +3 -0
- package/lib/network/gossip/snappy_bun.d.ts.map +1 -0
- package/lib/network/gossip/snappy_bun.js +3 -0
- package/lib/network/gossip/snappy_bun.js.map +1 -0
- package/lib/network/metadata.d.ts +1 -1
- package/lib/network/metadata.d.ts.map +1 -1
- package/lib/network/metadata.js +1 -0
- package/lib/network/metadata.js.map +1 -1
- package/lib/network/options.d.ts +0 -1
- package/lib/network/options.d.ts.map +1 -1
- package/lib/network/options.js.map +1 -1
- package/lib/network/peers/discover.js +2 -2
- package/lib/network/peers/discover.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +15 -1
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +3 -2
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/types.d.ts +2 -0
- package/lib/network/processor/types.d.ts.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.d.ts.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js +3 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts +2 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -3
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts +2 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +9 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts +2 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +9 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/index.js +6 -6
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/types.d.ts +1 -0
- package/lib/network/reqresp/types.d.ts.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 +2 -1
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +2 -1
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.js +1 -1
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +59 -15
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +204 -83
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts +8 -14
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +18 -33
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/sync/utils/remoteSyncType.d.ts +2 -1
- package/lib/sync/utils/remoteSyncType.d.ts.map +1 -1
- package/lib/sync/utils/remoteSyncType.js +19 -4
- package/lib/sync/utils/remoteSyncType.js.map +1 -1
- package/lib/util/blobs.d.ts +1 -1
- package/lib/util/blobs.d.ts.map +1 -1
- package/lib/util/blobs.js +53 -20
- package/lib/util/blobs.js.map +1 -1
- package/lib/util/profile.d.ts +6 -4
- package/lib/util/profile.d.ts.map +1 -1
- package/lib/util/profile.js +40 -3
- package/lib/util/profile.js.map +1 -1
- package/lib/util/sszBytes.d.ts +2 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +25 -0
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +32 -25
- package/src/api/impl/beacon/blocks/index.ts +47 -25
- package/src/api/impl/lodestar/index.ts +42 -10
- package/src/api/impl/node/utils.ts +1 -1
- package/src/chain/chain.ts +48 -23
- package/src/chain/errors/blobSidecarError.ts +12 -2
- package/src/chain/errors/dataColumnSidecarError.ts +31 -16
- package/src/chain/forkChoice/index.ts +178 -2
- package/src/chain/interface.ts +2 -0
- package/src/chain/options.ts +2 -3
- package/src/chain/stateCache/datastore/db.ts +89 -1
- package/src/chain/stateCache/datastore/file.ts +8 -0
- package/src/chain/stateCache/datastore/types.ts +1 -0
- package/src/chain/stateCache/persistentCheckpointsCache.ts +45 -2
- package/src/chain/validation/blobSidecar.ts +54 -10
- package/src/chain/validation/dataColumnSidecar.ts +76 -19
- package/src/index.ts +2 -0
- package/src/metrics/metrics/lodestar.ts +1 -1
- package/src/network/core/networkCore.ts +5 -1
- package/src/network/core/networkCoreWorker.ts +9 -9
- package/src/network/core/networkCoreWorkerHandler.ts +1 -1
- package/src/network/discv5/worker.ts +2 -7
- package/src/network/events.ts +1 -1
- package/src/network/gossip/encoding.ts +1 -1
- package/src/network/gossip/gossipsub.ts +7 -1
- package/src/network/gossip/interface.ts +2 -0
- package/src/network/gossip/snappy_bun.ts +2 -0
- package/src/network/metadata.ts +3 -1
- package/src/network/options.ts +0 -1
- package/src/network/peers/discover.ts +2 -2
- package/src/network/processor/gossipHandlers.ts +16 -1
- package/src/network/processor/gossipValidatorFn.ts +15 -2
- package/src/network/processor/types.ts +2 -0
- package/src/network/reqresp/ReqRespBeaconNode.ts +3 -1
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +18 -3
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +13 -1
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +13 -1
- package/src/network/reqresp/handlers/index.ts +6 -6
- package/src/network/reqresp/types.ts +1 -0
- package/src/node/nodejs.ts +3 -0
- package/src/sync/range/range.ts +2 -1
- package/src/sync/unknownBlock.ts +1 -1
- package/src/sync/utils/downloadByRange.ts +273 -108
- package/src/sync/utils/downloadByRoot.ts +22 -56
- package/src/sync/utils/remoteSyncType.ts +23 -4
- package/src/util/blobs.ts +64 -20
- package/src/util/profile.ts +45 -3
- package/src/util/sszBytes.ts +30 -0
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
isForkPostFulu,
|
|
10
10
|
} from "@lodestar/params";
|
|
11
11
|
import {BeaconBlockBody, BlobIndex, ColumnIndex, SignedBeaconBlock, Slot, deneb, fulu} from "@lodestar/types";
|
|
12
|
-
import {LodestarError, fromHex,
|
|
12
|
+
import {LodestarError, fromHex, prettyPrintIndices, toHex, toRootHex} from "@lodestar/utils";
|
|
13
13
|
import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
|
|
14
14
|
import {BlockInputSource, IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
15
15
|
import {ChainEventEmitter} from "../../chain/emitter.js";
|
|
16
|
-
import {
|
|
16
|
+
import {IBeaconChain} from "../../chain/interface.ts";
|
|
17
17
|
import {validateBlockBlobSidecars} from "../../chain/validation/blobSidecar.js";
|
|
18
18
|
import {validateBlockDataColumnSidecars} from "../../chain/validation/dataColumnSidecar.js";
|
|
19
19
|
import {INetwork} from "../../network/interface.js";
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
|
|
33
33
|
export type FetchByRootCoreProps = {
|
|
34
34
|
config: ChainForkConfig;
|
|
35
|
+
chain: IBeaconChain | null; // null for testing purposes
|
|
35
36
|
network: INetwork;
|
|
36
37
|
peerMeta: PeerSyncMeta;
|
|
37
38
|
};
|
|
@@ -63,13 +64,13 @@ export type FetchByRootResponses = {
|
|
|
63
64
|
|
|
64
65
|
export type DownloadByRootProps = FetchByRootCoreProps & {
|
|
65
66
|
cacheItem: BlockInputSyncCacheItem;
|
|
66
|
-
|
|
67
|
+
chain: IBeaconChain;
|
|
67
68
|
emitter: ChainEventEmitter;
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
export async function downloadByRoot({
|
|
71
72
|
config,
|
|
72
|
-
|
|
73
|
+
chain,
|
|
73
74
|
network,
|
|
74
75
|
emitter,
|
|
75
76
|
peerMeta,
|
|
@@ -84,6 +85,7 @@ export async function downloadByRoot({
|
|
|
84
85
|
warnings,
|
|
85
86
|
} = await fetchByRoot({
|
|
86
87
|
config,
|
|
88
|
+
chain,
|
|
87
89
|
network,
|
|
88
90
|
cacheItem,
|
|
89
91
|
blockRoot,
|
|
@@ -103,7 +105,7 @@ export async function downloadByRoot({
|
|
|
103
105
|
});
|
|
104
106
|
}
|
|
105
107
|
} else {
|
|
106
|
-
blockInput =
|
|
108
|
+
blockInput = chain.seenBlockInputCache.getByBlock({
|
|
107
109
|
block,
|
|
108
110
|
peerIdStr,
|
|
109
111
|
blockRootHex: rootHex,
|
|
@@ -119,7 +121,7 @@ export async function downloadByRoot({
|
|
|
119
121
|
if (!blobSidecars) {
|
|
120
122
|
throw new DownloadByRootError({
|
|
121
123
|
code: DownloadByRootErrorCode.MISSING_BLOB_RESPONSE,
|
|
122
|
-
blockRoot:
|
|
124
|
+
blockRoot: rootHex,
|
|
123
125
|
peer: peerIdStr,
|
|
124
126
|
});
|
|
125
127
|
}
|
|
@@ -157,7 +159,7 @@ export async function downloadByRoot({
|
|
|
157
159
|
if (!columnSidecars) {
|
|
158
160
|
throw new DownloadByRootError({
|
|
159
161
|
code: DownloadByRootErrorCode.MISSING_COLUMN_RESPONSE,
|
|
160
|
-
blockRoot:
|
|
162
|
+
blockRoot: rootHex,
|
|
161
163
|
peer: peerIdStr,
|
|
162
164
|
});
|
|
163
165
|
}
|
|
@@ -210,6 +212,7 @@ export async function downloadByRoot({
|
|
|
210
212
|
|
|
211
213
|
export async function fetchByRoot({
|
|
212
214
|
config,
|
|
215
|
+
chain,
|
|
213
216
|
network,
|
|
214
217
|
peerMeta,
|
|
215
218
|
blockRoot,
|
|
@@ -237,6 +240,7 @@ export async function fetchByRoot({
|
|
|
237
240
|
if (isBlockInputBlobs(cacheItem.blockInput)) {
|
|
238
241
|
blobSidecars = await fetchAndValidateBlobs({
|
|
239
242
|
config,
|
|
243
|
+
chain,
|
|
240
244
|
network,
|
|
241
245
|
peerIdStr,
|
|
242
246
|
forkName: forkName as ForkPreFulu,
|
|
@@ -248,6 +252,7 @@ export async function fetchByRoot({
|
|
|
248
252
|
if (isBlockInputColumns(cacheItem.blockInput)) {
|
|
249
253
|
columnSidecarResult = await fetchAndValidateColumns({
|
|
250
254
|
config,
|
|
255
|
+
chain,
|
|
251
256
|
network,
|
|
252
257
|
peerMeta,
|
|
253
258
|
forkName: forkName as ForkPostFulu,
|
|
@@ -268,6 +273,7 @@ export async function fetchByRoot({
|
|
|
268
273
|
if (isForkPostFulu(forkName)) {
|
|
269
274
|
columnSidecarResult = await fetchAndValidateColumns({
|
|
270
275
|
config,
|
|
276
|
+
chain,
|
|
271
277
|
network,
|
|
272
278
|
peerMeta,
|
|
273
279
|
forkName,
|
|
@@ -280,6 +286,7 @@ export async function fetchByRoot({
|
|
|
280
286
|
const blobCount = commitments.length;
|
|
281
287
|
blobSidecars = await fetchAndValidateBlobs({
|
|
282
288
|
config,
|
|
289
|
+
chain,
|
|
283
290
|
network,
|
|
284
291
|
peerIdStr,
|
|
285
292
|
forkName: forkName as ForkPreFulu,
|
|
@@ -305,14 +312,14 @@ export async function fetchAndValidateBlock({
|
|
|
305
312
|
network,
|
|
306
313
|
peerIdStr,
|
|
307
314
|
blockRoot,
|
|
308
|
-
}: FetchByRootAndValidateBlockProps): Promise<SignedBeaconBlock> {
|
|
315
|
+
}: Omit<FetchByRootAndValidateBlockProps, "chain">): Promise<SignedBeaconBlock> {
|
|
309
316
|
const response = await network.sendBeaconBlocksByRoot(peerIdStr, [blockRoot]);
|
|
310
317
|
const block = response.at(0)?.data;
|
|
311
318
|
if (!block) {
|
|
312
319
|
throw new DownloadByRootError({
|
|
313
320
|
code: DownloadByRootErrorCode.MISSING_BLOCK_RESPONSE,
|
|
314
321
|
peer: prettyPrintPeerIdStr(peerIdStr),
|
|
315
|
-
blockRoot:
|
|
322
|
+
blockRoot: toRootHex(blockRoot),
|
|
316
323
|
});
|
|
317
324
|
}
|
|
318
325
|
const receivedRoot = config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message);
|
|
@@ -321,8 +328,8 @@ export async function fetchAndValidateBlock({
|
|
|
321
328
|
{
|
|
322
329
|
code: DownloadByRootErrorCode.MISMATCH_BLOCK_ROOT,
|
|
323
330
|
peer: prettyPrintPeerIdStr(peerIdStr),
|
|
324
|
-
requestedBlockRoot:
|
|
325
|
-
receivedBlockRoot:
|
|
331
|
+
requestedBlockRoot: toRootHex(blockRoot),
|
|
332
|
+
receivedBlockRoot: toRootHex(receivedRoot),
|
|
326
333
|
},
|
|
327
334
|
"block does not match requested root"
|
|
328
335
|
);
|
|
@@ -331,6 +338,7 @@ export async function fetchAndValidateBlock({
|
|
|
331
338
|
}
|
|
332
339
|
|
|
333
340
|
export async function fetchAndValidateBlobs({
|
|
341
|
+
chain,
|
|
334
342
|
network,
|
|
335
343
|
peerIdStr,
|
|
336
344
|
blockRoot,
|
|
@@ -344,7 +352,7 @@ export async function fetchAndValidateBlobs({
|
|
|
344
352
|
missing,
|
|
345
353
|
});
|
|
346
354
|
|
|
347
|
-
await validateBlockBlobSidecars(block.message.slot, blockRoot, missing.length, blobSidecars);
|
|
355
|
+
await validateBlockBlobSidecars(chain, block.message.slot, blockRoot, missing.length, blobSidecars);
|
|
348
356
|
|
|
349
357
|
return blobSidecars;
|
|
350
358
|
}
|
|
@@ -368,6 +376,7 @@ export async function fetchBlobsByRoot({
|
|
|
368
376
|
}
|
|
369
377
|
|
|
370
378
|
export async function fetchAndValidateColumns({
|
|
379
|
+
chain,
|
|
371
380
|
network,
|
|
372
381
|
peerMeta,
|
|
373
382
|
block,
|
|
@@ -438,7 +447,7 @@ export async function fetchAndValidateColumns({
|
|
|
438
447
|
);
|
|
439
448
|
}
|
|
440
449
|
|
|
441
|
-
await validateBlockDataColumnSidecars(slot, blockRoot, blobCount, columnSidecars);
|
|
450
|
+
await validateBlockDataColumnSidecars(chain, slot, blockRoot, blobCount, columnSidecars);
|
|
442
451
|
|
|
443
452
|
return {result: columnSidecars, warnings: warnings.length > 0 ? warnings : null};
|
|
444
453
|
}
|
|
@@ -456,49 +465,6 @@ export async function fetchColumnsByRoot({
|
|
|
456
465
|
return await network.sendDataColumnSidecarsByRoot(peerMeta.peerId, [{blockRoot, columns: missing}]);
|
|
457
466
|
}
|
|
458
467
|
|
|
459
|
-
// TODO(fulu) not in use, remove?
|
|
460
|
-
export type ValidateColumnSidecarsProps = Pick<
|
|
461
|
-
FetchByRootAndValidateColumnsProps,
|
|
462
|
-
"config" | "peerMeta" | "blockRoot" | "missing"
|
|
463
|
-
> & {
|
|
464
|
-
slot: number;
|
|
465
|
-
blobCount: number;
|
|
466
|
-
needed?: fulu.DataColumnSidecars;
|
|
467
|
-
needToPublish?: fulu.DataColumnSidecars;
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
// TODO(fulu) not in use, remove?
|
|
471
|
-
export async function validateColumnSidecars({
|
|
472
|
-
peerMeta,
|
|
473
|
-
slot,
|
|
474
|
-
blockRoot,
|
|
475
|
-
blobCount,
|
|
476
|
-
missing,
|
|
477
|
-
needed = [],
|
|
478
|
-
needToPublish = [],
|
|
479
|
-
}: ValidateColumnSidecarsProps): Promise<void> {
|
|
480
|
-
const requestedIndices = missing;
|
|
481
|
-
const extraIndices: number[] = [];
|
|
482
|
-
for (const columnSidecar of needed) {
|
|
483
|
-
if (!requestedIndices.includes(columnSidecar.index)) {
|
|
484
|
-
extraIndices.push(columnSidecar.index);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
if (extraIndices.length > 0) {
|
|
488
|
-
throw new DownloadByRootError(
|
|
489
|
-
{
|
|
490
|
-
code: DownloadByRootErrorCode.EXTRA_SIDECAR_RECEIVED,
|
|
491
|
-
peer: prettyPrintPeerIdStr(peerMeta.peerId),
|
|
492
|
-
slot,
|
|
493
|
-
blockRoot: prettyBytes(blockRoot),
|
|
494
|
-
invalidIndices: prettyPrintIndices(extraIndices),
|
|
495
|
-
},
|
|
496
|
-
"Received a columnSidecar that was not requested"
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
await validateBlockDataColumnSidecars(slot, blockRoot, blobCount, [...needed, ...needToPublish]);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
468
|
export enum DownloadByRootErrorCode {
|
|
503
469
|
MISMATCH_BLOCK_ROOT = "DOWNLOAD_BY_ROOT_ERROR_MISMATCH_BLOCK_ROOT",
|
|
504
470
|
EXTRA_SIDECAR_RECEIVED = "DOWNLOAD_BY_ROOT_ERROR_EXTRA_SIDECAR_RECEIVED",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {IForkChoice} from "@lodestar/fork-choice";
|
|
2
2
|
import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
3
3
|
import {Slot, Status} from "@lodestar/types";
|
|
4
|
+
import {IBeaconChain} from "../../chain/interface.ts";
|
|
4
5
|
import {ChainTarget} from "../range/utils/index.js";
|
|
5
6
|
|
|
6
7
|
/** The type of peer relative to our current state */
|
|
@@ -103,8 +104,11 @@ export function getRangeSyncType(local: Status, remote: Status, forkChoice: IFor
|
|
|
103
104
|
export function getRangeSyncTarget(
|
|
104
105
|
local: Status,
|
|
105
106
|
remote: Status,
|
|
106
|
-
|
|
107
|
+
chain: IBeaconChain
|
|
107
108
|
): {syncType: RangeSyncType; startEpoch: Slot; target: ChainTarget} {
|
|
109
|
+
const forkChoice = chain.forkChoice;
|
|
110
|
+
|
|
111
|
+
// finalized sync
|
|
108
112
|
if (remote.finalizedEpoch > local.finalizedEpoch && !forkChoice.hasBlock(remote.finalizedRoot)) {
|
|
109
113
|
return {
|
|
110
114
|
// If RangeSyncType.Finalized, the range of blocks fetchable from startEpoch and target must allow to switch
|
|
@@ -131,11 +135,26 @@ export function getRangeSyncTarget(
|
|
|
131
135
|
},
|
|
132
136
|
};
|
|
133
137
|
}
|
|
138
|
+
|
|
139
|
+
// we don't want to sync from epoch < minEpoch
|
|
140
|
+
// if we boot from an unfinalized checkpoint state, we don't want to sync before anchorStateLatestBlockSlot
|
|
141
|
+
// if we boot from a finalized checkpoint state, anchorStateLatestBlockSlot is trusted and we also don't want to sync before it
|
|
142
|
+
const minEpoch = Math.max(remote.finalizedEpoch, computeEpochAtSlot(chain.anchorStateLatestBlockSlot));
|
|
143
|
+
|
|
144
|
+
// head sync
|
|
134
145
|
return {
|
|
135
146
|
syncType: RangeSyncType.Head,
|
|
136
|
-
// The new peer has the same finalized
|
|
137
|
-
//
|
|
138
|
-
|
|
147
|
+
// The new peer has the same finalized `remote.finalizedEpoch == local.finalizedEpoch` since
|
|
148
|
+
// previous filters should prevent a peer with an earlier finalized chain from reaching here.
|
|
149
|
+
//
|
|
150
|
+
// By default and during stable network conditions, the head sync always starts from
|
|
151
|
+
// the finalized epoch (even though it's the head sync) because finalized epoch is < local head.
|
|
152
|
+
// This is to prevent the issue noted here https://github.com/ChainSafe/lodestar/pull/7509#discussion_r1984353063.
|
|
153
|
+
//
|
|
154
|
+
// During non-finality of the network, when starting from an unfinalized checkpoint state, we don't want
|
|
155
|
+
// to sync before anchorStateLatestBlockSlot as finalized epoch is too far away. Local head will also be
|
|
156
|
+
// the same to that value at startup, the head sync always starts from anchorStateLatestBlockSlot in this case.
|
|
157
|
+
startEpoch: Math.min(computeEpochAtSlot(local.headSlot), minEpoch),
|
|
139
158
|
target: {
|
|
140
159
|
slot: remote.headSlot,
|
|
141
160
|
root: remote.headRoot,
|
package/src/util/blobs.ts
CHANGED
|
@@ -149,41 +149,85 @@ export async function dataColumnMatrixRecovery(
|
|
|
149
149
|
* Reconstruct blobs from a set of data columns, at least 50%+ of all the columns
|
|
150
150
|
* must be provided to allow to reconstruct the full data matrix
|
|
151
151
|
*/
|
|
152
|
-
export async function reconstructBlobs(sidecars: fulu.DataColumnSidecars): Promise<deneb.Blobs> {
|
|
152
|
+
export async function reconstructBlobs(sidecars: fulu.DataColumnSidecars, indices?: number[]): Promise<deneb.Blobs> {
|
|
153
153
|
if (sidecars.length < NUMBER_OF_COLUMNS / 2) {
|
|
154
154
|
throw Error(
|
|
155
155
|
`Expected at least ${NUMBER_OF_COLUMNS / 2} data columns to reconstruct blobs, received ${sidecars.length}`
|
|
156
156
|
);
|
|
157
157
|
}
|
|
158
|
+
const blobCount = sidecars[0].column.length;
|
|
158
159
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// Full columns, no need to recover
|
|
163
|
-
fullSidecars = sidecars;
|
|
164
|
-
} else {
|
|
165
|
-
const sidecarsByIndex = new Map<number, fulu.DataColumnSidecar>(sidecars.map((sc) => [sc.index, sc]));
|
|
166
|
-
const recoveredSidecars = await dataColumnMatrixRecovery(sidecarsByIndex);
|
|
167
|
-
if (recoveredSidecars === null) {
|
|
168
|
-
// Should not happen because we check the column count above
|
|
169
|
-
throw Error("Failed to reconstruct the full data matrix");
|
|
160
|
+
for (const index of indices ?? []) {
|
|
161
|
+
if (index < 0 || index >= blobCount) {
|
|
162
|
+
throw Error(`Invalid blob index ${index}, must be between 0 and ${blobCount - 1}`);
|
|
170
163
|
}
|
|
171
|
-
|
|
164
|
+
}
|
|
165
|
+
const indicesToReconstruct = indices ?? Array.from({length: blobCount}, (_, i) => i);
|
|
166
|
+
|
|
167
|
+
const recoveredCells = await recoverBlobCells(sidecars, indicesToReconstruct);
|
|
168
|
+
if (recoveredCells === null) {
|
|
169
|
+
// Should not happen because we check the column count above
|
|
170
|
+
throw Error("Failed to recover cells to reconstruct blobs");
|
|
172
171
|
}
|
|
173
172
|
|
|
174
|
-
const
|
|
175
|
-
const blobs: deneb.Blobs = new Array(blobCount);
|
|
173
|
+
const blobs: deneb.Blobs = new Array(indicesToReconstruct.length);
|
|
176
174
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
175
|
+
for (let i = 0; i < indicesToReconstruct.length; i++) {
|
|
176
|
+
const blobIndex = indicesToReconstruct[i];
|
|
177
|
+
const cells = recoveredCells.get(blobIndex);
|
|
178
|
+
if (!cells) {
|
|
179
|
+
throw Error(`Failed to get recovered cells for blob index ${blobIndex}`);
|
|
180
|
+
}
|
|
181
|
+
blobs[i] = cellsToBlob(cells);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
return blobs;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Recover cells for specific blob indices from a set of data columns
|
|
189
|
+
*/
|
|
190
|
+
async function recoverBlobCells(
|
|
191
|
+
partialSidecars: fulu.DataColumnSidecar[],
|
|
192
|
+
blobIndices: number[]
|
|
193
|
+
): Promise<Map<number, fulu.Cell[]> | null> {
|
|
194
|
+
const columnCount = partialSidecars.length;
|
|
195
|
+
if (columnCount < NUMBER_OF_COLUMNS / 2) {
|
|
196
|
+
// We don't have enough columns to recover
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const recoveredCells = new Map<number, fulu.Cell[]>();
|
|
201
|
+
// Sort data columns by index in ascending order
|
|
202
|
+
const partialSidecarsSorted = partialSidecars.slice().sort((a, b) => a.index - b.index);
|
|
203
|
+
|
|
204
|
+
if (columnCount === NUMBER_OF_COLUMNS) {
|
|
205
|
+
// Full columns, no need to recover
|
|
206
|
+
for (const blobIndex of blobIndices) {
|
|
207
|
+
// 128 cells that make up one "extended blob" row
|
|
208
|
+
const cells = partialSidecarsSorted.map((col) => col.column[blobIndex]);
|
|
209
|
+
recoveredCells.set(blobIndex, cells);
|
|
210
|
+
}
|
|
211
|
+
return recoveredCells;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
await Promise.all(
|
|
215
|
+
blobIndices.map(async (blobIndex) => {
|
|
216
|
+
const cellIndices: number[] = [];
|
|
217
|
+
const cells: fulu.Cell[] = [];
|
|
218
|
+
for (const dataColumn of partialSidecarsSorted) {
|
|
219
|
+
cellIndices.push(dataColumn.index);
|
|
220
|
+
cells.push(dataColumn.column[blobIndex]);
|
|
221
|
+
}
|
|
222
|
+
// Recover cells for this specific blob row
|
|
223
|
+
const recovered = await kzg.asyncRecoverCellsAndKzgProofs(cellIndices, cells);
|
|
224
|
+
recoveredCells.set(blobIndex, recovered.cells);
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
return recoveredCells;
|
|
229
|
+
}
|
|
230
|
+
|
|
187
231
|
/**
|
|
188
232
|
* Concatenate the systematic half (columns 0‑63) of a row of cells into
|
|
189
233
|
* the original 131072 byte blob. The parity half (64‑127) is ignored as
|
package/src/util/profile.ts
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import {sleep} from "@lodestar/utils";
|
|
2
4
|
|
|
5
|
+
export enum ProfileThread {
|
|
6
|
+
MAIN = "main",
|
|
7
|
+
NETWORK = "network",
|
|
8
|
+
DISC5 = "discv5",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The time to take a Bun profile.
|
|
13
|
+
* If we increase this time it'll potentiall cause the app to crash.
|
|
14
|
+
* If we decrease this time, profile recorded will be fragmented and hard to analyze.
|
|
15
|
+
*/
|
|
16
|
+
const BUN_PROFILE_MS = 3 * 1000;
|
|
17
|
+
|
|
18
|
+
export async function profileThread(thread: ProfileThread, durationMs: number, dirpath: string): Promise<string> {
|
|
19
|
+
return globalThis.Bun ? profileBun(thread, durationMs) : profileNodeJS(thread, durationMs, dirpath);
|
|
20
|
+
}
|
|
21
|
+
|
|
3
22
|
/**
|
|
4
|
-
* Take
|
|
23
|
+
* Take `durationMs` profile of the current thread and return the persisted file path.
|
|
5
24
|
*/
|
|
6
|
-
|
|
25
|
+
async function profileNodeJS(thread: ProfileThread, durationMs: number, dirpath: string): Promise<string> {
|
|
7
26
|
const inspector = await import("node:inspector");
|
|
8
27
|
|
|
9
28
|
// due to some typing issues, not able to use promisify here
|
|
10
|
-
|
|
29
|
+
const profile = await new Promise<string>((resolve, reject) => {
|
|
11
30
|
// Start the inspector and connect to it
|
|
12
31
|
const session = new inspector.Session();
|
|
13
32
|
session.connect();
|
|
@@ -29,6 +48,29 @@ export async function profileNodeJS(durationMs: number): Promise<string> {
|
|
|
29
48
|
});
|
|
30
49
|
});
|
|
31
50
|
});
|
|
51
|
+
|
|
52
|
+
const filePath = path.join(dirpath, `${thread}_thread_${new Date().toISOString()}.cpuprofile`);
|
|
53
|
+
fs.writeFileSync(filePath, profile);
|
|
54
|
+
return filePath;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Unlike NodeJS, Bun console.profile() api flush data to the inspector,
|
|
59
|
+
* so this api returns ms taken of this profile instead of file path.
|
|
60
|
+
*/
|
|
61
|
+
async function profileBun(thread: ProfileThread, durationMs: number): Promise<string> {
|
|
62
|
+
const start = Date.now();
|
|
63
|
+
let now = Date.now();
|
|
64
|
+
while (now - start < durationMs) {
|
|
65
|
+
// biome-ignore lint/suspicious/noConsole: need to use console api to profile in Bun
|
|
66
|
+
console.profile(String(now));
|
|
67
|
+
await sleep(BUN_PROFILE_MS);
|
|
68
|
+
// biome-ignore lint/suspicious/noConsole: need to use console api to profile in Bun
|
|
69
|
+
console.profileEnd(String(now));
|
|
70
|
+
now = Date.now();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return `Successfully take Bun ${thread} thread profile in ${now - start}ms. Check your inspector to see the profile.`;
|
|
32
74
|
}
|
|
33
75
|
|
|
34
76
|
/**
|
package/src/util/sszBytes.ts
CHANGED
|
@@ -417,6 +417,36 @@ export function getSlotFromDataColumnSidecarSerialized(data: Uint8Array): Slot |
|
|
|
417
417
|
return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
/**
|
|
421
|
+
* BeaconState of all forks (up until Electra, check with new forks)
|
|
422
|
+
* class BeaconState(Container):
|
|
423
|
+
* genesis_time: uint64 - 8 bytes
|
|
424
|
+
* genesis_validators_root: Root - 32 bytes
|
|
425
|
+
* slot: Slot - 8 bytes
|
|
426
|
+
* fork: Fork - 16 bytes
|
|
427
|
+
* latest_block_header: BeaconBlockHeader - fixed size
|
|
428
|
+
* slot: Slot - 8 bytes
|
|
429
|
+
*
|
|
430
|
+
*/
|
|
431
|
+
|
|
432
|
+
const BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE = 8 + 32 + 8 + 16;
|
|
433
|
+
export function getLastProcessedSlotFromBeaconStateSerialized(data: Uint8Array): Slot | null {
|
|
434
|
+
if (data.length < BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE + SLOT_SIZE) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return getSlotFromOffset(data, BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const SLOT_BYTES_POSITION_IN_BEACON_STATE = 8 + 32;
|
|
442
|
+
export function getSlotFromBeaconStateSerialized(data: Uint8Array): Slot | null {
|
|
443
|
+
if (data.length < SLOT_BYTES_POSITION_IN_BEACON_STATE) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_BEACON_STATE);
|
|
448
|
+
}
|
|
449
|
+
|
|
420
450
|
/**
|
|
421
451
|
* Read only the first 4 bytes of Slot, max value is 4,294,967,295 will be reached 1634 years after genesis
|
|
422
452
|
*
|