@lodestar/beacon-node 1.49.0-dev.07c3496aff → 1.49.0-dev.0a27fd270f
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/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +15 -6
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +4 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +28 -0
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +9 -7
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +1 -2
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +23 -7
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +5 -6
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +11 -2
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +46 -28
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +2 -2
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +15 -3
- package/lib/chain/archiveStore/utils/pruneHistory.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/blocks/index.js +2 -2
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/chain.d.ts +10 -0
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +85 -16
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +10 -0
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +4 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/attesterSlashingError.d.ts +4 -1
- package/lib/chain/errors/attesterSlashingError.d.ts.map +1 -1
- package/lib/chain/errors/attesterSlashingError.js +1 -0
- package/lib/chain/errors/attesterSlashingError.js.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.d.ts +23 -0
- package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -0
- package/lib/chain/errors/envelopeReconstructionError.js +13 -0
- package/lib/chain/errors/envelopeReconstructionError.js.map +1 -0
- package/lib/chain/errors/index.d.ts +1 -0
- package/lib/chain/errors/index.d.ts.map +1 -1
- package/lib/chain/errors/index.js +1 -0
- package/lib/chain/errors/index.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +4 -0
- package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
- package/lib/chain/errors/payloadAttestation.js +1 -0
- package/lib/chain/errors/payloadAttestation.js.map +1 -1
- package/lib/chain/errors/proposerSlashingError.d.ts +4 -1
- package/lib/chain/errors/proposerSlashingError.d.ts.map +1 -1
- package/lib/chain/errors/proposerSlashingError.js +1 -0
- package/lib/chain/errors/proposerSlashingError.js.map +1 -1
- package/lib/chain/interface.d.ts +6 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/options.d.ts +2 -4
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -1
- package/lib/chain/options.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/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +6 -6
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +3 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -2
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +8 -1
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +1 -2
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +2 -2
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/beacon.d.ts +1 -2
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +1 -3
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +0 -1
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +1 -1
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +1 -2
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.d.ts +5 -0
- package/lib/db/repositories/blockArchive.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.js +36 -1
- package/lib/db/repositories/blockArchive.js.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts +21 -9
- package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts.map +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js +32 -12
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
- package/lib/db/repositories/index.d.ts +1 -2
- package/lib/db/repositories/index.d.ts.map +1 -1
- package/lib/db/repositories/index.js +1 -2
- package/lib/db/repositories/index.js.map +1 -1
- package/lib/execution/engine/disabled.d.ts +1 -0
- package/lib/execution/engine/disabled.d.ts.map +1 -1
- package/lib/execution/engine/disabled.js +3 -0
- package/lib/execution/engine/disabled.js.map +1 -1
- package/lib/execution/engine/http.d.ts +2 -1
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +7 -1
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +3 -1
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/mock.d.ts +1 -0
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +14 -2
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +11 -0
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +10 -0
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +9 -0
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js +16 -0
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +35 -14
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +76 -24
- 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/network/gossip/encoding.d.ts +1 -2
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +5 -10
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +16 -1
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +9 -6
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +23 -8
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +16 -5
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +2 -0
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +88 -7
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +6 -2
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +62 -24
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +47 -34
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +23 -4
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/index.js +20 -4
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +19 -5
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/node/nodejs.d.ts +2 -7
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +2 -22
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/interface.d.ts +1 -2
- package/lib/sync/interface.d.ts.map +1 -1
- package/lib/sync/options.d.ts +0 -6
- package/lib/sync/options.d.ts.map +1 -1
- package/lib/sync/options.js +0 -2
- package/lib/sync/options.js.map +1 -1
- package/lib/sync/types.d.ts +39 -2
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +42 -2
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +20 -0
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +231 -26
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/execution.d.ts +42 -0
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +123 -2
- package/lib/util/execution.js.map +1 -1
- package/lib/util/headerEnvelope.d.ts +17 -0
- package/lib/util/headerEnvelope.d.ts.map +1 -0
- package/lib/util/headerEnvelope.js +43 -0
- package/lib/util/headerEnvelope.js.map +1 -0
- package/lib/util/shuffle.d.ts +0 -4
- package/lib/util/shuffle.d.ts.map +1 -1
- package/lib/util/shuffle.js +0 -8
- package/lib/util/shuffle.js.map +1 -1
- package/lib/util/sszBytes.d.ts +5 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +13 -1
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +16 -16
- package/src/api/impl/beacon/blocks/index.ts +18 -5
- package/src/api/impl/beacon/pool/index.ts +6 -2
- package/src/api/impl/beacon/state/index.ts +36 -0
- package/src/api/impl/validator/index.ts +9 -7
- package/src/chain/archiveStore/archiveStore.ts +24 -8
- package/src/chain/archiveStore/historicalState/metrics.ts +5 -7
- package/src/chain/archiveStore/utils/archiveBlocks.ts +54 -32
- package/src/chain/archiveStore/utils/pruneHistory.ts +18 -6
- package/src/chain/blocks/importExecutionPayload.ts +14 -0
- package/src/chain/blocks/index.ts +2 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -1
- package/src/chain/chain.ts +96 -20
- package/src/chain/emitter.ts +7 -2
- package/src/chain/errors/attesterSlashingError.ts +3 -1
- package/src/chain/errors/envelopeReconstructionError.ts +22 -0
- package/src/chain/errors/index.ts +1 -0
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/errors/proposerSlashingError.ts +3 -1
- package/src/chain/interface.ts +6 -1
- package/src/chain/options.ts +3 -5
- package/src/chain/produceBlock/computeNewStateRoot.ts +9 -2
- package/src/chain/produceBlock/produceBlockBody.ts +17 -6
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +3 -2
- package/src/chain/validation/attesterSlashing.ts +1 -2
- package/src/chain/validation/payloadAttestationMessage.ts +9 -0
- package/src/chain/validation/proposerSlashing.ts +1 -2
- package/src/chain/validatorMonitor.ts +12 -2
- package/src/db/beacon.ts +0 -5
- package/src/db/buckets.ts +2 -2
- package/src/db/interface.ts +0 -3
- package/src/db/repositories/blockArchive.ts +38 -1
- package/src/db/repositories/executionPayloadEnvelopeArchive.ts +43 -13
- package/src/db/repositories/index.ts +6 -2
- package/src/execution/engine/disabled.ts +4 -0
- package/src/execution/engine/http.ts +12 -0
- package/src/execution/engine/interface.ts +4 -1
- package/src/execution/engine/mock.ts +22 -2
- package/src/execution/engine/types.ts +25 -0
- package/src/metrics/metrics/beacon.ts +16 -0
- package/src/metrics/metrics/lodestar.ts +87 -28
- package/src/metrics/metrics.ts +7 -2
- package/src/network/gossip/encoding.ts +5 -8
- package/src/network/gossip/gossipsub.ts +19 -2
- package/src/network/gossip/topic.ts +25 -7
- package/src/network/processor/gossipHandlers.ts +18 -5
- package/src/network/processor/gossipValidatorFn.ts +111 -7
- package/src/network/processor/index.ts +81 -24
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +57 -39
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +27 -5
- package/src/network/reqresp/handlers/index.ts +49 -5
- package/src/network/reqresp/protocols.ts +31 -6
- package/src/node/nodejs.ts +0 -27
- package/src/sync/interface.ts +1 -2
- package/src/sync/options.ts +0 -8
- package/src/sync/types.ts +44 -5
- package/src/sync/unknownBlock.ts +254 -26
- package/src/util/execution.ts +175 -2
- package/src/util/headerEnvelope.ts +76 -0
- package/src/util/shuffle.ts +0 -9
- package/src/util/sszBytes.ts +18 -0
- package/lib/chain/archiveStore/utils/updateBackfillRange.d.ts +0 -20
- package/lib/chain/archiveStore/utils/updateBackfillRange.d.ts.map +0 -1
- package/lib/chain/archiveStore/utils/updateBackfillRange.js +0 -40
- package/lib/chain/archiveStore/utils/updateBackfillRange.js.map +0 -1
- package/lib/db/repositories/backfilledRanges.d.ts +0 -18
- package/lib/db/repositories/backfilledRanges.d.ts.map +0 -1
- package/lib/db/repositories/backfilledRanges.js +0 -26
- package/lib/db/repositories/backfilledRanges.js.map +0 -1
- package/lib/sync/backfill/backfill.d.ts +0 -169
- package/lib/sync/backfill/backfill.d.ts.map +0 -1
- package/lib/sync/backfill/backfill.js +0 -686
- package/lib/sync/backfill/backfill.js.map +0 -1
- package/lib/sync/backfill/errors.d.ts +0 -30
- package/lib/sync/backfill/errors.d.ts.map +0 -1
- package/lib/sync/backfill/errors.js +0 -15
- package/lib/sync/backfill/errors.js.map +0 -1
- package/lib/sync/backfill/index.d.ts +0 -2
- package/lib/sync/backfill/index.d.ts.map +0 -1
- package/lib/sync/backfill/index.js +0 -2
- package/lib/sync/backfill/index.js.map +0 -1
- package/lib/sync/backfill/verify.d.ts +0 -18
- package/lib/sync/backfill/verify.d.ts.map +0 -1
- package/lib/sync/backfill/verify.js +0 -36
- package/lib/sync/backfill/verify.js.map +0 -1
- package/src/chain/archiveStore/utils/updateBackfillRange.ts +0 -50
- package/src/db/repositories/backfilledRanges.ts +0 -29
- package/src/sync/backfill/backfill.ts +0 -916
- package/src/sync/backfill/errors.ts +0 -23
- package/src/sync/backfill/index.ts +0 -1
- package/src/sync/backfill/verify.ts +0 -58
package/src/sync/options.ts
CHANGED
|
@@ -16,12 +16,6 @@ export type SyncOptions = {
|
|
|
16
16
|
disableRangeSync?: boolean;
|
|
17
17
|
/** USE FOR TESTING ONLY. Disable unknown block sync completely */
|
|
18
18
|
disableBlockInputSync?: boolean;
|
|
19
|
-
/**
|
|
20
|
-
* The batch size of slots for backfill sync can attempt to sync/process before yielding
|
|
21
|
-
* to sync loop. This number can be increased or decreased to make a suitable resource
|
|
22
|
-
* allocation to backfill sync. The default of 0 would mean backfill sync will be skipped
|
|
23
|
-
*/
|
|
24
|
-
backfillBatchSize: number;
|
|
25
19
|
/** For testing only, MAX_PENDING_BLOCKS by default */
|
|
26
20
|
maxPendingBlocks?: number;
|
|
27
21
|
|
|
@@ -39,7 +33,5 @@ export type SyncOptions = {
|
|
|
39
33
|
export const defaultSyncOptions: SyncOptions = {
|
|
40
34
|
isSingleNode: false,
|
|
41
35
|
disableProcessAsChainSegment: false,
|
|
42
|
-
/** By default skip the backfill sync */
|
|
43
|
-
backfillBatchSize: 0,
|
|
44
36
|
slotImportTolerance: SLOTS_PER_EPOCH,
|
|
45
37
|
};
|
package/src/sync/types.ts
CHANGED
|
@@ -23,12 +23,54 @@ export enum PendingBlockType {
|
|
|
23
23
|
* Payload analog of UNKNOWN_BLOCK_ROOT: we have a beacon block root but not its execution payload envelope.
|
|
24
24
|
*/
|
|
25
25
|
UNKNOWN_PAYLOAD_BLOCK_ROOT = "unknown_payload_block_root",
|
|
26
|
+
/**
|
|
27
|
+
* Same as UNKNOWN_PAYLOAD_BLOCK_ROOT, but the trigger also carries the payload's slot
|
|
28
|
+
*/
|
|
29
|
+
UNKNOWN_PAYLOAD_BLOCK_ROOT_SLOT = "unknown_payload_block_root_slot",
|
|
30
|
+
/**
|
|
31
|
+
* Same as UNKNOWN_PAYLOAD_BLOCK_ROOT_SLOT for the current slot
|
|
32
|
+
*/
|
|
33
|
+
DEFERRED_PAYLOAD_BLOCK_ROOT = "deferred_payload_block_root",
|
|
26
34
|
/**
|
|
27
35
|
* Payload analog of INCOMPLETE_BLOCK_INPUT: we have a partial payload input that did not complete in time.
|
|
28
36
|
*/
|
|
29
37
|
INCOMPLETE_PAYLOAD_ENVELOPE = "incomplete_payload_envelope",
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
export enum DownloadResult {
|
|
41
|
+
/** payload fully downloaded, ready to process */
|
|
42
|
+
Resolved = "resolved",
|
|
43
|
+
/** payload fully downloaded, but it was already imported (via gossip) while we were downloading */
|
|
44
|
+
Late = "late",
|
|
45
|
+
/** envelope downloaded, held until the block lands in fork choice */
|
|
46
|
+
WaitingForBlock = "waiting_for_block",
|
|
47
|
+
/** fetch failed, the pending entry is retained for retry */
|
|
48
|
+
Failed = "failed",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export enum FetchResult {
|
|
52
|
+
SuccessResolved = "success_resolved",
|
|
53
|
+
SuccessMissingParent = "success_missing_parent",
|
|
54
|
+
SuccessLate = "success_late",
|
|
55
|
+
SuccessWaitingForBlock = "success_waiting_for_block",
|
|
56
|
+
FailureTriedAllPeers = "failure_tried_all_peers",
|
|
57
|
+
FailureMaxAttempts = "failure_max_attempts",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Outcome of a slot's deferred optimistic payload searches, recorded once when its poll loop ends.
|
|
62
|
+
*/
|
|
63
|
+
export enum DeferredPayloadResult {
|
|
64
|
+
/** gossip delivered every watched payload before PAYLOAD_DUE, no search was needed */
|
|
65
|
+
ImportedBeforeSearch = "imported_before_search",
|
|
66
|
+
/** at least one was missing at PAYLOAD_DUE, we searched, and all were imported afterwards */
|
|
67
|
+
ImportedAfterSearch = "imported_after_search",
|
|
68
|
+
/** something was still not in fork choice when the slot ended */
|
|
69
|
+
Unresolved = "unresolved",
|
|
70
|
+
/** the slot had no optimistic search and no payload import at all, eg a skipped slot */
|
|
71
|
+
ResolvedNoRoot = "resolved_no_root",
|
|
72
|
+
}
|
|
73
|
+
|
|
32
74
|
export enum PendingBlockInputStatus {
|
|
33
75
|
pending = "pending",
|
|
34
76
|
fetching = "fetching",
|
|
@@ -63,8 +105,6 @@ export enum DroppedItemReason {
|
|
|
63
105
|
invalidEnvelope = "invalid_envelope",
|
|
64
106
|
/** a payload had an invalid signature (INVALID_SIGNATURE) */
|
|
65
107
|
invalidSignature = "invalid_signature",
|
|
66
|
-
/** a block root was given up on after exhausting download attempts (data could not be fetched) */
|
|
67
|
-
unavailable = "unavailable",
|
|
68
108
|
/** an unexpected / unhandled error code led to removal */
|
|
69
109
|
unknown = "unknown",
|
|
70
110
|
}
|
|
@@ -110,9 +150,8 @@ export type PendingPayloadInput = {
|
|
|
110
150
|
export type PendingPayloadRootHex = {
|
|
111
151
|
status: PendingPayloadInputStatus.pending | PendingPayloadInputStatus.fetching;
|
|
112
152
|
rootHex: RootHex;
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
// payload/block slot. See BlockInputSync.resolvePayloadSlot.
|
|
153
|
+
// slot could be via fork choice or ChainEvent.unknownEnvelopeBlockRootSlot
|
|
154
|
+
// while ChainEvent.unknownEnvelopeBlockRoot does not provide one
|
|
116
155
|
slot?: Slot;
|
|
117
156
|
timeAddedSec: number;
|
|
118
157
|
timeSyncedSec?: number;
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas} from "@lodestar/params";
|
|
|
5
5
|
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
6
6
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
7
7
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
8
|
-
import {Logger, fromHex, prettyPrintIndices, pruneSetToMax, sleep, toRootHex} from "@lodestar/utils";
|
|
8
|
+
import {Logger, fromHex, isErrorAborted, prettyPrintIndices, pruneSetToMax, sleep, toRootHex} from "@lodestar/utils";
|
|
9
9
|
import {isBlockInputBlobs, isBlockInputColumns} from "../chain/blocks/blockInput/blockInput.js";
|
|
10
10
|
import {BlockInputSource, IBlockInput} from "../chain/blocks/blockInput/types.js";
|
|
11
11
|
import {PayloadError, PayloadErrorCode} from "../chain/blocks/importExecutionPayload.js";
|
|
@@ -18,6 +18,7 @@ import {Metrics} from "../metrics/index.js";
|
|
|
18
18
|
import {INetwork, NetworkEvent, NetworkEventData, prettyPrintPeerIdStr} from "../network/index.js";
|
|
19
19
|
import {PeerSyncMeta} from "../network/peers/peersData.js";
|
|
20
20
|
import {MAX_PEERS_PER_ROOT} from "../network/processor/constants.js";
|
|
21
|
+
import {ClockEvent} from "../util/clock.js";
|
|
21
22
|
import {PeerIdStr} from "../util/peerId.js";
|
|
22
23
|
import {shuffle} from "../util/shuffle.js";
|
|
23
24
|
import {sortBy} from "../util/sortBy.js";
|
|
@@ -26,7 +27,10 @@ import {MAX_CONCURRENT_REQUESTS} from "./constants.js";
|
|
|
26
27
|
import {SyncOptions} from "./options.js";
|
|
27
28
|
import {
|
|
28
29
|
BlockInputSyncCacheItem,
|
|
30
|
+
DeferredPayloadResult,
|
|
31
|
+
DownloadResult,
|
|
29
32
|
DroppedItemReason,
|
|
33
|
+
FetchResult,
|
|
30
34
|
PayloadSyncCacheItem,
|
|
31
35
|
PendingBlockInput,
|
|
32
36
|
PendingBlockInputStatus,
|
|
@@ -55,6 +59,11 @@ const MAX_PENDING_BLOCKS = 100;
|
|
|
55
59
|
*/
|
|
56
60
|
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
57
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Poll pending payloads of the current slot this often, between PAYLOAD_DUE and the end of the slot.
|
|
64
|
+
*/
|
|
65
|
+
export const PAYLOAD_POLL_INTERVAL_MS = 1_000;
|
|
66
|
+
|
|
58
67
|
type AdvancePendingBlockResult =
|
|
59
68
|
| "ready"
|
|
60
69
|
| "queued_block"
|
|
@@ -63,14 +72,6 @@ type AdvancePendingBlockResult =
|
|
|
63
72
|
| "blocked"
|
|
64
73
|
| "removed";
|
|
65
74
|
|
|
66
|
-
enum FetchResult {
|
|
67
|
-
SuccessResolved = "success_resolved",
|
|
68
|
-
SuccessMissingParent = "success_missing_parent",
|
|
69
|
-
SuccessLate = "success_late",
|
|
70
|
-
FailureTriedAllPeers = "failure_tried_all_peers",
|
|
71
|
-
FailureMaxAttempts = "failure_max_attempts",
|
|
72
|
-
}
|
|
73
|
-
|
|
74
75
|
class UnknownBlockRateLimitedError extends Error {
|
|
75
76
|
constructor(message: string) {
|
|
76
77
|
super(message);
|
|
@@ -122,6 +123,11 @@ export class BlockInputSync {
|
|
|
122
123
|
private subscribedToNetworkEvents = false;
|
|
123
124
|
private peerBalancer: UnknownBlockPeerBalancer;
|
|
124
125
|
private rateLimitBackoffTimeout: NodeJS.Timeout | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Search unknown payload starting at PAYLOAD_DUE_BPS.
|
|
128
|
+
*/
|
|
129
|
+
private readonly deferredPayloadRoots = new Map<RootHex, {slot: Slot; source: BlockInputSource; peer?: PeerIdStr}>();
|
|
130
|
+
private payloadPollAbortController: AbortController | undefined;
|
|
125
131
|
|
|
126
132
|
constructor(
|
|
127
133
|
private readonly config: ChainForkConfig,
|
|
@@ -158,6 +164,7 @@ export class BlockInputSync {
|
|
|
158
164
|
this.logger.verbose("BlockInputSync enabled.");
|
|
159
165
|
this.chain.emitter.on(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
|
|
160
166
|
this.chain.emitter.on(ChainEvent.unknownEnvelopeBlockRoot, this.onUnknownEnvelopeBlockRoot);
|
|
167
|
+
this.chain.emitter.on(ChainEvent.unknownEnvelopeBlockRootSlot, this.onUnknownEnvelopeBlockRootSlot);
|
|
161
168
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
162
169
|
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
163
170
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
@@ -166,6 +173,7 @@ export class BlockInputSync {
|
|
|
166
173
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
167
174
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
168
175
|
this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
176
|
+
this.chain.clock.on(ClockEvent.slot, this.startPayloadPollAtSlot);
|
|
169
177
|
|
|
170
178
|
// Seed the balancer with peers that connected before we subscribed
|
|
171
179
|
for (const peerId of this.network.getConnectedPeers()) {
|
|
@@ -177,14 +185,22 @@ export class BlockInputSync {
|
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
this.subscribedToNetworkEvents = true;
|
|
188
|
+
// we subscribe mid-slot, and a root can be deferred at any point in the slot, so start this
|
|
189
|
+
// slot's loop now instead of waiting for the next ClockEvent.slot
|
|
190
|
+
this.startPayloadPollAtSlot(this.chain.clock.currentSlot);
|
|
180
191
|
}
|
|
181
192
|
}
|
|
182
193
|
|
|
183
194
|
unsubscribeFromNetwork(): void {
|
|
184
195
|
this.logger.verbose("BlockInputSync disabled.");
|
|
185
196
|
this.clearRateLimitBackoffTimer();
|
|
197
|
+
this.chain.clock.off(ClockEvent.slot, this.startPayloadPollAtSlot);
|
|
198
|
+
this.payloadPollAbortController?.abort();
|
|
199
|
+
this.payloadPollAbortController = undefined;
|
|
200
|
+
this.deferredPayloadRoots.clear();
|
|
186
201
|
this.chain.emitter.off(ChainEvent.unknownBlockRoot, this.onUnknownBlockRoot);
|
|
187
202
|
this.chain.emitter.off(ChainEvent.unknownEnvelopeBlockRoot, this.onUnknownEnvelopeBlockRoot);
|
|
203
|
+
this.chain.emitter.off(ChainEvent.unknownEnvelopeBlockRootSlot, this.onUnknownEnvelopeBlockRootSlot);
|
|
188
204
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
189
205
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
190
206
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
@@ -211,8 +227,11 @@ export class BlockInputSync {
|
|
|
211
227
|
private onUnknownBlockRoot = (data: ChainEventData[ChainEvent.unknownBlockRoot]): void => {
|
|
212
228
|
try {
|
|
213
229
|
const isNewRoot = this.addByRootHex(data.rootHex, data.peer);
|
|
214
|
-
|
|
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) {
|
|
215
232
|
this.triggerUnknownBlockSearch();
|
|
233
|
+
}
|
|
234
|
+
if (isNewRoot) {
|
|
216
235
|
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_BLOCK_ROOT});
|
|
217
236
|
this.metrics?.blockInputSync.source.inc({source: data.source});
|
|
218
237
|
}
|
|
@@ -248,6 +267,33 @@ export class BlockInputSync {
|
|
|
248
267
|
}
|
|
249
268
|
};
|
|
250
269
|
|
|
270
|
+
private onUnknownEnvelopeBlockRootSlot = (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRootSlot]): void => {
|
|
271
|
+
try {
|
|
272
|
+
if (data.slot === this.chain.clock.currentSlot) {
|
|
273
|
+
// we receive the optimistic search, the poll loop searches it at PAYLOAD_DUE or on its next
|
|
274
|
+
// tick. If that loop already returned - every root it watched was imported - this root waits
|
|
275
|
+
// for the next slot, where startPayloadPollAtSlot moves it to pendingPayloads. That is not the normal
|
|
276
|
+
// scenario, and in a forking condition it only delays the search of the extra root
|
|
277
|
+
const deferred = this.deferredPayloadRoots.get(data.rootHex);
|
|
278
|
+
if (deferred === undefined) {
|
|
279
|
+
this.deferredPayloadRoots.set(data.rootHex, {slot: data.slot, source: data.source, peer: data.peer});
|
|
280
|
+
} else if (deferred.peer === undefined) {
|
|
281
|
+
deferred.peer = data.peer;
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const isNewRoot = this.addByPayloadRootHex(data.rootHex, data.peer, data.slot);
|
|
287
|
+
if (isNewRoot) {
|
|
288
|
+
this.triggerUnknownBlockSearch();
|
|
289
|
+
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT_SLOT});
|
|
290
|
+
this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
|
|
291
|
+
}
|
|
292
|
+
} catch (e) {
|
|
293
|
+
this.logger.debug("Error handling unknownEnvelopeBlockRootSlot event", {}, e as Error);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
251
297
|
private onIncompletePayloadEnvelope = (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]): void => {
|
|
252
298
|
try {
|
|
253
299
|
this.addByPayloadInput(data.payloadInput, data.peer);
|
|
@@ -350,12 +396,21 @@ export class BlockInputSync {
|
|
|
350
396
|
}
|
|
351
397
|
}
|
|
352
398
|
|
|
353
|
-
let
|
|
399
|
+
let belowFinalizedBlocks = 0;
|
|
400
|
+
let agedOutBlocks = 0;
|
|
354
401
|
for (const [rootHex, block] of this.pendingBlocks) {
|
|
355
402
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
356
|
-
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) {
|
|
357
412
|
this.pendingBlocks.delete(rootHex);
|
|
358
|
-
|
|
413
|
+
belowFinalizedBlocks++;
|
|
359
414
|
}
|
|
360
415
|
}
|
|
361
416
|
|
|
@@ -368,16 +423,20 @@ export class BlockInputSync {
|
|
|
368
423
|
if (agedOutPayloads > 0) {
|
|
369
424
|
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.agedOut}, agedOutPayloads);
|
|
370
425
|
}
|
|
371
|
-
if (
|
|
372
|
-
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);
|
|
373
431
|
}
|
|
374
432
|
|
|
375
|
-
this.logger.debug("BlockInputSync.pruneFinalized dropped
|
|
433
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped stale pending items", {
|
|
376
434
|
finalizedSlot,
|
|
377
435
|
finalizedRoot: checkpoint.rootHex,
|
|
378
436
|
belowFinalizedPayloads,
|
|
379
437
|
agedOutPayloads,
|
|
380
|
-
|
|
438
|
+
belowFinalizedBlocks,
|
|
439
|
+
agedOutBlocks,
|
|
381
440
|
unresolvedSlotPayloads,
|
|
382
441
|
});
|
|
383
442
|
};
|
|
@@ -791,6 +850,122 @@ export class BlockInputSync {
|
|
|
791
850
|
}
|
|
792
851
|
}
|
|
793
852
|
|
|
853
|
+
/**
|
|
854
|
+
* Start this slot's payload poll: end the previous slot's loop, move roots it never got to
|
|
855
|
+
* into pendingPayloads, then poll from PAYLOAD_DUE to the end of the slot.
|
|
856
|
+
*/
|
|
857
|
+
private startPayloadPollAtSlot = (slot: Slot): void => {
|
|
858
|
+
if (!isForkPostGloas(this.config.getForkName(slot))) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
// end the previous slot's loop, wherever it is: its sleep is bound to this same controller
|
|
862
|
+
this.payloadPollAbortController?.abort();
|
|
863
|
+
this.payloadPollAbortController = new AbortController();
|
|
864
|
+
// entries from an earlier slot is not polled
|
|
865
|
+
for (const [rootHex, {slot: deferredSlot, source, peer}] of this.deferredPayloadRoots) {
|
|
866
|
+
if (deferredSlot < slot) {
|
|
867
|
+
if (!this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
868
|
+
this.addDeferredPayloadRoot(rootHex, deferredSlot, source, peer);
|
|
869
|
+
}
|
|
870
|
+
this.deferredPayloadRoots.delete(rootHex);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
this.pollPayloadsAtSlot(slot, this.payloadPollAbortController.signal).catch((e) => {
|
|
874
|
+
if (!isErrorAborted(e)) {
|
|
875
|
+
this.logger.debug("Error polling payloads", {slot}, e as Error);
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Poll unknown payloads of the current slot.
|
|
882
|
+
*/
|
|
883
|
+
private async pollPayloadsAtSlot(slot: Slot, signal: AbortSignal): Promise<void> {
|
|
884
|
+
// fixed tick count so the loop cannot outlive its slot: for mainnet, it's 6 ticks
|
|
885
|
+
const tickCount = Math.floor(
|
|
886
|
+
(this.config.SLOT_DURATION_MS - this.config.getPayloadDueMs()) / PAYLOAD_POLL_INTERVAL_MS
|
|
887
|
+
);
|
|
888
|
+
// a root still missing on several ticks is searched on each of them, recorded once
|
|
889
|
+
const searchedRoots = new Set<RootHex>();
|
|
890
|
+
let polls = 0;
|
|
891
|
+
|
|
892
|
+
try {
|
|
893
|
+
await sleep(Math.max(0, this.config.getPayloadDueMs() - this.chain.clock.msFromSlot(slot)), signal);
|
|
894
|
+
|
|
895
|
+
for (polls = 0; polls < tickCount; polls++) {
|
|
896
|
+
let importedCount = 0;
|
|
897
|
+
for (const [rootHex, {slot: deferredSlot, source, peer}] of this.deferredPayloadRoots) {
|
|
898
|
+
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
899
|
+
importedCount++;
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
// copy to the regular pendingPayloads so that we can search
|
|
903
|
+
this.addDeferredPayloadRoot(rootHex, deferredSlot, source, peer);
|
|
904
|
+
searchedRoots.add(rootHex);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// an empty deferredPayloadRoots means there is no optimistic search comes before PAYLOAD_DUE
|
|
908
|
+
// we have to poll until the payload of current slot is imported or slot end
|
|
909
|
+
if (this.deferredPayloadRoots.size > 0 && importedCount === this.deferredPayloadRoots.size) {
|
|
910
|
+
if (searchedRoots.size > 0) {
|
|
911
|
+
// the backup search was needed
|
|
912
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({
|
|
913
|
+
result: DeferredPayloadResult.ImportedAfterSearch,
|
|
914
|
+
});
|
|
915
|
+
this.metrics?.blockInputSync.deferredPayloadPolls.observe(polls);
|
|
916
|
+
this.logger.verbose("Payload imported after unknown sync search", {
|
|
917
|
+
slot,
|
|
918
|
+
roots: Array.from(searchedRoots).join(","),
|
|
919
|
+
polls,
|
|
920
|
+
});
|
|
921
|
+
} else {
|
|
922
|
+
// this happens most of the time on devnet
|
|
923
|
+
// gossip delivered everything before PAYLOAD_DUE
|
|
924
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({
|
|
925
|
+
result: DeferredPayloadResult.ImportedBeforeSearch,
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
this.triggerUnknownBlockSearch();
|
|
932
|
+
if (polls < tickCount - 1) {
|
|
933
|
+
// no sleep after the last tick: the loop would otherwise end right at the slot boundary,
|
|
934
|
+
// where startPayloadPollAtSlot aborts it
|
|
935
|
+
await sleep(PAYLOAD_POLL_INTERVAL_MS, signal);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// reaching here means the early return never fired
|
|
940
|
+
if (this.deferredPayloadRoots.size > 0) {
|
|
941
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({result: DeferredPayloadResult.Unresolved});
|
|
942
|
+
this.logger.debug("Deferred payloads still missing at end of slot", {
|
|
943
|
+
slot,
|
|
944
|
+
roots: Array.from(this.deferredPayloadRoots.keys()).join(","),
|
|
945
|
+
});
|
|
946
|
+
} else {
|
|
947
|
+
// no optimistic search and no payload import ended the loop, eg a skipped slot
|
|
948
|
+
this.metrics?.blockInputSync.deferredPayloadResult.inc({result: DeferredPayloadResult.ResolvedNoRoot});
|
|
949
|
+
this.logger.debug("No deferred payloads at slot", {
|
|
950
|
+
slot,
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
} catch (e) {
|
|
954
|
+
if (!isErrorAborted(e)) {
|
|
955
|
+
throw e;
|
|
956
|
+
}
|
|
957
|
+
this.logger.verbose("Payload poll aborted", {slot, polls, msIntoSlot: this.chain.clock.msFromSlot(slot)});
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
private addDeferredPayloadRoot(rootHex: RootHex, slot: Slot, source: BlockInputSource, peer?: PeerIdStr): void {
|
|
962
|
+
const isNewRoot = this.addByPayloadRootHex(rootHex, peer, slot);
|
|
963
|
+
if (isNewRoot) {
|
|
964
|
+
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.DEFERRED_PAYLOAD_BLOCK_ROOT});
|
|
965
|
+
this.metrics?.blockInputSync.payloadSource.inc({source});
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
794
969
|
private async downloadBlock(block: BlockInputSyncCacheItem): Promise<void> {
|
|
795
970
|
if (block.status !== PendingBlockInputStatus.pending) {
|
|
796
971
|
return;
|
|
@@ -814,7 +989,12 @@ export class BlockInputSync {
|
|
|
814
989
|
if (!res.err) {
|
|
815
990
|
this.metrics?.blockInputSync.downloadedBlocksSuccess.inc();
|
|
816
991
|
const pending = res.result;
|
|
817
|
-
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);
|
|
818
998
|
const blockSlot = pending.blockInput.slot;
|
|
819
999
|
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
820
1000
|
const delaySec = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.chain.genesisTime);
|
|
@@ -876,8 +1056,11 @@ export class BlockInputSync {
|
|
|
876
1056
|
}
|
|
877
1057
|
|
|
878
1058
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
879
|
-
this.logger.debug("
|
|
880
|
-
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
|
+
}
|
|
881
1064
|
}
|
|
882
1065
|
}
|
|
883
1066
|
|
|
@@ -1125,16 +1308,26 @@ export class BlockInputSync {
|
|
|
1125
1308
|
return;
|
|
1126
1309
|
}
|
|
1127
1310
|
this.pendingPayloads.set(resultRootHex, pendingPayload);
|
|
1311
|
+
const result = isPendingPayloadEnvelope(pendingPayload)
|
|
1312
|
+
? DownloadResult.WaitingForBlock
|
|
1313
|
+
: this.chain.forkChoice.hasPayloadHexUnsafe(resultRootHex)
|
|
1314
|
+
? DownloadResult.Late
|
|
1315
|
+
: DownloadResult.Resolved;
|
|
1316
|
+
this.metrics?.blockInputSync.downloadedPayloadsResult.inc({result});
|
|
1128
1317
|
|
|
1129
1318
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
1130
1319
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
1131
1320
|
} else if (pendingPayload.status === PendingPayloadInputStatus.downloaded) {
|
|
1321
|
+
this.metrics?.blockInputSync.elapsedTimeTillPayloadReceived.observe(
|
|
1322
|
+
Date.now() / 1000 - computeTimeAtSlot(this.config, pendingPayload.payloadInput.slot, this.chain.genesisTime)
|
|
1323
|
+
);
|
|
1132
1324
|
await this.processPayload(pendingPayload);
|
|
1133
1325
|
}
|
|
1134
1326
|
return;
|
|
1135
1327
|
}
|
|
1136
1328
|
|
|
1137
|
-
this.
|
|
1329
|
+
this.metrics?.blockInputSync.downloadedPayloadsResult.inc({result: DownloadResult.Failed});
|
|
1330
|
+
this.logger.debug("Failed to download unknown payload root, retained for retry", logCtx, res.err);
|
|
1138
1331
|
if (!isPendingPayloadEnvelope(payload)) {
|
|
1139
1332
|
payload.status = PendingPayloadInputStatus.pending;
|
|
1140
1333
|
}
|
|
@@ -1248,6 +1441,11 @@ export class BlockInputSync {
|
|
|
1248
1441
|
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1249
1442
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
1250
1443
|
|
|
1444
|
+
const fetchStartSec = Date.now() / 1000;
|
|
1445
|
+
if (typeof slot === "number") {
|
|
1446
|
+
this.metrics?.blockInputSync.payloadFetchBegin.observe(this.chain.clock.secFromSlot(slot, fetchStartSec));
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1251
1449
|
let i = 0;
|
|
1252
1450
|
let deferredByRateLimit = false;
|
|
1253
1451
|
while (i++ < this.getMaxDownloadAttempts()) {
|
|
@@ -1271,6 +1469,11 @@ export class BlockInputSync {
|
|
|
1271
1469
|
pendingColumns.size > 0
|
|
1272
1470
|
? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1273
1471
|
: "no peer available to download pending payload";
|
|
1472
|
+
this.metrics?.blockInputSync.payloadFetchTime.observe(
|
|
1473
|
+
{result: FetchResult.FailureTriedAllPeers},
|
|
1474
|
+
Date.now() / 1000 - fetchStartSec
|
|
1475
|
+
);
|
|
1476
|
+
this.metrics?.blockInputSync.payloadFetchPeers.set({result: FetchResult.FailureTriedAllPeers}, i);
|
|
1274
1477
|
throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
|
|
1275
1478
|
}
|
|
1276
1479
|
|
|
@@ -1280,7 +1483,12 @@ export class BlockInputSync {
|
|
|
1280
1483
|
try {
|
|
1281
1484
|
if (!envelope) {
|
|
1282
1485
|
envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
|
|
1486
|
+
const slotWasUnknown = typeof slot !== "number";
|
|
1283
1487
|
slot = envelope.message.payload.slotNumber;
|
|
1488
|
+
if (slotWasUnknown) {
|
|
1489
|
+
// we were not able to observe the time into slot when starting the fetch, do it now
|
|
1490
|
+
this.metrics?.blockInputSync.payloadFetchBegin.observe(this.chain.clock.secFromSlot(slot, fetchStartSec));
|
|
1491
|
+
}
|
|
1284
1492
|
}
|
|
1285
1493
|
|
|
1286
1494
|
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
@@ -1289,6 +1497,11 @@ export class BlockInputSync {
|
|
|
1289
1497
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
1290
1498
|
// payloadInput may be seeded from the block body during download, so a non-null payloadInput does not
|
|
1291
1499
|
// imply the block is imported.
|
|
1500
|
+
this.metrics?.blockInputSync.payloadFetchTime.observe(
|
|
1501
|
+
{result: FetchResult.SuccessWaitingForBlock},
|
|
1502
|
+
Date.now() / 1000 - fetchStartSec
|
|
1503
|
+
);
|
|
1504
|
+
this.metrics?.blockInputSync.payloadFetchPeers.set({result: FetchResult.SuccessWaitingForBlock}, i);
|
|
1292
1505
|
return {
|
|
1293
1506
|
status: PendingPayloadInputStatus.waitingForBlock,
|
|
1294
1507
|
envelope,
|
|
@@ -1339,6 +1552,12 @@ export class BlockInputSync {
|
|
|
1339
1552
|
});
|
|
1340
1553
|
|
|
1341
1554
|
if (pendingPayload.status === PendingPayloadInputStatus.downloaded) {
|
|
1555
|
+
// late = the payload got imported (via gossip) while we were downloading, same as the block path
|
|
1556
|
+
const result = this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)
|
|
1557
|
+
? FetchResult.SuccessLate
|
|
1558
|
+
: FetchResult.SuccessResolved;
|
|
1559
|
+
this.metrics?.blockInputSync.payloadFetchTime.observe({result}, Date.now() / 1000 - fetchStartSec);
|
|
1560
|
+
this.metrics?.blockInputSync.payloadFetchPeers.set({result}, i);
|
|
1342
1561
|
return pendingPayload;
|
|
1343
1562
|
}
|
|
1344
1563
|
|
|
@@ -1373,12 +1592,17 @@ export class BlockInputSync {
|
|
|
1373
1592
|
}
|
|
1374
1593
|
}
|
|
1375
1594
|
|
|
1595
|
+
this.metrics?.blockInputSync.payloadFetchTime.observe(
|
|
1596
|
+
{result: FetchResult.FailureMaxAttempts},
|
|
1597
|
+
Date.now() / 1000 - fetchStartSec
|
|
1598
|
+
);
|
|
1599
|
+
this.metrics?.blockInputSync.payloadFetchPeers.set({result: FetchResult.FailureMaxAttempts}, i - 1);
|
|
1600
|
+
|
|
1376
1601
|
if (deferredByRateLimit && this.peerBalancer.getNextRateLimitRetryAt() !== null) {
|
|
1377
1602
|
throw new UnknownBlockRateLimitedError(
|
|
1378
1603
|
`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts: peers are rate-limited`
|
|
1379
1604
|
);
|
|
1380
1605
|
}
|
|
1381
|
-
|
|
1382
1606
|
throw Error(`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts.`);
|
|
1383
1607
|
}
|
|
1384
1608
|
|
|
@@ -1483,7 +1707,7 @@ export class BlockInputSync {
|
|
|
1483
1707
|
|
|
1484
1708
|
// no more peer with needed columns to try, throw error
|
|
1485
1709
|
const message = `Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`;
|
|
1486
|
-
this.metrics?.blockInputSync.
|
|
1710
|
+
this.metrics?.blockInputSync.fetchTime.observe(
|
|
1487
1711
|
{result: FetchResult.FailureTriedAllPeers},
|
|
1488
1712
|
Date.now() / 1000 - fetchStartSec
|
|
1489
1713
|
);
|
|
@@ -1563,7 +1787,11 @@ export class BlockInputSync {
|
|
|
1563
1787
|
this.peerBalancer.onRequestCompleted(peerId);
|
|
1564
1788
|
}
|
|
1565
1789
|
|
|
1566
|
-
|
|
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
|
+
}
|
|
1567
1795
|
|
|
1568
1796
|
if (cacheItem.status === PendingBlockInputStatus.downloaded) {
|
|
1569
1797
|
// download was successful, no need to go with another peer, return
|
|
@@ -1572,7 +1800,7 @@ export class BlockInputSync {
|
|
|
1572
1800
|
: this.chain.forkChoice.hasBlockHex(cacheItem.blockInput.parentRootHex)
|
|
1573
1801
|
? FetchResult.SuccessResolved
|
|
1574
1802
|
: FetchResult.SuccessMissingParent;
|
|
1575
|
-
this.metrics?.blockInputSync.
|
|
1803
|
+
this.metrics?.blockInputSync.fetchTime.observe({result}, Date.now() / 1000 - fetchStartSec);
|
|
1576
1804
|
this.metrics?.blockInputSync.fetchPeers.set({result}, i);
|
|
1577
1805
|
return cacheItem;
|
|
1578
1806
|
}
|
|
@@ -1606,7 +1834,7 @@ export class BlockInputSync {
|
|
|
1606
1834
|
}
|
|
1607
1835
|
}
|
|
1608
1836
|
|
|
1609
|
-
this.metrics?.blockInputSync.
|
|
1837
|
+
this.metrics?.blockInputSync.fetchTime.observe(
|
|
1610
1838
|
{result: FetchResult.FailureMaxAttempts},
|
|
1611
1839
|
Date.now() / 1000 - fetchStartSec
|
|
1612
1840
|
);
|