@lodestar/beacon-node 1.47.0-dev.90204c280d → 1.47.0-dev.9087110398

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 (41) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +3 -6
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +4 -0
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/validator/index.js +1 -1
  8. package/lib/api/impl/validator/index.js.map +1 -1
  9. package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
  10. package/lib/chain/archiveStore/historicalState/metrics.js +21 -0
  11. package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
  12. package/lib/chain/builderCircuitBreaker.d.ts +6 -6
  13. package/lib/chain/builderCircuitBreaker.d.ts.map +1 -1
  14. package/lib/chain/builderCircuitBreaker.js +19 -17
  15. package/lib/chain/builderCircuitBreaker.js.map +1 -1
  16. package/lib/chain/options.d.ts +1 -1
  17. package/lib/chain/options.d.ts.map +1 -1
  18. package/lib/chain/prepareNextSlot.js +1 -1
  19. package/lib/chain/prepareNextSlot.js.map +1 -1
  20. package/lib/metrics/metrics/beacon.d.ts +3 -3
  21. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  22. package/lib/metrics/metrics/beacon.js +9 -9
  23. package/lib/metrics/metrics/beacon.js.map +1 -1
  24. package/lib/metrics/metrics/lodestar.d.ts +10 -0
  25. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  26. package/lib/metrics/metrics/lodestar.js +16 -0
  27. package/lib/metrics/metrics/lodestar.js.map +1 -1
  28. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  29. package/lib/network/processor/gossipHandlers.js +7 -2
  30. package/lib/network/processor/gossipHandlers.js.map +1 -1
  31. package/package.json +14 -14
  32. package/src/api/impl/beacon/blocks/index.ts +4 -6
  33. package/src/api/impl/beacon/pool/index.ts +5 -0
  34. package/src/api/impl/validator/index.ts +1 -1
  35. package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
  36. package/src/chain/builderCircuitBreaker.ts +22 -19
  37. package/src/chain/options.ts +1 -1
  38. package/src/chain/prepareNextSlot.ts +1 -1
  39. package/src/metrics/metrics/beacon.ts +9 -9
  40. package/src/metrics/metrics/lodestar.ts +16 -0
  41. package/src/network/processor/gossipHandlers.ts +9 -0
@@ -1,3 +1,4 @@
1
+ import {ForkName} from "@lodestar/params";
1
2
  import {
2
3
  BeaconStateTransitionMetrics,
3
4
  EpochTransitionStep,
@@ -45,6 +46,27 @@ export function createHistoricalStateTransitionMetrics(
45
46
  labelNames: ["step"],
46
47
  buckets: [0.01, 0.05, 0.1, 0.2, 0.5, 0.75, 1],
47
48
  }),
49
+ forkUpgradeTime: metricsRegister.histogram<{fork: ForkName}>({
50
+ name: "lodestar_historical_state_stfn_fork_upgrade_seconds",
51
+ help: "Time to upgrade the state at a fork boundary in seconds",
52
+ labelNames: ["fork"],
53
+ buckets: [0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 30],
54
+ }),
55
+ onboardBuildersTime: metricsRegister.histogram({
56
+ name: "lodestar_historical_state_stfn_gloas_onboard_builders_seconds",
57
+ help: "Time spent in onboardBuildersFromPendingDeposits at the gloas fork transition",
58
+ buckets: [0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 30],
59
+ }),
60
+ onboardBuildersDeposits: metricsRegister.gauge<{outcome: "onboarded" | "topup" | "kept" | "dropped"}>({
61
+ name: "lodestar_historical_state_stfn_gloas_onboard_builders_deposits",
62
+ help: "Pending deposits handled at the gloas fork transition, by outcome",
63
+ labelNames: ["outcome"],
64
+ }),
65
+ onboardBuildersSignatureChecks: metricsRegister.gauge<{source: "cache" | "verified"}>({
66
+ name: "lodestar_historical_state_stfn_gloas_onboard_builders_signature_checks",
67
+ help: "Builder deposit signature checks at the gloas fork transition, by whether the pre-verify cache served them",
68
+ labelNames: ["source"],
69
+ }),
48
70
  processBlockTime: metricsRegister.histogram({
49
71
  name: "lodestar_historical_state_stfn_process_block_seconds",
50
72
  help: "Time to process a single block in seconds",
@@ -1,4 +1,4 @@
1
- import {IForkChoice} from "@lodestar/fork-choice";
1
+ import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
2
2
  import {Slot} from "@lodestar/types";
3
3
  import {Logger} from "@lodestar/utils";
4
4
  import {getFaultInspectionParams} from "../execution/builder/http.js";
@@ -21,9 +21,9 @@ const MIN_BLOCKS_TO_DEACTIVATE = 4;
21
21
  /**
22
22
  * Post-gloas circuit breaker for builder bids. The beacon block is produced by the proposer
23
23
  * regardless of bid source, so missed blocks are not a useful builder health signal. Instead
24
- * count blocks whose payload was never revealed. Activate when the non-reveal rate exceeds the
25
- * fault budget, and resume selecting builder bids only when the observed blocks are within budget
26
- * and meet the minimum recovery sample size.
24
+ * count canonical blocks selected as EMPTY. Activate when the proportion of EMPTY blocks exceeds
25
+ * the fault budget, and resume selecting builder bids only when the observed blocks are within
26
+ * budget and meet the minimum recovery sample size.
27
27
  */
28
28
  export class BuilderCircuitBreaker {
29
29
  readonly faultInspectionWindow: number;
@@ -39,46 +39,49 @@ export class BuilderCircuitBreaker {
39
39
  const {faultInspectionWindow, allowedFaults} = getFaultInspectionParams(opts);
40
40
  this.faultInspectionWindow = faultInspectionWindow;
41
41
  this.allowedFaults = allowedFaults;
42
+ this.modules.logger.info("Builder circuit breaker initialized", {faultInspectionWindow, allowedFaults});
42
43
  }
43
44
 
44
45
  /** Whether builder bids must be ignored for a block produced at clockSlot */
45
- isActive(clockSlot: Slot): boolean {
46
- this.update(clockSlot);
46
+ isActive(clockSlot: Slot, head: ProtoBlock): boolean {
47
+ this.update(clockSlot, head);
47
48
  return this.active;
48
49
  }
49
50
 
50
- update(clockSlot: Slot): void {
51
+ update(clockSlot: Slot, head: ProtoBlock): void {
51
52
  if (clockSlot <= this.lastUpdatedSlot) {
52
53
  return;
53
54
  }
54
55
  this.lastUpdatedSlot = clockSlot;
55
56
 
56
- // Exclude clockSlot itself, its payload reveal may still be in flight
57
- const {blocksPresent, payloadsRevealed} = this.modules.forkChoice.getPayloadRevealCounts(
57
+ // Exclude clockSlot itself, its payload status may still be unresolved
58
+ const {full, empty} = this.modules.forkChoice.getCanonicalPayloadCounts(
58
59
  Math.max(clockSlot - this.faultInspectionWindow, 0),
59
- clockSlot - 1
60
+ clockSlot - 1,
61
+ head
60
62
  );
61
- const faults = blocksPresent - payloadsRevealed;
63
+ const canonicalBlocks = full + empty;
62
64
 
63
65
  const wasActive = this.active;
64
- // Scale the fault budget by blocks present so sparse windows still trigger on high non-reveal rates
65
- const exceedsFaultBudget = faults * this.faultInspectionWindow > this.allowedFaults * blocksPresent;
66
+ // Scale the fault budget by canonical blocks so sparse windows still trigger on high EMPTY rates
67
+ const exceedsFaultBudget = empty * this.faultInspectionWindow > this.allowedFaults * canonicalBlocks;
66
68
  if (exceedsFaultBudget) {
67
69
  this.active = true;
68
- } else if (blocksPresent >= MIN_BLOCKS_TO_DEACTIVATE) {
70
+ } else if (canonicalBlocks >= MIN_BLOCKS_TO_DEACTIVATE) {
69
71
  // Require a minimum sample within the fault budget before accepting builder bids again
70
72
  this.active = false;
71
73
  }
72
74
 
73
75
  this.modules.metrics?.builderCircuitBreaker.active.set(this.active ? 1 : 0);
74
- this.modules.metrics?.builderCircuitBreaker.faults.set(faults);
75
- this.modules.metrics?.builderCircuitBreaker.blocksPresent.set(blocksPresent);
76
- this.modules.metrics?.builderCircuitBreaker.payloadsRevealed.set(payloadsRevealed);
76
+ this.modules.metrics?.builderCircuitBreaker.canonicalBlocks.set(canonicalBlocks);
77
+ this.modules.metrics?.builderCircuitBreaker.fullBlocks.set(full);
78
+ this.modules.metrics?.builderCircuitBreaker.emptyBlocks.set(empty);
77
79
 
78
80
  const logCtx = {
79
81
  clockSlot,
80
- blocksPresent,
81
- faults,
82
+ canonicalBlocks,
83
+ full,
84
+ empty,
82
85
  faultInspectionWindow: this.faultInspectionWindow,
83
86
  allowedFaults: this.allowedFaults,
84
87
  };
@@ -53,7 +53,7 @@ export type IChainOptions = BlockProcessOpts &
53
53
  nativeStateView?: boolean;
54
54
  /** Builder circuit breaker fault inspection window in slots */
55
55
  faultInspectionWindow?: number;
56
- /** Unrevealed payloads allowed per `faultInspectionWindow` observed blocks */
56
+ /** Canonical EMPTY blocks allowed per `faultInspectionWindow` observed blocks */
57
57
  allowedFaults?: number;
58
58
  };
59
59
 
@@ -169,7 +169,7 @@ export class PrepareNextSlotScheduler {
169
169
  }
170
170
 
171
171
  if (isForkPostGloas(fork)) {
172
- this.chain.builderCircuitBreaker.update(clockSlot);
172
+ this.chain.builderCircuitBreaker.update(clockSlot, updatedHead);
173
173
  } else {
174
174
  // Update the builder status, if enabled shoot an api call to check status
175
175
  this.chain.updateBuilderStatus(clockSlot);
@@ -143,17 +143,17 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
143
143
  name: "beacon_builder_circuit_breaker_active",
144
144
  help: "Whether the builder circuit breaker is active (1) causing builder bids to be ignored",
145
145
  }),
146
- faults: register.gauge({
147
- name: "beacon_builder_circuit_breaker_faults",
148
- help: "Count of blocks with unrevealed payloads in the fault inspection window",
146
+ canonicalBlocks: register.gauge({
147
+ name: "beacon_builder_circuit_breaker_canonical_blocks",
148
+ help: "Count of canonical FULL and EMPTY blocks in the fault inspection window",
149
149
  }),
150
- blocksPresent: register.gauge({
151
- name: "beacon_builder_circuit_breaker_blocks_present",
152
- help: "Count of blocks present in the fault inspection window",
150
+ fullBlocks: register.gauge({
151
+ name: "beacon_builder_circuit_breaker_full_blocks",
152
+ help: "Count of canonical FULL blocks in the fault inspection window",
153
153
  }),
154
- payloadsRevealed: register.gauge({
155
- name: "beacon_builder_circuit_breaker_payloads_revealed",
156
- help: "Count of blocks with revealed payloads in the fault inspection window",
154
+ emptyBlocks: register.gauge({
155
+ name: "beacon_builder_circuit_breaker_empty_blocks",
156
+ help: "Count of canonical EMPTY blocks in the fault inspection window",
157
157
  }),
158
158
  },
159
159
 
@@ -908,6 +908,22 @@ export function createLodestarMetrics(
908
908
  labelNames: ["error"],
909
909
  }),
910
910
  },
911
+ gossipExecutionPayloadBid: {
912
+ elapsedTimeTillReceived: register.histogram<{source: OpSource}>({
913
+ name: "lodestar_gossip_execution_payload_bid_elapsed_time_till_received_seconds",
914
+ help: "Time elapsed between the bid's slot start and the time the execution payload bid was received (negative = received before its slot, broadcast in the previous slot; positive = received during its own slot, late)",
915
+ labelNames: ["source"],
916
+ buckets: [-6, -3, -1, 0, 3],
917
+ }),
918
+ },
919
+ gossipPayloadAttestationMessage: {
920
+ elapsedTimeTillReceived: register.histogram<{source: OpSource}>({
921
+ name: "lodestar_gossip_payload_attestation_message_elapsed_time_till_received_seconds",
922
+ help: "Time elapsed between slot time and the time the payload attestation message was received",
923
+ labelNames: ["source"],
924
+ buckets: [3, 6, 9, 12],
925
+ }),
926
+ },
911
927
  // recovery in the case of specific blob rows required
912
928
  recoverBlobSidecars: {
913
929
  blobsReconstructed: register.counter({
@@ -1255,11 +1255,15 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1255
1255
  [GossipType.payload_attestation_message]: async ({
1256
1256
  gossipData,
1257
1257
  topic,
1258
+ seenTimestampSec,
1258
1259
  }: GossipHandlerParamGeneric<GossipType.payload_attestation_message>) => {
1259
1260
  const {serializedData} = gossipData;
1260
1261
  const payloadAttestationMessage = sszDeserialize(topic, serializedData);
1261
1262
  const validationResult = await validateGossipPayloadAttestationMessage(chain, payloadAttestationMessage);
1262
1263
 
1264
+ const delaySec = chain.clock.secFromSlot(payloadAttestationMessage.data.slot, seenTimestampSec);
1265
+ metrics?.gossipPayloadAttestationMessage.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
1266
+
1263
1267
  try {
1264
1268
  const insertOutcome = chain.payloadAttestationPool.add(
1265
1269
  payloadAttestationMessage,
@@ -1286,11 +1290,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
1286
1290
  [GossipType.execution_payload_bid]: async ({
1287
1291
  gossipData,
1288
1292
  topic,
1293
+ seenTimestampSec,
1289
1294
  }: GossipHandlerParamGeneric<GossipType.execution_payload_bid>) => {
1290
1295
  const {serializedData} = gossipData;
1291
1296
  const executionPayloadBid = sszDeserialize(topic, serializedData);
1292
1297
  const {proposerIndex} = await validateGossipExecutionPayloadBid(chain, executionPayloadBid);
1293
1298
 
1299
+ // this could be negative, because it's most likely the bid of next slot comes at this clock slot
1300
+ const elapsedSec = chain.clock.secFromSlot(executionPayloadBid.message.slot, seenTimestampSec);
1301
+ metrics?.gossipExecutionPayloadBid.elapsedTimeTillReceived.observe({source: OpSource.gossip}, elapsedSec);
1302
+
1294
1303
  // Handle valid payload bid by storing in a bid pool
1295
1304
  try {
1296
1305
  const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);