@lodestar/beacon-node 1.47.0-dev.2a04194b90 → 1.47.0-dev.2fba076c28
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 +6 -8
- 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 +25 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/events/index.d.ts +1 -1
- package/lib/api/impl/events/index.d.ts.map +1 -1
- package/lib/api/impl/events/index.js +12 -3
- package/lib/api/impl/events/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.js +1 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +7 -0
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +1 -1
- 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 +21 -0
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +2 -7
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +3 -2
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +11 -9
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +14 -6
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +22 -12
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/bls/index.d.ts +1 -1
- package/lib/chain/bls/index.d.ts.map +1 -1
- package/lib/chain/bls/interface.d.ts +5 -5
- package/lib/chain/bls/interface.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts +5 -11
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +116 -101
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/bls/multithread/jobItem.d.ts +5 -19
- package/lib/chain/bls/multithread/jobItem.d.ts.map +1 -1
- package/lib/chain/bls/multithread/jobItem.js +10 -65
- package/lib/chain/bls/multithread/jobItem.js.map +1 -1
- package/lib/chain/bls/multithread/types.d.ts +29 -8
- package/lib/chain/bls/multithread/types.d.ts.map +1 -1
- package/lib/chain/bls/multithread/types.js +12 -0
- package/lib/chain/bls/multithread/types.js.map +1 -1
- package/lib/chain/bls/multithread/utils.d.ts +4 -0
- package/lib/chain/bls/multithread/utils.d.ts.map +1 -1
- package/lib/chain/bls/multithread/utils.js +10 -0
- package/lib/chain/bls/multithread/utils.js.map +1 -1
- package/lib/chain/bls/multithread/worker.js +49 -23
- package/lib/chain/bls/multithread/worker.js.map +1 -1
- package/lib/chain/bls/singleThread.d.ts +4 -10
- package/lib/chain/bls/singleThread.d.ts.map +1 -1
- package/lib/chain/bls/singleThread.js +8 -45
- package/lib/chain/bls/singleThread.js.map +1 -1
- package/lib/chain/bls/utils.d.ts +4 -4
- package/lib/chain/bls/utils.d.ts.map +1 -1
- package/lib/chain/bls/utils.js +18 -19
- package/lib/chain/bls/utils.js.map +1 -1
- package/lib/chain/builderCircuitBreaker.d.ts +6 -6
- package/lib/chain/builderCircuitBreaker.d.ts.map +1 -1
- package/lib/chain/builderCircuitBreaker.js +19 -17
- package/lib/chain/builderCircuitBreaker.js.map +1 -1
- package/lib/chain/chain.d.ts +4 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +16 -6
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +1 -0
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +6 -1
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +6 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +1 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +4 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +4 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/attestationPool.d.ts +1 -1
- package/lib/chain/opPools/attestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/attestationPool.js +1 -1
- package/lib/chain/opPools/attestationPool.js.map +1 -1
- package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
- package/lib/chain/opPools/index.d.ts +1 -0
- package/lib/chain/opPools/index.d.ts.map +1 -1
- package/lib/chain/opPools/index.js +1 -0
- package/lib/chain/opPools/index.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +3 -3
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js +1 -1
- package/lib/chain/opPools/syncCommitteeMessagePool.js.map +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +1 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +1 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/options.d.ts +1 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +24 -6
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +4 -2
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +2 -2
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +5 -5
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/seenCache/seenAttesters.d.ts +9 -5
- package/lib/chain/seenCache/seenAttesters.d.ts.map +1 -1
- package/lib/chain/seenCache/seenAttesters.js +25 -4
- package/lib/chain/seenCache/seenAttesters.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +3 -3
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +1 -4
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +1 -1
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +1 -2
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +5 -14
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +13 -4
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +1 -1
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/chain/validation/voluntaryExit.d.ts +9 -1
- package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
- package/lib/chain/validation/voluntaryExit.js +46 -1
- package/lib/chain/validation/voluntaryExit.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +3 -3
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js +9 -9
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +72 -22
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +130 -52
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +13 -0
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +37 -0
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/libp2p/index.d.ts.map +1 -1
- package/lib/network/libp2p/index.js +10 -2
- package/lib/network/libp2p/index.js.map +1 -1
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +22 -43
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +12 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/score.d.ts.map +1 -1
- package/lib/network/reqresp/score.js +5 -11
- package/lib/network/reqresp/score.js.map +1 -1
- package/lib/network/util.d.ts +0 -1
- package/lib/network/util.d.ts.map +1 -1
- package/lib/network/util.js +0 -4
- package/lib/network/util.js.map +1 -1
- package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
- package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
- package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
- package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
- package/lib/node/nodejs.d.ts +2 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +2 -0
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/batch.d.ts +23 -5
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +61 -22
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +2 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +23 -10
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/types.d.ts +32 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +33 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +9 -0
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +173 -45
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +16 -11
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +3 -1
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/package.json +35 -35
- package/src/api/impl/beacon/blocks/index.ts +7 -8
- package/src/api/impl/beacon/pool/index.ts +27 -2
- package/src/api/impl/beacon/state/utils.ts +2 -1
- package/src/api/impl/events/index.ts +12 -4
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +1 -1
- package/src/api/rest/base.ts +7 -0
- package/src/chain/archiveStore/archiveStore.ts +1 -1
- package/src/chain/archiveStore/historicalState/metrics.ts +22 -0
- package/src/chain/blocks/importBlock.ts +1 -6
- package/src/chain/blocks/importExecutionPayload.ts +12 -11
- package/src/chain/blocks/index.ts +18 -6
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +25 -16
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -9
- package/src/chain/bls/index.ts +1 -1
- package/src/chain/bls/interface.ts +7 -3
- package/src/chain/bls/multithread/index.ts +148 -120
- package/src/chain/bls/multithread/jobItem.ts +12 -82
- package/src/chain/bls/multithread/types.ts +33 -8
- package/src/chain/bls/multithread/utils.ts +11 -0
- package/src/chain/bls/multithread/worker.ts +85 -27
- package/src/chain/bls/singleThread.ts +11 -55
- package/src/chain/bls/utils.ts +23 -24
- package/src/chain/builderCircuitBreaker.ts +22 -19
- package/src/chain/chain.ts +17 -6
- package/src/chain/errors/blockError.ts +8 -2
- package/src/chain/errors/proposerPreferences.ts +7 -0
- package/src/chain/forkChoice/index.ts +4 -0
- package/src/chain/interface.ts +4 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/opPools/attestationPool.ts +1 -1
- package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
- package/src/chain/opPools/index.ts +1 -0
- package/src/chain/opPools/payloadAttestationPool.ts +3 -3
- package/src/chain/opPools/syncCommitteeMessagePool.ts +1 -1
- package/src/chain/opPools/syncContributionAndProofPool.ts +1 -1
- package/src/chain/opPools/utils.ts +1 -1
- package/src/chain/options.ts +1 -1
- package/src/chain/prepareNextSlot.ts +24 -6
- package/src/chain/regen/interface.ts +4 -2
- package/src/chain/regen/queued.ts +5 -5
- package/src/chain/seenCache/seenAttesters.ts +31 -5
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
- package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
- package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
- package/src/chain/stateCache/types.ts +3 -3
- package/src/chain/validation/attestation.ts +1 -7
- package/src/chain/validation/attesterSlashing.ts +1 -8
- package/src/chain/validation/executionPayloadBid.ts +1 -2
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -1
- package/src/chain/validation/payloadAttestationMessage.ts +6 -17
- package/src/chain/validation/proposerPreferences.ts +15 -5
- package/src/chain/validation/proposerSlashing.ts +1 -1
- package/src/chain/validation/voluntaryExit.ts +59 -2
- package/src/metrics/metrics/beacon.ts +9 -9
- package/src/metrics/metrics/lodestar.ts +142 -57
- package/src/network/gossip/topic.ts +38 -0
- package/src/network/libp2p/index.ts +11 -2
- package/src/network/network.ts +27 -55
- package/src/network/processor/gossipHandlers.ts +14 -1
- package/src/network/reqresp/score.ts +5 -11
- package/src/network/util.ts +0 -5
- package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
- package/src/node/nodejs.ts +5 -1
- package/src/sync/range/batch.ts +83 -28
- package/src/sync/range/chain.ts +26 -11
- package/src/sync/types.ts +36 -1
- package/src/sync/unknownBlock.ts +197 -51
- package/src/sync/utils/downloadByRange.ts +21 -12
- package/src/sync/utils/downloadByRoot.ts +3 -1
- package/lib/chain/bls/maybeBatch.d.ts +0 -12
- package/lib/chain/bls/maybeBatch.d.ts.map +0 -1
- package/lib/chain/bls/maybeBatch.js +0 -35
- package/lib/chain/bls/maybeBatch.js.map +0 -1
- package/src/chain/bls/maybeBatch.ts +0 -45
package/src/sync/types.ts
CHANGED
|
@@ -36,6 +36,39 @@ export enum PendingBlockInputStatus {
|
|
|
36
36
|
processing = "processing",
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Why a pending item was dropped from BlockInputSync WITHOUT completing.
|
|
41
|
+
* The happy path — a successfully imported/processed block or payload — is removed by the import
|
|
42
|
+
* flow (onPayloadImported, processReadyBlock/processPayload success) and is deliberately NOT tracked
|
|
43
|
+
* here
|
|
44
|
+
*/
|
|
45
|
+
export enum DroppedItemReason {
|
|
46
|
+
/** prune: item's own slot is below the finalized slot (routine cleanup), or a downloaded block landed at/below finality */
|
|
47
|
+
belowFinalized = "below_finalized",
|
|
48
|
+
/** prune: slot-less, unresolvable payload evicted after PRUNE_UNRESOLVED_SLOT_EPOCHS */
|
|
49
|
+
agedOut = "aged_out",
|
|
50
|
+
/** pruneSetToMax evicted the oldest entry because the cache hit its capacity (DoS guard) */
|
|
51
|
+
capacity = "capacity",
|
|
52
|
+
/** descendant removed because an ancestor was invalid/removed */
|
|
53
|
+
invalidParent = "invalid_parent",
|
|
54
|
+
/** transient execution-engine error removal (EXECUTION_ENGINE_ERROR) */
|
|
55
|
+
elError = "el_error",
|
|
56
|
+
/** the execution engine declared the item invalid (EXECUTION_ENGINE_INVALID) */
|
|
57
|
+
elInvalid = "el_invalid",
|
|
58
|
+
/** a block failed our own checks (not correct w.r.t. our chain) */
|
|
59
|
+
invalidBlock = "invalid_block",
|
|
60
|
+
/** a block declares a parent payload hash that conflicts with the parent's actual payload (gloas) */
|
|
61
|
+
invalidParentPayload = "invalid_parent_payload",
|
|
62
|
+
/** a payload envelope failed gossip verification (ENVELOPE_VERIFICATION_ERROR) */
|
|
63
|
+
invalidEnvelope = "invalid_envelope",
|
|
64
|
+
/** a payload had an invalid signature (INVALID_SIGNATURE) */
|
|
65
|
+
invalidSignature = "invalid_signature",
|
|
66
|
+
/** a block root was given up on after exhausting download attempts (data could not be fetched) */
|
|
67
|
+
unavailable = "unavailable",
|
|
68
|
+
/** an unexpected / unhandled error code led to removal */
|
|
69
|
+
unknown = "unknown",
|
|
70
|
+
}
|
|
71
|
+
|
|
39
72
|
export enum PendingPayloadInputStatus {
|
|
40
73
|
pending = "pending",
|
|
41
74
|
fetching = "fetching",
|
|
@@ -77,7 +110,9 @@ export type PendingPayloadInput = {
|
|
|
77
110
|
export type PendingPayloadRootHex = {
|
|
78
111
|
status: PendingPayloadInputStatus.pending | PendingPayloadInputStatus.fetching;
|
|
79
112
|
rootHex: RootHex;
|
|
80
|
-
//
|
|
113
|
+
// Trusted slot only (fork choice / validated data), may be missing until resolved. NOT the gossip
|
|
114
|
+
// message slot from ChainEvent.unknownEnvelopeBlockRoot, which is untrusted and not necessarily the
|
|
115
|
+
// payload/block slot. See BlockInputSync.resolvePayloadSlot.
|
|
81
116
|
slot?: Slot;
|
|
82
117
|
timeAddedSec: number;
|
|
83
118
|
timeSyncedSec?: number;
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
-
import {
|
|
3
|
+
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
4
|
+
import {ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas} from "@lodestar/params";
|
|
4
5
|
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
5
6
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
6
7
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
@@ -24,6 +25,7 @@ import {MAX_CONCURRENT_REQUESTS} from "./constants.js";
|
|
|
24
25
|
import {SyncOptions} from "./options.js";
|
|
25
26
|
import {
|
|
26
27
|
BlockInputSyncCacheItem,
|
|
28
|
+
DroppedItemReason,
|
|
27
29
|
PayloadSyncCacheItem,
|
|
28
30
|
PendingBlockInput,
|
|
29
31
|
PendingBlockInputStatus,
|
|
@@ -47,6 +49,10 @@ import {getRateLimitedUntilMs} from "./utils/rateLimit.js";
|
|
|
47
49
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
48
50
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
49
51
|
const MAX_PENDING_BLOCKS = 100;
|
|
52
|
+
/**
|
|
53
|
+
* A pending payload without slot can only stay at most 2 epochs.
|
|
54
|
+
*/
|
|
55
|
+
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
50
56
|
|
|
51
57
|
type AdvancePendingBlockResult =
|
|
52
58
|
| "ready"
|
|
@@ -154,6 +160,7 @@ export class BlockInputSync {
|
|
|
154
160
|
this.chain.emitter.on(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
155
161
|
this.chain.emitter.on(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
156
162
|
this.chain.emitter.on(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
163
|
+
this.chain.emitter.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
157
164
|
this.chain.emitter.on(routes.events.EventType.block, this.onBlockImported);
|
|
158
165
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
159
166
|
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
@@ -170,6 +177,7 @@ export class BlockInputSync {
|
|
|
170
177
|
this.chain.emitter.off(ChainEvent.incompleteBlockInput, this.onIncompleteBlockInput);
|
|
171
178
|
this.chain.emitter.off(ChainEvent.incompletePayloadEnvelope, this.onIncompletePayloadEnvelope);
|
|
172
179
|
this.chain.emitter.off(ChainEvent.blockUnknownParent, this.onUnknownParent);
|
|
180
|
+
this.chain.emitter.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized);
|
|
173
181
|
this.chain.emitter.off(routes.events.EventType.block, this.onBlockImported);
|
|
174
182
|
this.chain.emitter.off(routes.events.EventType.executionPayload, this.onPayloadImported);
|
|
175
183
|
this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
@@ -215,7 +223,7 @@ export class BlockInputSync {
|
|
|
215
223
|
|
|
216
224
|
private onUnknownEnvelopeBlockRoot = (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]): void => {
|
|
217
225
|
try {
|
|
218
|
-
this.addByPayloadRootHex(data.rootHex, data.peer, data.
|
|
226
|
+
this.addByPayloadRootHex(data.rootHex, data.peer, this.resolvePayloadSlot(data.rootHex));
|
|
219
227
|
this.triggerUnknownBlockSearch();
|
|
220
228
|
this.metrics?.blockInputSync.requests.inc({type: PendingBlockType.UNKNOWN_PAYLOAD_BLOCK_ROOT});
|
|
221
229
|
this.metrics?.blockInputSync.payloadSource.inc({source: data.source});
|
|
@@ -252,13 +260,13 @@ export class BlockInputSync {
|
|
|
252
260
|
parentRoot: missingDependency.parentRootHex,
|
|
253
261
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
254
262
|
});
|
|
255
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
263
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
256
264
|
}
|
|
257
265
|
return;
|
|
258
266
|
}
|
|
259
267
|
|
|
260
268
|
if (missingDependency.kind === "parentPayload") {
|
|
261
|
-
this.addByPayloadRootHex(missingDependency.rootHex, data.peer);
|
|
269
|
+
this.addByPayloadRootHex(missingDependency.rootHex, data.peer, missingDependency.slot);
|
|
262
270
|
} else if (missingDependency.kind === "parentBlock") {
|
|
263
271
|
this.addByRootHex(missingDependency.rootHex, data.peer);
|
|
264
272
|
}
|
|
@@ -284,6 +292,80 @@ export class BlockInputSync {
|
|
|
284
292
|
this.triggerUnknownBlockSearch();
|
|
285
293
|
};
|
|
286
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Drop pending payloads and blocks that fell below the finalized slot.
|
|
297
|
+
* See https://github.com/ChainSafe/lodestar/issues/9800
|
|
298
|
+
*/
|
|
299
|
+
private pruneFinalized = (checkpoint: CheckpointWithHex): void => {
|
|
300
|
+
// Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
|
|
301
|
+
// because the payload at the finalized block is not finalized
|
|
302
|
+
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
303
|
+
const nowSec = Date.now() / 1000;
|
|
304
|
+
const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
|
|
305
|
+
|
|
306
|
+
let belowFinalizedPayloads = 0;
|
|
307
|
+
let agedOutPayloads = 0;
|
|
308
|
+
let unresolvedSlotPayloads = 0;
|
|
309
|
+
|
|
310
|
+
for (const [rootHex, payload] of this.pendingPayloads) {
|
|
311
|
+
// Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
|
|
312
|
+
if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
|
|
313
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
314
|
+
if (payload.slot === undefined) {
|
|
315
|
+
// Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
|
|
316
|
+
if (nowSec - payload.timeAddedSec > maxSecInCache) {
|
|
317
|
+
this.pendingPayloads.delete(rootHex);
|
|
318
|
+
agedOutPayloads++;
|
|
319
|
+
} else {
|
|
320
|
+
unresolvedSlotPayloads++;
|
|
321
|
+
this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
|
|
322
|
+
root: rootHex,
|
|
323
|
+
finalizedSlot,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const slot = getPayloadSyncCacheItemSlot(payload);
|
|
331
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
332
|
+
this.pendingPayloads.delete(rootHex);
|
|
333
|
+
belowFinalizedPayloads++;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
let deletedBlocks = 0;
|
|
338
|
+
for (const [rootHex, block] of this.pendingBlocks) {
|
|
339
|
+
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
340
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
341
|
+
this.pendingBlocks.delete(rootHex);
|
|
342
|
+
deletedBlocks++;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (belowFinalizedPayloads > 0) {
|
|
347
|
+
this.metrics?.blockInputSync.removedPayloads.inc(
|
|
348
|
+
{reason: DroppedItemReason.belowFinalized},
|
|
349
|
+
belowFinalizedPayloads
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
if (agedOutPayloads > 0) {
|
|
353
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.agedOut}, agedOutPayloads);
|
|
354
|
+
}
|
|
355
|
+
if (deletedBlocks > 0) {
|
|
356
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.belowFinalized}, deletedBlocks);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
|
|
360
|
+
finalizedSlot,
|
|
361
|
+
finalizedRoot: checkpoint.rootHex,
|
|
362
|
+
belowFinalizedPayloads,
|
|
363
|
+
agedOutPayloads,
|
|
364
|
+
deletedBlocks,
|
|
365
|
+
unresolvedSlotPayloads,
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
|
|
287
369
|
private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
|
|
288
370
|
let pendingBlock = this.pendingBlocks.get(rootHex);
|
|
289
371
|
let added = false;
|
|
@@ -311,6 +393,7 @@ export class BlockInputSync {
|
|
|
311
393
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
312
394
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
313
395
|
if (prunedItemCount > 0) {
|
|
396
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
314
397
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
315
398
|
}
|
|
316
399
|
return added;
|
|
@@ -345,6 +428,7 @@ export class BlockInputSync {
|
|
|
345
428
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
346
429
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
347
430
|
if (prunedItemCount > 0) {
|
|
431
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
348
432
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
349
433
|
}
|
|
350
434
|
};
|
|
@@ -369,6 +453,14 @@ export class BlockInputSync {
|
|
|
369
453
|
root: rootHex,
|
|
370
454
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
371
455
|
});
|
|
456
|
+
} else if (
|
|
457
|
+
// update a previously slot-less entry once a slot becomes available
|
|
458
|
+
slot !== undefined &&
|
|
459
|
+
!isPendingPayloadInput(pendingPayload) &&
|
|
460
|
+
!isPendingPayloadEnvelope(pendingPayload) &&
|
|
461
|
+
pendingPayload.slot === undefined
|
|
462
|
+
) {
|
|
463
|
+
pendingPayload.slot = slot;
|
|
372
464
|
}
|
|
373
465
|
|
|
374
466
|
if (peerIdStr) {
|
|
@@ -377,11 +469,35 @@ export class BlockInputSync {
|
|
|
377
469
|
|
|
378
470
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
379
471
|
if (prunedItemCount > 0) {
|
|
472
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
380
473
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
381
474
|
}
|
|
382
475
|
return added;
|
|
383
476
|
};
|
|
384
477
|
|
|
478
|
+
/**
|
|
479
|
+
* Resolve the block slot for a payload root.
|
|
480
|
+
*/
|
|
481
|
+
private resolvePayloadSlot(rootHex: RootHex): Slot | undefined {
|
|
482
|
+
// a pending block of the same root
|
|
483
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
484
|
+
if (pendingBlock) {
|
|
485
|
+
const pendingBlockSlot = getBlockInputSyncCacheItemSlot(pendingBlock);
|
|
486
|
+
if (typeof pendingBlockSlot === "number") {
|
|
487
|
+
return pendingBlockSlot;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// the block is in fork choice
|
|
492
|
+
const forkChoiceSlot = this.chain.forkChoice.getBlockHexDefaultStatus(rootHex)?.slot;
|
|
493
|
+
if (forkChoiceSlot !== undefined) {
|
|
494
|
+
return forkChoiceSlot;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// a seen payload envelope input
|
|
498
|
+
return this.chain.seenPayloadEnvelopeInputCache.get(rootHex)?.slot;
|
|
499
|
+
}
|
|
500
|
+
|
|
385
501
|
private addByPayloadInput = (
|
|
386
502
|
payloadInput: PayloadEnvelopeInput,
|
|
387
503
|
peerIdStr?: PeerIdStr,
|
|
@@ -407,6 +523,7 @@ export class BlockInputSync {
|
|
|
407
523
|
|
|
408
524
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
409
525
|
if (prunedItemCount > 0) {
|
|
526
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
410
527
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
411
528
|
}
|
|
412
529
|
};
|
|
@@ -436,10 +553,12 @@ export class BlockInputSync {
|
|
|
436
553
|
blockInput: IBlockInput
|
|
437
554
|
):
|
|
438
555
|
| {kind: "ready"}
|
|
439
|
-
| {kind: "block" | "parentBlock"
|
|
556
|
+
| {kind: "block" | "parentBlock"; rootHex: RootHex}
|
|
557
|
+
| {kind: "parentPayload"; rootHex: RootHex; slot: Slot}
|
|
440
558
|
| {kind: "invalidParentPayload"; parentRootHex: RootHex; parentBlockHashHex: RootHex} {
|
|
441
559
|
const parentRootHex = blockInput.parentRootHex;
|
|
442
|
-
|
|
560
|
+
const parentBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRootHex);
|
|
561
|
+
if (!parentBlock) {
|
|
443
562
|
return {kind: "parentBlock", rootHex: parentRootHex};
|
|
444
563
|
}
|
|
445
564
|
|
|
@@ -461,16 +580,8 @@ export class BlockInputSync {
|
|
|
461
580
|
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
462
581
|
}
|
|
463
582
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (parentPayloadInput.getBlockHashHex() === parentBlockHashHex) {
|
|
467
|
-
return {kind: "parentPayload", rootHex: parentRootHex};
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return {kind: "parentPayload", rootHex: parentRootHex};
|
|
583
|
+
// Parent is in fork choice but its payload `parentBlockHashHex` isn't revealed yet
|
|
584
|
+
return {kind: "parentPayload", rootHex: parentRootHex, slot: parentBlock.slot};
|
|
474
585
|
}
|
|
475
586
|
|
|
476
587
|
private advancePendingBlock(pendingBlock: PendingBlockInput): AdvancePendingBlockResult {
|
|
@@ -493,9 +604,9 @@ export class BlockInputSync {
|
|
|
493
604
|
}
|
|
494
605
|
|
|
495
606
|
case "parentPayload": {
|
|
496
|
-
let added = this.addByPayloadRootHex(missingDependency.rootHex);
|
|
607
|
+
let added = this.addByPayloadRootHex(missingDependency.rootHex, undefined, missingDependency.slot);
|
|
497
608
|
for (const peerIdStr of pendingBlock.peerIdStrings) {
|
|
498
|
-
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr) || added;
|
|
609
|
+
added = this.addByPayloadRootHex(missingDependency.rootHex, peerIdStr, missingDependency.slot) || added;
|
|
499
610
|
}
|
|
500
611
|
return added ? "queued_parent_payload" : "blocked";
|
|
501
612
|
}
|
|
@@ -507,7 +618,7 @@ export class BlockInputSync {
|
|
|
507
618
|
parentRoot: missingDependency.parentRootHex,
|
|
508
619
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
509
620
|
});
|
|
510
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
621
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
511
622
|
return "removed";
|
|
512
623
|
}
|
|
513
624
|
}
|
|
@@ -730,7 +841,7 @@ export class BlockInputSync {
|
|
|
730
841
|
...logCtx2,
|
|
731
842
|
finalizedSlot,
|
|
732
843
|
});
|
|
733
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
844
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
|
|
734
845
|
} else {
|
|
735
846
|
this.onUnknownBlockRoot({rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot});
|
|
736
847
|
}
|
|
@@ -747,7 +858,7 @@ export class BlockInputSync {
|
|
|
747
858
|
|
|
748
859
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
749
860
|
this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
|
|
750
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
861
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
|
|
751
862
|
}
|
|
752
863
|
}
|
|
753
864
|
|
|
@@ -811,9 +922,9 @@ export class BlockInputSync {
|
|
|
811
922
|
this.pendingBlocks.delete(blockRootHex);
|
|
812
923
|
|
|
813
924
|
if (isForkPostGloas(fork)) {
|
|
814
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.
|
|
925
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(blockRootHex);
|
|
815
926
|
if (!payloadInput) {
|
|
816
|
-
this.logger.
|
|
927
|
+
this.logger.debug("PayloadEnvelopeInput unavailable during unknown sync processReadyBlock()", logCtx);
|
|
817
928
|
} else {
|
|
818
929
|
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
819
930
|
// The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
|
|
@@ -855,19 +966,19 @@ export class BlockInputSync {
|
|
|
855
966
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
856
967
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
857
968
|
// recover on a latter download + verify attempt.
|
|
858
|
-
this.removeAllDescendants(pendingBlock);
|
|
969
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
859
970
|
break;
|
|
860
971
|
|
|
861
972
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
862
973
|
// the peer served a bad block
|
|
863
974
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
864
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
975
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
865
976
|
break;
|
|
866
977
|
|
|
867
978
|
default:
|
|
868
979
|
// Block is not correct with respect to our chain. Log error loudly
|
|
869
980
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
870
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
981
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
871
982
|
}
|
|
872
983
|
}
|
|
873
984
|
|
|
@@ -886,12 +997,12 @@ export class BlockInputSync {
|
|
|
886
997
|
*/
|
|
887
998
|
private async reconcilePayloadEnvelope(pendingPayload: PendingPayloadEnvelope): Promise<void> {
|
|
888
999
|
const rootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
1000
|
+
|
|
889
1001
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(rootHex)) {
|
|
890
1002
|
this.pendingPayloads.delete(rootHex);
|
|
891
1003
|
return;
|
|
892
1004
|
}
|
|
893
1005
|
|
|
894
|
-
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
895
1006
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
896
1007
|
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
897
1008
|
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
@@ -907,8 +1018,9 @@ export class BlockInputSync {
|
|
|
907
1018
|
return;
|
|
908
1019
|
}
|
|
909
1020
|
|
|
1021
|
+
const payloadInput = await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
910
1022
|
if (!payloadInput) {
|
|
911
|
-
this.logger.debug("
|
|
1023
|
+
this.logger.debug("PayloadEnvelopeInput not yet reloadable for imported block, will retry", {
|
|
912
1024
|
root: rootHex,
|
|
913
1025
|
});
|
|
914
1026
|
return;
|
|
@@ -928,6 +1040,7 @@ export class BlockInputSync {
|
|
|
928
1040
|
const pendingPayloadByRoot: PendingPayloadRootHex = {
|
|
929
1041
|
status: PendingPayloadInputStatus.pending,
|
|
930
1042
|
rootHex,
|
|
1043
|
+
slot: pendingPayload.envelope.message.payload.slotNumber,
|
|
931
1044
|
timeAddedSec: pendingPayload.timeAddedSec,
|
|
932
1045
|
peerIdStrings: new Set(pendingPayload.peerIdStrings),
|
|
933
1046
|
};
|
|
@@ -967,6 +1080,10 @@ export class BlockInputSync {
|
|
|
967
1080
|
return;
|
|
968
1081
|
}
|
|
969
1082
|
|
|
1083
|
+
if (!isPendingPayloadInput(payload) && payload.slot === undefined) {
|
|
1084
|
+
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
970
1087
|
const payloadSlot = getPayloadSyncCacheItemSlot(payload);
|
|
971
1088
|
const logCtx = {
|
|
972
1089
|
slot: payloadSlot,
|
|
@@ -982,7 +1099,13 @@ export class BlockInputSync {
|
|
|
982
1099
|
const res = await wrapError(this.fetchPayloadInput(payload));
|
|
983
1100
|
if (!res.err) {
|
|
984
1101
|
const pendingPayload = res.result;
|
|
985
|
-
|
|
1102
|
+
const resultRootHex = getPayloadSyncCacheItemRootHex(pendingPayload);
|
|
1103
|
+
// finalization may have deleted this entry while we were awaiting the network, do not resurrect it via the set below
|
|
1104
|
+
if (!this.pendingPayloads.has(resultRootHex)) {
|
|
1105
|
+
this.logger.verbose("Dropping downloaded payload, entry pruned during fetch", logCtx);
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
this.pendingPayloads.set(resultRootHex, pendingPayload);
|
|
986
1109
|
|
|
987
1110
|
if (isPendingPayloadEnvelope(pendingPayload)) {
|
|
988
1111
|
await this.reconcilePayloadEnvelope(pendingPayload);
|
|
@@ -1059,17 +1182,27 @@ export class BlockInputSync {
|
|
|
1059
1182
|
break;
|
|
1060
1183
|
|
|
1061
1184
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1185
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1186
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1187
|
+
break;
|
|
1188
|
+
|
|
1062
1189
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1190
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1191
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1192
|
+
break;
|
|
1193
|
+
|
|
1063
1194
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1064
1195
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1065
1196
|
// reintroducing envelope replacement / recreation flows.
|
|
1066
1197
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1067
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1198
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1068
1199
|
break;
|
|
1069
1200
|
|
|
1070
1201
|
default:
|
|
1071
1202
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1072
|
-
this.pendingPayloads.delete(rootHex)
|
|
1203
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1204
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.unknown}, 1);
|
|
1205
|
+
}
|
|
1073
1206
|
}
|
|
1074
1207
|
return;
|
|
1075
1208
|
}
|
|
@@ -1093,7 +1226,7 @@ export class BlockInputSync {
|
|
|
1093
1226
|
let slot = getPayloadSyncCacheItemSlot(cacheItem);
|
|
1094
1227
|
let payloadInput = isPendingPayloadInput(cacheItem)
|
|
1095
1228
|
? cacheItem.payloadInput
|
|
1096
|
-
: this.chain.seenPayloadEnvelopeInputCache.
|
|
1229
|
+
: await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1097
1230
|
let envelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : undefined;
|
|
1098
1231
|
|
|
1099
1232
|
let i = 0;
|
|
@@ -1110,9 +1243,11 @@ export class BlockInputSync {
|
|
|
1110
1243
|
);
|
|
1111
1244
|
}
|
|
1112
1245
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1246
|
+
const reason =
|
|
1247
|
+
pendingColumns.size > 0
|
|
1248
|
+
? `cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`
|
|
1249
|
+
: "no peer available to download pending payload";
|
|
1250
|
+
throw Error(`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: ${reason}`);
|
|
1116
1251
|
}
|
|
1117
1252
|
|
|
1118
1253
|
const {peerId, client: peerClient} = peerMeta;
|
|
@@ -1124,7 +1259,7 @@ export class BlockInputSync {
|
|
|
1124
1259
|
slot = envelope.message.payload.slotNumber;
|
|
1125
1260
|
}
|
|
1126
1261
|
|
|
1127
|
-
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.
|
|
1262
|
+
payloadInput ??= await this.chain.seenPayloadEnvelopeInputCache.getOrReload(rootHex);
|
|
1128
1263
|
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
1129
1264
|
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
1130
1265
|
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
@@ -1457,9 +1592,9 @@ export class BlockInputSync {
|
|
|
1457
1592
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1458
1593
|
* referenced more than one bad block.
|
|
1459
1594
|
*/
|
|
1460
|
-
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem): void {
|
|
1595
|
+
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
|
|
1461
1596
|
// Get all blocks that are a descendant of this one
|
|
1462
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1597
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1463
1598
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1464
1599
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1465
1600
|
|
|
@@ -1486,18 +1621,22 @@ export class BlockInputSync {
|
|
|
1486
1621
|
}
|
|
1487
1622
|
|
|
1488
1623
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1489
|
-
private removePendingPayloadAndDescendants(rootHex: RootHex): void {
|
|
1624
|
+
private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
|
|
1490
1625
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1491
1626
|
// later validation/finalization logic decides when it can leave memory.
|
|
1492
|
-
this.pendingPayloads.delete(rootHex)
|
|
1627
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1628
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1629
|
+
}
|
|
1493
1630
|
|
|
1494
1631
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1495
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1496
|
-
|
|
1497
1632
|
for (const block of badPendingBlocks) {
|
|
1498
1633
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1499
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1500
|
-
|
|
1634
|
+
if (this.pendingBlocks.delete(descendantRootHex)) {
|
|
1635
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.invalidParent}, 1);
|
|
1636
|
+
}
|
|
1637
|
+
if (this.pendingPayloads.delete(descendantRootHex)) {
|
|
1638
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.invalidParent}, 1);
|
|
1639
|
+
}
|
|
1501
1640
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1502
1641
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1503
1642
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1507,18 +1646,25 @@ export class BlockInputSync {
|
|
|
1507
1646
|
}
|
|
1508
1647
|
}
|
|
1509
1648
|
|
|
1510
|
-
private removeAllDescendants(
|
|
1649
|
+
private removeAllDescendants(
|
|
1650
|
+
block: BlockInputSyncCacheItem,
|
|
1651
|
+
headReason: DroppedItemReason,
|
|
1652
|
+
descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
|
|
1653
|
+
): BlockInputSyncCacheItem[] {
|
|
1511
1654
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1512
1655
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1513
|
-
// Get all blocks that are a descendant of this one
|
|
1656
|
+
// Get all blocks that are a descendant of this one (index 0 is the head item itself)
|
|
1514
1657
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1515
1658
|
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
for (const block of badPendingBlocks) {
|
|
1659
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1519
1660
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1520
|
-
|
|
1521
|
-
this.
|
|
1661
|
+
const reason = i === 0 ? headReason : descendantReason;
|
|
1662
|
+
if (this.pendingBlocks.delete(rootHex)) {
|
|
1663
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason}, 1);
|
|
1664
|
+
}
|
|
1665
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1666
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason}, 1);
|
|
1667
|
+
}
|
|
1522
1668
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1523
1669
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1524
1670
|
// importBlock()-owned lifecycle.
|
|
@@ -136,6 +136,8 @@ export function cacheByRangeResponses({
|
|
|
136
136
|
const source = BlockInputSource.byRange;
|
|
137
137
|
const updatedBatchBlocks = new Map<Slot, IBlockInput>(batchBlocks.map((block) => [block.slot, block]));
|
|
138
138
|
|
|
139
|
+
const payloadEnvelopes = new Map<Slot, PayloadEnvelopeInput>(existingPayloadEnvelopes);
|
|
140
|
+
|
|
139
141
|
const blocks = responses.validatedBlocks ?? [];
|
|
140
142
|
for (let i = 0; i < blocks.length; i++) {
|
|
141
143
|
const {block, blockRoot} = blocks[i];
|
|
@@ -172,15 +174,17 @@ export function cacheByRangeResponses({
|
|
|
172
174
|
// later step) can throw and abort the batch — otherwise a gloas block would sit in
|
|
173
175
|
// seenBlockInputCache but unseeded here, and payload-by-root sync would later throw "Missing
|
|
174
176
|
// PayloadEnvelopeInput for known block" (see issue #9306). add() is idempotent.
|
|
175
|
-
if (isForkPostGloas(blockInput.forkName)) {
|
|
176
|
-
seenPayloadEnvelopeInputCache.add({
|
|
177
|
+
if (isForkPostGloas(blockInput.forkName) && !payloadEnvelopes.has(blockInput.slot)) {
|
|
178
|
+
const payloadInput = seenPayloadEnvelopeInputCache.add({
|
|
177
179
|
blockRootHex: blockInput.blockRootHex,
|
|
178
180
|
block: blockInput.getBlock() as SignedBeaconBlock<ForkPostGloas>,
|
|
179
181
|
forkName: blockInput.forkName,
|
|
180
182
|
sampledColumns: custodyConfig.sampledColumns,
|
|
181
183
|
custodyColumns: custodyConfig.custodyColumns,
|
|
182
|
-
|
|
184
|
+
seenTimestampSec,
|
|
185
|
+
source: PayloadEnvelopeInputSource.byRange,
|
|
183
186
|
});
|
|
187
|
+
payloadEnvelopes.set(blockInput.slot, payloadInput);
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
|
|
@@ -222,17 +226,22 @@ export function cacheByRangeResponses({
|
|
|
222
226
|
}
|
|
223
227
|
}
|
|
224
228
|
|
|
225
|
-
let payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null =
|
|
226
|
-
existingPayloadEnvelopes !== null ? new Map(existingPayloadEnvelopes) : null;
|
|
227
229
|
if (downloadedPayloadEnvelopes !== null) {
|
|
228
|
-
payloadEnvelopes ??= new Map();
|
|
229
230
|
for (const [slot, envelope] of downloadedPayloadEnvelopes) {
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
// the only PayloadEnvelopeInput miss is the dangling parent, which we can get from the seen cache
|
|
232
|
+
let payloadInput = payloadEnvelopes.get(slot);
|
|
232
233
|
if (payloadInput === undefined) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
234
|
+
if (updatedBatchBlocks.has(slot)) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Missing PayloadEnvelopeInput for in-batch slot ${slot} root ${toRootHex(envelope.message.beaconBlockRoot)}`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
payloadInput = seenPayloadEnvelopeInputCache.get(toRootHex(envelope.message.beaconBlockRoot));
|
|
240
|
+
}
|
|
241
|
+
if (payloadInput === undefined) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`Missing PayloadEnvelopeInput for slot ${slot} root ${toRootHex(envelope.message.beaconBlockRoot)}`
|
|
244
|
+
);
|
|
236
245
|
}
|
|
237
246
|
|
|
238
247
|
if (!payloadInput.hasPayloadEnvelope()) {
|
|
@@ -262,7 +271,7 @@ export function cacheByRangeResponses({
|
|
|
262
271
|
// Gloas columns are attached to the matching PayloadEnvelopeInput, NOT to IBlockInput.
|
|
263
272
|
// Gloas DataColumnSidecar has `slot` directly (no signedBlockHeader).
|
|
264
273
|
const dataSlot = firstColumn.slot;
|
|
265
|
-
const payloadInput = payloadEnvelopes
|
|
274
|
+
const payloadInput = payloadEnvelopes.get(dataSlot);
|
|
266
275
|
if (!payloadInput) {
|
|
267
276
|
// Should not happen: we built payloadInputs for all gloas blocks above
|
|
268
277
|
continue;
|
|
@@ -13,6 +13,7 @@ import {BlobIndex, ColumnIndex, SignedBeaconBlock, Slot, deneb, fulu} from "@lod
|
|
|
13
13
|
import {LodestarError, byteArrayEquals, fromHex, prettyPrintIndices, toHex, toRootHex} from "@lodestar/utils";
|
|
14
14
|
import {isBlockInputBlobs, isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
|
|
15
15
|
import {BlockInputSource, IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
16
|
+
import {PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/index.js";
|
|
16
17
|
import {ChainEventEmitter} from "../../chain/emitter.js";
|
|
17
18
|
import {IBeaconChain} from "../../chain/interface.js";
|
|
18
19
|
import {validateBlockBlobSidecars} from "../../chain/validation/blobSidecar.js";
|
|
@@ -122,7 +123,8 @@ export async function downloadByRoot({
|
|
|
122
123
|
forkName: blockInput.forkName,
|
|
123
124
|
sampledColumns: chain.custodyConfig.sampledColumns,
|
|
124
125
|
custodyColumns: chain.custodyConfig.custodyColumns,
|
|
125
|
-
|
|
126
|
+
seenTimestampSec: Date.now() / 1000,
|
|
127
|
+
source: PayloadEnvelopeInputSource.byRoot,
|
|
126
128
|
});
|
|
127
129
|
}
|
|
128
130
|
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { PublicKey } from "@chainsafe/blst";
|
|
2
|
-
export type SignatureSetDeserialized = {
|
|
3
|
-
publicKey: PublicKey;
|
|
4
|
-
message: Uint8Array;
|
|
5
|
-
signature: Uint8Array;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* Verify signatures sets with batch verification or regular core verify depending on the set count.
|
|
9
|
-
* Abstracted in a separate file to be consumed by the threaded pool and the main thread implementation.
|
|
10
|
-
*/
|
|
11
|
-
export declare function verifySignatureSetsMaybeBatch(sets: SignatureSetDeserialized[]): boolean;
|
|
12
|
-
//# sourceMappingURL=maybeBatch.d.ts.map
|