@lodestar/beacon-node 1.43.0-dev.4358217e12 → 1.43.0-dev.549a5b8115

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 (65) hide show
  1. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/pool/index.js +45 -2
  3. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  4. package/lib/api/impl/validator/index.d.ts.map +1 -1
  5. package/lib/api/impl/validator/index.js +66 -1
  6. package/lib/api/impl/validator/index.js.map +1 -1
  7. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  8. package/lib/chain/blocks/importBlock.js +1 -3
  9. package/lib/chain/blocks/importBlock.js.map +1 -1
  10. package/lib/chain/blocks/importExecutionPayload.d.ts +2 -1
  11. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  12. package/lib/chain/blocks/importExecutionPayload.js +4 -4
  13. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  14. package/lib/chain/blocks/index.d.ts.map +1 -1
  15. package/lib/chain/blocks/index.js +7 -5
  16. package/lib/chain/blocks/index.js.map +1 -1
  17. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +1 -0
  18. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  19. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +4 -1
  20. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  21. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -0
  22. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  23. package/lib/chain/blocks/verifyBlock.d.ts +2 -1
  24. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  25. package/lib/chain/blocks/verifyBlock.js +26 -7
  26. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  27. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  28. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
  29. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  30. package/lib/chain/chain.d.ts.map +1 -1
  31. package/lib/chain/chain.js +2 -0
  32. package/lib/chain/chain.js.map +1 -1
  33. package/lib/chain/opPools/payloadAttestationPool.d.ts +1 -0
  34. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  35. package/lib/chain/opPools/payloadAttestationPool.js +22 -0
  36. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  37. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +8 -2
  38. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  39. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +8 -2
  40. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  41. package/lib/network/interface.d.ts +1 -0
  42. package/lib/network/interface.d.ts.map +1 -1
  43. package/lib/network/network.d.ts +1 -0
  44. package/lib/network/network.d.ts.map +1 -1
  45. package/lib/network/network.js +5 -0
  46. package/lib/network/network.js.map +1 -1
  47. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  48. package/lib/network/processor/gossipHandlers.js +4 -3
  49. package/lib/network/processor/gossipHandlers.js.map +1 -1
  50. package/package.json +15 -15
  51. package/src/api/impl/beacon/pool/index.ts +83 -1
  52. package/src/api/impl/validator/index.ts +80 -0
  53. package/src/chain/blocks/importBlock.ts +0 -1
  54. package/src/chain/blocks/importExecutionPayload.ts +11 -4
  55. package/src/chain/blocks/index.ts +14 -6
  56. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +5 -1
  57. package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
  58. package/src/chain/blocks/verifyBlock.ts +39 -9
  59. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
  60. package/src/chain/chain.ts +2 -0
  61. package/src/chain/opPools/payloadAttestationPool.ts +25 -0
  62. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +13 -3
  63. package/src/network/interface.ts +1 -0
  64. package/src/network/network.ts +11 -0
  65. package/src/network/processor/gossipHandlers.ts +8 -4
@@ -41,7 +41,8 @@ export async function verifyBlocksInEpoch(
41
41
  postStates: IBeaconStateView[];
42
42
  proposerBalanceDeltas: number[];
43
43
  segmentExecStatus: SegmentExecStatus;
44
- dataAvailabilityStatuses: DataAvailabilityStatus[];
44
+ blockDAStatuses: DataAvailabilityStatus[];
45
+ payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
45
46
  indexedAttestationsByBlock: IndexedAttestation[][];
46
47
  }> {
47
48
  const blocks = blockInputs.map((blockInput) => blockInput.getBlock());
@@ -116,8 +117,12 @@ export async function verifyBlocksInEpoch(
116
117
 
117
118
  // Pick the data-availability source by fork:
118
119
  // - Pre-Gloas: blob/Fulu-column data lives in IBlockInput → verifyBlocksDataAvailability.
119
- // - Post-Gloas: verifyPayloadsDataAvailability
120
- const daAvailabilityPromise =
120
+ // - Post-Gloas: verifyPayloadsDataAvailability (payload-level DA, keyed by slot).
121
+ const daAvailabilityPromise: Promise<{
122
+ blockDAStatuses: DataAvailabilityStatus[];
123
+ payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
124
+ availableTime: number;
125
+ }> =
121
126
  fork >= ForkSeq.gloas
122
127
  ? (async () => {
123
128
  const payloadInputsForDa: PayloadEnvelopeInput[] = [];
@@ -125,19 +130,37 @@ export async function verifyBlocksInEpoch(
125
130
  const pi = payloadEnvelopes?.get(input.slot);
126
131
  if (pi !== undefined) payloadInputsForDa.push(pi);
127
132
  }
128
- await verifyPayloadsDataAvailability(payloadInputsForDa, abortController.signal);
133
+ const {dataAvailabilityStatuses, availableTime} = await verifyPayloadsDataAvailability(
134
+ payloadInputsForDa,
135
+ abortController.signal
136
+ );
137
+ const payloadDAStatuses = new Map<Slot, DataAvailabilityStatus>();
138
+ for (let i = 0; i < payloadInputsForDa.length; i++) {
139
+ payloadDAStatuses.set(payloadInputsForDa[i].slot, dataAvailabilityStatuses[i]);
140
+ }
129
141
  return {
130
142
  // post-gloas, DataAvailabilityStatus is NotRequired for forkChoice.onBlock() ProtoBlock
131
- dataAvailabilityStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
132
- availableTime: Date.now(),
143
+ blockDAStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
144
+ payloadDAStatuses,
145
+ availableTime,
133
146
  };
134
147
  })()
135
- : verifyBlocksDataAvailability(blockInputs, abortController.signal);
148
+ : (async () => {
149
+ const {dataAvailabilityStatuses, availableTime} = await verifyBlocksDataAvailability(
150
+ blockInputs,
151
+ abortController.signal
152
+ );
153
+ return {
154
+ blockDAStatuses: dataAvailabilityStatuses,
155
+ payloadDAStatuses: new Map<Slot, DataAvailabilityStatus>(),
156
+ availableTime,
157
+ };
158
+ })();
136
159
 
137
160
  // batch all I/O operations to reduce overhead
138
161
  const [
139
162
  segmentExecStatus,
140
- {dataAvailabilityStatuses, availableTime},
163
+ {blockDAStatuses, payloadDAStatuses, availableTime},
141
164
  {postStates, proposerBalanceDeltas, verifyStateTime},
142
165
  {verifySignaturesTime},
143
166
  ] = await Promise.all([
@@ -258,7 +281,14 @@ export async function verifyBlocksInEpoch(
258
281
  );
259
282
  }
260
283
 
261
- return {postStates, dataAvailabilityStatuses, proposerBalanceDeltas, segmentExecStatus, indexedAttestationsByBlock};
284
+ return {
285
+ postStates,
286
+ blockDAStatuses,
287
+ payloadDAStatuses,
288
+ proposerBalanceDeltas,
289
+ segmentExecStatus,
290
+ indexedAttestationsByBlock,
291
+ };
262
292
  } finally {
263
293
  abortController.abort();
264
294
  }
@@ -28,11 +28,14 @@ export async function verifyPayloadsDataAvailability(
28
28
  await Promise.all(promises);
29
29
 
30
30
  const availableTime = Math.max(0, Math.max(...payloadInputs.map((payloadInput) => payloadInput.getTimeComplete())));
31
- const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) =>
32
- payloadInput.getBlobKzgCommitments().length === 0
31
+ const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) => {
32
+ if (payloadInput.daOutOfRange) {
33
+ return DataAvailabilityStatus.OutOfRange;
34
+ }
35
+ return payloadInput.getBlobKzgCommitments().length === 0
33
36
  ? DataAvailabilityStatus.NotRequired
34
- : DataAvailabilityStatus.Available
35
- );
37
+ : DataAvailabilityStatus.Available;
38
+ });
36
39
 
37
40
  return {dataAvailabilityStatuses, availableTime};
38
41
  }
@@ -336,6 +336,8 @@ export class BeaconChain implements IBeaconChain {
336
336
  logger,
337
337
  });
338
338
  this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
339
+ config,
340
+ clock,
339
341
  chainEvents: emitter,
340
342
  signal,
341
343
  serializedCache: this.serializedCache,
@@ -119,6 +119,31 @@ export class PayloadAttestationPool {
119
119
  .map(fastToPayloadAttestation);
120
120
  }
121
121
 
122
+ getAll(slot?: Slot): gloas.PayloadAttestation[] {
123
+ const aggregates: AggregateFast[] = [];
124
+
125
+ const addAggregates = (aggregateByDataRootByBlockRoot: Map<BlockRootHex, Map<DataRootHex, AggregateFast>>) => {
126
+ for (const aggregateByDataRoot of aggregateByDataRootByBlockRoot.values()) {
127
+ aggregates.push(...aggregateByDataRoot.values());
128
+ }
129
+ };
130
+
131
+ if (slot !== undefined) {
132
+ const aggregateByDataRootByBlockRoot = this.aggregateByDataRootByBlockRootBySlot.get(slot);
133
+ if (aggregateByDataRootByBlockRoot) {
134
+ addAggregates(aggregateByDataRootByBlockRoot);
135
+ }
136
+ } else {
137
+ for (const aggregateByDataRootByBlockRoot of this.aggregateByDataRootByBlockRootBySlot.values()) {
138
+ addAggregates(aggregateByDataRootByBlockRoot);
139
+ }
140
+ }
141
+
142
+ return aggregates
143
+ .sort((a, b) => b.aggregationBits.getTrueBitIndexes().length - a.aggregationBits.getTrueBitIndexes().length)
144
+ .map(fastToPayloadAttestation);
145
+ }
146
+
122
147
  prune(clockSlot: Slot): void {
123
148
  pruneBySlot(this.aggregateByDataRootByBlockRootBySlot, clockSlot, SLOTS_RETAINED);
124
149
  this.lowestPermissibleSlot = clockSlot;
@@ -1,9 +1,12 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
1
2
  import {CheckpointWithHex} from "@lodestar/fork-choice";
2
3
  import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
3
4
  import {RootHex, Slot} from "@lodestar/types";
4
5
  import {Logger} from "@lodestar/utils";
5
6
  import {Metrics} from "../../metrics/metrics.js";
7
+ import {IClock} from "../../util/clock.js";
6
8
  import {SerializedCache} from "../../util/serializedCache.js";
9
+ import {isDaOutOfRange} from "../blocks/blockInput/index.js";
7
10
  import {CreateFromBlockProps, PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
8
11
  import {ChainEvent, ChainEventEmitter} from "../emitter.js";
9
12
 
@@ -11,6 +14,8 @@ export type {PayloadEnvelopeInputState} from "../blocks/payloadEnvelopeInput/ind
11
14
  export {PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
12
15
 
13
16
  export type SeenPayloadEnvelopeInputModules = {
17
+ config: ChainForkConfig;
18
+ clock: IClock;
14
19
  chainEvents: ChainEventEmitter;
15
20
  signal: AbortSignal;
16
21
  serializedCache: SerializedCache;
@@ -32,6 +37,8 @@ export type SeenPayloadEnvelopeInputModules = {
32
37
  * ticks; subsequent ticks settle it back.
33
38
  */
34
39
  export class SeenPayloadEnvelopeInput {
40
+ private readonly config: ChainForkConfig;
41
+ private readonly clock: IClock;
35
42
  private readonly chainEvents: ChainEventEmitter;
36
43
  private readonly signal: AbortSignal;
37
44
  private readonly serializedCache: SerializedCache;
@@ -39,7 +46,9 @@ export class SeenPayloadEnvelopeInput {
39
46
  private readonly logger?: Logger;
40
47
  private payloadInputs = new Map<RootHex, PayloadEnvelopeInput>();
41
48
 
42
- constructor({chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
49
+ constructor({config, clock, chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
50
+ this.config = config;
51
+ this.clock = clock;
43
52
  this.chainEvents = chainEvents;
44
53
  this.signal = signal;
45
54
  this.serializedCache = serializedCache;
@@ -68,11 +77,12 @@ export class SeenPayloadEnvelopeInput {
68
77
  this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch));
69
78
  };
70
79
 
71
- add(props: CreateFromBlockProps): PayloadEnvelopeInput {
80
+ add(props: Omit<CreateFromBlockProps, "daOutOfRange">): PayloadEnvelopeInput {
72
81
  if (this.payloadInputs.has(props.blockRootHex)) {
73
82
  throw new Error(`PayloadEnvelopeInput already exists for block ${props.blockRootHex}`);
74
83
  }
75
- const input = PayloadEnvelopeInput.createFromBlock(props);
84
+ const daOutOfRange = isDaOutOfRange(this.config, props.forkName, props.block.message.slot, this.clock.currentEpoch);
85
+ const input = PayloadEnvelopeInput.createFromBlock({...props, daOutOfRange});
76
86
  this.payloadInputs.set(props.blockRootHex, input);
77
87
  this.metrics?.seenCache.payloadEnvelopeInput.created.inc();
78
88
  return input;
@@ -112,6 +112,7 @@ export interface INetwork extends INetworkCorePublic {
112
112
  publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
113
113
  publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
114
114
  publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
115
+ publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number>;
115
116
 
116
117
  // Debug
117
118
  dumpGossipQueue(gossipType: GossipType): Promise<PendingGossipsubMessage[]>;
@@ -515,6 +515,17 @@ export class Network implements INetwork {
515
515
  );
516
516
  }
517
517
 
518
+ async publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number> {
519
+ const epoch = computeEpochAtSlot(payloadAttestationMessage.data.slot);
520
+ const boundary = this.config.getForkBoundaryAtEpoch(epoch);
521
+
522
+ return this.publishGossip<GossipType.payload_attestation_message>(
523
+ {type: GossipType.payload_attestation_message, boundary},
524
+ payloadAttestationMessage,
525
+ {ignoreDuplicatePublishError: true}
526
+ );
527
+ }
528
+
518
529
  private async publishGossip<K extends GossipType>(
519
530
  topic: GossipTopicMap[K],
520
531
  object: GossipTypeMap[K],
@@ -52,6 +52,8 @@ import {
52
52
  ExecutionPayloadEnvelopeErrorCode,
53
53
  GossipAction,
54
54
  GossipActionError,
55
+ PayloadAttestationError,
56
+ PayloadAttestationErrorCode,
55
57
  SyncCommitteeError,
56
58
  } from "../../chain/errors/index.js";
57
59
  import {IBeaconChain} from "../../chain/interface.js";
@@ -1301,10 +1303,12 @@ export async function validateGossipFnRetryUnknownRoot<T>(
1301
1303
  try {
1302
1304
  return await fn();
1303
1305
  } catch (e) {
1304
- if (
1305
- e instanceof AttestationError &&
1306
- e.type.code === AttestationErrorCode.UNKNOWN_OR_PREFINALIZED_BEACON_BLOCK_ROOT
1307
- ) {
1306
+ const isUnknownAttestationRoot =
1307
+ e instanceof AttestationError && e.type.code === AttestationErrorCode.UNKNOWN_OR_PREFINALIZED_BEACON_BLOCK_ROOT;
1308
+ const isUnknownPayloadAttestationRoot =
1309
+ e instanceof PayloadAttestationError && e.type.code === PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT;
1310
+
1311
+ if (isUnknownAttestationRoot || isUnknownPayloadAttestationRoot) {
1308
1312
  if (unknownBlockRootRetries === 0) {
1309
1313
  // Trigger unknown block root search here
1310
1314
  const rootHex = toRootHex(blockRoot);