@lodestar/beacon-node 1.47.0-dev.bd761ec9ea → 1.47.0-dev.beabd3a65c

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.
Files changed (45) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +2 -1
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
  5. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  6. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
  7. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  8. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
  9. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  10. package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
  11. package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
  12. package/lib/chain/chain.d.ts.map +1 -1
  13. package/lib/chain/chain.js +6 -1
  14. package/lib/chain/chain.js.map +1 -1
  15. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
  16. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  17. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
  18. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  19. package/lib/metrics/metrics/lodestar.d.ts +7 -2
  20. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  21. package/lib/metrics/metrics/lodestar.js +7 -1
  22. package/lib/metrics/metrics/lodestar.js.map +1 -1
  23. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  24. package/lib/network/processor/gossipHandlers.js +5 -1
  25. package/lib/network/processor/gossipHandlers.js.map +1 -1
  26. package/lib/sync/unknownBlock.d.ts.map +1 -1
  27. package/lib/sync/unknownBlock.js +7 -13
  28. package/lib/sync/unknownBlock.js.map +1 -1
  29. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  30. package/lib/sync/utils/downloadByRange.js +16 -11
  31. package/lib/sync/utils/downloadByRange.js.map +1 -1
  32. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  33. package/lib/sync/utils/downloadByRoot.js +3 -1
  34. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  35. package/package.json +31 -31
  36. package/src/api/impl/beacon/blocks/index.ts +2 -1
  37. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
  38. package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
  39. package/src/chain/chain.ts +6 -1
  40. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
  41. package/src/metrics/metrics/lodestar.ts +12 -3
  42. package/src/network/processor/gossipHandlers.ts +5 -1
  43. package/src/sync/unknownBlock.ts +8 -15
  44. package/src/sync/utils/downloadByRange.ts +21 -12
  45. package/src/sync/utils/downloadByRoot.ts +3 -1
@@ -544,15 +544,7 @@ export class BlockInputSync {
544
544
  return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
545
545
  }
546
546
 
547
- const parentPayloadInput = this.chain.seenPayloadEnvelopeInputCache.get(parentRootHex);
548
- if (parentPayloadInput) {
549
- if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
550
- return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
551
- }
552
-
553
- return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
554
- }
555
-
547
+ // Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
556
548
  return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
557
549
  }
558
550
 
@@ -894,9 +886,9 @@ export class BlockInputSync {
894
886
  this.pendingBlocks.delete(blockRootHex);
895
887
 
896
888
  if (isForkPostGloas(fork)) {
897
- const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
889
+ const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
898
890
  if (!payloadInput) {
899
- this.logger.warn("PayloadEnvelopeInput not seeded during unknown sync processReadyBlock()", logCtx);
891
+ this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
900
892
  } else {
901
893
  // Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
902
894
  // The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
@@ -969,12 +961,12 @@ export class BlockInputSync {
969
961
  */
970
962
  private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
971
963
  const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
964
+
972
965
  if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
973
966
  this.pendingPayloads.delete(rootHex);
974
967
  return;
975
968
  }
976
969
 
977
- const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
978
970
  if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
979
971
  // Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
980
972
  // non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
@@ -990,8 +982,9 @@ export class BlockInputSync {
990
982
  return;
991
983
  }
992
984
 
985
+ const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
993
986
  if (!payloadInput) {
994
- this.logger.debug("Missing PayloadEnvelopeInput for known block while reconciling payload envelope", {
987
+ this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
995
988
  root: rootHex,
996
989
  });
997
990
  return;
@@ -1187,7 +1180,7 @@ export class BlockInputSync {
1187
1180
  let slot = getPayloadSyncCacheItemSlot(cacheItem);
1188
1181
  let payloadInput = isPendingPayloadInput(cacheItem)
1189
1182
  ? cacheItem.payloadInput
1190
- : this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
1183
+ : await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1191
1184
  let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
1192
1185
 
1193
1186
  let i = 0;
@@ -1220,7 +1213,7 @@ export class BlockInputSync {
1220
1213
  slot = envelope.message.payload.slotNumber;
1221
1214
  }
1222
1215
 
1223
- payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
1216
+ payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
1224
1217
  if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
1225
1218
  // Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
1226
1219
  // envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
@@ -136,6 +136,8 @@ export function cacheByRangeResponses({
136
136
  const source = BlockInputSource.byRange;
137
137
  const updatedBatchBlocks = new Map<Slot, IBlockInput>(batchBlocks.map((block) => [block.slot, block]));
138
138
 
139
+ const payloadEnvelopes = new Map<Slot, PayloadEnvelopeInput>(existingPayloadEnvelopes);
140
+
139
141
  const blocks = responses.validatedBlocks ?? [];
140
142
  for (let i = 0; i < blocks.length; i++) {
141
143
  const {block, blockRoot} = blocks[i];
@@ -172,15 +174,17 @@ export function cacheByRangeResponses({
172
174
  // later step) can throw and abort the batch — otherwise a gloas block would sit in
173
175
  // seenBlockInputCache but unseeded here, and payload-by-root sync would later throw "Missing
174
176
  // PayloadEnvelopeInput for known block" (see issue #9306). add() is idempotent.
175
- if (isForkPostGloas(blockInput.forkName)) {
176
- seenPayloadEnvelopeInputCache.add({
177
+ if (isForkPostGloas(blockInput.forkName) && !payloadEnvelopes.has(blockInput.slot)) {
178
+ const payloadInput = seenPayloadEnvelopeInputCache.add({
177
179
  blockRootHex: blockInput.blockRootHex,
178
180
  block: blockInput.getBlock() as SignedBeaconBlock<ForkPostGloas>,
179
181
  forkName: blockInput.forkName,
180
182
  sampledColumns: custodyConfig.sampledColumns,
181
183
  custodyColumns: custodyConfig.custodyColumns,
182
- timeCreatedSec: seenTimestampSec,
184
+ seenTimestampSec,
185
+ source: PayloadEnvelopeInputSource.byRange,
183
186
  });
187
+ payloadEnvelopes.set(blockInput.slot, payloadInput);
184
188
  }
185
189
  }
186
190
 
@@ -222,17 +226,22 @@ export function cacheByRangeResponses({
222
226
  }
223
227
  }
224
228
 
225
- let payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null =
226
- existingPayloadEnvelopes !== null ? new Map(existingPayloadEnvelopes) : null;
227
229
  if (downloadedPayloadEnvelopes !== null) {
228
- payloadEnvelopes ??= new Map();
229
230
  for (const [slot, envelope] of downloadedPayloadEnvelopes) {
230
- const envelopeBlockRootHex = toRootHex(envelope.message.beaconBlockRoot);
231
- const payloadInput = seenPayloadEnvelopeInputCache.get(envelopeBlockRootHex);
231
+ // the only PayloadEnvelopeInput miss is the dangling parent, which we can get from the seen cache
232
+ let payloadInput = payloadEnvelopes.get(slot);
232
233
  if (payloadInput === undefined) {
233
- // Unreachable given the validatedBlocks loop above seeded an entry for every gloas block in
234
- // the batch. for the parent block, it's populated at BeaconChain init
235
- throw new Error(`Missing PayloadEnvelopeInput for block ${envelopeBlockRootHex}`);
234
+ if (updatedBatchBlocks.has(slot)) {
235
+ throw new Error(
236
+ `Missing PayloadEnvelopeInput for in-batch slot ${slot} root ${toRootHex(envelope.message.beaconBlockRoot)}`
237
+ );
238
+ }
239
+ payloadInput = seenPayloadEnvelopeInputCache.get(toRootHex(envelope.message.beaconBlockRoot));
240
+ }
241
+ if (payloadInput === undefined) {
242
+ throw new Error(
243
+ `Missing PayloadEnvelopeInput for slot ${slot} root ${toRootHex(envelope.message.beaconBlockRoot)}`
244
+ );
236
245
  }
237
246
 
238
247
  if (!payloadInput.hasPayloadEnvelope()) {
@@ -262,7 +271,7 @@ export function cacheByRangeResponses({
262
271
  // Gloas columns are attached to the matching PayloadEnvelopeInput, NOT to IBlockInput.
263
272
  // Gloas DataColumnSidecar has `slot` directly (no signedBlockHeader).
264
273
  const dataSlot = firstColumn.slot;
265
- const payloadInput = payloadEnvelopes?.get(dataSlot);
274
+ const payloadInput = payloadEnvelopes.get(dataSlot);
266
275
  if (!payloadInput) {
267
276
  // Should not happen: we built payloadInputs for all gloas blocks above
268
277
  continue;
@@ -13,6 +13,7 @@ import {BlobIndex, ColumnIndex, SignedBeaconBlock, Slot, deneb, fulu} from "@lod
13
13
  import {LodestarError, byteArrayEquals, fromHex, prettyPrintIndices, toHex, toRootHex} from "@lodestar/utils";
14
14
  import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
15
15
  import {BlockInputSource, IBlockInput} from "../../chain/blocks/blockInput/types.js";
16
+ import {PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/index.js";
16
17
  import {ChainEventEmitter} from "../../chain/emitter.js";
17
18
  import {IBeaconChain} from "../../chain/interface.js";
18
19
  import {validateBlockBlobSidecars} from "../../chain/validation/blobSidecar.js";
@@ -122,7 +123,8 @@ export async function downloadByRoot({
122
123
  forkName: blockInput.forkName,
123
124
  sampledColumns: chain.custodyConfig.sampledColumns,
124
125
  custodyColumns: chain.custodyConfig.custodyColumns,
125
- timeCreatedSec: Date.now() / 1000,
126
+ seenTimestampSec: Date.now() / 1000,
127
+ source: PayloadEnvelopeInputSource.byRoot,
126
128
  });
127
129
  }
128
130