@lodestar/beacon-node 1.48.0-dev.3211c1bdd4 → 1.48.0-dev.386c085495

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 (87) hide show
  1. package/lib/api/impl/validator/index.d.ts.map +1 -1
  2. package/lib/api/impl/validator/index.js +39 -39
  3. package/lib/api/impl/validator/index.js.map +1 -1
  4. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  5. package/lib/chain/blocks/importBlock.js +0 -13
  6. package/lib/chain/blocks/importBlock.js.map +1 -1
  7. package/lib/chain/blocks/index.d.ts +8 -4
  8. package/lib/chain/blocks/index.d.ts.map +1 -1
  9. package/lib/chain/blocks/index.js +16 -18
  10. package/lib/chain/blocks/index.js.map +1 -1
  11. package/lib/chain/blocks/types.d.ts +8 -0
  12. package/lib/chain/blocks/types.d.ts.map +1 -1
  13. package/lib/chain/blocks/types.js.map +1 -1
  14. package/lib/chain/blocks/utils/chainSegment.d.ts +1 -1
  15. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  16. package/lib/chain/blocks/utils/chainSegment.js +10 -1
  17. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  18. package/lib/chain/chain.d.ts +7 -2
  19. package/lib/chain/chain.d.ts.map +1 -1
  20. package/lib/chain/chain.js +69 -40
  21. package/lib/chain/chain.js.map +1 -1
  22. package/lib/chain/emitter.d.ts +1 -1
  23. package/lib/chain/emitter.js +1 -1
  24. package/lib/chain/errors/blockError.d.ts +3 -5
  25. package/lib/chain/errors/blockError.d.ts.map +1 -1
  26. package/lib/chain/errors/blockError.js +2 -2
  27. package/lib/chain/errors/blockError.js.map +1 -1
  28. package/lib/chain/errors/executionPayloadEnvelope.d.ts +0 -11
  29. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  30. package/lib/chain/errors/executionPayloadEnvelope.js +0 -2
  31. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  32. package/lib/chain/errors/gossipValidation.d.ts +2 -1
  33. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  34. package/lib/chain/errors/gossipValidation.js +1 -0
  35. package/lib/chain/errors/gossipValidation.js.map +1 -1
  36. package/lib/chain/interface.d.ts +2 -2
  37. package/lib/chain/interface.d.ts.map +1 -1
  38. package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
  39. package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
  40. package/lib/chain/prepareNextSlot.js +13 -13
  41. package/lib/chain/prepareNextSlot.js.map +1 -1
  42. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  43. package/lib/chain/produceBlock/produceBlockBody.js +46 -64
  44. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  45. package/lib/chain/validation/block.d.ts.map +1 -1
  46. package/lib/chain/validation/block.js +9 -50
  47. package/lib/chain/validation/block.js.map +1 -1
  48. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  49. package/lib/chain/validation/executionPayloadEnvelope.js +0 -29
  50. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  51. package/lib/chain/validatorMonitor.d.ts +7 -1
  52. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  53. package/lib/chain/validatorMonitor.js +22 -5
  54. package/lib/chain/validatorMonitor.js.map +1 -1
  55. package/lib/execution/engine/mock.d.ts.map +1 -1
  56. package/lib/execution/engine/mock.js +4 -1
  57. package/lib/execution/engine/mock.js.map +1 -1
  58. package/lib/network/processor/index.d.ts.map +1 -1
  59. package/lib/network/processor/index.js +11 -0
  60. package/lib/network/processor/index.js.map +1 -1
  61. package/lib/network/reqresp/rateLimit.js +2 -1
  62. package/lib/network/reqresp/rateLimit.js.map +1 -1
  63. package/lib/sync/range/range.d.ts.map +1 -1
  64. package/lib/sync/range/range.js +24 -1
  65. package/lib/sync/range/range.js.map +1 -1
  66. package/package.json +15 -15
  67. package/src/api/impl/validator/index.ts +44 -43
  68. package/src/chain/blocks/importBlock.ts +0 -13
  69. package/src/chain/blocks/index.ts +31 -25
  70. package/src/chain/blocks/types.ts +9 -0
  71. package/src/chain/blocks/utils/chainSegment.ts +10 -1
  72. package/src/chain/chain.ts +78 -50
  73. package/src/chain/emitter.ts +1 -1
  74. package/src/chain/errors/blockError.ts +3 -3
  75. package/src/chain/errors/executionPayloadEnvelope.ts +0 -9
  76. package/src/chain/errors/gossipValidation.ts +1 -0
  77. package/src/chain/interface.ts +2 -2
  78. package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
  79. package/src/chain/prepareNextSlot.ts +16 -16
  80. package/src/chain/produceBlock/produceBlockBody.ts +50 -71
  81. package/src/chain/validation/block.ts +9 -66
  82. package/src/chain/validation/executionPayloadEnvelope.ts +0 -37
  83. package/src/chain/validatorMonitor.ts +24 -7
  84. package/src/execution/engine/mock.ts +4 -1
  85. package/src/network/processor/index.ts +13 -0
  86. package/src/network/reqresp/rateLimit.ts +2 -1
  87. package/src/sync/range/range.ts +23 -1
@@ -1,5 +1,5 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
3
3
  import {
4
4
  IBeaconStateView,
5
5
  ParticipationFlags,
@@ -1117,7 +1117,11 @@ function isCanonical(rootCache: RootHexCache, block: AttestationBlockInclusion):
1117
1117
 
1118
1118
  /** Returns true if root at slot is the same at slot - 1 == there was no new block at slot */
1119
1119
  function isMissedSlot(rootCache: RootHexCache, slot: Slot): boolean {
1120
- return slot > 0 && rootCache.getBlockRootAtSlot(slot) === rootCache.getBlockRootAtSlot(slot - 1);
1120
+ if (slot <= 0) {
1121
+ return false;
1122
+ }
1123
+ const root = rootCache.getBlockRootAtSlot(slot);
1124
+ return root !== null && root === rootCache.getBlockRootAtSlot(slot - 1);
1121
1125
  }
1122
1126
 
1123
1127
  function renderBlockProposalSummary(
@@ -1131,7 +1135,11 @@ function renderBlockProposalSummary(
1131
1135
  return "not_submitted";
1132
1136
  }
1133
1137
 
1134
- if (rootCache.getBlockRootAtSlot(proposalSlot) === proposal.blockRoot) {
1138
+ const canonicalRoot = rootCache.getBlockRootAtSlot(proposalSlot);
1139
+ if (canonicalRoot === null) {
1140
+ return "unknown";
1141
+ }
1142
+ if (canonicalRoot === proposal.blockRoot) {
1135
1143
  // Canonical state includes our block
1136
1144
  return "canonical";
1137
1145
  }
@@ -1157,14 +1165,23 @@ function renderBlockProposalSummary(
1157
1165
  * In normal network conditions the same root is read multiple times, specially the target.
1158
1166
  */
1159
1167
  export class RootHexCache {
1160
- private readonly blockRootSlotCache = new Map<Slot, RootHex>();
1168
+ private readonly blockRootSlotCache = new Map<Slot, RootHex | null>();
1161
1169
 
1162
1170
  constructor(private readonly state: IBeaconStateView) {}
1163
1171
 
1164
- getBlockRootAtSlot(slot: Slot): RootHex {
1172
+ /**
1173
+ * Block root at `slot`, null if the state does not cover it: slots at or after the state's own slot (an inclusion
1174
+ * in a block the head state does not know yet, e.g. an orphaned block, or the slot after the last attestation of
1175
+ * the epoch) or slots older than `SLOTS_PER_HISTORICAL_ROOT`. Callers treat null as unknown instead of throwing
1176
+ * and aborting the summaries of all remaining validators.
1177
+ */
1178
+ getBlockRootAtSlot(slot: Slot): RootHex | null {
1165
1179
  let root = this.blockRootSlotCache.get(slot);
1166
- if (!root) {
1167
- root = toRootHex(this.state.getBlockRootAtSlot(slot));
1180
+ if (root === undefined) {
1181
+ root =
1182
+ slot < this.state.slot && slot >= this.state.slot - SLOTS_PER_HISTORICAL_ROOT
1183
+ ? toRootHex(this.state.getBlockRootAtSlot(slot))
1184
+ : null;
1168
1185
  this.blockRootSlotCache.set(slot, root);
1169
1186
  }
1170
1187
  return root;
@@ -296,7 +296,10 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
296
296
 
297
297
  // 5. Client software MUST update its forkchoice state if payloads referenced by forkchoiceState.headBlockHash and
298
298
  // forkchoiceState.finalizedBlockHash are VALID.
299
- if (!this.validBlocks.has(finalizedBlockHash)) {
299
+ //
300
+ // A zero finalizedBlockHash means "no finalized execution block" (e.g. the finalized checkpoint has no execution
301
+ // payload, pre-merge/pre-TTD). Real EL clients accept this, so the mock must not treat it as an unknown block.
302
+ if (finalizedBlockHash !== ZERO_HASH_HEX && !this.validBlocks.has(finalizedBlockHash)) {
300
303
  throw Error(`Unknown finalizedBlockHash ${finalizedBlockHash}`);
301
304
  }
302
305
  this.headBlockHash = headBlockHash;
@@ -360,6 +360,19 @@ export class NetworkProcessor {
360
360
  return;
361
361
  }
362
362
 
363
+ // DOS protection: drop far-future messages before allocating any unknown-root tracking entry.
364
+ // Bound each topic to what its gossip validator accepts; only execution_payload_bid accepts the next slot.
365
+ const latestPermissableSlot =
366
+ this.chain.clock.currentSlotWithGossipDisparity + (topicType === GossipType.execution_payload_bid ? 1 : 0);
367
+ if (slot > latestPermissableSlot) {
368
+ // No need to report the dropped job to gossip. It will be eventually pruned from the mcache
369
+ this.metrics?.networkProcessor.gossipValidationError.inc({
370
+ topic: topicType,
371
+ error: GossipErrorCode.FUTURE_SLOT,
372
+ });
373
+ return;
374
+ }
375
+
363
376
  message.msgSlot = slot;
364
377
 
365
378
  // this determines whether this message needs to wait for a Block or Envelope
@@ -126,7 +126,8 @@ function getRequestCountFn<T extends ReqRespMethod>(
126
126
  const type = requestSszTypeByMethod(fork, config)[method];
127
127
  return (reqData: Uint8Array) => {
128
128
  try {
129
- return (type && fn(type.deserialize(reqData))) ?? 1;
129
+ // A schema-valid request can cost 0 tokens; floor to 1 so it stays charged and bannable, not free.
130
+ return Math.max(1, (type && fn(type.deserialize(reqData))) ?? 1);
130
131
  } catch (_e) {
131
132
  return 1;
132
133
  }
@@ -217,7 +217,29 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
217
217
  }
218
218
  }
219
219
  } else {
220
- await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
220
+ const {orphaned, skipped} = await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
221
+ // We log orphaned payloads to work with different clients
222
+ // in the future, consider applying penalties in certain conditions
223
+ // make sure the payload source was from range sync in that case
224
+ for (const {slot, payloadEnvelopeInput} of orphaned) {
225
+ const {peerIdStr, source} = payloadEnvelopeInput.getPayloadEnvelopeSource();
226
+ let client = "unknown";
227
+ if (peerIdStr !== undefined) {
228
+ try {
229
+ client = this.getConnectedPeerSyncMeta(peerIdStr).client;
230
+ } catch {
231
+ // peer disconnected since serving the envelope, keep "unknown"
232
+ }
233
+ }
234
+ this.logger.debug("Orphaned payload envelope in range sync batch", {
235
+ slot,
236
+ root: payloadEnvelopeInput.blockRootHex,
237
+ peer: peerIdStr ?? "unknown",
238
+ source,
239
+ client,
240
+ skipped,
241
+ });
242
+ }
221
243
  }
222
244
  };
223
245