@lodestar/beacon-node 1.42.0-dev.8961934298 → 1.42.0-dev.89ddaaeed2
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 +11 -11
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/chain/ColumnReconstructionTracker.d.ts +2 -1
- package/lib/chain/ColumnReconstructionTracker.d.ts.map +1 -1
- package/lib/chain/ColumnReconstructionTracker.js +5 -5
- package/lib/chain/ColumnReconstructionTracker.js.map +1 -1
- package/lib/chain/GetBlobsTracker.d.ts +2 -1
- package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
- package/lib/chain/GetBlobsTracker.js +14 -12
- package/lib/chain/GetBlobsTracker.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +5 -5
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/blockInput/types.d.ts +4 -4
- package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +13 -1
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +14 -6
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +33 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/chain.d.ts +3 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +55 -20
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +29 -7
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +12 -3
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.d.ts +31 -1
- package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.js +7 -0
- package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
- package/lib/chain/interface.d.ts +4 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +2 -2
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +2 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts +11 -4
- package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +184 -5
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/db/buckets.d.ts +2 -2
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +2 -2
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/dataColumnSidecar.d.ts.map +1 -1
- package/lib/db/repositories/dataColumnSidecar.js +4 -2
- package/lib/db/repositories/dataColumnSidecar.js.map +1 -1
- package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
- package/lib/db/repositories/dataColumnSidecarArchive.js +4 -2
- package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +20 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +33 -0
- package/lib/metrics/metrics/lodestar.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 +3 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/extractSlotRootFns.d.ts +1 -1
- package/lib/network/processor/extractSlotRootFns.d.ts.map +1 -1
- package/lib/network/processor/extractSlotRootFns.js +25 -5
- package/lib/network/processor/extractSlotRootFns.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +242 -66
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +11 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +232 -22
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/types.d.ts +3 -3
- package/lib/network/reqresp/types.d.ts.map +1 -1
- package/lib/network/reqresp/types.js +9 -3
- package/lib/network/reqresp/types.js.map +1 -1
- package/lib/sync/unknownBlock.js +2 -2
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +3 -3
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +4 -2
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts +3 -3
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +10 -5
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/util/blobs.d.ts +3 -3
- package/lib/util/blobs.d.ts.map +1 -1
- package/lib/util/blobs.js +21 -10
- package/lib/util/blobs.js.map +1 -1
- package/lib/util/dataColumns.d.ts +18 -11
- package/lib/util/dataColumns.d.ts.map +1 -1
- package/lib/util/dataColumns.js +51 -17
- package/lib/util/dataColumns.js.map +1 -1
- package/lib/util/execution.d.ts +6 -2
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +49 -25
- package/lib/util/execution.js.map +1 -1
- package/lib/util/sszBytes.d.ts +25 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +189 -2
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +17 -14
- package/src/api/impl/debug/index.ts +2 -2
- package/src/chain/ColumnReconstructionTracker.ts +6 -5
- package/src/chain/GetBlobsTracker.ts +14 -12
- package/src/chain/blocks/blockInput/blockInput.ts +8 -8
- package/src/chain/blocks/blockInput/types.ts +4 -4
- package/src/chain/blocks/importBlock.ts +18 -1
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +53 -12
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
- package/src/chain/chain.ts +63 -24
- package/src/chain/emitter.ts +25 -7
- package/src/chain/errors/dataColumnSidecarError.ts +32 -1
- package/src/chain/interface.ts +4 -2
- package/src/chain/seenCache/seenGossipBlockInput.ts +2 -2
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +2 -2
- package/src/chain/validation/dataColumnSidecar.ts +230 -7
- package/src/db/buckets.ts +2 -2
- package/src/db/repositories/dataColumnSidecar.ts +4 -2
- package/src/db/repositories/dataColumnSidecarArchive.ts +4 -2
- package/src/metrics/metrics/lodestar.ts +34 -0
- package/src/network/interface.ts +3 -2
- package/src/network/network.ts +7 -4
- package/src/network/processor/extractSlotRootFns.ts +32 -6
- package/src/network/processor/gossipHandlers.ts +305 -79
- package/src/network/processor/index.ts +302 -22
- package/src/network/reqresp/types.ts +13 -5
- package/src/sync/unknownBlock.ts +3 -3
- package/src/sync/utils/downloadByRange.ts +9 -7
- package/src/sync/utils/downloadByRoot.ts +16 -12
- package/src/util/blobs.ts +35 -15
- package/src/util/dataColumns.ts +69 -25
- package/src/util/execution.ts +49 -30
- package/src/util/sszBytes.ts +245 -3
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {BeaconConfig, ChainForkConfig} from "@lodestar/config";
|
|
3
|
+
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
3
4
|
import {
|
|
4
5
|
ForkName,
|
|
5
6
|
ForkPostDeneb,
|
|
6
7
|
ForkPostElectra,
|
|
8
|
+
ForkPostGloas,
|
|
7
9
|
ForkPreElectra,
|
|
8
10
|
ForkSeq,
|
|
9
11
|
NUMBER_OF_COLUMNS,
|
|
10
12
|
isForkPostElectra,
|
|
13
|
+
isForkPostGloas,
|
|
11
14
|
} from "@lodestar/params";
|
|
12
15
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
13
16
|
import {
|
|
@@ -19,6 +22,8 @@ import {
|
|
|
19
22
|
UintNum64,
|
|
20
23
|
deneb,
|
|
21
24
|
fulu,
|
|
25
|
+
gloas,
|
|
26
|
+
isGloasDataColumnSidecar,
|
|
22
27
|
ssz,
|
|
23
28
|
sszTypesFor,
|
|
24
29
|
} from "@lodestar/types";
|
|
@@ -30,7 +35,7 @@ import {
|
|
|
30
35
|
IBlockInput,
|
|
31
36
|
isBlockInputColumns,
|
|
32
37
|
} from "../../chain/blocks/blockInput/index.js";
|
|
33
|
-
import {PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/index.js";
|
|
38
|
+
import {PayloadEnvelopeInput, PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/index.js";
|
|
34
39
|
import {BlobSidecarValidation} from "../../chain/blocks/types.js";
|
|
35
40
|
import {ChainEvent} from "../../chain/emitter.js";
|
|
36
41
|
import {
|
|
@@ -51,7 +56,10 @@ import {
|
|
|
51
56
|
} from "../../chain/errors/index.js";
|
|
52
57
|
import {IBeaconChain} from "../../chain/interface.js";
|
|
53
58
|
import {validateGossipBlobSidecar} from "../../chain/validation/blobSidecar.js";
|
|
54
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
validateGossipFuluDataColumnSidecar,
|
|
61
|
+
validateGossipGloasDataColumnSidecar,
|
|
62
|
+
} from "../../chain/validation/dataColumnSidecar.js";
|
|
55
63
|
import {validateGossipExecutionPayloadBid} from "../../chain/validation/executionPayloadBid.js";
|
|
56
64
|
import {validateGossipExecutionPayloadEnvelope} from "../../chain/validation/executionPayloadEnvelope.js";
|
|
57
65
|
import {
|
|
@@ -74,7 +82,7 @@ import {validateGossipPayloadAttestationMessage} from "../../chain/validation/pa
|
|
|
74
82
|
import {OpSource} from "../../chain/validatorMonitor.js";
|
|
75
83
|
import {Metrics} from "../../metrics/index.js";
|
|
76
84
|
import {kzgCommitmentToVersionedHash} from "../../util/blobs.js";
|
|
77
|
-
import {getBlobKzgCommitments} from "../../util/dataColumns.js";
|
|
85
|
+
import {getBlobKzgCommitments, getDataColumnSidecarSlot} from "../../util/dataColumns.js";
|
|
78
86
|
import {INetworkCore} from "../core/index.js";
|
|
79
87
|
import {NetworkEventBus} from "../events.js";
|
|
80
88
|
import {
|
|
@@ -161,16 +169,19 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
161
169
|
|
|
162
170
|
logger.debug("Received gossip block", {...logCtx});
|
|
163
171
|
|
|
164
|
-
|
|
172
|
+
// optimistically add gossip block to the seen cache
|
|
173
|
+
// if validation fails, we will NOT forward this gossip block to peers
|
|
174
|
+
// - if PARENT_UNKNOWN error, blockInput will then be queued inside BlockInputSync. If the gossip block is really invalid, it will be pruned there
|
|
175
|
+
// - if other validator errors, blockInput will stay in the seen cache and will be pruned on finalization
|
|
176
|
+
const blockInput = chain.seenBlockInputCache.getByBlock({
|
|
177
|
+
block: signedBlock,
|
|
178
|
+
blockRootHex,
|
|
179
|
+
source: BlockInputSource.gossip,
|
|
180
|
+
seenTimestampSec,
|
|
181
|
+
peerIdStr,
|
|
182
|
+
});
|
|
165
183
|
try {
|
|
166
184
|
await validateGossipBlock(config, chain, signedBlock, fork);
|
|
167
|
-
blockInput = chain.seenBlockInputCache.getByBlock({
|
|
168
|
-
block: signedBlock,
|
|
169
|
-
blockRootHex,
|
|
170
|
-
source: BlockInputSource.gossip,
|
|
171
|
-
seenTimestampSec,
|
|
172
|
-
peerIdStr,
|
|
173
|
-
});
|
|
174
185
|
const blockInputMeta = blockInput.getLogMeta();
|
|
175
186
|
|
|
176
187
|
const recvToValidation = Date.now() / 1000 - seenTimestampSec;
|
|
@@ -186,9 +197,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
186
197
|
return blockInput;
|
|
187
198
|
} catch (e) {
|
|
188
199
|
if (e instanceof BlockGossipError) {
|
|
200
|
+
logger.debug("Gossip block has error", {slot, root: blockShortHex, code: e.type.code});
|
|
189
201
|
if (e.type.code === BlockErrorCode.PARENT_UNKNOWN && blockInput) {
|
|
190
|
-
|
|
191
|
-
chain.emitter.emit(ChainEvent.unknownParent, {
|
|
202
|
+
chain.emitter.emit(ChainEvent.blockUnknownParent, {
|
|
192
203
|
blockInput,
|
|
193
204
|
peer: peerIdStr,
|
|
194
205
|
source: BlockInputSource.gossip,
|
|
@@ -324,7 +335,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
324
335
|
const blockInput = chain.seenBlockInputCache.get(blockRootHex);
|
|
325
336
|
if (blockInput && isBlockInputColumns(blockInput) && blockInput.hasColumn(dataColumnSidecar.index)) {
|
|
326
337
|
metrics?.peerDas.dataColumnSidecarProcessingSkip.inc();
|
|
327
|
-
logger.debug("Already have column sidecar, skipping processing", {
|
|
338
|
+
logger.debug("Already have column sidecar in BlockInput, skipping processing", {
|
|
328
339
|
...blockInput.getLogMeta(),
|
|
329
340
|
index: dataColumnSidecar.index,
|
|
330
341
|
});
|
|
@@ -339,10 +350,11 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
339
350
|
const verificationTimer = metrics?.peerDas.dataColumnSidecarGossipVerificationTime.startTimer();
|
|
340
351
|
|
|
341
352
|
const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
|
|
353
|
+
const secFromSlot = chain.clock.secFromSlot(slot);
|
|
342
354
|
const recvToValLatency = Date.now() / 1000 - seenTimestampSec;
|
|
343
355
|
|
|
344
356
|
try {
|
|
345
|
-
await
|
|
357
|
+
await validateGossipFuluDataColumnSidecar(chain, dataColumnSidecar, gossipSubnet, metrics);
|
|
346
358
|
const blockInput = chain.seenBlockInputCache.getByColumn({
|
|
347
359
|
blockRootHex,
|
|
348
360
|
columnSidecar: dataColumnSidecar,
|
|
@@ -372,6 +384,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
372
384
|
currentSlot: chain.clock.currentSlot,
|
|
373
385
|
peerId: peerIdStr,
|
|
374
386
|
delaySec,
|
|
387
|
+
secFromSlot,
|
|
375
388
|
gossipSubnet,
|
|
376
389
|
columnIndex: dataColumnSidecar.index,
|
|
377
390
|
recvToValLatency,
|
|
@@ -401,6 +414,131 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
401
414
|
}
|
|
402
415
|
}
|
|
403
416
|
|
|
417
|
+
async function validatePayloadDataColumn(
|
|
418
|
+
dataColumnSidecar: gloas.DataColumnSidecar,
|
|
419
|
+
gossipSubnet: SubnetID,
|
|
420
|
+
peerIdStr: string,
|
|
421
|
+
seenTimestampSec: number
|
|
422
|
+
): Promise<PayloadEnvelopeInput> {
|
|
423
|
+
metrics?.peerDas.dataColumnSidecarProcessingRequests.inc();
|
|
424
|
+
const slot = dataColumnSidecar.slot;
|
|
425
|
+
const blockRootHex = toRootHex(dataColumnSidecar.beaconBlockRoot);
|
|
426
|
+
|
|
427
|
+
// check to see if payload has already been processed and PayloadEnvelopeInput has been deleted (column received via reqresp or other means)
|
|
428
|
+
if (chain.forkChoice.getBlockHex(blockRootHex, PayloadStatus.FULL) !== null) {
|
|
429
|
+
metrics?.peerDas.dataColumnSidecarProcessingSkip.inc();
|
|
430
|
+
logger.debug("Already processed payload for column sidecar, skipping processing", {
|
|
431
|
+
slot,
|
|
432
|
+
blockRoot: blockRootHex,
|
|
433
|
+
index: dataColumnSidecar.index,
|
|
434
|
+
});
|
|
435
|
+
throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
|
|
436
|
+
code: DataColumnSidecarErrorCode.ALREADY_KNOWN,
|
|
437
|
+
columnIndex: dataColumnSidecar.index,
|
|
438
|
+
slot,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
443
|
+
|
|
444
|
+
if (!payloadInput) {
|
|
445
|
+
// This should not happen for gossip because the network processor queues `data_column_sidecar`
|
|
446
|
+
// until block import creates the corresponding PayloadEnvelopeInput.
|
|
447
|
+
throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
|
|
448
|
+
code: DataColumnSidecarErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING,
|
|
449
|
+
slot,
|
|
450
|
+
blockRoot: blockRootHex,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// [IGNORE] The sidecar is the first sidecar for the tuple
|
|
455
|
+
// (sidecar.beacon_block_root, sidecar.index) with valid kzg proof.
|
|
456
|
+
if (payloadInput.hasColumn(dataColumnSidecar.index)) {
|
|
457
|
+
metrics?.peerDas.dataColumnSidecarProcessingSkip.inc();
|
|
458
|
+
logger.debug("Already have column sidecar in PayloadEnvelopeInput, skipping processing", {
|
|
459
|
+
...payloadInput.getLogMeta(),
|
|
460
|
+
index: dataColumnSidecar.index,
|
|
461
|
+
});
|
|
462
|
+
throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
|
|
463
|
+
code: DataColumnSidecarErrorCode.ALREADY_KNOWN,
|
|
464
|
+
columnIndex: dataColumnSidecar.index,
|
|
465
|
+
slot,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const verificationTimer = metrics?.peerDas.dataColumnSidecarGossipVerificationTime.startTimer();
|
|
470
|
+
|
|
471
|
+
const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
|
|
472
|
+
const secFromSlot = chain.clock.secFromSlot(slot);
|
|
473
|
+
const recvToValLatency = Date.now() / 1000 - seenTimestampSec;
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
await validateGossipGloasDataColumnSidecar(chain, payloadInput, dataColumnSidecar, gossipSubnet, metrics);
|
|
477
|
+
|
|
478
|
+
const addedColumn = payloadInput.addColumn({
|
|
479
|
+
columnSidecar: dataColumnSidecar,
|
|
480
|
+
source: PayloadEnvelopeInputSource.gossip,
|
|
481
|
+
seenTimestampSec,
|
|
482
|
+
peerIdStr,
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
if (!addedColumn) {
|
|
486
|
+
metrics?.peerDas.dataColumnSidecarProcessingSkip.inc();
|
|
487
|
+
logger.debug("Already have column sidecar in PayloadEnvelopeInput, skipping processing", {
|
|
488
|
+
...payloadInput.getLogMeta(),
|
|
489
|
+
index: dataColumnSidecar.index,
|
|
490
|
+
});
|
|
491
|
+
throw new DataColumnSidecarGossipError(GossipAction.IGNORE, {
|
|
492
|
+
code: DataColumnSidecarErrorCode.ALREADY_KNOWN,
|
|
493
|
+
columnIndex: dataColumnSidecar.index,
|
|
494
|
+
slot,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const recvToValidation = Date.now() / 1000 - seenTimestampSec;
|
|
499
|
+
const validationTime = recvToValidation - recvToValLatency;
|
|
500
|
+
|
|
501
|
+
metrics?.peerDas.dataColumnSidecarProcessingSuccesses.inc();
|
|
502
|
+
metrics?.gossipBlob.recvToValidation.observe(recvToValidation);
|
|
503
|
+
metrics?.gossipBlob.validationTime.observe(validationTime);
|
|
504
|
+
|
|
505
|
+
if (chain.emitter.listenerCount(routes.events.EventType.dataColumnSidecar)) {
|
|
506
|
+
chain.emitter.emit(routes.events.EventType.dataColumnSidecar, {
|
|
507
|
+
blockRoot: blockRootHex,
|
|
508
|
+
slot,
|
|
509
|
+
index: dataColumnSidecar.index,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
logger.debug("Received gossip dataColumn", {
|
|
514
|
+
...payloadInput.getLogMeta(),
|
|
515
|
+
currentSlot: chain.clock.currentSlot,
|
|
516
|
+
peerId: peerIdStr,
|
|
517
|
+
delaySec,
|
|
518
|
+
secFromSlot,
|
|
519
|
+
gossipSubnet,
|
|
520
|
+
columnIndex: dataColumnSidecar.index,
|
|
521
|
+
recvToValLatency,
|
|
522
|
+
recvToValidation,
|
|
523
|
+
validationTime,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
return payloadInput;
|
|
527
|
+
} catch (e) {
|
|
528
|
+
if (e instanceof DataColumnSidecarGossipError && e.action === GossipAction.REJECT) {
|
|
529
|
+
chain.persistInvalidSszValue(
|
|
530
|
+
sszTypesFor(payloadInput.forkName as ForkPostGloas).DataColumnSidecar,
|
|
531
|
+
dataColumnSidecar,
|
|
532
|
+
`gossip_reject_slot_${slot}_index_${dataColumnSidecar.index}`
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
throw e;
|
|
537
|
+
} finally {
|
|
538
|
+
verificationTimer?.();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
404
542
|
function handleValidBeaconBlock(blockInput: IBlockInput, peerIdStr: string, seenTimestampSec: number): void {
|
|
405
543
|
const signedBlock = blockInput.getBlock();
|
|
406
544
|
const slot = signedBlock.message.slot;
|
|
@@ -554,78 +692,137 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
554
692
|
peerIdStr,
|
|
555
693
|
seenTimestampSec,
|
|
556
694
|
}: GossipHandlerParamGeneric<GossipType.data_column_sidecar>) => {
|
|
695
|
+
const {fork} = topic.boundary;
|
|
557
696
|
const {serializedData} = gossipData;
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
const dataColumnSlot = dataColumnSidecar.signedBlockHeader.message.slot;
|
|
697
|
+
const dataColumnSidecar = sszDeserialize(topic, serializedData);
|
|
698
|
+
const dataColumnSlot = getDataColumnSidecarSlot(dataColumnSidecar);
|
|
561
699
|
const index = dataColumnSidecar.index;
|
|
562
|
-
|
|
563
|
-
if (config.getForkSeq(dataColumnSlot) < ForkSeq.fulu) {
|
|
564
|
-
throw new GossipActionError(GossipAction.REJECT, {code: "PRE_FULU_BLOCK"});
|
|
565
|
-
}
|
|
566
700
|
const delaySec = chain.clock.secFromSlot(dataColumnSlot, seenTimestampSec);
|
|
567
|
-
const blockInput = await validateBeaconDataColumn(
|
|
568
|
-
dataColumnSidecar,
|
|
569
|
-
serializedData,
|
|
570
|
-
topic.subnet,
|
|
571
|
-
peerIdStr,
|
|
572
|
-
seenTimestampSec
|
|
573
|
-
);
|
|
574
|
-
chain.serializedCache.set(dataColumnSidecar, serializedData);
|
|
575
|
-
const blockInputMeta = blockInput.getLogMeta();
|
|
576
|
-
const {receivedColumns} = blockInputMeta;
|
|
577
|
-
// it's not helpful to track every single column received
|
|
578
|
-
// instead of that, track 1st, 8th, 16th 32th, 64th, and 128th column
|
|
579
|
-
switch (receivedColumns) {
|
|
580
|
-
case 1:
|
|
581
|
-
case config.SAMPLES_PER_SLOT:
|
|
582
|
-
case 2 * config.SAMPLES_PER_SLOT:
|
|
583
|
-
case NUMBER_OF_COLUMNS / 4:
|
|
584
|
-
case NUMBER_OF_COLUMNS / 2:
|
|
585
|
-
case NUMBER_OF_COLUMNS:
|
|
586
|
-
metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
|
|
587
|
-
break;
|
|
588
|
-
}
|
|
589
701
|
|
|
590
|
-
if (
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
702
|
+
if (isForkPostGloas(fork)) {
|
|
703
|
+
if (!isGloasDataColumnSidecar(dataColumnSidecar)) {
|
|
704
|
+
throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
|
|
705
|
+
code: DataColumnSidecarErrorCode.INCORRECT_TYPE,
|
|
706
|
+
slot: dataColumnSlot,
|
|
707
|
+
columnIndex: index,
|
|
708
|
+
fork,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// After gloas, data columns are tracked in PayloadEnvelopeInput
|
|
713
|
+
const payloadInput = await validatePayloadDataColumn(
|
|
714
|
+
dataColumnSidecar,
|
|
715
|
+
topic.subnet,
|
|
716
|
+
peerIdStr,
|
|
717
|
+
seenTimestampSec
|
|
718
|
+
);
|
|
719
|
+
chain.serializedCache.set(dataColumnSidecar, serializedData);
|
|
720
|
+
|
|
721
|
+
const payloadInputMeta = payloadInput.getLogMeta();
|
|
722
|
+
const {receivedColumns} = payloadInputMeta;
|
|
723
|
+
// it's not helpful to track every single column received
|
|
724
|
+
// instead of that, track 1st, 8th, 16th 32th, 64th, and 128th column
|
|
725
|
+
switch (receivedColumns) {
|
|
726
|
+
case 1:
|
|
727
|
+
case config.SAMPLES_PER_SLOT:
|
|
728
|
+
case 2 * config.SAMPLES_PER_SLOT:
|
|
729
|
+
case NUMBER_OF_COLUMNS / 4:
|
|
730
|
+
case NUMBER_OF_COLUMNS / 2:
|
|
731
|
+
case NUMBER_OF_COLUMNS:
|
|
732
|
+
metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
|
|
733
|
+
break;
|
|
596
734
|
}
|
|
597
|
-
}
|
|
598
735
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
736
|
+
if (!payloadInput.hasComputedAllData()) {
|
|
737
|
+
// if we've received at least half of the columns, trigger reconstruction of the rest
|
|
738
|
+
if (receivedColumns >= NUMBER_OF_COLUMNS / 2) {
|
|
739
|
+
chain.columnReconstructionTracker.triggerColumnReconstruction(payloadInput);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
chain.logger.debug("Received gossip data column, payload envelope input not yet complete", {
|
|
743
|
+
dataColumnIndex: index,
|
|
744
|
+
...payloadInputMeta,
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
|
|
608
749
|
chain.logger.debug(
|
|
609
|
-
"
|
|
610
|
-
{
|
|
611
|
-
|
|
612
|
-
...blockInputMeta,
|
|
613
|
-
}
|
|
750
|
+
"Error processing execution payload from gossip data column",
|
|
751
|
+
{slot: dataColumnSlot, root: payloadInput.blockRootHex},
|
|
752
|
+
e as Error
|
|
614
753
|
);
|
|
615
|
-
chain.emitter.emit(ChainEvent.incompleteBlockInput, {
|
|
616
|
-
blockInput,
|
|
617
|
-
peer: peerIdStr,
|
|
618
|
-
source: BlockInputSource.gossip,
|
|
619
|
-
});
|
|
620
754
|
});
|
|
621
|
-
}
|
|
755
|
+
} else {
|
|
756
|
+
if (config.getForkSeq(dataColumnSlot) < ForkSeq.fulu) {
|
|
757
|
+
throw new GossipActionError(GossipAction.REJECT, {code: "PRE_FULU_BLOCK"});
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (isGloasDataColumnSidecar(dataColumnSidecar)) {
|
|
761
|
+
throw new DataColumnSidecarGossipError(GossipAction.REJECT, {
|
|
762
|
+
code: DataColumnSidecarErrorCode.INCORRECT_TYPE,
|
|
763
|
+
slot: dataColumnSlot,
|
|
764
|
+
columnIndex: index,
|
|
765
|
+
fork,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Before gloas, data columns are tracked in BlockInput
|
|
770
|
+
const blockInput = await validateBeaconDataColumn(
|
|
771
|
+
dataColumnSidecar,
|
|
772
|
+
serializedData,
|
|
773
|
+
topic.subnet,
|
|
774
|
+
peerIdStr,
|
|
775
|
+
seenTimestampSec
|
|
776
|
+
);
|
|
777
|
+
chain.serializedCache.set(dataColumnSidecar, serializedData);
|
|
778
|
+
const blockInputMeta = blockInput.getLogMeta();
|
|
779
|
+
const {receivedColumns} = blockInputMeta;
|
|
780
|
+
// it's not helpful to track every single column received
|
|
781
|
+
// instead of that, track 1st, 8th, 16th 32th, 64th, and 128th column
|
|
782
|
+
switch (receivedColumns) {
|
|
783
|
+
case 1:
|
|
784
|
+
case config.SAMPLES_PER_SLOT:
|
|
785
|
+
case 2 * config.SAMPLES_PER_SLOT:
|
|
786
|
+
case NUMBER_OF_COLUMNS / 4:
|
|
787
|
+
case NUMBER_OF_COLUMNS / 2:
|
|
788
|
+
case NUMBER_OF_COLUMNS:
|
|
789
|
+
metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
|
|
790
|
+
break;
|
|
791
|
+
}
|
|
622
792
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
793
|
+
if (!blockInput.hasComputedAllData()) {
|
|
794
|
+
// immediately attempt fetch of data columns from execution engine
|
|
795
|
+
chain.getBlobsTracker.triggerGetBlobs(blockInput);
|
|
796
|
+
// if we've received at least half of the columns, trigger reconstruction of the rest
|
|
797
|
+
if (blockInput.columnCount >= NUMBER_OF_COLUMNS / 2) {
|
|
798
|
+
chain.columnReconstructionTracker.triggerColumnReconstruction(blockInput);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (!blockInput.hasBlockAndAllData()) {
|
|
803
|
+
const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
|
|
804
|
+
chain.logger.debug("Received gossip data column, waiting for full data availability", {
|
|
805
|
+
msToWait: cutoffTimeMs,
|
|
806
|
+
dataColumnIndex: index,
|
|
807
|
+
...blockInputMeta,
|
|
808
|
+
});
|
|
809
|
+
// do not await here to not delay gossip validation
|
|
810
|
+
blockInput.waitForBlockAndAllData(cutoffTimeMs).catch((_e) => {
|
|
811
|
+
chain.logger.debug(
|
|
812
|
+
"Waited for data after receiving gossip column. Cut-off reached so attempting to fetch remainder of BlockInput",
|
|
813
|
+
{
|
|
814
|
+
dataColumnIndex: index,
|
|
815
|
+
...blockInputMeta,
|
|
816
|
+
}
|
|
817
|
+
);
|
|
818
|
+
chain.emitter.emit(ChainEvent.incompleteBlockInput, {
|
|
819
|
+
blockInput,
|
|
820
|
+
peer: peerIdStr,
|
|
821
|
+
source: BlockInputSource.gossip,
|
|
822
|
+
});
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
}
|
|
629
826
|
},
|
|
630
827
|
|
|
631
828
|
[GossipType.beacon_aggregate_and_proof]: async ({
|
|
@@ -843,8 +1040,37 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
843
1040
|
const {serializedData} = gossipData;
|
|
844
1041
|
const signedEnvelope = sszDeserialize(topic, serializedData);
|
|
845
1042
|
const envelope = signedEnvelope.message;
|
|
846
|
-
|
|
847
|
-
|
|
1043
|
+
|
|
1044
|
+
// TODO GLOAS: consider optimistically create PayloadEnvelopeInput here similar to how we do that for beacon_block
|
|
1045
|
+
// so that UnknownBlockSync can handle backward sync
|
|
1046
|
+
// the problem now is we cannot create a PayloadEnvelopeInput without the beacon block being known, we need at least the proposer index
|
|
1047
|
+
// we can achieve that by looking into the EpochCache
|
|
1048
|
+
try {
|
|
1049
|
+
await validateGossipExecutionPayloadEnvelope(chain, signedEnvelope);
|
|
1050
|
+
} catch (e) {
|
|
1051
|
+
if (e instanceof ExecutionPayloadEnvelopeError) {
|
|
1052
|
+
const {slot, beaconBlockRoot} = signedEnvelope.message;
|
|
1053
|
+
logger.debug("Gossip envelope has error", {slot, root: toRootHex(beaconBlockRoot), code: e.type.code});
|
|
1054
|
+
if (e.type.code === ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN) {
|
|
1055
|
+
// TODO GLOAS: UnknownBlockSync to handle this
|
|
1056
|
+
chain.emitter.emit(ChainEvent.envelopeUnknownBlock, {
|
|
1057
|
+
envelope: signedEnvelope,
|
|
1058
|
+
peer: peerIdStr,
|
|
1059
|
+
source: BlockInputSource.gossip,
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
if (e.action === GossipAction.REJECT) {
|
|
1064
|
+
chain.persistInvalidSszValue(
|
|
1065
|
+
ssz.gloas.SignedExecutionPayloadEnvelope,
|
|
1066
|
+
signedEnvelope,
|
|
1067
|
+
`gossip_reject_slot_${slot}`
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
throw e;
|
|
1073
|
+
}
|
|
848
1074
|
|
|
849
1075
|
const slot = envelope.slot;
|
|
850
1076
|
const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);
|