@lodestar/beacon-node 1.43.0-dev.ff409c46ce → 1.43.0-rc.2
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/chain/blocks/importExecutionPayload.d.ts +4 -0
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +12 -1
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +6 -7
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +8 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +19 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +16 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +5 -4
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +15 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +7 -6
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +21 -5
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/sync/range/batch.d.ts +7 -3
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +96 -17
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +7 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +11 -2
- 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 +14 -3
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +13 -0
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +29 -8
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +85 -38
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/package.json +16 -16
- package/src/chain/blocks/importExecutionPayload.ts +18 -6
- package/src/chain/blocks/index.ts +5 -7
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -1
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -0
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +7 -4
- package/src/chain/chain.ts +17 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +23 -6
- package/src/sync/range/batch.ts +118 -20
- package/src/sync/range/chain.ts +20 -3
- package/src/sync/range/range.ts +16 -3
- package/src/sync/sync.ts +13 -1
- package/src/sync/utils/downloadByRange.ts +145 -39
|
@@ -7,7 +7,7 @@ import {Metrics} from "../../metrics/metrics.js";
|
|
|
7
7
|
import {IClock} from "../../util/clock.js";
|
|
8
8
|
import {SerializedCache} from "../../util/serializedCache.js";
|
|
9
9
|
import {isDaOutOfRange} from "../blocks/blockInput/index.js";
|
|
10
|
-
import {CreateFromBlockProps, PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
|
|
10
|
+
import {CreateFromBidProps, CreateFromBlockProps, PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
|
|
11
11
|
import {ChainEvent, ChainEventEmitter} from "../emitter.js";
|
|
12
12
|
|
|
13
13
|
export type {PayloadEnvelopeInputState} from "../blocks/payloadEnvelopeInput/index.js";
|
|
@@ -27,11 +27,7 @@ export type SeenPayloadEnvelopeInputModules = {
|
|
|
27
27
|
/**
|
|
28
28
|
* Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
|
|
29
29
|
*
|
|
30
|
-
* Created
|
|
31
|
-
* - `prepareNextSlot` calls `pruneBelow(headParentSlot)` every slot once the head we'll build
|
|
32
|
-
* on is known.
|
|
33
|
-
* - `onFinalized` calls `pruneBelow(finalizedSlot)` on every finalization for bulk cleanup.
|
|
34
|
-
*
|
|
30
|
+
* Created whenever we have a block because it needs block bid.
|
|
35
31
|
* Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head
|
|
36
32
|
* (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can
|
|
37
33
|
* transiently hold more during forks, range-sync bursts, or when `prepareNextSlot` skips
|
|
@@ -123,6 +119,27 @@ export class SeenPayloadEnvelopeInput {
|
|
|
123
119
|
return input;
|
|
124
120
|
}
|
|
125
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Used at chain initialization to seed the anchor block's PayloadEnvelopeInput from
|
|
124
|
+
* `state.latestExecutionPayloadBid`.
|
|
125
|
+
*/
|
|
126
|
+
addFromBid(props: Omit<CreateFromBidProps, "daOutOfRange">): PayloadEnvelopeInput {
|
|
127
|
+
const existing = this.payloadInputs.get(props.blockRootHex);
|
|
128
|
+
if (existing !== undefined) {
|
|
129
|
+
return existing;
|
|
130
|
+
}
|
|
131
|
+
const daOutOfRange = isDaOutOfRange(this.config, props.forkName, props.slot, this.clock.currentEpoch);
|
|
132
|
+
const input = PayloadEnvelopeInput.createFromBid({...props, daOutOfRange});
|
|
133
|
+
this.payloadInputs.set(props.blockRootHex, input);
|
|
134
|
+
this.metrics?.seenCache.payloadEnvelopeInput.created.inc();
|
|
135
|
+
this.logger?.verbose("SeenPayloadEnvelopeInput.addFromBid created new entry", {
|
|
136
|
+
slot: input.slot,
|
|
137
|
+
root: props.blockRootHex,
|
|
138
|
+
daOutOfRange,
|
|
139
|
+
});
|
|
140
|
+
return input;
|
|
141
|
+
}
|
|
142
|
+
|
|
126
143
|
get(blockRootHex: RootHex): PayloadEnvelopeInput | undefined {
|
|
127
144
|
return this.payloadInputs.get(blockRootHex);
|
|
128
145
|
}
|
package/src/sync/range/batch.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {ForkName, isForkPostDeneb, isForkPostFulu, isForkPostGloas} from "@lodestar/params";
|
|
3
|
-
import {Epoch, RootHex, Slot, phase0} from "@lodestar/types";
|
|
4
|
-
import {LodestarError, prettyPrintIndices} from "@lodestar/utils";
|
|
3
|
+
import {Epoch, RootHex, SignedBeaconBlock, Slot, gloas, phase0} from "@lodestar/types";
|
|
4
|
+
import {LodestarError, byteArrayEquals, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
5
5
|
import {isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
|
|
6
6
|
import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
7
7
|
import {isDaOutOfRange} from "../../chain/blocks/blockInput/utils.js";
|
|
8
8
|
import {PayloadEnvelopeInput} from "../../chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
9
9
|
import {BlockError, BlockErrorCode} from "../../chain/errors/index.js";
|
|
10
|
+
import {ZERO_HASH} from "../../constants/constants.js";
|
|
10
11
|
import {PeerSyncMeta} from "../../network/peers/peersData.js";
|
|
11
12
|
import {IClock} from "../../util/clock.js";
|
|
12
13
|
import {CustodyConfig} from "../../util/dataColumns.js";
|
|
13
14
|
import {PeerIdStr} from "../../util/peerId.js";
|
|
14
15
|
import {MAX_BATCH_DOWNLOAD_ATTEMPTS, MAX_BATCH_PROCESSING_ATTEMPTS} from "../constants.js";
|
|
15
|
-
import {DownloadByRangeRequests} from "../utils/downloadByRange.js";
|
|
16
|
+
import {DownloadByRangeRequests, ParentPayloadCommitments} from "../utils/downloadByRange.js";
|
|
16
17
|
import {getBatchSlotRange, hashBlocks} from "./utils/index.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -141,8 +142,18 @@ export class Batch {
|
|
|
141
142
|
private readonly config: ChainForkConfig;
|
|
142
143
|
private readonly clock: IClock;
|
|
143
144
|
private readonly custodyConfig: CustodyConfig;
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
private readonly isFirstBatchInChain: boolean;
|
|
146
|
+
private readonly latestBid: gloas.ExecutionPayloadBid | undefined;
|
|
147
|
+
|
|
148
|
+
constructor(
|
|
149
|
+
startEpoch: Epoch,
|
|
150
|
+
config: ChainForkConfig,
|
|
151
|
+
clock: IClock,
|
|
152
|
+
custodyConfig: CustodyConfig,
|
|
153
|
+
isFirstBatchInChain: boolean,
|
|
154
|
+
latestBid: gloas.ExecutionPayloadBid | undefined,
|
|
155
|
+
targetSlot: Slot
|
|
156
|
+
) {
|
|
146
157
|
this.config = config;
|
|
147
158
|
this.clock = clock;
|
|
148
159
|
this.custodyConfig = custodyConfig;
|
|
@@ -151,10 +162,40 @@ export class Batch {
|
|
|
151
162
|
this.forkName = this.config.getForkName(startSlot);
|
|
152
163
|
this.startEpoch = startEpoch;
|
|
153
164
|
this.startSlot = startSlot;
|
|
154
|
-
this.count = count;
|
|
165
|
+
this.count = Math.min(count, targetSlot - startSlot + 1);
|
|
166
|
+
this.isFirstBatchInChain = isFirstBatchInChain;
|
|
167
|
+
this.latestBid = latestBid;
|
|
155
168
|
this.requests = this.getRequests([]);
|
|
156
169
|
}
|
|
157
170
|
|
|
171
|
+
private shouldDownloadParentEnvelope(firstBlock?: SignedBeaconBlock): boolean {
|
|
172
|
+
if (!this.isFirstBatchInChain) return false;
|
|
173
|
+
|
|
174
|
+
if (this.startSlot === 0 || !isForkPostGloas(this.config.getForkName(this.startSlot - 1))) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// we only know if we should download parent envelope if firstBlock is downloaded
|
|
179
|
+
if (firstBlock === undefined) return false;
|
|
180
|
+
if (this.latestBid === undefined) return false;
|
|
181
|
+
const firstBlockBidParentHash = (firstBlock.message.body as gloas.BeaconBlockBody).signedExecutionPayloadBid.message
|
|
182
|
+
.parentBlockHash;
|
|
183
|
+
return byteArrayEquals(firstBlockBidParentHash, this.latestBid.blockHash);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
getParentPayloadCommitments(parentBlockRoot: Uint8Array): ParentPayloadCommitments {
|
|
187
|
+
if (this.latestBid === undefined) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Coding error: getParentPayloadCommitments called without latestBid for parentBlockRoot=${toRootHex(parentBlockRoot)}`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
blockRoot: parentBlockRoot,
|
|
194
|
+
blockRootHex: toRootHex(parentBlockRoot),
|
|
195
|
+
kzgCommitments: this.latestBid.blobKzgCommitments,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
158
199
|
/**
|
|
159
200
|
* Builds ByRange requests for block, blobs and columns
|
|
160
201
|
*/
|
|
@@ -292,6 +333,36 @@ export class Batch {
|
|
|
292
333
|
};
|
|
293
334
|
}
|
|
294
335
|
|
|
336
|
+
// Only the first batch of a SyncChain may need the dangling-parent payload by-root.
|
|
337
|
+
if (blocks.length > 0 && this.shouldDownloadParentEnvelope(blocks[0].getBlock())) {
|
|
338
|
+
// shouldDownloadParentEnvelope() = true means there are at least 1 block
|
|
339
|
+
const parentRoot = blocks[0].getBlock().message.parentRoot;
|
|
340
|
+
if (!byteArrayEquals(parentRoot, ZERO_HASH)) {
|
|
341
|
+
const parentRootHex = toRootHex(parentRoot);
|
|
342
|
+
let parentPayloadInput: PayloadEnvelopeInput | undefined;
|
|
343
|
+
if (this.state.payloadEnvelopes) {
|
|
344
|
+
for (const pi of this.state.payloadEnvelopes.values()) {
|
|
345
|
+
if (pi.blockRootHex === parentRootHex) {
|
|
346
|
+
parentPayloadInput = pi;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const needsEnvelope = !parentPayloadInput?.hasPayloadEnvelope();
|
|
353
|
+
const missingColumns = parentPayloadInput
|
|
354
|
+
? parentPayloadInput.getMissingSampledColumnMeta().missing
|
|
355
|
+
: this.custodyConfig.sampledColumns;
|
|
356
|
+
|
|
357
|
+
if (needsEnvelope || missingColumns.length > 0) {
|
|
358
|
+
requests.parentPayloadRequest = {
|
|
359
|
+
...(needsEnvelope ? {envelopeBlockRoot: parentRoot} : {}),
|
|
360
|
+
...(missingColumns.length > 0 ? {blockRoot: parentRoot, columns: missingColumns} : {}),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
295
366
|
return requests;
|
|
296
367
|
}
|
|
297
368
|
|
|
@@ -303,24 +374,28 @@ export class Batch {
|
|
|
303
374
|
return this.requests;
|
|
304
375
|
}
|
|
305
376
|
|
|
306
|
-
// post-fulu we need to ensure that we only request columns that the peer has advertised
|
|
307
|
-
const {columnsRequest} = this.requests;
|
|
308
|
-
if (columnsRequest == null) {
|
|
309
|
-
return this.requests;
|
|
310
|
-
}
|
|
377
|
+
// post-fulu we need to ensure that we only request columns that the peer has advertised.
|
|
378
|
+
const {columnsRequest, parentPayloadRequest} = this.requests;
|
|
311
379
|
|
|
312
380
|
const peerColumns = new Set(peer.custodyColumns ?? []);
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
381
|
+
const filteredColumnsRequest =
|
|
382
|
+
columnsRequest != null ? columnsRequest.columns.filter((c) => peerColumns.has(c)) : null;
|
|
383
|
+
const parentColumns = parentPayloadRequest?.columns;
|
|
384
|
+
const filteredParentColumns = parentColumns != null ? parentColumns.filter((c) => peerColumns.has(c)) : null;
|
|
385
|
+
|
|
386
|
+
const updatedColumnRequest =
|
|
387
|
+
columnsRequest != null && filteredColumnsRequest != null
|
|
388
|
+
? {columnsRequest: {...columnsRequest, columns: filteredColumnsRequest}}
|
|
389
|
+
: {};
|
|
390
|
+
const updatedParentPayloadRequest =
|
|
391
|
+
parentPayloadRequest != null && filteredParentColumns != null
|
|
392
|
+
? {parentPayloadRequest: {...parentPayloadRequest, columns: filteredParentColumns}}
|
|
393
|
+
: {};
|
|
317
394
|
|
|
318
395
|
return {
|
|
319
396
|
...this.requests,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
columns: requestedColumns,
|
|
323
|
-
},
|
|
397
|
+
...updatedColumnRequest,
|
|
398
|
+
...updatedParentPayloadRequest,
|
|
324
399
|
};
|
|
325
400
|
}
|
|
326
401
|
|
|
@@ -422,7 +497,7 @@ export class Batch {
|
|
|
422
497
|
if (allComplete && isForkPostGloas(this.forkName)) {
|
|
423
498
|
for (const block of blocks) {
|
|
424
499
|
const payloadInput = newPayloadEnvelopes?.get(block.slot);
|
|
425
|
-
// by_range needs
|
|
500
|
+
// by_range needs every block's envelope and all sampled columns.
|
|
426
501
|
if (!payloadInput?.hasPayloadEnvelope() || !payloadInput.hasComputedAllData()) {
|
|
427
502
|
allComplete = false;
|
|
428
503
|
break;
|
|
@@ -430,6 +505,29 @@ export class Batch {
|
|
|
430
505
|
}
|
|
431
506
|
}
|
|
432
507
|
|
|
508
|
+
// First batch of a sync chain must additionally have the dangling-parent payload fully
|
|
509
|
+
// present, otherwise `processBlocks` will throw PARENT_PAYLOAD_UNKNOWN. The parent's
|
|
510
|
+
// `PayloadEnvelopeInput` is identified by `blockRootHex` matching `blocks[0].parentRoot`.
|
|
511
|
+
if (allComplete && blocks.length > 0 && this.shouldDownloadParentEnvelope(blocks[0].getBlock())) {
|
|
512
|
+
const parentRoot = blocks[0].getBlock().message.parentRoot;
|
|
513
|
+
// Genesis has no parent payload — nothing to wait for.
|
|
514
|
+
if (!byteArrayEquals(parentRoot, ZERO_HASH)) {
|
|
515
|
+
const parentRootHex = toRootHex(parentRoot);
|
|
516
|
+
let parentPayloadComplete = false;
|
|
517
|
+
if (newPayloadEnvelopes) {
|
|
518
|
+
for (const payloadInput of newPayloadEnvelopes.values()) {
|
|
519
|
+
if (payloadInput.blockRootHex === parentRootHex) {
|
|
520
|
+
parentPayloadComplete = payloadInput.hasPayloadEnvelope() && payloadInput.hasComputedAllData();
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (!parentPayloadComplete) {
|
|
526
|
+
allComplete = false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
433
531
|
if (allComplete) {
|
|
434
532
|
this.state = {status: BatchStatus.AwaitingProcessing, blocks, payloadEnvelopes: newPayloadEnvelopes};
|
|
435
533
|
} else {
|
package/src/sync/range/chain.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {RequestErrorCode} from "@lodestar/reqresp";
|
|
3
|
-
import {Epoch, Root, Slot} from "@lodestar/types";
|
|
3
|
+
import {Epoch, Root, Slot, gloas} from "@lodestar/types";
|
|
4
4
|
import {ErrorAborted, LodestarError, Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
5
5
|
import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
|
|
6
6
|
import {BlockInputErrorCode} from "../../chain/blocks/blockInput/errors.js";
|
|
@@ -145,6 +145,10 @@ export class SyncChain {
|
|
|
145
145
|
private readonly batchProcessor = new ItTrigger();
|
|
146
146
|
/** Sorted map of batches undergoing some kind of processing. */
|
|
147
147
|
private readonly batches = new Map<Epoch, Batch>();
|
|
148
|
+
/**
|
|
149
|
+
* `true` until the first `Batch` is constructed via `includeNextBatch`
|
|
150
|
+
*/
|
|
151
|
+
private isFirstBatch = true;
|
|
148
152
|
private readonly peerset = new Map<PeerIdStr, ChainTarget>();
|
|
149
153
|
/**
|
|
150
154
|
* Tracks peers that have rate-limited us, mapped to the timestamp (ms) until which we should avoid them.
|
|
@@ -158,13 +162,15 @@ export class SyncChain {
|
|
|
158
162
|
private readonly clock: IClock;
|
|
159
163
|
private readonly metrics: Metrics | null;
|
|
160
164
|
private readonly custodyConfig: CustodyConfig;
|
|
165
|
+
private readonly latestBid: gloas.ExecutionPayloadBid | undefined;
|
|
161
166
|
|
|
162
167
|
constructor(
|
|
163
168
|
initialBatchEpoch: Epoch,
|
|
164
169
|
initialTarget: ChainTarget,
|
|
165
170
|
syncType: RangeSyncType,
|
|
166
171
|
fns: SyncChainFns,
|
|
167
|
-
modules: SyncChainModules
|
|
172
|
+
modules: SyncChainModules,
|
|
173
|
+
latestBid: gloas.ExecutionPayloadBid | undefined
|
|
168
174
|
) {
|
|
169
175
|
const {config, clock, custodyConfig, logger, metrics} = modules;
|
|
170
176
|
this.firstBatchEpoch = initialBatchEpoch;
|
|
@@ -180,6 +186,7 @@ export class SyncChain {
|
|
|
180
186
|
this.clock = clock;
|
|
181
187
|
this.metrics = metrics;
|
|
182
188
|
this.custodyConfig = custodyConfig;
|
|
189
|
+
this.latestBid = latestBid;
|
|
183
190
|
this.logger = logger;
|
|
184
191
|
this.logId = `${syncType}-${nextChainId++}`;
|
|
185
192
|
|
|
@@ -481,7 +488,17 @@ export class SyncChain {
|
|
|
481
488
|
return null;
|
|
482
489
|
}
|
|
483
490
|
|
|
484
|
-
const batch = new Batch(
|
|
491
|
+
const batch = new Batch(
|
|
492
|
+
startEpoch,
|
|
493
|
+
this.config,
|
|
494
|
+
this.clock,
|
|
495
|
+
this.custodyConfig,
|
|
496
|
+
this.isFirstBatch,
|
|
497
|
+
// `latestBid` is only meaningful for the first batch's parent-payload check
|
|
498
|
+
this.isFirstBatch ? this.latestBid : undefined,
|
|
499
|
+
this.target.slot
|
|
500
|
+
);
|
|
501
|
+
this.isFirstBatch = false;
|
|
485
502
|
this.batches.set(startEpoch, batch);
|
|
486
503
|
return batch;
|
|
487
504
|
}
|
package/src/sync/range/range.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {EventEmitter} from "node:events";
|
|
2
2
|
import {StrictEventEmitter} from "strict-event-emitter-types";
|
|
3
3
|
import {BeaconConfig} from "@lodestar/config";
|
|
4
|
-
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
|
+
import {IBeaconStateViewGloas, computeStartSlotAtEpoch, isStatePostGloas} from "@lodestar/state-transition";
|
|
5
5
|
import {Epoch, Status, fulu} from "@lodestar/types";
|
|
6
6
|
import {Logger, toRootHex} from "@lodestar/utils";
|
|
7
7
|
import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
@@ -206,14 +206,18 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
206
206
|
|
|
207
207
|
private downloadByRange: SyncChainFns["downloadByRange"] = async (peer, batch) => {
|
|
208
208
|
const batchBlocks = batch.getBlocks();
|
|
209
|
+
const requests = batch.getRequestsForPeer(peer);
|
|
210
|
+
const parentRoot = requests.parentPayloadRequest?.envelopeBlockRoot ?? requests.parentPayloadRequest?.blockRoot;
|
|
211
|
+
const parentPayloadCommitments = parentRoot ? batch.getParentPayloadCommitments(parentRoot) : undefined;
|
|
209
212
|
const {result, warnings} = await downloadByRange({
|
|
210
213
|
config: this.config,
|
|
211
214
|
network: this.network,
|
|
212
215
|
logger: this.logger,
|
|
213
216
|
peerIdStr: peer.peerId,
|
|
214
217
|
batchBlocks,
|
|
218
|
+
parentPayloadCommitments,
|
|
215
219
|
peerDasMetrics: this.chain.metrics?.peerDas,
|
|
216
|
-
...
|
|
220
|
+
...requests,
|
|
217
221
|
});
|
|
218
222
|
const {responses, payloadEnvelopes: downloadedPayloadEnvelopes} = result;
|
|
219
223
|
const {blocks, payloadEnvelopes} = cacheByRangeResponses({
|
|
@@ -258,6 +262,14 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
258
262
|
private addPeerOrCreateChain(startEpoch: Epoch, target: ChainTarget, peer: PeerIdStr, syncType: RangeSyncType): void {
|
|
259
263
|
let syncChain = this.chains.get(syncType);
|
|
260
264
|
if (!syncChain) {
|
|
265
|
+
// The first batch of a new sync chain may need to detect whether the parent block was an
|
|
266
|
+
// gloas "empty" block (no envelope produced). It does so by comparing the first
|
|
267
|
+
// downloaded block's `bid.parentBlockHash` against the head state's `latestExecutionPayloadBid.blockHash`.
|
|
268
|
+
const headState = this.chain.getHeadState();
|
|
269
|
+
const latestBid = isStatePostGloas(headState)
|
|
270
|
+
? (headState as IBeaconStateViewGloas).latestExecutionPayloadBid
|
|
271
|
+
: undefined;
|
|
272
|
+
|
|
261
273
|
syncChain = new SyncChain(
|
|
262
274
|
startEpoch,
|
|
263
275
|
target,
|
|
@@ -276,7 +288,8 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
276
288
|
logger: this.logger,
|
|
277
289
|
custodyConfig: this.chain.custodyConfig,
|
|
278
290
|
metrics: this.metrics,
|
|
279
|
-
}
|
|
291
|
+
},
|
|
292
|
+
latestBid
|
|
280
293
|
);
|
|
281
294
|
this.chains.set(syncType, syncChain);
|
|
282
295
|
|
package/src/sync/sync.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
2
|
import {Slot} from "@lodestar/types";
|
|
3
|
-
import {Logger} from "@lodestar/utils";
|
|
3
|
+
import {Logger, toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {IBeaconChain} from "../chain/index.js";
|
|
5
5
|
import {GENESIS_SLOT} from "../constants/constants.js";
|
|
6
6
|
import {ExecutionEngineState} from "../execution/index.js";
|
|
@@ -188,6 +188,18 @@ export class BeaconSync implements IBeaconSync {
|
|
|
188
188
|
private addPeer = (data: NetworkEventData[NetworkEvent.peerConnected]): void => {
|
|
189
189
|
const localStatus = this.chain.getStatus();
|
|
190
190
|
const syncType = getPeerSyncType(localStatus, data.status, this.chain.forkChoice, this.slotImportTolerance);
|
|
191
|
+
this.logger.verbose("Peer sync type classified", {
|
|
192
|
+
peer: data.peer,
|
|
193
|
+
syncType,
|
|
194
|
+
localFinalizedEpoch: localStatus.finalizedEpoch,
|
|
195
|
+
localFinalizedRoot: toRootHex(localStatus.finalizedRoot),
|
|
196
|
+
localHeadSlot: localStatus.headSlot,
|
|
197
|
+
localHeadRoot: toRootHex(localStatus.headRoot),
|
|
198
|
+
remoteFinalizedEpoch: data.status.finalizedEpoch,
|
|
199
|
+
remoteFinalizedRoot: toRootHex(data.status.finalizedRoot),
|
|
200
|
+
remoteHeadSlot: data.status.headSlot,
|
|
201
|
+
remoteHeadRoot: toRootHex(data.status.headRoot),
|
|
202
|
+
});
|
|
191
203
|
|
|
192
204
|
// For metrics only
|
|
193
205
|
this.peerSyncType.set(data.peer, syncType);
|