@lodestar/beacon-node 1.49.0 → 1.50.0-dev.023ad6c012

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 (170) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +6 -1
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.js +4 -4
  5. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  6. package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
  7. package/lib/api/impl/beacon/state/index.js +28 -0
  8. package/lib/api/impl/beacon/state/index.js.map +1 -1
  9. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  10. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  11. package/lib/chain/archiveStore/archiveStore.js +26 -3
  12. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  13. package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
  14. package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/utils/pruneHistory.js +4 -3
  16. package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
  17. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  18. package/lib/chain/blocks/importBlock.js +4 -0
  19. package/lib/chain/blocks/importBlock.js.map +1 -1
  20. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
  21. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  22. package/lib/chain/chain.d.ts +5 -4
  23. package/lib/chain/chain.d.ts.map +1 -1
  24. package/lib/chain/chain.js +14 -16
  25. package/lib/chain/chain.js.map +1 -1
  26. package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
  27. package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
  28. package/lib/chain/errors/envelopeReconstructionError.js +2 -0
  29. package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
  30. package/lib/chain/errors/gossipValidation.d.ts +3 -1
  31. package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
  32. package/lib/chain/errors/gossipValidation.js +2 -0
  33. package/lib/chain/errors/gossipValidation.js.map +1 -1
  34. package/lib/chain/initState.d.ts +2 -9
  35. package/lib/chain/initState.d.ts.map +1 -1
  36. package/lib/chain/initState.js +5 -38
  37. package/lib/chain/initState.js.map +1 -1
  38. package/lib/chain/interface.d.ts +2 -2
  39. package/lib/chain/interface.d.ts.map +1 -1
  40. package/lib/chain/opPools/opPool.d.ts +5 -4
  41. package/lib/chain/opPools/opPool.d.ts.map +1 -1
  42. package/lib/chain/opPools/opPool.js +62 -18
  43. package/lib/chain/opPools/opPool.js.map +1 -1
  44. package/lib/chain/opPools/utils.d.ts +4 -1
  45. package/lib/chain/opPools/utils.d.ts.map +1 -1
  46. package/lib/chain/opPools/utils.js +11 -1
  47. package/lib/chain/opPools/utils.js.map +1 -1
  48. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  49. package/lib/chain/prepareNextSlot.js +5 -2
  50. package/lib/chain/prepareNextSlot.js.map +1 -1
  51. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
  52. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  53. package/lib/chain/produceBlock/produceBlockBody.js +6 -2
  54. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  55. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
  56. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  57. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
  58. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  59. package/lib/chain/validation/attesterSlashing.d.ts +4 -4
  60. package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
  61. package/lib/chain/validation/attesterSlashing.js +3 -0
  62. package/lib/chain/validation/attesterSlashing.js.map +1 -1
  63. package/lib/chain/validation/proposerSlashing.d.ts +3 -3
  64. package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
  65. package/lib/chain/validation/proposerSlashing.js +3 -0
  66. package/lib/chain/validation/proposerSlashing.js.map +1 -1
  67. package/lib/db/beacon.d.ts.map +1 -1
  68. package/lib/db/beacon.js +2 -0
  69. package/lib/db/beacon.js.map +1 -1
  70. package/lib/db/repositories/proposerSlashing.d.ts +1 -0
  71. package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
  72. package/lib/db/repositories/proposerSlashing.js +5 -1
  73. package/lib/db/repositories/proposerSlashing.js.map +1 -1
  74. package/lib/execution/engine/http.d.ts.map +1 -1
  75. package/lib/execution/engine/http.js +5 -4
  76. package/lib/execution/engine/http.js.map +1 -1
  77. package/lib/execution/engine/jsonRpcHttpClient.d.ts +2 -1
  78. package/lib/execution/engine/jsonRpcHttpClient.d.ts.map +1 -1
  79. package/lib/execution/engine/jsonRpcHttpClient.js +6 -1
  80. package/lib/execution/engine/jsonRpcHttpClient.js.map +1 -1
  81. package/lib/execution/engine/transportMetrics.d.ts +13 -0
  82. package/lib/execution/engine/transportMetrics.d.ts.map +1 -0
  83. package/lib/execution/engine/transportMetrics.js +2 -0
  84. package/lib/execution/engine/transportMetrics.js.map +1 -0
  85. package/lib/index.d.ts +1 -1
  86. package/lib/index.d.ts.map +1 -1
  87. package/lib/index.js +1 -1
  88. package/lib/index.js.map +1 -1
  89. package/lib/metrics/metrics/beacon.d.ts +1 -1
  90. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  91. package/lib/metrics/metrics/beacon.js.map +1 -1
  92. package/lib/metrics/metrics/lodestar.d.ts +6 -0
  93. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  94. package/lib/metrics/metrics/lodestar.js +10 -0
  95. package/lib/metrics/metrics/lodestar.js.map +1 -1
  96. package/lib/network/metadata.d.ts +1 -1
  97. package/lib/network/metadata.d.ts.map +1 -1
  98. package/lib/network/metadata.js +17 -17
  99. package/lib/network/metadata.js.map +1 -1
  100. package/lib/network/peers/datastore.d.ts +9 -9
  101. package/lib/network/peers/datastore.d.ts.map +1 -1
  102. package/lib/network/peers/datastore.js +36 -36
  103. package/lib/network/peers/datastore.js.map +1 -1
  104. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  105. package/lib/network/processor/gossipHandlers.js +7 -4
  106. package/lib/network/processor/gossipHandlers.js.map +1 -1
  107. package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
  108. package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
  109. package/lib/network/processor/gossipValidatorFn.js +7 -3
  110. package/lib/network/processor/gossipValidatorFn.js.map +1 -1
  111. package/lib/network/processor/index.d.ts +9 -0
  112. package/lib/network/processor/index.d.ts.map +1 -1
  113. package/lib/network/processor/index.js +77 -10
  114. package/lib/network/processor/index.js.map +1 -1
  115. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  116. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
  117. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  118. package/lib/sync/range/batch.d.ts +6 -0
  119. package/lib/sync/range/batch.d.ts.map +1 -1
  120. package/lib/sync/range/batch.js +10 -0
  121. package/lib/sync/range/batch.js.map +1 -1
  122. package/lib/sync/range/range.d.ts.map +1 -1
  123. package/lib/sync/range/range.js +50 -18
  124. package/lib/sync/range/range.js.map +1 -1
  125. package/lib/sync/unknownBlock.d.ts +9 -5
  126. package/lib/sync/unknownBlock.d.ts.map +1 -1
  127. package/lib/sync/unknownBlock.js +88 -46
  128. package/lib/sync/unknownBlock.js.map +1 -1
  129. package/lib/util/execution.d.ts +11 -11
  130. package/lib/util/execution.d.ts.map +1 -1
  131. package/lib/util/execution.js +11 -7
  132. package/lib/util/execution.js.map +1 -1
  133. package/package.json +15 -15
  134. package/src/api/impl/beacon/blocks/index.ts +7 -4
  135. package/src/api/impl/beacon/pool/index.ts +4 -4
  136. package/src/api/impl/beacon/state/index.ts +36 -0
  137. package/src/chain/archiveStore/archiveStore.ts +29 -4
  138. package/src/chain/archiveStore/utils/pruneHistory.ts +6 -4
  139. package/src/chain/blocks/importBlock.ts +4 -0
  140. package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
  141. package/src/chain/chain.ts +17 -19
  142. package/src/chain/errors/envelopeReconstructionError.ts +3 -0
  143. package/src/chain/errors/gossipValidation.ts +2 -0
  144. package/src/chain/initState.ts +8 -60
  145. package/src/chain/interface.ts +2 -2
  146. package/src/chain/opPools/opPool.ts +91 -18
  147. package/src/chain/opPools/utils.ts +31 -3
  148. package/src/chain/prepareNextSlot.ts +6 -3
  149. package/src/chain/produceBlock/produceBlockBody.ts +8 -1
  150. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
  151. package/src/chain/validation/attesterSlashing.ts +7 -4
  152. package/src/chain/validation/proposerSlashing.ts +7 -4
  153. package/src/db/beacon.ts +2 -0
  154. package/src/db/repositories/proposerSlashing.ts +6 -1
  155. package/src/execution/engine/http.ts +6 -4
  156. package/src/execution/engine/jsonRpcHttpClient.ts +9 -2
  157. package/src/execution/engine/transportMetrics.ts +9 -0
  158. package/src/index.ts +1 -1
  159. package/src/metrics/metrics/beacon.ts +1 -1
  160. package/src/metrics/metrics/lodestar.ts +10 -0
  161. package/src/network/metadata.ts +17 -17
  162. package/src/network/peers/datastore.ts +36 -36
  163. package/src/network/processor/gossipHandlers.ts +7 -4
  164. package/src/network/processor/gossipValidatorFn.ts +13 -3
  165. package/src/network/processor/index.ts +89 -10
  166. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
  167. package/src/sync/range/batch.ts +11 -0
  168. package/src/sync/range/range.ts +50 -18
  169. package/src/sync/unknownBlock.ts +99 -50
  170. package/src/util/execution.ts +26 -14
@@ -1,9 +1,37 @@
1
1
  import {Signature} from "@chainsafe/lodestar-z/blst";
2
- import {BLS_WITHDRAWAL_PREFIX} from "@lodestar/params";
3
- import {IBeaconStateView} from "@lodestar/state-transition";
4
- import {Slot, capella} from "@lodestar/types";
2
+ import {BeaconConfig} from "@lodestar/config";
3
+ import {BLS_WITHDRAWAL_PREFIX, DOMAIN_BEACON_ATTESTER, DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
4
+ import {IBeaconStateView, computeStartSlotAtEpoch} from "@lodestar/state-transition";
5
+ import {AttesterSlashing, Domain, Slot, capella, phase0} from "@lodestar/types";
5
6
  import {AggregateFast, AggregateFastElectra} from "./attestationPool.js";
6
7
 
8
+ export function getProposerSlashingSignatureDomain(
9
+ config: BeaconConfig,
10
+ stateSlot: Slot,
11
+ slashing: phase0.ProposerSlashing
12
+ ): Domain {
13
+ return config.getDomain(stateSlot, DOMAIN_BEACON_PROPOSER, Number(slashing.signedHeader1.message.slot));
14
+ }
15
+
16
+ export function getAttesterSlashingSignatureDomains(
17
+ config: BeaconConfig,
18
+ stateSlot: Slot,
19
+ slashing: AttesterSlashing
20
+ ): [Domain, Domain] {
21
+ return [
22
+ config.getDomain(
23
+ stateSlot,
24
+ DOMAIN_BEACON_ATTESTER,
25
+ computeStartSlotAtEpoch(Number(slashing.attestation1.data.target.epoch))
26
+ ),
27
+ config.getDomain(
28
+ stateSlot,
29
+ DOMAIN_BEACON_ATTESTER,
30
+ computeStartSlotAtEpoch(Number(slashing.attestation2.data.target.epoch))
31
+ ),
32
+ ];
33
+ }
34
+
7
35
  /**
8
36
  * Prune a Map indexed by slot to keep the most recent slots, up to `slotsRetained`
9
37
  */
@@ -244,6 +244,10 @@ export class PrepareNextSlotScheduler {
244
244
  parentBlockHash = preparedState.latestExecutionPayloadHeader.blockHash;
245
245
  }
246
246
 
247
+ // The payload_attributes event must carry the same hashes we send to the EL
248
+ const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice, this.logger);
249
+ const finalizedBlockHash = getFinalizedExecutionBlockHash(this.chain.forkChoice);
250
+
247
251
  let payloadAttributes: PayloadAttributes | undefined;
248
252
  // If emitPayloadAttributes is true emit a SSE payloadAttributes event for
249
253
  // every slot. Without the flag, only emit the event if we are proposing in the next slot.
@@ -256,6 +260,8 @@ export class PrepareNextSlotScheduler {
256
260
  prepareSlot,
257
261
  parentBlockRoot: fromHex(updatedHead.blockRoot),
258
262
  parentBlockHash,
263
+ safeBlockHash,
264
+ finalizedBlockHash,
259
265
  feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
260
266
  });
261
267
  this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
@@ -267,9 +273,6 @@ export class PrepareNextSlotScheduler {
267
273
  computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
268
274
  this.metrics?.blockPayload.payloadAdvancePrepTime.observe(preparationTime);
269
275
 
270
- const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice, this.logger);
271
- const finalizedBlockHash = getFinalizedExecutionBlockHash(this.chain.forkChoice);
272
-
273
276
  // awaiting here instead of throwing an async call because there is no other task
274
277
  // left for scheduler and this gives nice semantics to catch and log errors in the
275
278
  // try/catch wrapper here.
@@ -845,6 +845,8 @@ export function getPayloadAttributesForSSE(
845
845
  prepareSlot,
846
846
  parentBlockRoot,
847
847
  parentBlockHash,
848
+ safeBlockHash,
849
+ finalizedBlockHash,
848
850
  feeRecipient,
849
851
  }: {
850
852
  /**
@@ -855,6 +857,8 @@ export function getPayloadAttributesForSSE(
855
857
  prepareSlot: Slot;
856
858
  parentBlockRoot: Root;
857
859
  parentBlockHash: Bytes32;
860
+ safeBlockHash: RootHex;
861
+ finalizedBlockHash: RootHex;
858
862
  feeRecipient: string;
859
863
  }
860
864
  ): SSEPayloadAttributes {
@@ -874,7 +878,10 @@ export function getPayloadAttributesForSSE(
874
878
  payloadAttributes,
875
879
  } as SSEPayloadAttributes;
876
880
 
877
- if (!isForkPostGloas(fork)) {
881
+ if (isForkPostGloas(fork)) {
882
+ (ssePayloadAttributes as gloas.SSEPayloadAttributes).safeBlockHash = fromHex(safeBlockHash);
883
+ (ssePayloadAttributes as gloas.SSEPayloadAttributes).finalizedBlockHash = fromHex(finalizedBlockHash);
884
+ } else {
878
885
  // Removed in Gloas, builders can get the block number from the EL via the block hash if required
879
886
  (ssePayloadAttributes as bellatrix.SSEPayloadAttributes).parentBlockNumber = prepareState.payloadBlockNumber;
880
887
  }
@@ -47,8 +47,9 @@ export type SeenPayloadEnvelopeInputModules = {
47
47
  * Created whenever we have a block because it needs block bid.
48
48
  * Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head
49
49
  * (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can
50
- * transiently hold more during forks, range-sync bursts, or when `prepareNextSlot` skips
51
- * ticks; subsequent ticks settle it back.
50
+ * hold more during forks and range sync, or while an entry's payload is not FULL on the head's
51
+ * branch or its columns are still being gathered. Such entries are evicted on a later block
52
+ * import, on finalization or by the size cap.
52
53
  */
53
54
  export class SeenPayloadEnvelopeInput {
54
55
  private readonly config: ChainForkConfig;
@@ -264,18 +265,35 @@ export class SeenPayloadEnvelopeInput {
264
265
  }
265
266
  }
266
267
 
268
+ /**
269
+ * Removes a PayloadEnvelopeInput whose envelope failed import with a definitive invalid verdict. The envelope
270
+ * cannot be replaced in place, sync recreates the entry via `getOrReload()` or `add()` to fetch another one.
271
+ * No-op if the cached entry is a different object, it may already be the recreated one.
272
+ */
273
+ removeInvalid(payloadInput: PayloadEnvelopeInput): void {
274
+ if (this.payloadInputs.get(payloadInput.blockRootHex) === payloadInput) {
275
+ this.evictPayloadInput(payloadInput, "invalid");
276
+ }
277
+ }
278
+
267
279
  pruneBelowParent(parentBlock: ProtoBlock): void {
268
- for (const block of this.forkChoice.getAllAncestorBlocks(parentBlock.blockRoot, parentBlock.payloadStatus)) {
269
- // Only evict once the payload is FULL (revealed/imported) — on an EMPTY/PENDING branch we may
270
- // still need to download the FULL envelope (see #9475), and evicting would make payload-by-root
271
- // sync throw "Missing PayloadEnvelopeInput for known block".
272
- if (block.slot < parentBlock.slot && block.payloadStatus === PayloadStatus.FULL) {
273
- const input = this.payloadInputs.get(block.blockRoot);
274
- // ...and don't evict while columns are still being gathered: writeDataColumnsToDb awaits the
275
- // same hasComputedAllData() before persisting. Such entries are pruned by a later call.
276
- if (input?.hasComputedAllData()) {
277
- this.evictPayloadInput(input, "belowParent");
278
- }
280
+ for (const input of this.payloadInputs.values()) {
281
+ if (
282
+ input.slot < parentBlock.slot &&
283
+ // Don't evict while columns are still being gathered: writeDataColumnsToDb awaits the same
284
+ // hasComputedAllData() before persisting. Such entries are pruned by a later call.
285
+ input.hasComputedAllData() &&
286
+ // Only evict once the payload is FULL (revealed/imported) on the parent's branch. On an EMPTY/PENDING
287
+ // branch we may still need to download the FULL envelope (see #9475), and without the entry gossip
288
+ // for the block is ignored until by-root sync reloads it.
289
+ this.forkChoice.isDescendant(
290
+ input.blockRootHex,
291
+ PayloadStatus.FULL,
292
+ parentBlock.blockRoot,
293
+ parentBlock.payloadStatus
294
+ )
295
+ ) {
296
+ this.evictPayloadInput(input, "belowParent");
279
297
  }
280
298
  }
281
299
  }
@@ -4,14 +4,15 @@ import {
4
4
  getAttesterSlashingSignatureSets,
5
5
  isSlashableValidator,
6
6
  } from "@lodestar/state-transition";
7
- import {AttesterSlashing} from "@lodestar/types";
7
+ import {AttesterSlashing, Domain} from "@lodestar/types";
8
8
  import {AttesterSlashingError, AttesterSlashingErrorCode, GossipAction} from "../errors/index.js";
9
9
  import {IBeaconChain} from "../index.js";
10
+ import {getAttesterSlashingSignatureDomains} from "../opPools/utils.js";
10
11
 
11
12
  export async function validateApiAttesterSlashing(
12
13
  chain: IBeaconChain,
13
14
  attesterSlashing: AttesterSlashing
14
- ): Promise<void> {
15
+ ): Promise<[Domain, Domain]> {
15
16
  const prioritizeBls = true;
16
17
  return validateAttesterSlashing(chain, attesterSlashing, prioritizeBls);
17
18
  }
@@ -19,7 +20,7 @@ export async function validateApiAttesterSlashing(
19
20
  export async function validateGossipAttesterSlashing(
20
21
  chain: IBeaconChain,
21
22
  attesterSlashing: AttesterSlashing
22
- ): Promise<void> {
23
+ ): Promise<[Domain, Domain]> {
23
24
  return validateAttesterSlashing(chain, attesterSlashing);
24
25
  }
25
26
 
@@ -27,7 +28,7 @@ export async function validateAttesterSlashing(
27
28
  chain: IBeaconChain,
28
29
  attesterSlashing: AttesterSlashing,
29
30
  prioritizeBls = false
30
- ): Promise<void> {
31
+ ): Promise<[Domain, Domain]> {
31
32
  // [IGNORE] At least one index in the intersection of the attesting indices of each attestation has not yet been seen
32
33
  // in any prior attester_slashing (i.e.
33
34
  // attester_slashed_indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices
@@ -61,9 +62,11 @@ export async function validateAttesterSlashing(
61
62
  }
62
63
 
63
64
  const signatureSets = getAttesterSlashingSignatureSets(chain.config, state.slot, attesterSlashing);
65
+ const verifiedDomains = getAttesterSlashingSignatureDomains(chain.config, state.slot, attesterSlashing);
64
66
  if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
65
67
  throw new AttesterSlashingError(GossipAction.REJECT, {
66
68
  code: AttesterSlashingErrorCode.INVALID_SIGNATURE,
67
69
  });
68
70
  }
71
+ return verifiedDomains;
69
72
  }
@@ -1,12 +1,13 @@
1
1
  import {assertValidProposerSlashing, getProposerSlashingSignatureSets} from "@lodestar/state-transition";
2
- import {phase0} from "@lodestar/types";
2
+ import {Domain, phase0} from "@lodestar/types";
3
3
  import {GossipAction, ProposerSlashingError, ProposerSlashingErrorCode} from "../errors/index.js";
4
4
  import {IBeaconChain} from "../index.js";
5
+ import {getProposerSlashingSignatureDomain} from "../opPools/utils.js";
5
6
 
6
7
  export async function validateApiProposerSlashing(
7
8
  chain: IBeaconChain,
8
9
  proposerSlashing: phase0.ProposerSlashing
9
- ): Promise<void> {
10
+ ): Promise<Domain> {
10
11
  const prioritizeBls = true;
11
12
  return validateProposerSlashing(chain, proposerSlashing, prioritizeBls);
12
13
  }
@@ -14,7 +15,7 @@ export async function validateApiProposerSlashing(
14
15
  export async function validateGossipProposerSlashing(
15
16
  chain: IBeaconChain,
16
17
  proposerSlashing: phase0.ProposerSlashing
17
- ): Promise<void> {
18
+ ): Promise<Domain> {
18
19
  return validateProposerSlashing(chain, proposerSlashing);
19
20
  }
20
21
 
@@ -22,7 +23,7 @@ async function validateProposerSlashing(
22
23
  chain: IBeaconChain,
23
24
  proposerSlashing: phase0.ProposerSlashing,
24
25
  prioritizeBls = false
25
- ): Promise<void> {
26
+ ): Promise<Domain> {
26
27
  // [IGNORE] The proposer slashing is the first valid proposer slashing received for the proposer with index
27
28
  // proposer_slashing.signed_header_1.message.proposer_index.
28
29
  if (chain.opPool.hasSeenProposerSlashing(proposerSlashing.signedHeader1.message.proposerIndex)) {
@@ -46,9 +47,11 @@ async function validateProposerSlashing(
46
47
  }
47
48
 
48
49
  const signatureSets = getProposerSlashingSignatureSets(chain.config, state.slot, proposerSlashing);
50
+ const verifiedDomain = getProposerSlashingSignatureDomain(chain.config, state.slot, proposerSlashing);
49
51
  if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
50
52
  throw new ProposerSlashingError(GossipAction.REJECT, {
51
53
  code: ProposerSlashingErrorCode.INVALID_SIGNATURE,
52
54
  });
53
55
  }
56
+ return verifiedDomain;
54
57
  }
package/src/db/beacon.ts CHANGED
@@ -122,6 +122,8 @@ export class BeaconDb implements IBeaconDb {
122
122
  async pruneHotDb(): Promise<void> {
123
123
  // Prune all hot blobs
124
124
  await this.blobSidecars.batchDelete(await this.blobSidecars.keys());
125
+ // New columns are persisted in flat files, so this bucket only contains legacy hot data.
126
+ await this.deleteBucketData(Bucket.allForks_dataColumnSidecars);
125
127
  // Prune all hot blocks
126
128
  // TODO: Enable once it's deemed safe
127
129
  // await this.block.batchDelete(await this.block.keys());
@@ -1,6 +1,7 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {Db, Repository} from "@lodestar/db";
2
+ import {BUCKET_LENGTH, Db, Repository} from "@lodestar/db";
3
3
  import {ValidatorIndex, phase0, ssz} from "@lodestar/types";
4
+ import {bytesToInt} from "@lodestar/utils";
4
5
  import {Bucket, getBucketNameByValue} from "../buckets.js";
5
6
 
6
7
  export class ProposerSlashingRepository extends Repository<ValidatorIndex, phase0.ProposerSlashing> {
@@ -12,4 +13,8 @@ export class ProposerSlashingRepository extends Repository<ValidatorIndex, phase
12
13
  getId(value: phase0.ProposerSlashing): ValidatorIndex {
13
14
  return value.signedHeader1.message.proposerIndex;
14
15
  }
16
+
17
+ decodeKey(data: Uint8Array): ValidatorIndex {
18
+ return bytesToInt(data.subarray(BUCKET_LENGTH), "be");
19
+ }
15
20
  }
@@ -516,12 +516,13 @@ export class ExecutionEngineHttp implements IExecutionEngine {
516
516
  ): Promise<(BlobAndProof | null)[]>;
517
517
  async getBlobs(
518
518
  fork: ForkName,
519
- versionedHashes: VersionedHashes
519
+ versionedHashes: VersionedHashes,
520
+ buffers?: Uint8Array[]
520
521
  ): Promise<BlobAndProofV2[] | (BlobAndProof | null)[] | null> {
521
522
  assertReqSizeLimit(versionedHashes.length, MAX_VERSIONED_HASHES);
522
523
  const versionedHashesHex = versionedHashes.map(bytesToData);
523
524
  if (isForkPostFulu(fork)) {
524
- return await this.getBlobsV2(versionedHashesHex);
525
+ return await this.getBlobsV2(versionedHashesHex, buffers);
525
526
  }
526
527
  return await this.getBlobsV1(versionedHashesHex);
527
528
  }
@@ -551,8 +552,9 @@ export class ExecutionEngineHttp implements IExecutionEngine {
551
552
 
552
553
  private async getBlobsV2(versionedHashesHex: string[], buffers?: Uint8Array[]) {
553
554
  if (buffers) {
554
- if (buffers.length !== versionedHashesHex.length) {
555
- throw Error(`Invalid buffers length=${buffers.length} versionedHashes=${versionedHashesHex.length}`);
555
+ // Callers preallocate one buffer per max blobs of the epoch, only the first entries are used
556
+ if (buffers.length < versionedHashesHex.length) {
557
+ throw Error(`Not enough buffers length=${buffers.length} versionedHashes=${versionedHashesHex.length}`);
556
558
  }
557
559
 
558
560
  for (const [i, buffer] of buffers.entries()) {
@@ -2,6 +2,7 @@ import {EventEmitter} from "node:events";
2
2
  import {StrictEventEmitter} from "strict-event-emitter-types";
3
3
  import {ErrorAborted, Gauge, Histogram, TimeoutError, fetch, isValidHttpUrl, retry} from "@lodestar/utils";
4
4
  import {JwtClaim, encodeJwtToken} from "./jwt.js";
5
+ import {EngineTransportMetrics} from "./transportMetrics.js";
5
6
  import {IJson, RpcPayload} from "./utils.js";
6
7
 
7
8
  export enum JsonRpcHttpClientEvent {
@@ -55,7 +56,7 @@ export type ReqOpts = {
55
56
  shouldRetry?: (lastError: Error) => boolean;
56
57
  };
57
58
 
58
- export type JsonRpcHttpClientMetrics = {
59
+ export type JsonRpcHttpClientMetrics = EngineTransportMetrics & {
59
60
  requestTime: Histogram<{routeId: string}>;
60
61
  streamTime: Histogram<{routeId: string}>;
61
62
  requestErrors: Gauge<{routeId: string}>;
@@ -270,15 +271,21 @@ export class JsonRpcHttpClient implements IJsonRpcHttpClient {
270
271
  headers.Authorization = `Bearer ${token}`;
271
272
  }
272
273
 
274
+ const body = JSON.stringify(json);
275
+ // Json rpc bodies are ASCII (json syntax, 0x hex, digits) so String#length is the byte count.
276
+ // Buffer.byteLength would add a ~4ms scan of a 35MB getBlobsV2 body for no accuracy gain.
277
+ this.metrics?.requestBytes.inc({routeId}, body.length);
278
+
273
279
  const res = await fetch(url, {
274
280
  method: "post",
275
- body: JSON.stringify(json),
281
+ body,
276
282
  headers,
277
283
  signal: controller.signal,
278
284
  });
279
285
 
280
286
  const streamTimer = this.metrics?.streamTime.startTimer({routeId});
281
287
  const bodyText = await res.text();
288
+ this.metrics?.responseBytes.inc({routeId}, bodyText.length);
282
289
  if (!res.ok) {
283
290
  // Infura errors:
284
291
  // - No project ID: Forbidden: {"jsonrpc":"2.0","id":0,"error":{"code":-32600,"message":"project ID is required","data":{"reason":"project ID not provided","see":"https://infura.io/dashboard"}}}
@@ -0,0 +1,9 @@
1
+ import {Counter} from "@lodestar/utils";
2
+
3
+ /**
4
+ * Common metrics type for both json rpc and ssz transport.
5
+ */
6
+ export type EngineTransportMetrics = {
7
+ requestBytes: Counter<{routeId: string}>;
8
+ responseBytes: Counter<{routeId: string}>;
9
+ };
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  export type {RestApiServerMetrics, RestApiServerModules, RestApiServerOpts} from "./api/rest/base.js";
4
4
  export {RestApiServer} from "./api/rest/base.js";
5
- export {checkAndPersistAnchorState} from "./chain/index.js";
5
+ export {persistAnchorState} from "./chain/index.js";
6
6
  export {DbCPStateDatastore} from "./chain/stateCache/datastore/db.js";
7
7
  export {FileCPStateDatastore} from "./chain/stateCache/datastore/file.js";
8
8
  export {BeaconDb, type IBeaconDb} from "./db/index.js";
@@ -216,7 +216,7 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
216
216
  }),
217
217
  },
218
218
  payloadEnvelopeReconstruction: {
219
- envelopes: register.counter<{result: "ok" | "unavailable" | "mismatch"}>({
219
+ envelopes: register.counter<{result: "success" | "unavailable" | "mismatch"}>({
220
220
  name: "beacon_payload_envelope_reconstruction_envelopes_total",
221
221
  help: "Archived header envelopes rebuilt from EL bodies, by outcome",
222
222
  labelNames: ["result"],
@@ -2053,6 +2053,16 @@ export function createLodestarMetrics(
2053
2053
  help: "ExecutionEngineHttp client - total count of request retries",
2054
2054
  labelNames: ["routeId"],
2055
2055
  }),
2056
+ requestBytes: register.counter<{routeId: string}>({
2057
+ name: "lodestar_execution_engine_http_client_request_bytes_total",
2058
+ help: "ExecutionEngineHttp client - total bytes sent to the execution client by routeId",
2059
+ labelNames: ["routeId"],
2060
+ }),
2061
+ responseBytes: register.counter<{routeId: string}>({
2062
+ name: "lodestar_execution_engine_http_client_response_bytes_total",
2063
+ help: "ExecutionEngineHttp client - total bytes received from the execution client by routeId",
2064
+ labelNames: ["routeId"],
2065
+ }),
2056
2066
  requestUsedFallbackUrl: register.gauge<{routeId: string}>({
2057
2067
  name: "lodestar_execution_engine_http_client_request_used_fallback_url_total",
2058
2068
  help: "ExecutionEngineHttp client - total count of requests on fallback url(s)",
@@ -42,13 +42,13 @@ export class MetadataController {
42
42
  private onSetValue: (key: string, value: Uint8Array) => void;
43
43
  private networkConfig: NetworkConfig;
44
44
  private logger: Logger;
45
- private _metadata: fulu.Metadata;
45
+ private metadata: fulu.Metadata;
46
46
 
47
47
  constructor(opts: MetadataOpts, modules: MetadataModules) {
48
48
  this.networkConfig = modules.networkConfig;
49
49
  this.logger = modules.logger;
50
50
  this.onSetValue = modules.onSetValue;
51
- this._metadata = opts.metadata ?? {
51
+ this.metadata = opts.metadata ?? {
52
52
  ...ssz.fulu.Metadata.defaultValue(),
53
53
  custodyGroupCount: modules.networkConfig.custodyConfig.targetCustodyGroupCount,
54
54
  };
@@ -58,61 +58,61 @@ export class MetadataController {
58
58
  // updateEth2Field() MUST be called with clock epoch
59
59
  this.updateEth2Field(currentEpoch);
60
60
 
61
- this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this._metadata.attnets));
61
+ this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(this.metadata.attnets));
62
62
 
63
63
  const config = this.networkConfig.config;
64
64
 
65
65
  if (config.getForkSeq(computeStartSlotAtEpoch(currentEpoch)) >= ForkSeq.altair) {
66
66
  // Only persist syncnets if altair fork is already activated. If currentFork is altair but head is phase0
67
67
  // adding syncnets to the ENR is not a problem, we will just have a useless field for a few hours.
68
- this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this._metadata.syncnets));
68
+ this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(this.metadata.syncnets));
69
69
  }
70
70
 
71
71
  // Set CGC regardless of fork. It may be useful to clients before Fulu, and will be ignored otherwise.
72
- this.onSetValue(ENRKey.cgc, serializeCgc(this._metadata.custodyGroupCount));
72
+ this.onSetValue(ENRKey.cgc, serializeCgc(this.metadata.custodyGroupCount));
73
73
  }
74
74
 
75
75
  get seqNumber(): bigint {
76
- return this._metadata.seqNumber;
76
+ return this.metadata.seqNumber;
77
77
  }
78
78
 
79
79
  get syncnets(): BitArray {
80
- return this._metadata.syncnets;
80
+ return this.metadata.syncnets;
81
81
  }
82
82
 
83
83
  set syncnets(syncnets: BitArray) {
84
84
  this.onSetValue(ENRKey.syncnets, ssz.altair.SyncSubnets.serialize(syncnets));
85
- this._metadata.seqNumber++;
86
- this._metadata.syncnets = syncnets;
85
+ this.metadata.seqNumber++;
86
+ this.metadata.syncnets = syncnets;
87
87
  }
88
88
 
89
89
  get attnets(): BitArray {
90
- return this._metadata.attnets;
90
+ return this.metadata.attnets;
91
91
  }
92
92
 
93
93
  set attnets(attnets: BitArray) {
94
94
  this.onSetValue(ENRKey.attnets, ssz.phase0.AttestationSubnets.serialize(attnets));
95
- this._metadata.seqNumber++;
96
- this._metadata.attnets = attnets;
95
+ this.metadata.seqNumber++;
96
+ this.metadata.attnets = attnets;
97
97
  }
98
98
 
99
99
  get custodyGroupCount(): number {
100
- return this._metadata.custodyGroupCount;
100
+ return this.metadata.custodyGroupCount;
101
101
  }
102
102
 
103
103
  set custodyGroupCount(custodyGroupCount: number) {
104
- if (custodyGroupCount === this._metadata.custodyGroupCount) {
104
+ if (custodyGroupCount === this.metadata.custodyGroupCount) {
105
105
  return;
106
106
  }
107
107
  this.onSetValue(ENRKey.cgc, serializeCgc(custodyGroupCount));
108
108
  this.logger.debug("Updated cgc field in ENR", {custodyGroupCount});
109
- this._metadata.seqNumber++;
110
- this._metadata.custodyGroupCount = custodyGroupCount;
109
+ this.metadata.seqNumber++;
110
+ this.metadata.custodyGroupCount = custodyGroupCount;
111
111
  }
112
112
 
113
113
  /** Consumers that need the phase0.Metadata type can just ignore the .syncnets property */
114
114
  get json(): fulu.Metadata {
115
- return this._metadata;
115
+ return this.metadata;
116
116
  }
117
117
 
118
118
  /**