@lodestar/beacon-node 1.49.0-dev.52a50304a4 → 1.49.0-dev.60ba78ee68
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.
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +2 -7
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +13 -0
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +4 -2
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +7 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +15 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/metrics/metrics.d.ts +3 -1
- package/lib/metrics/metrics.d.ts.map +1 -1
- package/lib/metrics/metrics.js +2 -2
- package/lib/metrics/metrics.js.map +1 -1
- package/lib/sync/types.d.ts +0 -2
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +0 -2
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +41 -13
- package/lib/sync/unknownBlock.js.map +1 -1
- package/package.json +15 -16
- package/src/chain/archiveStore/historicalState/metrics.ts +3 -9
- package/src/chain/blocks/importExecutionPayload.ts +14 -0
- package/src/chain/produceBlock/computeNewStateRoot.ts +9 -2
- package/src/metrics/metrics/lodestar.ts +17 -0
- package/src/metrics/metrics.ts +7 -2
- package/src/sync/types.ts +0 -2
- package/src/sync/unknownBlock.ts +41 -13
|
@@ -12,6 +12,7 @@ import {isQueueErrorAborted} from "../../util/queue/index.js";
|
|
|
12
12
|
import {BeaconChain} from "../chain.js";
|
|
13
13
|
import {RegenCaller} from "../regen/interface.js";
|
|
14
14
|
import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
|
|
15
|
+
import {PayloadEnvelopeInputSource} from "./payloadEnvelopeInput/index.js";
|
|
15
16
|
import {ImportPayloadOpts} from "./types.js";
|
|
16
17
|
import {
|
|
17
18
|
verifyExecutionPayloadEnvelope,
|
|
@@ -172,6 +173,9 @@ export async function importExecutionPayload(
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
// 4a. Run EL and signature verification in parallel
|
|
176
|
+
const logCtx = {slot, root: blockRootHex, executionBlock: envelope.payload.blockNumber};
|
|
177
|
+
this.logger.debug("Call engine api newPayload", logCtx);
|
|
178
|
+
const verifyStartSec = Date.now() / 1000;
|
|
175
179
|
const [execResult, signatureValid] = await Promise.all([
|
|
176
180
|
this.executionEngine.notifyNewPayload(
|
|
177
181
|
fork,
|
|
@@ -191,6 +195,8 @@ export async function importExecutionPayload(
|
|
|
191
195
|
this.bls
|
|
192
196
|
),
|
|
193
197
|
]);
|
|
198
|
+
this.logger.debug("Receive engine api newPayload result", {...logCtx, status: execResult.status});
|
|
199
|
+
this.metrics?.engineNotifyNewPayloadResult.inc({result: execResult.status});
|
|
194
200
|
|
|
195
201
|
// 4b. Check signature verification result
|
|
196
202
|
if (!signatureValid) {
|
|
@@ -223,6 +229,14 @@ export async function importExecutionPayload(
|
|
|
223
229
|
});
|
|
224
230
|
}
|
|
225
231
|
|
|
232
|
+
const {source, seenTimestampSec} = payloadInput.getPayloadEnvelopeSource();
|
|
233
|
+
if (execResult.status === ExecutionPayloadStatus.VALID && source === PayloadEnvelopeInputSource.gossip) {
|
|
234
|
+
const nowSec = Date.now() / 1000;
|
|
235
|
+
this.metrics?.gossipExecutionPayloadEnvelope.executionPayload.recvToValidation.observe(nowSec - seenTimestampSec);
|
|
236
|
+
// same metric as pre-gloas, it tracks EL verification time regardless of fork
|
|
237
|
+
this.metrics?.gossipBlock.executionPayload.validationTime.observe(nowSec - verifyStartSec);
|
|
238
|
+
}
|
|
239
|
+
|
|
226
240
|
// 5. Persist payload envelope to hot DB. Wait for write-queue space here to apply backpressure
|
|
227
241
|
// on the import pipeline during sync, then perform the write asynchronously to avoid blocking.
|
|
228
242
|
await this.unfinalizedPayloadEnvelopeWrites.waitForSpace();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {EMPTY_SIGNATURE, IBeaconStateView} from "@lodestar/state-transition";
|
|
1
|
+
import {EMPTY_SIGNATURE, IBeaconStateView, StateHashTreeRootSource} from "@lodestar/state-transition";
|
|
2
2
|
import {BeaconBlock, BlindedBeaconBlock, Gwei, Root} from "@lodestar/types";
|
|
3
3
|
import {Metrics} from "../../metrics/index.js";
|
|
4
4
|
|
|
@@ -14,5 +14,12 @@ export function computeNewStateRoot(
|
|
|
14
14
|
): {newStateRoot: Root; proposerReward: Gwei; postState: IBeaconStateView} {
|
|
15
15
|
// Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type
|
|
16
16
|
const signedBlock = {message: block, signature: EMPTY_SIGNATURE};
|
|
17
|
-
|
|
17
|
+
const {newStateRoot, proposerReward, postState, hashTreeRootTime} = state.computeNewStateRoot(
|
|
18
|
+
{block: signedBlock},
|
|
19
|
+
{metrics}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
metrics?.stateHashTreeRootTime.observe({source: StateHashTreeRootSource.computeNewStateRoot}, hashTreeRootTime);
|
|
23
|
+
|
|
24
|
+
return {newStateRoot, proposerReward, postState};
|
|
18
25
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** biome-ignore-all lint/suspicious/noTemplateCurlyInString: The metric templates requires to have `${}` in a normal string */
|
|
2
2
|
import {NotReorgedReason} from "@lodestar/fork-choice";
|
|
3
|
+
import {StateHashTreeRootSource} from "@lodestar/state-transition";
|
|
3
4
|
import {ArchiveStoreTask} from "../../chain/archiveStore/archiveStore.js";
|
|
4
5
|
import {FrequencyStateArchiveStep} from "../../chain/archiveStore/strategies/frequencyStateArchiveStrategy.js";
|
|
5
6
|
import type {LateCanonicalBlockReason} from "../../chain/archiveStore/utils/archiveBlocks.js";
|
|
@@ -408,6 +409,15 @@ export function createLodestarMetrics(
|
|
|
408
409
|
labelNames: ["caller"],
|
|
409
410
|
}),
|
|
410
411
|
|
|
412
|
+
// this metrics stay here instead of @lodestar/state-transition in order to prepare
|
|
413
|
+
// for the native zig state-transition in the future
|
|
414
|
+
stateHashTreeRootTime: register.histogram<{source: StateHashTreeRootSource}>({
|
|
415
|
+
name: "lodestar_stfn_hash_tree_root_seconds",
|
|
416
|
+
help: "Time to compute the hash tree root of a post state in seconds",
|
|
417
|
+
buckets: [0.01, 0.05, 0.1, 0.2, 0.3, 0.5],
|
|
418
|
+
labelNames: ["source"],
|
|
419
|
+
}),
|
|
420
|
+
|
|
411
421
|
// BLS verifier thread pool and queue
|
|
412
422
|
|
|
413
423
|
bls: {
|
|
@@ -1001,6 +1011,13 @@ export function createLodestarMetrics(
|
|
|
1001
1011
|
help: "Count of errors, by error type, while processing execution payload envelopes",
|
|
1002
1012
|
labelNames: ["error"],
|
|
1003
1013
|
}),
|
|
1014
|
+
executionPayload: {
|
|
1015
|
+
recvToValidation: register.histogram({
|
|
1016
|
+
name: "lodestar_gossip_execution_payload_envelope_received_to_execution_payload_verification_seconds",
|
|
1017
|
+
help: "Time elapsed between execution payload envelope received and execution payload verification",
|
|
1018
|
+
buckets: [0.05, 0.1, 0.25, 0.5, 1, 2, 3, 4, 6, 8],
|
|
1019
|
+
}),
|
|
1020
|
+
},
|
|
1004
1021
|
},
|
|
1005
1022
|
gossipExecutionPayloadBid: {
|
|
1006
1023
|
elapsedTimeTillReceived: register.histogram<{source: OpSource}>({
|
package/src/metrics/metrics.ts
CHANGED
|
@@ -12,7 +12,12 @@ export type Metrics = BeaconMetrics &
|
|
|
12
12
|
BeaconStateTransitionMetrics &
|
|
13
13
|
LodestarMetrics & {register: RegistryMetricCreator; close: () => void};
|
|
14
14
|
|
|
15
|
-
export function createMetrics(
|
|
15
|
+
export function createMetrics(
|
|
16
|
+
opts: MetricsOptions,
|
|
17
|
+
genesisTime: number,
|
|
18
|
+
externalRegistries: Registry[] = [],
|
|
19
|
+
{collectNodeMetrics = true}: {collectNodeMetrics?: boolean} = {}
|
|
20
|
+
): Metrics {
|
|
16
21
|
const register = new RegistryMetricCreator();
|
|
17
22
|
const beacon = createBeaconMetrics(register);
|
|
18
23
|
const forkChoice = getForkChoiceMetrics(register);
|
|
@@ -24,7 +29,7 @@ export function createMetrics(opts: MetricsOptions, genesisTime: number, externa
|
|
|
24
29
|
};
|
|
25
30
|
process.on("unhandledRejection", onUnhandledRejection);
|
|
26
31
|
|
|
27
|
-
const nodeJsMetricsClose = collectNodeJSMetrics(register);
|
|
32
|
+
const nodeJsMetricsClose = collectNodeMetrics ? collectNodeJSMetrics(register) : (): void => {};
|
|
28
33
|
const close = (): void => {
|
|
29
34
|
process.removeListener("unhandledRejection", onUnhandledRejection);
|
|
30
35
|
nodeJsMetricsClose();
|
package/src/sync/types.ts
CHANGED
|
@@ -105,8 +105,6 @@ export enum DroppedItemReason {
|
|
|
105
105
|
invalidEnvelope = "invalid_envelope",
|
|
106
106
|
/** a payload had an invalid signature (INVALID_SIGNATURE) */
|
|
107
107
|
invalidSignature = "invalid_signature",
|
|
108
|
-
/** a block root was given up on after exhausting download attempts (data could not be fetched) */
|
|
109
|
-
unavailable = "unavailable",
|
|
110
108
|
/** an unexpected / unhandled error code led to removal */
|
|
111
109
|
unknown = "unknown",
|
|
112
110
|
}
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -227,8 +227,11 @@ export class BlockInputSync {
|
|
|
227
227
|
private onUnknownBlockRoot = (data: ChainEventData[ChainEvent.unknownBlockRoot]): void => {
|
|
228
228
|
try {
|
|
229
229
|
const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
|
|
230
|
-
|
|
230
|
+
// A retained entry (an earlier download failed, #10018) is not new but still needs a retry pass.
|
|
231
|
+
if (this.pendingBlocks.get(data.rootHex)?.status === PendingBlockInputStatus.pending) {
|
|
231
232
|
this.triggerUnknownBlockSearch();
|
|
233
|
+
}
|
|
234
|
+
if (isNewRoot) {
|
|
232
235
|
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_BLOCK_ROOT});
|
|
233
236
|
this.metrics?.blockInputSync.source.inc({source: data.source});
|
|
234
237
|
}
|
|
@@ -393,12 +396,21 @@ export class BlockInputSync {
|
|
|
393
396
|
}
|
|
394
397
|
}
|
|
395
398
|
|
|
396
|
-
let
|
|
399
|
+
let belowFinalizedBlocks = 0;
|
|
400
|
+
let agedOutBlocks = 0;
|
|
397
401
|
for (const [rootHex, block] of this.pendingBlocks) {
|
|
398
402
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
399
|
-
if (typeof slot
|
|
403
|
+
if (typeof slot !== "number") {
|
|
404
|
+
// we may not be able to download a root, hence there is no slot for it
|
|
405
|
+
if (nowSec - block.timeAddedSec > maxSecInCache) {
|
|
406
|
+
this.pendingBlocks.delete(rootHex);
|
|
407
|
+
agedOutBlocks++;
|
|
408
|
+
}
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (slot < finalizedSlot) {
|
|
400
412
|
this.pendingBlocks.delete(rootHex);
|
|
401
|
-
|
|
413
|
+
belowFinalizedBlocks++;
|
|
402
414
|
}
|
|
403
415
|
}
|
|
404
416
|
|
|
@@ -411,16 +423,20 @@ export class BlockInputSync {
|
|
|
411
423
|
if (agedOutPayloads > 0) {
|
|
412
424
|
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.agedOut}, agedOutPayloads);
|
|
413
425
|
}
|
|
414
|
-
if (
|
|
415
|
-
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.belowFinalized},
|
|
426
|
+
if (belowFinalizedBlocks > 0) {
|
|
427
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.belowFinalized}, belowFinalizedBlocks);
|
|
428
|
+
}
|
|
429
|
+
if (agedOutBlocks > 0) {
|
|
430
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.agedOut}, agedOutBlocks);
|
|
416
431
|
}
|
|
417
432
|
|
|
418
|
-
this.logger.debug("BlockInputSync.pruneFinalized dropped
|
|
433
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped stale pending items", {
|
|
419
434
|
finalizedSlot,
|
|
420
435
|
finalizedRoot: checkpoint.rootHex,
|
|
421
436
|
belowFinalizedPayloads,
|
|
422
437
|
agedOutPayloads,
|
|
423
|
-
|
|
438
|
+
belowFinalizedBlocks,
|
|
439
|
+
agedOutBlocks,
|
|
424
440
|
unresolvedSlotPayloads,
|
|
425
441
|
});
|
|
426
442
|
};
|
|
@@ -973,7 +989,12 @@ export class BlockInputSync {
|
|
|
973
989
|
if (!res.err) {
|
|
974
990
|
this.metrics?.blockInputSync.downloadedBlocksSuccess.inc();
|
|
975
991
|
const pending = res.result;
|
|
976
|
-
this
|
|
992
|
+
// pruning may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
|
|
993
|
+
if (!this.pendingBlocks.has(rootHex)) {
|
|
994
|
+
this.logger.verbose("Dropping downloaded block, entry pruned during fetch", logCtx);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
this.pendingBlocks.set(rootHex, pending);
|
|
977
998
|
const blockSlot = pending.blockInput.slot;
|
|
978
999
|
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
979
1000
|
const delaySec = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.chain.genesisTime);
|
|
@@ -1035,8 +1056,11 @@ export class BlockInputSync {
|
|
|
1035
1056
|
}
|
|
1036
1057
|
|
|
1037
1058
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
1038
|
-
this.logger.debug("
|
|
1039
|
-
this.
|
|
1059
|
+
this.logger.debug("Failed to download unknown block root, retained for retry", logCtx, res.err);
|
|
1060
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
1061
|
+
if (pendingBlock && pendingBlock.status === PendingBlockInputStatus.fetching) {
|
|
1062
|
+
pendingBlock.status = PendingBlockInputStatus.pending;
|
|
1063
|
+
}
|
|
1040
1064
|
}
|
|
1041
1065
|
}
|
|
1042
1066
|
|
|
@@ -1303,7 +1327,7 @@ export class BlockInputSync {
|
|
|
1303
1327
|
}
|
|
1304
1328
|
|
|
1305
1329
|
this.metrics?.blockInputSync.downloadedPayloadsResult.inc({result: DownloadResult.Failed});
|
|
1306
|
-
this.logger.debug("
|
|
1330
|
+
this.logger.debug("Failed to download unknown payload root, retained for retry", logCtx, res.err);
|
|
1307
1331
|
if (!isPendingPayloadEnvelope(payload)) {
|
|
1308
1332
|
payload.status = PendingPayloadInputStatus.pending;
|
|
1309
1333
|
}
|
|
@@ -1763,7 +1787,11 @@ export class BlockInputSync {
|
|
|
1763
1787
|
this.peerBalancer.onRequestCompleted(peerId);
|
|
1764
1788
|
}
|
|
1765
1789
|
|
|
1766
|
-
|
|
1790
|
+
const cacheItemRootHex = getBlockInputSyncCacheItemRootHex(cacheItem);
|
|
1791
|
+
// pruning may have dropped it mid-fetch
|
|
1792
|
+
if (this.pendingBlocks.has(cacheItemRootHex)) {
|
|
1793
|
+
this.pendingBlocks.set(cacheItemRootHex, cacheItem);
|
|
1794
|
+
}
|
|
1767
1795
|
|
|
1768
1796
|
if (cacheItem.status === PendingBlockInputStatus.downloaded) {
|
|
1769
1797
|
// download was successful, no need to go with another peer, return
|