@lodestar/beacon-node 1.40.0-dev.ba0be9204c → 1.40.0-dev.c04ddcbf20
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 +8 -18
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +3 -6
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +5 -2
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +9 -8
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +4 -0
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +2 -0
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js +6 -0
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +2 -6
- 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 +0 -14
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +0 -2
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +0 -7
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.d.ts +1 -4
- package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.js +20 -28
- package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +14 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +158 -10
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/interface.d.ts +14 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +6 -4
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +5 -3
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +2 -8
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +0 -1
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +1 -2
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +2 -16
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +3 -7
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +3 -16
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/validation/blobSidecar.js +1 -1
- package/lib/chain/validation/blobSidecar.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +1 -1
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +2 -0
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +42 -3
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/repositories/checkpointState.js +0 -1
- package/lib/db/repositories/checkpointState.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +7 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +24 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +0 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +2 -4
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRoot.d.ts +1 -2
- package/lib/network/reqresp/handlers/beaconBlocksByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRoot.js +5 -26
- package/lib/network/reqresp/handlers/beaconBlocksByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRoot.d.ts +1 -2
- package/lib/network/reqresp/handlers/blobSidecarsByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRoot.js +5 -7
- package/lib/network/reqresp/handlers/blobSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +1 -2
- 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 +1 -5
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/index.js +2 -2
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +0 -1
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +0 -3
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +0 -3
- package/lib/sync/unknownBlock.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +8 -18
- package/src/api/impl/debug/index.ts +2 -6
- package/src/api/impl/lodestar/index.ts +6 -3
- package/src/api/impl/validator/index.ts +12 -11
- package/src/chain/archiveStore/utils/archiveBlocks.ts +4 -0
- package/src/chain/blocks/blockInput/blockInput.ts +8 -0
- package/src/chain/blocks/importBlock.ts +2 -6
- package/src/chain/blocks/index.ts +0 -19
- package/src/chain/blocks/types.ts +0 -2
- package/src/chain/blocks/verifyBlock.ts +0 -8
- package/src/chain/blocks/writeBlockInputToDb.ts +24 -30
- package/src/chain/chain.ts +180 -20
- package/src/chain/interface.ts +16 -0
- package/src/chain/prepareNextSlot.ts +6 -6
- package/src/chain/produceBlock/produceBlockBody.ts +7 -5
- package/src/chain/regen/interface.ts +1 -12
- package/src/chain/regen/queued.ts +3 -23
- package/src/chain/regen/regen.ts +4 -24
- package/src/chain/validation/blobSidecar.ts +1 -1
- package/src/chain/validation/dataColumnSidecar.ts +1 -1
- package/src/chain/validatorMonitor.ts +52 -3
- package/src/db/repositories/checkpointState.ts +1 -1
- package/src/metrics/metrics/lodestar.ts +25 -0
- package/src/network/processor/gossipHandlers.ts +0 -3
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +2 -4
- package/src/network/reqresp/handlers/beaconBlocksByRoot.ts +5 -32
- package/src/network/reqresp/handlers/blobSidecarsByRoot.ts +5 -9
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +5 -2
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -5
- package/src/network/reqresp/handlers/index.ts +2 -2
- package/src/sync/range/chain.ts +0 -1
- package/src/sync/range/range.ts +0 -3
- package/src/sync/unknownBlock.ts +0 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
|
|
2
2
|
import {routes} from "@lodestar/api";
|
|
3
3
|
import {ApplicationMethods} from "@lodestar/api/server";
|
|
4
|
-
import {ExecutionStatus} from "@lodestar/fork-choice";
|
|
4
|
+
import {ExecutionStatus, ProtoBlock} from "@lodestar/fork-choice";
|
|
5
5
|
import {
|
|
6
6
|
ForkName,
|
|
7
7
|
ForkPostBellatrix,
|
|
@@ -413,10 +413,10 @@ export function getValidatorApi(
|
|
|
413
413
|
// as of now fee recipient checks can not be performed because builder does not return bid recipient
|
|
414
414
|
{
|
|
415
415
|
commonBlockBodyPromise,
|
|
416
|
-
|
|
416
|
+
parentBlock,
|
|
417
417
|
}: Omit<routes.validator.ExtraProduceBlockOpts, "builderSelection"> & {
|
|
418
418
|
commonBlockBodyPromise: Promise<CommonBlockBody>;
|
|
419
|
-
|
|
419
|
+
parentBlock: ProtoBlock;
|
|
420
420
|
}
|
|
421
421
|
): Promise<ProduceBlindedBlockRes> {
|
|
422
422
|
const version = config.getForkName(slot);
|
|
@@ -447,7 +447,7 @@ export function getValidatorApi(
|
|
|
447
447
|
timer = metrics?.blockProductionTime.startTimer();
|
|
448
448
|
const {block, executionPayloadValue, consensusBlockValue} = await chain.produceBlindedBlock({
|
|
449
449
|
slot,
|
|
450
|
-
|
|
450
|
+
parentBlock,
|
|
451
451
|
randaoReveal,
|
|
452
452
|
graffiti,
|
|
453
453
|
commonBlockBodyPromise,
|
|
@@ -482,10 +482,10 @@ export function getValidatorApi(
|
|
|
482
482
|
feeRecipient,
|
|
483
483
|
strictFeeRecipientCheck,
|
|
484
484
|
commonBlockBodyPromise,
|
|
485
|
-
|
|
485
|
+
parentBlock,
|
|
486
486
|
}: Omit<routes.validator.ExtraProduceBlockOpts, "builderSelection"> & {
|
|
487
487
|
commonBlockBodyPromise: Promise<CommonBlockBody>;
|
|
488
|
-
|
|
488
|
+
parentBlock: ProtoBlock;
|
|
489
489
|
}
|
|
490
490
|
): Promise<ProduceBlockContentsRes & {shouldOverrideBuilder?: boolean}> {
|
|
491
491
|
const source = ProducedBlockSource.engine;
|
|
@@ -496,7 +496,7 @@ export function getValidatorApi(
|
|
|
496
496
|
timer = metrics?.blockProductionTime.startTimer();
|
|
497
497
|
const {block, executionPayloadValue, consensusBlockValue, shouldOverrideBuilder} = await chain.produceBlock({
|
|
498
498
|
slot,
|
|
499
|
-
|
|
499
|
+
parentBlock,
|
|
500
500
|
randaoReveal,
|
|
501
501
|
graffiti,
|
|
502
502
|
feeRecipient,
|
|
@@ -569,7 +569,8 @@ export function getValidatorApi(
|
|
|
569
569
|
notWhileSyncing();
|
|
570
570
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
571
571
|
|
|
572
|
-
const
|
|
572
|
+
const parentBlock = chain.getProposerHead(slot);
|
|
573
|
+
const {blockRoot: parentBlockRootHex, slot: parentSlot} = parentBlock;
|
|
573
574
|
const parentBlockRoot = fromHex(parentBlockRootHex);
|
|
574
575
|
notOnOutOfRangeData(parentBlockRoot);
|
|
575
576
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
@@ -638,7 +639,7 @@ export function getValidatorApi(
|
|
|
638
639
|
// can't do fee recipient checks as builder bid doesn't return feeRecipient as of now
|
|
639
640
|
strictFeeRecipientCheck: false,
|
|
640
641
|
commonBlockBodyPromise,
|
|
641
|
-
|
|
642
|
+
parentBlock,
|
|
642
643
|
})
|
|
643
644
|
: Promise.reject(new Error("Builder disabled"));
|
|
644
645
|
|
|
@@ -647,7 +648,7 @@ export function getValidatorApi(
|
|
|
647
648
|
feeRecipient,
|
|
648
649
|
strictFeeRecipientCheck,
|
|
649
650
|
commonBlockBodyPromise,
|
|
650
|
-
|
|
651
|
+
parentBlock,
|
|
651
652
|
}).then((engineBlock) => {
|
|
652
653
|
// Once the engine returns a block, in the event of either:
|
|
653
654
|
// - suspected builder censorship
|
|
@@ -689,7 +690,7 @@ export function getValidatorApi(
|
|
|
689
690
|
chain
|
|
690
691
|
.produceCommonBlockBody({
|
|
691
692
|
slot,
|
|
692
|
-
|
|
693
|
+
parentBlock,
|
|
693
694
|
randaoReveal,
|
|
694
695
|
graffiti: graffitiBytes,
|
|
695
696
|
})
|
|
@@ -238,6 +238,7 @@ async function migrateBlocksFromHotToColdDb(db: IBeaconDb, blocks: BlockRootSlot
|
|
|
238
238
|
// load Buffer instead of SignedBeaconBlock to improve performance
|
|
239
239
|
const canonicalBlockEntries: BlockArchiveBatchPutBinaryItem[] = await Promise.all(
|
|
240
240
|
canonicalBlocks.map(async (block) => {
|
|
241
|
+
// Here we assume the blocks are already in the hot db
|
|
241
242
|
const blockBuffer = await db.block.getBinary(block.root);
|
|
242
243
|
if (!blockBuffer) {
|
|
243
244
|
throw Error(`Block not found for slot ${block.slot} root ${toRootHex(block.root)}`);
|
|
@@ -294,6 +295,8 @@ async function migrateBlobSidecarsFromHotToColdDb(
|
|
|
294
295
|
);
|
|
295
296
|
})
|
|
296
297
|
.map(async (block) => {
|
|
298
|
+
// Here we assume the blob sidecars are already in the hot db
|
|
299
|
+
// instead of checking first the block input cache
|
|
297
300
|
const bytes = await db.blobSidecars.getBinary(block.root);
|
|
298
301
|
if (!bytes) {
|
|
299
302
|
throw Error(`No blobSidecars found for slot ${block.slot} root ${toRootHex(block.root)}`);
|
|
@@ -343,6 +346,7 @@ async function migrateDataColumnSidecarsFromHotToColdDb(
|
|
|
343
346
|
continue;
|
|
344
347
|
}
|
|
345
348
|
|
|
349
|
+
// Here we assume the data column sidecars are already in the hot db
|
|
346
350
|
const dataColumnSidecarBytes = await fromAsync(db.dataColumnSidecar.valuesStreamBinary(block.root));
|
|
347
351
|
// there could be 0 dataColumnSidecarBytes if block has no blob
|
|
348
352
|
logger.verbose("migrateDataColumnSidecarsFromHotToColdDb", {
|
|
@@ -412,6 +412,10 @@ export class BlockInputBlobs extends AbstractBlockInput<ForkBlobsDA, deneb.BlobS
|
|
|
412
412
|
return this.blobsCache.has(blobIndex);
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
+
getBlob(blobIndex: BlobIndex): deneb.BlobSidecar | undefined {
|
|
416
|
+
return this.blobsCache.get(blobIndex)?.blobSidecar;
|
|
417
|
+
}
|
|
418
|
+
|
|
415
419
|
addBlob(
|
|
416
420
|
{blockRootHex, blobSidecar, source, peerIdStr, seenTimestampSec}: AddBlob,
|
|
417
421
|
opts = {throwOnDuplicateAdd: true}
|
|
@@ -787,6 +791,10 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
|
|
|
787
791
|
return this.columnsCache.has(columnIndex);
|
|
788
792
|
}
|
|
789
793
|
|
|
794
|
+
getColumn(columnIndex: number): fulu.DataColumnSidecar | undefined {
|
|
795
|
+
return this.columnsCache.get(columnIndex)?.columnSidecar;
|
|
796
|
+
}
|
|
797
|
+
|
|
790
798
|
getVersionedHashes(): VersionedHashes {
|
|
791
799
|
return this.state.versionedHashes;
|
|
792
800
|
}
|
|
@@ -34,7 +34,6 @@ import {toCheckpointHex} from "../stateCache/index.js";
|
|
|
34
34
|
import {isBlockInputBlobs, isBlockInputColumns} from "./blockInput/blockInput.js";
|
|
35
35
|
import {AttestationImportOpt, FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
|
|
36
36
|
import {getCheckpointFromState} from "./utils/checkpoint.js";
|
|
37
|
-
import {writeBlockInputToDb} from "./writeBlockInputToDb.js";
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
39
|
* Fork-choice allows to import attestations from current (0) or past (1) epoch.
|
|
@@ -91,11 +90,8 @@ export async function importBlock(
|
|
|
91
90
|
throw Error("Unavailable block can not be imported in forkchoice");
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
// 1. Persist block to hot DB (
|
|
95
|
-
|
|
96
|
-
if (!opts.eagerPersistBlock) {
|
|
97
|
-
await writeBlockInputToDb.call(this, [blockInput]);
|
|
98
|
-
}
|
|
93
|
+
// 1. Persist block to hot DB (performed asynchronously to avoid blocking head selection)
|
|
94
|
+
void this.unfinalizedBlockWrites.push([blockInput]);
|
|
99
95
|
|
|
100
96
|
// Without forcefully clearing this cache, we would rely on WeakMap to evict memory which is not reliable
|
|
101
97
|
this.serializedCache.clear();
|
|
@@ -11,7 +11,6 @@ import {FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
|
|
|
11
11
|
import {assertLinearChainSegment} from "./utils/chainSegment.js";
|
|
12
12
|
import {verifyBlocksInEpoch} from "./verifyBlock.js";
|
|
13
13
|
import {verifyBlocksSanityChecks} from "./verifyBlocksSanityChecks.js";
|
|
14
|
-
import {removeEagerlyPersistedBlockInputs} from "./writeBlockInputToDb.js";
|
|
15
14
|
|
|
16
15
|
export {AttestationImportOpt, type ImportBlockOpts} from "./types.js";
|
|
17
16
|
|
|
@@ -143,24 +142,6 @@ export async function processBlocks(
|
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
|
|
146
|
-
// Clean db if we don't have blocks in forkchoice but already persisted them to db
|
|
147
|
-
//
|
|
148
|
-
// NOTE: this function is awaited to ensure that DB size remains constant, otherwise an attacker may bloat the
|
|
149
|
-
// disk with big malicious payloads. Our sequential block importer will wait for this promise before importing
|
|
150
|
-
// another block. The removal call error is not propagated since that would halt the chain.
|
|
151
|
-
//
|
|
152
|
-
// LOG: Because the error is not propagated and there's a risk of db bloat, the error is logged at warn level
|
|
153
|
-
// to alert the user of potential db bloat. This error _should_ never happen user must act and report to us
|
|
154
|
-
if (opts.eagerPersistBlock) {
|
|
155
|
-
await removeEagerlyPersistedBlockInputs.call(this, blocks).catch((e) => {
|
|
156
|
-
this.logger.warn(
|
|
157
|
-
"Error pruning eagerly imported block inputs, DB may grow in size if this error happens frequently",
|
|
158
|
-
{slot: blocks.map((block) => block.getBlock().message.slot).join(",")},
|
|
159
|
-
e
|
|
160
|
-
);
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
145
|
throw err;
|
|
165
146
|
}
|
|
166
147
|
}
|
|
@@ -21,7 +21,6 @@ import {verifyBlocksDataAvailability} from "./verifyBlocksDataAvailability.js";
|
|
|
21
21
|
import {SegmentExecStatus, verifyBlocksExecutionPayload} from "./verifyBlocksExecutionPayloads.js";
|
|
22
22
|
import {verifyBlocksSignatures} from "./verifyBlocksSignatures.js";
|
|
23
23
|
import {verifyBlocksStateTransitionOnly} from "./verifyBlocksStateTransitionOnly.js";
|
|
24
|
-
import {writeBlockInputToDb} from "./writeBlockInputToDb.js";
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
26
|
* Verifies 1 or more blocks are fully valid; from a linear sequence of blocks.
|
|
@@ -156,13 +155,6 @@ export async function verifyBlocksInEpoch(
|
|
|
156
155
|
opts
|
|
157
156
|
)
|
|
158
157
|
: Promise.resolve({verifySignaturesTime: Date.now()}),
|
|
159
|
-
|
|
160
|
-
// ideally we want to only persist blocks after verifying them however the reality is there are
|
|
161
|
-
// rarely invalid blocks we'll batch all I/O operation here to reduce the overhead if there's
|
|
162
|
-
// an error, we'll remove blocks not in forkchoice
|
|
163
|
-
opts.verifyOnly !== true && opts.eagerPersistBlock
|
|
164
|
-
? writeBlockInputToDb.call(this, blockInputs)
|
|
165
|
-
: Promise.resolve(),
|
|
166
158
|
]);
|
|
167
159
|
|
|
168
160
|
if (opts.verifyOnly !== true) {
|
|
@@ -98,35 +98,29 @@ export async function writeBlockInputToDb(this: BeaconChain, blocksInputs: IBloc
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
dataColumnsToRemove.push(blockRoot);
|
|
119
|
-
} else if (isBlockInputBlobs(blockInput)) {
|
|
120
|
-
const blobSidecars = blockInput.getBlobs();
|
|
121
|
-
blobsToRemove.push({blockRoot, slot, blobSidecars});
|
|
101
|
+
export async function persistBlockInputs(this: BeaconChain, blockInputs: IBlockInput[]): Promise<void> {
|
|
102
|
+
await writeBlockInputToDb
|
|
103
|
+
.call(this, blockInputs)
|
|
104
|
+
.catch((e) => {
|
|
105
|
+
this.logger.debug(
|
|
106
|
+
"Error persisting block input in hot db",
|
|
107
|
+
{
|
|
108
|
+
count: blockInputs.length,
|
|
109
|
+
slot: blockInputs[0].slot,
|
|
110
|
+
root: blockInputs[0].blockRootHex,
|
|
111
|
+
},
|
|
112
|
+
e
|
|
113
|
+
);
|
|
114
|
+
})
|
|
115
|
+
.finally(() => {
|
|
116
|
+
for (const blockInput of blockInputs) {
|
|
117
|
+
this.seenBlockInputCache.prune(blockInput.blockRootHex);
|
|
122
118
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.db.dataColumnSidecar.deleteMany(dataColumnsToRemove),
|
|
131
|
-
]);
|
|
119
|
+
if (blockInputs.length === 1) {
|
|
120
|
+
this.logger.debug("Pruned block input", {
|
|
121
|
+
slot: blockInputs[0].slot,
|
|
122
|
+
root: blockInputs[0].blockRootHex,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
});
|
|
132
126
|
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -37,14 +37,19 @@ import {
|
|
|
37
37
|
UintNum64,
|
|
38
38
|
ValidatorIndex,
|
|
39
39
|
Wei,
|
|
40
|
+
deneb,
|
|
41
|
+
fulu,
|
|
40
42
|
isBlindedBeaconBlock,
|
|
41
43
|
phase0,
|
|
42
44
|
rewards,
|
|
45
|
+
ssz,
|
|
46
|
+
sszTypesFor,
|
|
43
47
|
} from "@lodestar/types";
|
|
44
48
|
import {Logger, fromHex, gweiToWei, isErrorAborted, pruneSetToMax, sleep, toRootHex} from "@lodestar/utils";
|
|
45
49
|
import {ProcessShutdownCallback} from "@lodestar/validator";
|
|
46
50
|
import {GENESIS_EPOCH, ZERO_HASH} from "../constants/index.js";
|
|
47
51
|
import {IBeaconDb} from "../db/index.js";
|
|
52
|
+
import {BLOB_SIDECARS_IN_WRAPPER_INDEX} from "../db/repositories/blobSidecars.ts";
|
|
48
53
|
import {BuilderStatus} from "../execution/builder/http.js";
|
|
49
54
|
import {IExecutionBuilder, IExecutionEngine} from "../execution/index.js";
|
|
50
55
|
import {Metrics} from "../metrics/index.js";
|
|
@@ -55,12 +60,15 @@ import {CustodyConfig, getValidatorsCustodyRequirement} from "../util/dataColumn
|
|
|
55
60
|
import {callInNextEventLoop} from "../util/eventLoop.js";
|
|
56
61
|
import {ensureDir, writeIfNotExist} from "../util/file.js";
|
|
57
62
|
import {isOptimisticBlock} from "../util/forkChoice.js";
|
|
63
|
+
import {JobItemQueue} from "../util/queue/itemQueue.ts";
|
|
58
64
|
import {SerializedCache} from "../util/serializedCache.js";
|
|
65
|
+
import {getSlotFromSignedBeaconBlockSerialized} from "../util/sszBytes.ts";
|
|
59
66
|
import {ArchiveStore} from "./archiveStore/archiveStore.js";
|
|
60
67
|
import {CheckpointBalancesCache} from "./balancesCache.js";
|
|
61
68
|
import {BeaconProposerCache} from "./beaconProposerCache.js";
|
|
62
|
-
import {IBlockInput} from "./blocks/blockInput/index.js";
|
|
69
|
+
import {IBlockInput, isBlockInputBlobs, isBlockInputColumns} from "./blocks/blockInput/index.js";
|
|
63
70
|
import {BlockProcessor, ImportBlockOpts} from "./blocks/index.js";
|
|
71
|
+
import {persistBlockInputs} from "./blocks/writeBlockInputToDb.ts";
|
|
64
72
|
import {BlsMultiThreadWorkerPool, BlsSingleThreadVerifier, IBlsVerifier} from "./bls/index.js";
|
|
65
73
|
import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
|
|
66
74
|
import {ChainEvent, ChainEventEmitter} from "./emitter.js";
|
|
@@ -113,6 +121,11 @@ import {ValidatorMonitor} from "./validatorMonitor.js";
|
|
|
113
121
|
*/
|
|
114
122
|
const DEFAULT_MAX_CACHED_PRODUCED_RESULTS = 4;
|
|
115
123
|
|
|
124
|
+
/**
|
|
125
|
+
* The maximum number of pending unfinalized block writes to the database before backpressure is applied.
|
|
126
|
+
*/
|
|
127
|
+
const DEFAULT_MAX_PENDING_UNFINALIZED_BLOCK_WRITES = 32;
|
|
128
|
+
|
|
116
129
|
export class BeaconChain implements IBeaconChain {
|
|
117
130
|
readonly genesisTime: UintNum64;
|
|
118
131
|
readonly genesisValidatorsRoot: Root;
|
|
@@ -136,6 +149,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
136
149
|
readonly lightClientServer?: LightClientServer;
|
|
137
150
|
readonly reprocessController: ReprocessController;
|
|
138
151
|
readonly archiveStore: ArchiveStore;
|
|
152
|
+
readonly unfinalizedBlockWrites: JobItemQueue<[IBlockInput[]], void>;
|
|
139
153
|
|
|
140
154
|
// Ops pool
|
|
141
155
|
readonly attestationPool: AttestationPool;
|
|
@@ -405,6 +419,15 @@ export class BeaconChain implements IBeaconChain {
|
|
|
405
419
|
signal
|
|
406
420
|
);
|
|
407
421
|
|
|
422
|
+
this.unfinalizedBlockWrites = new JobItemQueue(
|
|
423
|
+
persistBlockInputs.bind(this),
|
|
424
|
+
{
|
|
425
|
+
maxLength: DEFAULT_MAX_PENDING_UNFINALIZED_BLOCK_WRITES,
|
|
426
|
+
signal,
|
|
427
|
+
},
|
|
428
|
+
metrics?.unfinalizedBlockWritesQueue
|
|
429
|
+
);
|
|
430
|
+
|
|
408
431
|
// always run PrepareNextSlotScheduler except for fork_choice spec tests
|
|
409
432
|
if (!opts?.disablePrepareNextSlot) {
|
|
410
433
|
new PrepareNextSlotScheduler(this, this.config, metrics, this.logger, signal);
|
|
@@ -430,6 +453,12 @@ export class BeaconChain implements IBeaconChain {
|
|
|
430
453
|
async close(): Promise<void> {
|
|
431
454
|
await this.archiveStore.close();
|
|
432
455
|
await this.bls.close();
|
|
456
|
+
|
|
457
|
+
// Since we don't persist unfinalized fork-choice,
|
|
458
|
+
// we can abort any ongoing unfinalized block writes.
|
|
459
|
+
// TODO: persist fork choice to disk and allow unfinalized block writes to complete.
|
|
460
|
+
this.unfinalizedBlockWrites.dropAllJobs();
|
|
461
|
+
|
|
433
462
|
this.abortController.abort();
|
|
434
463
|
}
|
|
435
464
|
|
|
@@ -501,7 +530,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
501
530
|
// only use regen queue if necessary, it'll cache in checkpointStateCache if regen gets through epoch transition
|
|
502
531
|
const head = this.forkChoice.getHead();
|
|
503
532
|
const startSlot = computeStartSlotAtEpoch(epoch);
|
|
504
|
-
return this.regen.getBlockSlotState(head
|
|
533
|
+
return this.regen.getBlockSlotState(head, startSlot, {dontTransferCache: true}, regenCaller);
|
|
505
534
|
}
|
|
506
535
|
|
|
507
536
|
async getStateBySlot(
|
|
@@ -519,12 +548,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
519
548
|
if (opts?.allowRegen) {
|
|
520
549
|
// Find closest canonical block to slot, then trigger regen
|
|
521
550
|
const block = this.forkChoice.getCanonicalBlockClosestLteSlot(slot) ?? finalizedBlock;
|
|
522
|
-
const state = await this.regen.getBlockSlotState(
|
|
523
|
-
block.blockRoot,
|
|
524
|
-
slot,
|
|
525
|
-
{dontTransferCache: true},
|
|
526
|
-
RegenCaller.restApi
|
|
527
|
-
);
|
|
551
|
+
const state = await this.regen.getBlockSlotState(block, slot, {dontTransferCache: true}, RegenCaller.restApi);
|
|
528
552
|
return {
|
|
529
553
|
state,
|
|
530
554
|
executionOptimistic: isOptimisticBlock(block),
|
|
@@ -652,6 +676,13 @@ export class BeaconChain implements IBeaconChain {
|
|
|
652
676
|
// Unfinalized slot, attempt to find in fork-choice
|
|
653
677
|
const block = this.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
654
678
|
if (block) {
|
|
679
|
+
// Block found in fork-choice.
|
|
680
|
+
// It may be in the block input cache, awaiting full DA reconstruction, check there first
|
|
681
|
+
// Otherwise (most likely), check the hot db
|
|
682
|
+
const blockInput = this.seenBlockInputCache.get(block.blockRoot);
|
|
683
|
+
if (blockInput?.hasBlock()) {
|
|
684
|
+
return {block: blockInput.getBlock(), executionOptimistic: isOptimisticBlock(block), finalized: false};
|
|
685
|
+
}
|
|
655
686
|
const data = await this.db.block.get(fromHex(block.blockRoot));
|
|
656
687
|
if (data) {
|
|
657
688
|
return {block: data, executionOptimistic: isOptimisticBlock(block), finalized: false};
|
|
@@ -671,6 +702,13 @@ export class BeaconChain implements IBeaconChain {
|
|
|
671
702
|
): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
672
703
|
const block = this.forkChoice.getBlockHex(root);
|
|
673
704
|
if (block) {
|
|
705
|
+
// Block found in fork-choice.
|
|
706
|
+
// It may be in the block input cache, awaiting full DA reconstruction, check there first
|
|
707
|
+
// Otherwise (most likely), check the hot db
|
|
708
|
+
const blockInput = this.seenBlockInputCache.get(block.blockRoot);
|
|
709
|
+
if (blockInput?.hasBlock()) {
|
|
710
|
+
return {block: blockInput.getBlock(), executionOptimistic: isOptimisticBlock(block), finalized: false};
|
|
711
|
+
}
|
|
674
712
|
const data = await this.db.block.get(fromHex(root));
|
|
675
713
|
if (data) {
|
|
676
714
|
return {block: data, executionOptimistic: isOptimisticBlock(block), finalized: false};
|
|
@@ -683,10 +721,137 @@ export class BeaconChain implements IBeaconChain {
|
|
|
683
721
|
return data && {block: data, executionOptimistic: false, finalized: true};
|
|
684
722
|
}
|
|
685
723
|
|
|
724
|
+
async getSerializedBlockByRoot(
|
|
725
|
+
root: string
|
|
726
|
+
): Promise<{block: Uint8Array; executionOptimistic: boolean; finalized: boolean; slot: Slot} | null> {
|
|
727
|
+
const block = this.forkChoice.getBlockHex(root);
|
|
728
|
+
if (block) {
|
|
729
|
+
// Block found in fork-choice.
|
|
730
|
+
// It may be in the block input cache, awaiting full DA reconstruction, check there first
|
|
731
|
+
// Otherwise (most likely), check the hot db
|
|
732
|
+
const blockInput = this.seenBlockInputCache.get(block.blockRoot);
|
|
733
|
+
if (blockInput?.hasBlock()) {
|
|
734
|
+
const signedBlock = blockInput.getBlock();
|
|
735
|
+
const serialized = this.serializedCache.get(signedBlock);
|
|
736
|
+
if (serialized) {
|
|
737
|
+
return {
|
|
738
|
+
block: serialized,
|
|
739
|
+
executionOptimistic: isOptimisticBlock(block),
|
|
740
|
+
finalized: false,
|
|
741
|
+
slot: blockInput.slot,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
return {
|
|
745
|
+
block: sszTypesFor(blockInput.forkName).SignedBeaconBlock.serialize(signedBlock),
|
|
746
|
+
executionOptimistic: isOptimisticBlock(block),
|
|
747
|
+
finalized: false,
|
|
748
|
+
slot: blockInput.slot,
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
const data = await this.db.block.getBinary(fromHex(root));
|
|
752
|
+
if (data) {
|
|
753
|
+
const slot = getSlotFromSignedBeaconBlockSerialized(data);
|
|
754
|
+
if (slot === null) throw new Error(`Invalid block data stored in DB for root: ${root}`);
|
|
755
|
+
return {block: data, executionOptimistic: isOptimisticBlock(block), finalized: false, slot};
|
|
756
|
+
}
|
|
757
|
+
// If block is not found in hot db, try cold db since there could be an archive cycle happening
|
|
758
|
+
// TODO: Add a lock to the archiver to have deterministic behavior on where are blocks
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const data = await this.db.blockArchive.getBinaryEntryByRoot(fromHex(root));
|
|
762
|
+
return data && {block: data.value, executionOptimistic: false, finalized: true, slot: data.key};
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
async getBlobSidecars(blockSlot: Slot, blockRootHex: string): Promise<deneb.BlobSidecars | null> {
|
|
766
|
+
const blockInput = this.seenBlockInputCache.get(blockRootHex);
|
|
767
|
+
if (blockInput) {
|
|
768
|
+
if (!isBlockInputBlobs(blockInput)) {
|
|
769
|
+
throw new Error(`Expected block input to have blobs: slot=${blockSlot} root=${blockRootHex}`);
|
|
770
|
+
}
|
|
771
|
+
if (!blockInput.hasAllData()) {
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
return blockInput.getBlobs();
|
|
775
|
+
}
|
|
776
|
+
const unfinalizedBlobSidecars = (await this.db.blobSidecars.get(fromHex(blockRootHex)))?.blobSidecars ?? null;
|
|
777
|
+
if (unfinalizedBlobSidecars) {
|
|
778
|
+
return unfinalizedBlobSidecars;
|
|
779
|
+
}
|
|
780
|
+
return (await this.db.blobSidecarsArchive.get(blockSlot))?.blobSidecars ?? null;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
async getSerializedBlobSidecars(blockSlot: Slot, blockRootHex: string): Promise<Uint8Array | null> {
|
|
784
|
+
const blockInput = this.seenBlockInputCache.get(blockRootHex);
|
|
785
|
+
if (blockInput) {
|
|
786
|
+
if (!isBlockInputBlobs(blockInput)) {
|
|
787
|
+
throw new Error(`Expected block input to have blobs: slot=${blockSlot} root=${blockRootHex}`);
|
|
788
|
+
}
|
|
789
|
+
if (!blockInput.hasAllData()) {
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
return ssz.deneb.BlobSidecars.serialize(blockInput.getBlobs());
|
|
793
|
+
}
|
|
794
|
+
const unfinalizedBlobSidecarsWrapper = await this.db.blobSidecars.getBinary(fromHex(blockRootHex));
|
|
795
|
+
if (unfinalizedBlobSidecarsWrapper) {
|
|
796
|
+
return unfinalizedBlobSidecarsWrapper.slice(BLOB_SIDECARS_IN_WRAPPER_INDEX);
|
|
797
|
+
}
|
|
798
|
+
const finalizedBlobSidecarsWrapper = await this.db.blobSidecarsArchive.getBinary(blockSlot);
|
|
799
|
+
if (finalizedBlobSidecarsWrapper) {
|
|
800
|
+
return finalizedBlobSidecarsWrapper.slice(BLOB_SIDECARS_IN_WRAPPER_INDEX);
|
|
801
|
+
}
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<fulu.DataColumnSidecars> {
|
|
806
|
+
const blockInput = this.seenBlockInputCache.get(blockRootHex);
|
|
807
|
+
if (blockInput) {
|
|
808
|
+
if (!isBlockInputColumns(blockInput)) {
|
|
809
|
+
throw new Error(`Expected block input to have columns: slot=${blockSlot} root=${blockRootHex}`);
|
|
810
|
+
}
|
|
811
|
+
return blockInput.getAllColumns();
|
|
812
|
+
}
|
|
813
|
+
const sidecarsUnfinalized = await this.db.dataColumnSidecar.values(fromHex(blockRootHex));
|
|
814
|
+
if (sidecarsUnfinalized.length > 0) {
|
|
815
|
+
return sidecarsUnfinalized;
|
|
816
|
+
}
|
|
817
|
+
const sidecarsFinalized = await this.db.dataColumnSidecarArchive.values(blockSlot);
|
|
818
|
+
return sidecarsFinalized;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
async getSerializedDataColumnSidecars(
|
|
822
|
+
blockSlot: Slot,
|
|
823
|
+
blockRootHex: string,
|
|
824
|
+
indices: number[]
|
|
825
|
+
): Promise<(Uint8Array | undefined)[]> {
|
|
826
|
+
const blockInput = this.seenBlockInputCache.get(blockRootHex);
|
|
827
|
+
if (blockInput) {
|
|
828
|
+
if (!isBlockInputColumns(blockInput)) {
|
|
829
|
+
throw new Error(`Expected block input to have columns: slot=${blockSlot} root=${blockRootHex}`);
|
|
830
|
+
}
|
|
831
|
+
return indices.map((index) => {
|
|
832
|
+
const sidecar = blockInput.getColumn(index);
|
|
833
|
+
if (!sidecar) {
|
|
834
|
+
return undefined;
|
|
835
|
+
}
|
|
836
|
+
const serialized = this.serializedCache.get(sidecar);
|
|
837
|
+
if (serialized) {
|
|
838
|
+
return serialized;
|
|
839
|
+
}
|
|
840
|
+
return ssz.fulu.DataColumnSidecar.serialize(sidecar);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
const sidecarsUnfinalized = await this.db.dataColumnSidecar.getManyBinary(fromHex(blockRootHex), indices);
|
|
844
|
+
if (sidecarsUnfinalized.some((sidecar) => sidecar != null)) {
|
|
845
|
+
return sidecarsUnfinalized;
|
|
846
|
+
}
|
|
847
|
+
const sidecarsFinalized = await this.db.dataColumnSidecarArchive.getManyBinary(blockSlot, indices);
|
|
848
|
+
return sidecarsFinalized;
|
|
849
|
+
}
|
|
850
|
+
|
|
686
851
|
async produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody> {
|
|
687
|
-
const {slot,
|
|
852
|
+
const {slot, parentBlock} = blockAttributes;
|
|
688
853
|
const state = await this.regen.getBlockSlotState(
|
|
689
|
-
|
|
854
|
+
parentBlock,
|
|
690
855
|
slot,
|
|
691
856
|
{dontTransferCache: true},
|
|
692
857
|
RegenCaller.produceBlock
|
|
@@ -723,7 +888,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
723
888
|
slot,
|
|
724
889
|
feeRecipient,
|
|
725
890
|
commonBlockBodyPromise,
|
|
726
|
-
|
|
891
|
+
parentBlock,
|
|
727
892
|
}: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}
|
|
728
893
|
): Promise<{
|
|
729
894
|
block: AssembledBlockType<T>;
|
|
@@ -732,7 +897,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
732
897
|
shouldOverrideBuilder?: boolean;
|
|
733
898
|
}> {
|
|
734
899
|
const state = await this.regen.getBlockSlotState(
|
|
735
|
-
|
|
900
|
+
parentBlock,
|
|
736
901
|
slot,
|
|
737
902
|
{dontTransferCache: true},
|
|
738
903
|
RegenCaller.produceBlock
|
|
@@ -749,7 +914,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
749
914
|
graffiti,
|
|
750
915
|
slot,
|
|
751
916
|
feeRecipient,
|
|
752
|
-
|
|
917
|
+
parentBlock,
|
|
753
918
|
proposerIndex,
|
|
754
919
|
proposerPubKey,
|
|
755
920
|
commonBlockBodyPromise,
|
|
@@ -772,7 +937,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
772
937
|
const block = {
|
|
773
938
|
slot,
|
|
774
939
|
proposerIndex,
|
|
775
|
-
parentRoot:
|
|
940
|
+
parentRoot: fromHex(parentBlock.blockRoot),
|
|
776
941
|
stateRoot: ZERO_HASH,
|
|
777
942
|
body,
|
|
778
943
|
} as AssembledBlockType<T>;
|
|
@@ -968,12 +1133,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
968
1133
|
// thanks to one epoch look ahead, we don't need to dial up to attEpoch
|
|
969
1134
|
const targetSlot = computeStartSlotAtEpoch(attEpoch - 1);
|
|
970
1135
|
this.metrics?.gossipAttestation.useHeadBlockStateDialedToTargetEpoch.inc({caller: regenCaller});
|
|
971
|
-
state = await this.regen.getBlockSlotState(
|
|
972
|
-
attHeadBlock.blockRoot,
|
|
973
|
-
targetSlot,
|
|
974
|
-
{dontTransferCache: true},
|
|
975
|
-
regenCaller
|
|
976
|
-
);
|
|
1136
|
+
state = await this.regen.getBlockSlotState(attHeadBlock, targetSlot, {dontTransferCache: true}, regenCaller);
|
|
977
1137
|
} else if (blockEpoch > attEpoch) {
|
|
978
1138
|
// should not happen, handled inside attestation verification code
|
|
979
1139
|
throw Error(`Block epoch ${blockEpoch} is after attestation epoch ${attEpoch}`);
|
package/src/chain/interface.ts
CHANGED
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
Wei,
|
|
23
23
|
altair,
|
|
24
24
|
capella,
|
|
25
|
+
deneb,
|
|
26
|
+
fulu,
|
|
25
27
|
phase0,
|
|
26
28
|
rewards,
|
|
27
29
|
} from "@lodestar/types";
|
|
@@ -193,12 +195,26 @@ export interface IBeaconChain {
|
|
|
193
195
|
getCanonicalBlockAtSlot(
|
|
194
196
|
slot: Slot
|
|
195
197
|
): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>;
|
|
198
|
+
/**
|
|
199
|
+
* Get local block by root, does not fetch from the network
|
|
200
|
+
*/
|
|
201
|
+
getSerializedBlockByRoot(
|
|
202
|
+
root: RootHex
|
|
203
|
+
): Promise<{block: Uint8Array; executionOptimistic: boolean; finalized: boolean; slot: Slot} | null>;
|
|
196
204
|
/**
|
|
197
205
|
* Get local block by root, does not fetch from the network
|
|
198
206
|
*/
|
|
199
207
|
getBlockByRoot(
|
|
200
208
|
root: RootHex
|
|
201
209
|
): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>;
|
|
210
|
+
getBlobSidecars(blockSlot: Slot, blockRootHex: string): Promise<deneb.BlobSidecars | null>;
|
|
211
|
+
getSerializedBlobSidecars(blockSlot: Slot, blockRootHex: string): Promise<Uint8Array | null>;
|
|
212
|
+
getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<fulu.DataColumnSidecars>;
|
|
213
|
+
getSerializedDataColumnSidecars(
|
|
214
|
+
blockSlot: Slot,
|
|
215
|
+
blockRootHex: string,
|
|
216
|
+
indices: number[]
|
|
217
|
+
): Promise<(Uint8Array | undefined)[]>;
|
|
202
218
|
|
|
203
219
|
produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
|
|
204
220
|
produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}): Promise<{
|