@lodestar/beacon-node 1.41.0-dev.9fa839a030 → 1.41.0-dev.a35cbde8b3
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 +121 -3
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/lightclient/index.d.ts.map +1 -1
- package/lib/api/impl/lightclient/index.js +19 -2
- package/lib/api/impl/lightclient/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +101 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +0 -2
- 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 +2 -1
- 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 +3 -0
- package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +23 -5
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +2 -2
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +1 -1
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +3 -3
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts +10 -2
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +24 -2
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +22 -7
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +102 -9
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts +2 -2
- package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +1 -0
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js +5 -0
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +5 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +9 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/metrics/metrics.d.ts.map +1 -1
- package/lib/metrics/metrics.js +8 -3
- package/lib/metrics/metrics.js.map +1 -1
- package/lib/network/gossip/interface.d.ts +3 -3
- package/lib/network/gossip/interface.d.ts.map +1 -1
- package/lib/network/gossip/topic.d.ts +66 -16
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +2 -2
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/interface.d.ts +3 -2
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +3 -2
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +10 -1
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +5 -1
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js +7 -1
- package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js.map +1 -1
- package/lib/util/dataColumns.d.ts +11 -1
- package/lib/util/dataColumns.d.ts.map +1 -1
- package/lib/util/dataColumns.js +31 -0
- package/lib/util/dataColumns.js.map +1 -1
- package/lib/util/serializedCache.d.ts +5 -0
- package/lib/util/serializedCache.d.ts.map +1 -1
- package/lib/util/serializedCache.js +5 -0
- package/lib/util/serializedCache.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +145 -2
- package/src/api/impl/lightclient/index.ts +19 -2
- package/src/api/impl/validator/index.ts +124 -1
- package/src/chain/blocks/importBlock.ts +0 -3
- package/src/chain/blocks/index.ts +2 -1
- package/src/chain/blocks/writeBlockInputToDb.ts +3 -0
- package/src/chain/chain.ts +35 -6
- package/src/chain/emitter.ts +2 -2
- package/src/chain/lightClient/index.ts +4 -1
- package/src/chain/options.ts +1 -0
- package/src/chain/prepareNextSlot.ts +5 -5
- package/src/chain/produceBlock/computeNewStateRoot.ts +35 -3
- package/src/chain/produceBlock/produceBlockBody.ts +154 -11
- package/src/chain/validation/dataColumnSidecar.ts +2 -5
- package/src/metrics/metrics/beacon.ts +5 -0
- package/src/metrics/metrics/lodestar.ts +9 -0
- package/src/metrics/metrics.ts +8 -3
- package/src/network/gossip/interface.ts +3 -3
- package/src/network/gossip/topic.ts +2 -1
- package/src/network/interface.ts +4 -1
- package/src/network/network.ts +20 -4
- package/src/network/processor/gossipHandlers.ts +7 -1
- package/src/network/reqresp/handlers/lightClientUpdatesByRange.ts +6 -1
- package/src/util/dataColumns.ts +41 -0
- package/src/util/serializedCache.ts +5 -0
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CachedBeaconStateAllForks,
|
|
3
|
+
CachedBeaconStateGloas,
|
|
3
4
|
DataAvailabilityStatus,
|
|
4
5
|
ExecutionPayloadStatus,
|
|
6
|
+
G2_POINT_AT_INFINITY,
|
|
5
7
|
StateHashTreeRootSource,
|
|
6
8
|
stateTransition,
|
|
7
9
|
} from "@lodestar/state-transition";
|
|
8
|
-
import {
|
|
10
|
+
import {processExecutionPayloadEnvelope} from "@lodestar/state-transition/block";
|
|
11
|
+
import {BeaconBlock, BlindedBeaconBlock, Gwei, Root, gloas} from "@lodestar/types";
|
|
9
12
|
import {ZERO_HASH} from "../../constants/index.js";
|
|
10
13
|
import {Metrics} from "../../metrics/index.js";
|
|
11
14
|
|
|
@@ -18,7 +21,7 @@ export function computeNewStateRoot(
|
|
|
18
21
|
metrics: Metrics | null,
|
|
19
22
|
state: CachedBeaconStateAllForks,
|
|
20
23
|
block: BeaconBlock | BlindedBeaconBlock
|
|
21
|
-
): {newStateRoot: Root; proposerReward: Gwei} {
|
|
24
|
+
): {newStateRoot: Root; proposerReward: Gwei; postState: CachedBeaconStateAllForks} {
|
|
22
25
|
// Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type
|
|
23
26
|
const blockEmptySig = {message: block, signature: ZERO_HASH};
|
|
24
27
|
|
|
@@ -51,5 +54,34 @@ export function computeNewStateRoot(
|
|
|
51
54
|
const newStateRoot = postState.hashTreeRoot();
|
|
52
55
|
hashTreeRootTimer?.();
|
|
53
56
|
|
|
54
|
-
return {newStateRoot, proposerReward};
|
|
57
|
+
return {newStateRoot, proposerReward, postState};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Compute the state root after processing an execution payload envelope.
|
|
62
|
+
* Similar to `computeNewStateRoot` but for payload envelope processing.
|
|
63
|
+
*
|
|
64
|
+
* The `postBlockState` is mutated in place, callers must ensure it is not needed afterward.
|
|
65
|
+
*/
|
|
66
|
+
export function computeEnvelopeStateRoot(
|
|
67
|
+
metrics: Metrics | null,
|
|
68
|
+
postBlockState: CachedBeaconStateGloas,
|
|
69
|
+
envelope: gloas.ExecutionPayloadEnvelope
|
|
70
|
+
): Root {
|
|
71
|
+
const signedEnvelope: gloas.SignedExecutionPayloadEnvelope = {
|
|
72
|
+
message: envelope,
|
|
73
|
+
signature: G2_POINT_AT_INFINITY,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer();
|
|
77
|
+
processExecutionPayloadEnvelope(postBlockState, signedEnvelope, false);
|
|
78
|
+
processEnvelopeTimer?.();
|
|
79
|
+
|
|
80
|
+
const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
|
|
81
|
+
source: StateHashTreeRootSource.computeEnvelopeStateRoot,
|
|
82
|
+
});
|
|
83
|
+
const stateRoot = postBlockState.hashTreeRoot();
|
|
84
|
+
hashTreeRootTimer?.();
|
|
85
|
+
|
|
86
|
+
return stateRoot;
|
|
55
87
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {ProtoBlock, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
2
|
+
import {IForkChoice, ProtoBlock, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
3
3
|
import {
|
|
4
|
+
BUILDER_INDEX_SELF_BUILD,
|
|
4
5
|
ForkName,
|
|
5
6
|
ForkPostBellatrix,
|
|
6
7
|
ForkPostDeneb,
|
|
7
8
|
ForkPostFulu,
|
|
9
|
+
ForkPostGloas,
|
|
8
10
|
ForkPreGloas,
|
|
9
11
|
ForkSeq,
|
|
10
12
|
isForkPostAltair,
|
|
@@ -16,6 +18,8 @@ import {
|
|
|
16
18
|
CachedBeaconStateBellatrix,
|
|
17
19
|
CachedBeaconStateCapella,
|
|
18
20
|
CachedBeaconStateExecutions,
|
|
21
|
+
CachedBeaconStateGloas,
|
|
22
|
+
G2_POINT_AT_INFINITY,
|
|
19
23
|
computeTimeAtSlot,
|
|
20
24
|
getExpectedWithdrawals,
|
|
21
25
|
getRandaoMix,
|
|
@@ -42,6 +46,7 @@ import {
|
|
|
42
46
|
deneb,
|
|
43
47
|
electra,
|
|
44
48
|
fulu,
|
|
49
|
+
gloas,
|
|
45
50
|
} from "@lodestar/types";
|
|
46
51
|
import {Logger, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
47
52
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
@@ -99,6 +104,20 @@ export type AssembledBodyType<T extends BlockType> = T extends BlockType.Full
|
|
|
99
104
|
: BlindedBeaconBlockBody;
|
|
100
105
|
export type AssembledBlockType<T extends BlockType> = T extends BlockType.Full ? BeaconBlock : BlindedBeaconBlock;
|
|
101
106
|
|
|
107
|
+
export type ProduceFullGloas = {
|
|
108
|
+
type: BlockType.Full;
|
|
109
|
+
fork: ForkPostGloas;
|
|
110
|
+
executionPayload: ExecutionPayload<ForkPostGloas>;
|
|
111
|
+
executionRequests: electra.ExecutionRequests;
|
|
112
|
+
blobsBundle: BlobsBundle<ForkPostGloas>;
|
|
113
|
+
cells: fulu.Cell[][];
|
|
114
|
+
/**
|
|
115
|
+
* Cached envelope state root computed during block production.
|
|
116
|
+
* This is the state root after running `processExecutionPayloadEnvelope` on the
|
|
117
|
+
* post-block state, and later used to construct the `ExecutionPayloadEnvelope`.
|
|
118
|
+
*/
|
|
119
|
+
envelopeStateRoot: Root;
|
|
120
|
+
};
|
|
102
121
|
export type ProduceFullFulu = {
|
|
103
122
|
type: BlockType.Full;
|
|
104
123
|
fork: ForkPostFulu;
|
|
@@ -131,6 +150,7 @@ export type ProduceBlinded = {
|
|
|
131
150
|
|
|
132
151
|
/** The result of local block production, everything that's not the block itself */
|
|
133
152
|
export type ProduceResult =
|
|
153
|
+
| ProduceFullGloas
|
|
134
154
|
| ProduceFullFulu
|
|
135
155
|
| ProduceFullDeneb
|
|
136
156
|
| ProduceFullBellatrix
|
|
@@ -180,12 +200,112 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
180
200
|
this.logger.verbose("Producing beacon block body", logMeta);
|
|
181
201
|
|
|
182
202
|
if (isForkPostGloas(fork)) {
|
|
183
|
-
// TODO GLOAS:
|
|
203
|
+
// TODO GLOAS: support non self-building here, the block type differentiation between
|
|
204
|
+
// full and blinded no longer makes sense in gloas, it might be a good idea to move
|
|
205
|
+
// this into a completely separate function and have pre/post gloas more separated
|
|
206
|
+
const gloasState = currentState as CachedBeaconStateGloas;
|
|
207
|
+
const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
|
|
208
|
+
const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
209
|
+
const feeRecipient = requestedFeeRecipient ?? this.beaconProposerCache.getOrDefault(proposerIndex);
|
|
210
|
+
|
|
211
|
+
const endExecutionPayload = this.metrics?.executionBlockProductionTimeSteps.startTimer();
|
|
212
|
+
|
|
213
|
+
this.logger.verbose("Preparing execution payload from engine", {
|
|
214
|
+
slot: blockSlot,
|
|
215
|
+
parentBlockRoot: toRootHex(parentBlockRoot),
|
|
216
|
+
feeRecipient,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Get execution payload from EL
|
|
220
|
+
const prepareRes = await prepareExecutionPayload(
|
|
221
|
+
this,
|
|
222
|
+
this.logger,
|
|
223
|
+
fork,
|
|
224
|
+
parentBlockRoot,
|
|
225
|
+
safeBlockHash,
|
|
226
|
+
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
227
|
+
gloasState,
|
|
228
|
+
feeRecipient
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const {prepType, payloadId} = prepareRes;
|
|
232
|
+
Object.assign(logMeta, {executionPayloadPrepType: prepType});
|
|
233
|
+
|
|
234
|
+
if (prepType !== PayloadPreparationType.Cached) {
|
|
235
|
+
await sleep(PAYLOAD_GENERATION_TIME_MS);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.logger.verbose("Fetching execution payload from engine", {slot: blockSlot, payloadId});
|
|
239
|
+
const payloadRes = await this.executionEngine.getPayload(fork, payloadId);
|
|
240
|
+
|
|
241
|
+
endExecutionPayload?.({step: BlockProductionStep.executionPayload});
|
|
242
|
+
|
|
243
|
+
const {executionPayload, blobsBundle, executionRequests} = payloadRes;
|
|
244
|
+
executionPayloadValue = payloadRes.executionPayloadValue;
|
|
245
|
+
shouldOverrideBuilder = payloadRes.shouldOverrideBuilder;
|
|
246
|
+
|
|
247
|
+
if (blobsBundle === undefined) {
|
|
248
|
+
throw Error(`Missing blobsBundle response from getPayload at fork=${fork}`);
|
|
249
|
+
}
|
|
250
|
+
if (executionRequests === undefined) {
|
|
251
|
+
throw Error(`Missing executionRequests response from getPayload at fork=${fork}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const cells = blobsBundle.blobs.map((blob) => kzg.computeCells(blob));
|
|
255
|
+
if (this.opts.sanityCheckExecutionEngineBlobs) {
|
|
256
|
+
await validateCellsAndKzgCommitments(blobsBundle.commitments, blobsBundle.proofs, cells);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Create self-build execution payload bid
|
|
260
|
+
const bid: gloas.ExecutionPayloadBid = {
|
|
261
|
+
parentBlockHash: gloasState.latestBlockHash,
|
|
262
|
+
parentBlockRoot: parentBlockRoot,
|
|
263
|
+
blockHash: executionPayload.blockHash,
|
|
264
|
+
prevRandao: getRandaoMix(gloasState, gloasState.epochCtx.epoch),
|
|
265
|
+
feeRecipient: executionPayload.feeRecipient,
|
|
266
|
+
gasLimit: BigInt(executionPayload.gasLimit),
|
|
267
|
+
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
268
|
+
slot: blockSlot,
|
|
269
|
+
value: 0,
|
|
270
|
+
executionPayment: 0,
|
|
271
|
+
blobKzgCommitments: blobsBundle.commitments,
|
|
272
|
+
};
|
|
273
|
+
const signedBid: gloas.SignedExecutionPayloadBid = {
|
|
274
|
+
message: bid,
|
|
275
|
+
signature: G2_POINT_AT_INFINITY,
|
|
276
|
+
};
|
|
277
|
+
|
|
184
278
|
const commonBlockBody = await commonBlockBodyPromise;
|
|
185
|
-
|
|
186
|
-
|
|
279
|
+
const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
|
|
280
|
+
gloasBody.signedExecutionPayloadBid = signedBid;
|
|
281
|
+
// TODO GLOAS: Get payload attestations from pool for previous slot
|
|
282
|
+
gloasBody.payloadAttestations = [];
|
|
283
|
+
blockBody = gloasBody as AssembledBodyType<T>;
|
|
284
|
+
|
|
285
|
+
// Store execution payload data required to construct execution payload envelope later
|
|
286
|
+
const gloasResult = produceResult as ProduceFullGloas;
|
|
287
|
+
gloasResult.executionPayload = executionPayload as ExecutionPayload<ForkPostGloas>;
|
|
288
|
+
gloasResult.executionRequests = executionRequests;
|
|
289
|
+
gloasResult.blobsBundle = blobsBundle;
|
|
290
|
+
gloasResult.cells = cells;
|
|
291
|
+
|
|
292
|
+
const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime);
|
|
293
|
+
this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
|
|
294
|
+
this.logger.verbose("Produced block with self-build bid", {
|
|
295
|
+
slot: blockSlot,
|
|
296
|
+
executionPayloadValue,
|
|
297
|
+
prepType,
|
|
298
|
+
payloadId,
|
|
299
|
+
fetchedTime,
|
|
300
|
+
executionBlockHash: toRootHex(executionPayload.blockHash),
|
|
301
|
+
blobs: blobsBundle.commitments.length,
|
|
302
|
+
});
|
|
187
303
|
|
|
188
|
-
|
|
304
|
+
Object.assign(logMeta, {
|
|
305
|
+
transactions: executionPayload.transactions.length,
|
|
306
|
+
blobs: blobsBundle.commitments.length,
|
|
307
|
+
shouldOverrideBuilder,
|
|
308
|
+
});
|
|
189
309
|
} else if (isForkPostBellatrix(fork)) {
|
|
190
310
|
const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
|
|
191
311
|
const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
@@ -480,10 +600,12 @@ export async function prepareExecutionPayload(
|
|
|
480
600
|
parentBlockRoot: Root,
|
|
481
601
|
safeBlockHash: RootHex,
|
|
482
602
|
finalizedBlockHash: RootHex,
|
|
483
|
-
state: CachedBeaconStateExecutions,
|
|
603
|
+
state: CachedBeaconStateExecutions | CachedBeaconStateGloas,
|
|
484
604
|
suggestedFeeRecipient: string
|
|
485
605
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
486
|
-
const parentHash =
|
|
606
|
+
const parentHash = isForkPostGloas(fork)
|
|
607
|
+
? (state as CachedBeaconStateGloas).latestBlockHash
|
|
608
|
+
: (state as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockHash;
|
|
487
609
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
488
610
|
const prevRandao = getRandaoMix(state, state.epochCtx.epoch);
|
|
489
611
|
|
|
@@ -568,25 +690,46 @@ export function getPayloadAttributesForSSE(
|
|
|
568
690
|
fork: ForkPostBellatrix,
|
|
569
691
|
chain: {
|
|
570
692
|
config: ChainForkConfig;
|
|
693
|
+
forkChoice: IForkChoice;
|
|
571
694
|
},
|
|
572
695
|
{
|
|
573
696
|
prepareState,
|
|
574
697
|
prepareSlot,
|
|
575
698
|
parentBlockRoot,
|
|
576
699
|
feeRecipient,
|
|
577
|
-
}: {
|
|
700
|
+
}: {
|
|
701
|
+
prepareState: CachedBeaconStateExecutions | CachedBeaconStateGloas;
|
|
702
|
+
prepareSlot: Slot;
|
|
703
|
+
parentBlockRoot: Root;
|
|
704
|
+
feeRecipient: string;
|
|
705
|
+
}
|
|
578
706
|
): SSEPayloadAttributes {
|
|
579
|
-
const parentHash =
|
|
707
|
+
const parentHash = isForkPostGloas(fork)
|
|
708
|
+
? (prepareState as CachedBeaconStateGloas).latestBlockHash
|
|
709
|
+
: (prepareState as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockHash;
|
|
580
710
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
581
711
|
prepareState,
|
|
582
712
|
prepareSlot,
|
|
583
713
|
parentBlockRoot,
|
|
584
714
|
feeRecipient,
|
|
585
715
|
});
|
|
716
|
+
|
|
717
|
+
let parentBlockNumber: number;
|
|
718
|
+
if (isForkPostGloas(fork)) {
|
|
719
|
+
// TODO GLOAS: revisit this after fork choice changes are merged
|
|
720
|
+
const parentBlock = chain.forkChoice.getBlock(parentBlockRoot);
|
|
721
|
+
if (parentBlock?.executionPayloadBlockHash == null) {
|
|
722
|
+
throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
|
|
723
|
+
}
|
|
724
|
+
parentBlockNumber = parentBlock.executionPayloadNumber;
|
|
725
|
+
} else {
|
|
726
|
+
parentBlockNumber = (prepareState as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockNumber;
|
|
727
|
+
}
|
|
728
|
+
|
|
586
729
|
const ssePayloadAttributes: SSEPayloadAttributes = {
|
|
587
730
|
proposerIndex: prepareState.epochCtx.getBeaconProposer(prepareSlot),
|
|
588
731
|
proposalSlot: prepareSlot,
|
|
589
|
-
parentBlockNumber
|
|
732
|
+
parentBlockNumber,
|
|
590
733
|
parentBlockRoot,
|
|
591
734
|
parentBlockHash: parentHash,
|
|
592
735
|
payloadAttributes,
|
|
@@ -605,7 +748,7 @@ function preparePayloadAttributes(
|
|
|
605
748
|
parentBlockRoot,
|
|
606
749
|
feeRecipient,
|
|
607
750
|
}: {
|
|
608
|
-
prepareState: CachedBeaconStateExecutions;
|
|
751
|
+
prepareState: CachedBeaconStateExecutions | CachedBeaconStateGloas;
|
|
609
752
|
prepareSlot: Slot;
|
|
610
753
|
parentBlockRoot: Root;
|
|
611
754
|
feeRecipient: string;
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
getBlockHeaderProposerSignatureSetByHeaderSlot,
|
|
11
11
|
getBlockHeaderProposerSignatureSetByParentStateSlot,
|
|
12
12
|
} from "@lodestar/state-transition";
|
|
13
|
-
import {Root, Slot, SubnetID, fulu, ssz} from "@lodestar/types";
|
|
13
|
+
import {DataColumnSidecar, Root, Slot, SubnetID, fulu, ssz} from "@lodestar/types";
|
|
14
14
|
import {byteArrayEquals, toRootHex, verifyMerkleBranch} from "@lodestar/utils";
|
|
15
15
|
import {Metrics} from "../../metrics/metrics.js";
|
|
16
16
|
import {kzg} from "../../util/kzg.js";
|
|
@@ -457,9 +457,6 @@ export async function validateBlockDataColumnSidecars(
|
|
|
457
457
|
* SPEC FUNCTION
|
|
458
458
|
* https://github.com/ethereum/consensus-specs/blob/v1.6.0-alpha.4/specs/fulu/p2p-interface.md#compute_subnet_for_data_column_sidecar
|
|
459
459
|
*/
|
|
460
|
-
export function computeSubnetForDataColumnSidecar(
|
|
461
|
-
config: ChainConfig,
|
|
462
|
-
columnSidecar: fulu.DataColumnSidecar
|
|
463
|
-
): SubnetID {
|
|
460
|
+
export function computeSubnetForDataColumnSidecar(config: ChainConfig, columnSidecar: DataColumnSidecar): SubnetID {
|
|
464
461
|
return columnSidecar.index % config.DATA_COLUMN_SIDECAR_SUBNET_COUNT;
|
|
465
462
|
}
|
|
@@ -144,6 +144,11 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
|
|
|
144
144
|
help: "Time for preparing payload in advance",
|
|
145
145
|
buckets: [0.1, 1, 3, 5, 10],
|
|
146
146
|
}),
|
|
147
|
+
executionPayloadEnvelopeProcessingTime: register.histogram({
|
|
148
|
+
name: "beacon_block_payload_envelope_processing_seconds",
|
|
149
|
+
help: "Time to process execution payload envelope during block production",
|
|
150
|
+
buckets: [0.005, 0.01, 0.05, 0.1, 0.2, 0.5, 1],
|
|
151
|
+
}),
|
|
147
152
|
payloadFetchedTime: register.histogram<{prepType: PayloadPreparationType}>({
|
|
148
153
|
name: "beacon_block_payload_fetched_time",
|
|
149
154
|
help: "Time to fetch the payload from EL",
|
|
@@ -827,6 +827,15 @@ export function createLodestarMetrics(
|
|
|
827
827
|
help: "Total number of blobs retrieved from execution engine and published to gossip",
|
|
828
828
|
}),
|
|
829
829
|
},
|
|
830
|
+
// Gossip execution payload envelope
|
|
831
|
+
gossipExecutionPayloadEnvelope: {
|
|
832
|
+
elapsedTimeTillReceived: register.histogram<{source: OpSource}>({
|
|
833
|
+
name: "lodestar_gossip_execution_payload_envelope_elapsed_time_till_received",
|
|
834
|
+
help: "Time elapsed between slot time and the time execution payload envelope received",
|
|
835
|
+
labelNames: ["source"],
|
|
836
|
+
buckets: [0.5, 1, 2, 4, 6, 12],
|
|
837
|
+
}),
|
|
838
|
+
},
|
|
830
839
|
recoverDataColumnSidecars: {
|
|
831
840
|
recoverTime: register.histogram({
|
|
832
841
|
name: "lodestar_recover_data_column_sidecar_recover_time_seconds",
|
package/src/metrics/metrics.ts
CHANGED
|
@@ -19,11 +19,16 @@ export function createMetrics(opts: MetricsOptions, genesisTime: number, externa
|
|
|
19
19
|
const lodestar = createLodestarMetrics(register, opts.metadata, genesisTime);
|
|
20
20
|
const stateTransition = getMetrics(register);
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
const onUnhandledRejection = (_error: unknown): void => {
|
|
23
23
|
lodestar.unhandledPromiseRejections.inc();
|
|
24
|
-
}
|
|
24
|
+
};
|
|
25
|
+
process.on("unhandledRejection", onUnhandledRejection);
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
+
const nodeJsMetricsClose = collectNodeJSMetrics(register);
|
|
28
|
+
const close = (): void => {
|
|
29
|
+
process.removeListener("unhandledRejection", onUnhandledRejection);
|
|
30
|
+
nodeJsMetricsClose();
|
|
31
|
+
};
|
|
27
32
|
|
|
28
33
|
// Merge external registries
|
|
29
34
|
for (const externalRegister of externalRegistries) {
|
|
@@ -4,6 +4,7 @@ import {PeerIdStr} from "@chainsafe/libp2p-gossipsub/types";
|
|
|
4
4
|
import {BeaconConfig, ForkBoundary} from "@lodestar/config";
|
|
5
5
|
import {
|
|
6
6
|
AttesterSlashing,
|
|
7
|
+
DataColumnSidecar,
|
|
7
8
|
LightClientFinalityUpdate,
|
|
8
9
|
LightClientOptimisticUpdate,
|
|
9
10
|
SignedAggregateAndProof,
|
|
@@ -14,7 +15,6 @@ import {
|
|
|
14
15
|
altair,
|
|
15
16
|
capella,
|
|
16
17
|
deneb,
|
|
17
|
-
fulu,
|
|
18
18
|
gloas,
|
|
19
19
|
phase0,
|
|
20
20
|
} from "@lodestar/types";
|
|
@@ -98,7 +98,7 @@ export type GossipTypeMap = {
|
|
|
98
98
|
[GossipType.blob_sidecar]: deneb.BlobSidecar;
|
|
99
99
|
[GossipType.beacon_aggregate_and_proof]: SignedAggregateAndProof;
|
|
100
100
|
[GossipType.beacon_attestation]: SingleAttestation;
|
|
101
|
-
[GossipType.data_column_sidecar]:
|
|
101
|
+
[GossipType.data_column_sidecar]: DataColumnSidecar;
|
|
102
102
|
[GossipType.voluntary_exit]: phase0.SignedVoluntaryExit;
|
|
103
103
|
[GossipType.proposer_slashing]: phase0.ProposerSlashing;
|
|
104
104
|
[GossipType.attester_slashing]: AttesterSlashing;
|
|
@@ -117,7 +117,7 @@ export type GossipFnByType = {
|
|
|
117
117
|
[GossipType.blob_sidecar]: (blobSidecar: deneb.BlobSidecar) => Promise<void> | void;
|
|
118
118
|
[GossipType.beacon_aggregate_and_proof]: (aggregateAndProof: SignedAggregateAndProof) => Promise<void> | void;
|
|
119
119
|
[GossipType.beacon_attestation]: (attestation: SingleAttestation) => Promise<void> | void;
|
|
120
|
-
[GossipType.data_column_sidecar]: (dataColumnSidecar:
|
|
120
|
+
[GossipType.data_column_sidecar]: (dataColumnSidecar: DataColumnSidecar) => Promise<void> | void;
|
|
121
121
|
[GossipType.voluntary_exit]: (voluntaryExit: phase0.SignedVoluntaryExit) => Promise<void> | void;
|
|
122
122
|
[GossipType.proposer_slashing]: (proposerSlashing: phase0.ProposerSlashing) => Promise<void> | void;
|
|
123
123
|
[GossipType.attester_slashing]: (attesterSlashing: AttesterSlashing) => Promise<void> | void;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
SYNC_COMMITTEE_SUBNET_COUNT,
|
|
7
7
|
isForkPostAltair,
|
|
8
8
|
isForkPostElectra,
|
|
9
|
+
isForkPostFulu,
|
|
9
10
|
} from "@lodestar/params";
|
|
10
11
|
import {Attestation, SingleAttestation, ssz, sszTypesFor} from "@lodestar/types";
|
|
11
12
|
import {GossipAction, GossipActionError, GossipErrorCode} from "../../chain/errors/gossipValidation.js";
|
|
@@ -92,7 +93,7 @@ export function getGossipSSZType(topic: GossipTopic) {
|
|
|
92
93
|
case GossipType.blob_sidecar:
|
|
93
94
|
return ssz.deneb.BlobSidecar;
|
|
94
95
|
case GossipType.data_column_sidecar:
|
|
95
|
-
return ssz.fulu.DataColumnSidecar;
|
|
96
|
+
return isForkPostFulu(fork) ? sszTypesFor(fork).DataColumnSidecar : ssz.fulu.DataColumnSidecar;
|
|
96
97
|
case GossipType.beacon_aggregate_and_proof:
|
|
97
98
|
return sszTypesFor(fork).SignedAggregateAndProof;
|
|
98
99
|
case GossipType.beacon_attestation:
|
package/src/network/interface.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type {Datastore} from "interface-datastore";
|
|
|
19
19
|
import {Libp2p as ILibp2p} from "libp2p";
|
|
20
20
|
import {
|
|
21
21
|
AttesterSlashing,
|
|
22
|
+
DataColumnSidecar,
|
|
22
23
|
LightClientFinalityUpdate,
|
|
23
24
|
LightClientOptimisticUpdate,
|
|
24
25
|
SignedAggregateAndProof,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
capella,
|
|
32
33
|
deneb,
|
|
33
34
|
fulu,
|
|
35
|
+
gloas,
|
|
34
36
|
phase0,
|
|
35
37
|
} from "@lodestar/types";
|
|
36
38
|
import {BlockInputSource} from "../chain/blocks/blockInput/types.js";
|
|
@@ -86,7 +88,7 @@ export interface INetwork extends INetworkCorePublic {
|
|
|
86
88
|
publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise<number>;
|
|
87
89
|
publishBeaconAggregateAndProof(aggregateAndProof: SignedAggregateAndProof): Promise<number>;
|
|
88
90
|
publishBeaconAttestation(attestation: SingleAttestation, subnet: SubnetID): Promise<number>;
|
|
89
|
-
publishDataColumnSidecar(dataColumnSideCar:
|
|
91
|
+
publishDataColumnSidecar(dataColumnSideCar: DataColumnSidecar): Promise<number>;
|
|
90
92
|
publishVoluntaryExit(voluntaryExit: phase0.SignedVoluntaryExit): Promise<number>;
|
|
91
93
|
publishBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): Promise<number>;
|
|
92
94
|
publishProposerSlashing(proposerSlashing: phase0.ProposerSlashing): Promise<number>;
|
|
@@ -95,6 +97,7 @@ export interface INetwork extends INetworkCorePublic {
|
|
|
95
97
|
publishContributionAndProof(contributionAndProof: altair.SignedContributionAndProof): Promise<number>;
|
|
96
98
|
publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
|
|
97
99
|
publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
|
|
100
|
+
publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
|
|
98
101
|
|
|
99
102
|
// Debug
|
|
100
103
|
dumpGossipQueue(gossipType: GossipType): Promise<PendingGossipsubMessage[]>;
|
package/src/network/network.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {ResponseIncoming} from "@lodestar/reqresp";
|
|
|
10
10
|
import {computeEpochAtSlot} from "@lodestar/state-transition";
|
|
11
11
|
import {
|
|
12
12
|
AttesterSlashing,
|
|
13
|
+
DataColumnSidecar,
|
|
13
14
|
LightClientBootstrap,
|
|
14
15
|
LightClientFinalityUpdate,
|
|
15
16
|
LightClientOptimisticUpdate,
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
capella,
|
|
25
26
|
deneb,
|
|
26
27
|
fulu,
|
|
28
|
+
gloas,
|
|
27
29
|
phase0,
|
|
28
30
|
} from "@lodestar/types";
|
|
29
31
|
import {prettyPrintIndices, sleep} from "@lodestar/utils";
|
|
@@ -33,7 +35,7 @@ import {computeSubnetForDataColumnSidecar} from "../chain/validation/dataColumnS
|
|
|
33
35
|
import {IBeaconDb} from "../db/interface.js";
|
|
34
36
|
import {Metrics, RegistryMetricCreator} from "../metrics/index.js";
|
|
35
37
|
import {IClock} from "../util/clock.js";
|
|
36
|
-
import {CustodyConfig} from "../util/dataColumns.js";
|
|
38
|
+
import {CustodyConfig, isGloasDataColumnSidecar} from "../util/dataColumns.js";
|
|
37
39
|
import {PeerIdStr, peerIdToString} from "../util/peerId.js";
|
|
38
40
|
import {promiseAllMaybeAsync} from "../util/promises.js";
|
|
39
41
|
import {BeaconBlocksByRootRequest, BlobSidecarsByRootRequest, DataColumnSidecarsByRootRequest} from "../util/types.js";
|
|
@@ -354,8 +356,11 @@ export class Network implements INetwork {
|
|
|
354
356
|
});
|
|
355
357
|
}
|
|
356
358
|
|
|
357
|
-
async publishDataColumnSidecar(dataColumnSidecar:
|
|
358
|
-
const
|
|
359
|
+
async publishDataColumnSidecar(dataColumnSidecar: DataColumnSidecar): Promise<number> {
|
|
360
|
+
const slot = isGloasDataColumnSidecar(dataColumnSidecar)
|
|
361
|
+
? dataColumnSidecar.slot
|
|
362
|
+
: dataColumnSidecar.signedBlockHeader.message.slot;
|
|
363
|
+
const epoch = computeEpochAtSlot(slot);
|
|
359
364
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
360
365
|
|
|
361
366
|
const subnet = computeSubnetForDataColumnSidecar(this.config, dataColumnSidecar);
|
|
@@ -489,6 +494,17 @@ export class Network implements INetwork {
|
|
|
489
494
|
);
|
|
490
495
|
}
|
|
491
496
|
|
|
497
|
+
async publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number> {
|
|
498
|
+
const epoch = computeEpochAtSlot(signedEnvelope.message.slot);
|
|
499
|
+
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
500
|
+
|
|
501
|
+
return this.publishGossip<GossipType.execution_payload>(
|
|
502
|
+
{type: GossipType.execution_payload, boundary},
|
|
503
|
+
signedEnvelope,
|
|
504
|
+
{ignoreDuplicatePublishError: true}
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
492
508
|
private async publishGossip<K extends GossipType>(
|
|
493
509
|
topic: GossipTopicMap[K],
|
|
494
510
|
object: GossipTypeMap[K],
|
|
@@ -765,7 +781,7 @@ export class Network implements INetwork {
|
|
|
765
781
|
this.core.setTargetGroupCount(count);
|
|
766
782
|
};
|
|
767
783
|
|
|
768
|
-
private onPublishDataColumns = (sidecars:
|
|
784
|
+
private onPublishDataColumns = (sidecars: DataColumnSidecar[]): Promise<number[]> => {
|
|
769
785
|
return promiseAllMaybeAsync(sidecars.map((sidecar) => () => this.publishDataColumnSidecar(sidecar)));
|
|
770
786
|
};
|
|
771
787
|
|
|
@@ -548,7 +548,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
548
548
|
seenTimestampSec,
|
|
549
549
|
}: GossipHandlerParamGeneric<GossipType.data_column_sidecar>) => {
|
|
550
550
|
const {serializedData} = gossipData;
|
|
551
|
-
|
|
551
|
+
// TODO GLOAS: handle gloas.DataColumnSidecar
|
|
552
|
+
const dataColumnSidecar = sszDeserialize(topic, serializedData) as fulu.DataColumnSidecar;
|
|
552
553
|
const dataColumnSlot = dataColumnSidecar.signedBlockHeader.message.slot;
|
|
553
554
|
const index = dataColumnSidecar.index;
|
|
554
555
|
|
|
@@ -821,11 +822,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
821
822
|
[GossipType.execution_payload]: async ({
|
|
822
823
|
gossipData,
|
|
823
824
|
topic,
|
|
825
|
+
seenTimestampSec,
|
|
824
826
|
}: GossipHandlerParamGeneric<GossipType.execution_payload>) => {
|
|
825
827
|
const {serializedData} = gossipData;
|
|
826
828
|
const executionPayloadEnvelope = sszDeserialize(topic, serializedData);
|
|
827
829
|
await validateGossipExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
|
|
828
830
|
|
|
831
|
+
const slot = executionPayloadEnvelope.message.slot;
|
|
832
|
+
const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);
|
|
833
|
+
metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
834
|
+
|
|
829
835
|
// TODO GLOAS: Handle valid envelope. Need an import flow that calls `processExecutionPayloadEnvelope` and fork choice
|
|
830
836
|
},
|
|
831
837
|
[GossipType.payload_attestation_message]: async ({
|
|
@@ -19,6 +19,7 @@ export async function* onLightClientUpdatesByRange(
|
|
|
19
19
|
assertLightClientServer(chain.lightClientServer);
|
|
20
20
|
|
|
21
21
|
const count = Math.min(MAX_REQUEST_LIGHT_CLIENT_UPDATES, requestBody.count);
|
|
22
|
+
let started = false;
|
|
22
23
|
for (let period = requestBody.startPeriod; period < requestBody.startPeriod + count; period++) {
|
|
23
24
|
try {
|
|
24
25
|
const update = await chain.lightClientServer.getUpdate(period);
|
|
@@ -29,9 +30,13 @@ export async function* onLightClientUpdatesByRange(
|
|
|
29
30
|
data: type.serialize(update),
|
|
30
31
|
boundary,
|
|
31
32
|
};
|
|
33
|
+
started = true;
|
|
32
34
|
} catch (e) {
|
|
33
35
|
if ((e as LightClientServerError).type?.code === LightClientServerErrorCode.RESOURCE_UNAVAILABLE) {
|
|
34
|
-
|
|
36
|
+
// Period not available, if we already started yielding, stop to
|
|
37
|
+
// preserve consecutive order. Otherwise skip leading gaps.
|
|
38
|
+
if (started) return;
|
|
39
|
+
continue;
|
|
35
40
|
}
|
|
36
41
|
throw new ResponseError(RespStatus.SERVER_ERROR, (e as Error).message);
|
|
37
42
|
}
|
package/src/util/dataColumns.ts
CHANGED
|
@@ -15,9 +15,12 @@ import {
|
|
|
15
15
|
BeaconBlockBody,
|
|
16
16
|
ColumnIndex,
|
|
17
17
|
CustodyIndex,
|
|
18
|
+
DataColumnSidecar,
|
|
19
|
+
Root,
|
|
18
20
|
SSZTypesFor,
|
|
19
21
|
SignedBeaconBlock,
|
|
20
22
|
SignedBeaconBlockHeader,
|
|
23
|
+
Slot,
|
|
21
24
|
deneb,
|
|
22
25
|
fulu,
|
|
23
26
|
gloas,
|
|
@@ -277,6 +280,11 @@ export function getBlobKzgCommitments(
|
|
|
277
280
|
return (signedBlock.message.body as BeaconBlockBody<ForkPostFulu & ForkPreGloas>).blobKzgCommitments;
|
|
278
281
|
}
|
|
279
282
|
|
|
283
|
+
/** Type guard for `gloas.DataColumnSidecar` */
|
|
284
|
+
export function isGloasDataColumnSidecar(sidecar: DataColumnSidecar): sidecar is gloas.DataColumnSidecar {
|
|
285
|
+
return (sidecar as gloas.DataColumnSidecar).beaconBlockRoot !== undefined;
|
|
286
|
+
}
|
|
287
|
+
|
|
280
288
|
/**
|
|
281
289
|
* Given a signed block header and the commitments, inclusion proof, cells/proofs associated with
|
|
282
290
|
* each blob in the block, assemble the sidecars which can be distributed to peers.
|
|
@@ -359,6 +367,39 @@ export function getDataColumnSidecarsFromColumnSidecar(
|
|
|
359
367
|
);
|
|
360
368
|
}
|
|
361
369
|
|
|
370
|
+
/**
|
|
371
|
+
* In Gloas, data column sidecars have a simplified structure with `slot` and `beaconBlockRoot`
|
|
372
|
+
* instead of `signedBlockHeader`, `kzgCommitments`, and `kzgCommitmentsInclusionProof`.
|
|
373
|
+
*/
|
|
374
|
+
export function getDataColumnSidecarsForGloas(
|
|
375
|
+
slot: Slot,
|
|
376
|
+
beaconBlockRoot: Root,
|
|
377
|
+
cellsAndKzgProofs: {cells: Uint8Array[]; proofs: Uint8Array[]}[]
|
|
378
|
+
): gloas.DataColumnSidecars {
|
|
379
|
+
// No need to create data column sidecars if there are no blobs
|
|
380
|
+
if (cellsAndKzgProofs.length === 0) {
|
|
381
|
+
return [];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const sidecars: gloas.DataColumnSidecars = [];
|
|
385
|
+
for (let columnIndex = 0; columnIndex < NUMBER_OF_COLUMNS; columnIndex++) {
|
|
386
|
+
const column: Uint8Array[] = [];
|
|
387
|
+
const kzgProofs: Uint8Array[] = [];
|
|
388
|
+
for (const {cells, proofs} of cellsAndKzgProofs) {
|
|
389
|
+
column.push(cells[columnIndex]);
|
|
390
|
+
kzgProofs.push(proofs[columnIndex]);
|
|
391
|
+
}
|
|
392
|
+
sidecars.push({
|
|
393
|
+
index: columnIndex,
|
|
394
|
+
column,
|
|
395
|
+
kzgProofs,
|
|
396
|
+
slot,
|
|
397
|
+
beaconBlockRoot,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return sidecars;
|
|
401
|
+
}
|
|
402
|
+
|
|
362
403
|
/**
|
|
363
404
|
* If we receive more than half of NUMBER_OF_COLUMNS (64) we should recover all remaining columns
|
|
364
405
|
*/
|
|
@@ -14,6 +14,11 @@ export class SerializedCache {
|
|
|
14
14
|
this.map.set(obj, serialized);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Replace the internal WeakMap to force GC of all cached entries.
|
|
19
|
+
* Must only be called after all DB writes that may read from this cache have completed,
|
|
20
|
+
* otherwise cached serialized bytes will be unavailable and data will be re-serialized unnecessarily.
|
|
21
|
+
*/
|
|
17
22
|
clear(): void {
|
|
18
23
|
this.map = new WeakMap();
|
|
19
24
|
}
|