@lodestar/beacon-node 1.44.0-dev.7418ddd9f7 → 1.44.0-dev.762dd5f186
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 +36 -33
- 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 +1 -26
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +5 -0
- package/lib/api/impl/config/constants.d.ts.map +1 -1
- package/lib/api/impl/config/constants.js +6 -1
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +69 -12
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +28 -0
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +41 -36
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +0 -4
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +15 -3
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +3 -3
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/chain.d.ts +2 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +9 -2
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +2 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +0 -7
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +0 -3
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +12 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +2 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +6 -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/forkChoice/index.d.ts +4 -4
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +10 -7
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -0
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +1 -10
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +2 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +7 -7
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -1
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +0 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +0 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +8 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +0 -2
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/block.d.ts +5 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +4 -14
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +45 -6
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +1 -3
- 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 +24 -4
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/execution/engine/http.js +1 -1
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -2
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/interface.js +6 -2
- package/lib/execution/engine/interface.js.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +3 -5
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +15 -7
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +47 -9
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +4 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +10 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +770 -3
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +10 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +2 -2
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +25 -23
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +9 -5
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +13 -3
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +16 -6
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +15 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/index.js +4 -4
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +23 -5
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/sync/types.d.ts +9 -1
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +9 -2
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +64 -30
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/dataColumns.d.ts.map +1 -1
- package/lib/util/dataColumns.js +16 -11
- package/lib/util/dataColumns.js.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/package.json +15 -17
- package/src/api/impl/beacon/blocks/index.ts +40 -42
- package/src/api/impl/beacon/pool/index.ts +2 -41
- package/src/api/impl/config/constants.ts +10 -0
- package/src/api/impl/debug/index.ts +73 -12
- package/src/api/impl/lodestar/index.ts +30 -0
- package/src/api/impl/validator/index.ts +52 -53
- package/src/chain/archiveStore/archiveStore.ts +0 -5
- package/src/chain/blocks/importBlock.ts +20 -5
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +3 -5
- package/src/chain/chain.ts +12 -4
- package/src/chain/emitter.ts +3 -2
- package/src/chain/errors/blockError.ts +0 -4
- package/src/chain/errors/executionPayloadBid.ts +14 -0
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/forkChoice/index.ts +13 -0
- package/src/chain/interface.ts +1 -2
- package/src/chain/options.ts +1 -0
- package/src/chain/prepareNextSlot.ts +1 -11
- package/src/chain/produceBlock/produceBlockBody.ts +10 -10
- package/src/chain/regen/interface.ts +1 -1
- package/src/chain/regen/queued.ts +0 -5
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +8 -3
- package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
- package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
- package/src/chain/stateCache/types.ts +0 -2
- package/src/chain/validation/block.ts +12 -16
- package/src/chain/validation/executionPayloadBid.ts +48 -6
- package/src/chain/validation/executionPayloadEnvelope.ts +1 -3
- package/src/chain/validation/payloadAttestationMessage.ts +26 -4
- package/src/execution/engine/http.ts +1 -1
- package/src/execution/engine/interface.ts +12 -2
- package/src/execution/engine/mock.ts +6 -5
- package/src/execution/engine/types.ts +75 -14
- package/src/metrics/metrics/lodestar.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +10 -2
- package/src/network/processor/index.ts +27 -27
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +12 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -3
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +22 -6
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +20 -1
- package/src/network/reqresp/handlers/index.ts +4 -4
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +22 -5
- package/src/sync/types.ts +11 -2
- package/src/sync/unknownBlock.ts +70 -31
- package/src/util/dataColumns.ts +17 -12
- package/src/util/sszBytes.ts +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
+
import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
|
|
2
3
|
import {
|
|
3
4
|
computeEpochAtSlot,
|
|
4
5
|
createSingleSignatureSetFromComponents,
|
|
@@ -35,10 +36,6 @@ async function validateExecutionPayloadBid(
|
|
|
35
36
|
const bid = signedExecutionPayloadBid.message;
|
|
36
37
|
const parentBlockRootHex = toRootHex(bid.parentBlockRoot);
|
|
37
38
|
const parentBlockHashHex = toRootHex(bid.parentBlockHash);
|
|
38
|
-
const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.validateGossipExecutionPayloadBid);
|
|
39
|
-
if (!isStatePostGloas(state)) {
|
|
40
|
-
throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
|
|
41
|
-
}
|
|
42
39
|
|
|
43
40
|
// [IGNORE] `bid.slot` is the current slot or the next slot.
|
|
44
41
|
const currentSlot = chain.clock.currentSlot;
|
|
@@ -111,9 +108,31 @@ async function validateExecutionPayloadBid(
|
|
|
111
108
|
});
|
|
112
109
|
}
|
|
113
110
|
|
|
111
|
+
// Use the bid's parent branch state for builder checks
|
|
112
|
+
const state = await chain.regen
|
|
113
|
+
.getBlockSlotState(parentBlock, bid.slot, {dontTransferCache: true}, RegenCaller.validateGossipExecutionPayloadBid)
|
|
114
|
+
.catch(() => {
|
|
115
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
116
|
+
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
117
|
+
parentBlockRoot: parentBlockRootHex,
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (!isStatePostGloas(state)) {
|
|
122
|
+
throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
114
125
|
// [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
|
|
115
126
|
// `is_active_builder(state, bid.builder_index)` returns `True`.
|
|
116
|
-
|
|
127
|
+
let builder: gloas.Builder;
|
|
128
|
+
try {
|
|
129
|
+
builder = state.getBuilder(bid.builderIndex);
|
|
130
|
+
} catch {
|
|
131
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
132
|
+
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
133
|
+
builderIndex: bid.builderIndex,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
117
136
|
if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) {
|
|
118
137
|
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
119
138
|
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
@@ -121,6 +140,17 @@ async function validateExecutionPayloadBid(
|
|
|
121
140
|
});
|
|
122
141
|
}
|
|
123
142
|
|
|
143
|
+
// [REJECT] The builder version is `PAYLOAD_BUILDER_VERSION` -- i.e.
|
|
144
|
+
// `state.builders[bid.builder_index].version == PAYLOAD_BUILDER_VERSION`.
|
|
145
|
+
if (builder.version !== PAYLOAD_BUILDER_VERSION) {
|
|
146
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
147
|
+
code: ExecutionPayloadBidErrorCode.INVALID_BUILDER_VERSION,
|
|
148
|
+
builderIndex: bid.builderIndex,
|
|
149
|
+
version: builder.version,
|
|
150
|
+
expectedVersion: PAYLOAD_BUILDER_VERSION,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
124
154
|
// [REJECT] `bid.execution_payment` is zero.
|
|
125
155
|
if (bid.executionPayment !== 0) {
|
|
126
156
|
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
@@ -157,7 +187,7 @@ async function validateExecutionPayloadBid(
|
|
|
157
187
|
// [IGNORE] `is_gas_limit_target_compatible(parent_gas_limit, bid.gas_limit, target_gas_limit)`,
|
|
158
188
|
// where `parent_gas_limit` is the `gas_limit` of the parent execution payload and
|
|
159
189
|
// `target_gas_limit` is `proposer_preferences.target_gas_limit`.
|
|
160
|
-
const bidGasLimit =
|
|
190
|
+
const bidGasLimit = bid.gasLimit;
|
|
161
191
|
const parentGasLimit = parentPayloadVariant.executionPayloadGasLimit;
|
|
162
192
|
const targetGasLimit = proposerPreferences.message.targetGasLimit;
|
|
163
193
|
if (!isGasLimitTargetCompatible(parentGasLimit, bidGasLimit, targetGasLimit)) {
|
|
@@ -214,6 +244,18 @@ async function validateExecutionPayloadBid(
|
|
|
214
244
|
});
|
|
215
245
|
}
|
|
216
246
|
|
|
247
|
+
// [REJECT] `bid.prev_randao` is the correct RANDAO mix -- i.e. validate that
|
|
248
|
+
// `bid.prev_randao == get_randao_mix(parent_state, get_current_epoch(parent_state))`.
|
|
249
|
+
const randaoMix = state.getRandaoMix(computeEpochAtSlot(state.slot));
|
|
250
|
+
if (!byteArrayEquals(bid.prevRandao, randaoMix)) {
|
|
251
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
252
|
+
code: ExecutionPayloadBidErrorCode.INVALID_PREV_RANDAO,
|
|
253
|
+
builderIndex: bid.builderIndex,
|
|
254
|
+
bidPrevRandao: toHex(bid.prevRandao),
|
|
255
|
+
expectedPrevRandao: toHex(randaoMix),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
217
259
|
// [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
|
|
218
260
|
const signatureSet = createSingleSignatureSetFromComponents(
|
|
219
261
|
PublicKey.fromBytes(builder.pubkey),
|
|
@@ -35,8 +35,6 @@ async function validateExecutionPayloadEnvelope(
|
|
|
35
35
|
// [IGNORE] The envelope's block root `envelope.beacon_block_root` has been seen (via
|
|
36
36
|
// gossip or non-gossip sources) (a client MAY queue payload for processing once
|
|
37
37
|
// the block is retrieved).
|
|
38
|
-
// TODO GLOAS: Need to review this, we should queue the envelope for later
|
|
39
|
-
// processing if the block is not yet known, otherwise we would ignore it here
|
|
40
38
|
const block = chain.forkChoice.getBlockDefaultStatus(envelope.beaconBlockRoot);
|
|
41
39
|
if (block === null) {
|
|
42
40
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
@@ -108,7 +106,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
108
106
|
}
|
|
109
107
|
|
|
110
108
|
// [REJECT] `hash_tree_root(envelope.execution_requests) == bid.execution_requests_root`
|
|
111
|
-
const requestsRoot = ssz.
|
|
109
|
+
const requestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
|
|
112
110
|
if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
|
|
113
111
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
114
112
|
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
|
|
@@ -8,6 +8,7 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
|
|
|
8
8
|
import {toRootHex} from "@lodestar/utils";
|
|
9
9
|
import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
|
|
10
10
|
import {IBeaconChain} from "../index.js";
|
|
11
|
+
import {RegenCaller} from "../regen/index.js";
|
|
11
12
|
|
|
12
13
|
export type PayloadAttestationValidationResult = {
|
|
13
14
|
attDataRootHex: RootHex;
|
|
@@ -61,22 +62,43 @@ async function validatePayloadAttestationMessage(
|
|
|
61
62
|
// [IGNORE] The message's block `data.beacon_block_root` has been seen (via
|
|
62
63
|
// gossip or non-gossip sources) (a client MAY queue attestation for processing
|
|
63
64
|
// once the block is retrieved. Note a client might want to request payload after).
|
|
64
|
-
|
|
65
|
+
const block = chain.forkChoice.getBlockDefaultStatus(data.beaconBlockRoot);
|
|
66
|
+
if (!block) {
|
|
65
67
|
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
66
68
|
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
67
69
|
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
// [IGNORE] The block referenced by `data.beacon_block_root` is at slot `data.slot`,
|
|
74
|
+
// i.e. the block has `block.slot == data.slot`.
|
|
75
|
+
if (block.slot !== data.slot) {
|
|
76
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
77
|
+
code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT,
|
|
78
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
79
|
+
blockSlot: block.slot,
|
|
80
|
+
slot: data.slot,
|
|
81
|
+
});
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
// [REJECT] The message's block `data.beacon_block_root` passes validation.
|
|
77
85
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
78
86
|
// it is possible that the block didn't pass the validation
|
|
79
87
|
|
|
88
|
+
// Use the referenced block's branch state for the PTC committee check
|
|
89
|
+
const state = await chain.regen
|
|
90
|
+
.getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage)
|
|
91
|
+
.catch(() => {
|
|
92
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
93
|
+
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
94
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!isStatePostGloas(state)) {
|
|
99
|
+
throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
// [REJECT] The message's validator index is within the payload committee in
|
|
81
103
|
// `get_ptc(state, data.slot)`. The `state` is the head state corresponding to
|
|
82
104
|
// processing the block up to the current slot as determined by the fork choice.
|
|
@@ -244,7 +244,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
244
244
|
if (executionRequests === undefined) {
|
|
245
245
|
throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`);
|
|
246
246
|
}
|
|
247
|
-
const serializedExecutionRequests = serializeExecutionRequests(executionRequests);
|
|
247
|
+
const serializedExecutionRequests = serializeExecutionRequests(fork, executionRequests);
|
|
248
248
|
engineRequest = {
|
|
249
249
|
method: ForkSeq[fork] >= ForkSeq.gloas ? "engine_newPayloadV5" : "engine_newPayloadV4",
|
|
250
250
|
params: [
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {
|
|
3
|
+
BUILDER_DEPOSIT_REQUEST_TYPE,
|
|
4
|
+
BUILDER_EXIT_REQUEST_TYPE,
|
|
3
5
|
CONSOLIDATION_REQUEST_TYPE,
|
|
4
6
|
DEPOSIT_REQUEST_TYPE,
|
|
5
7
|
ForkName,
|
|
@@ -51,10 +53,18 @@ export enum ExecutionEngineState {
|
|
|
51
53
|
export type ExecutionRequestType =
|
|
52
54
|
| typeof DEPOSIT_REQUEST_TYPE
|
|
53
55
|
| typeof WITHDRAWAL_REQUEST_TYPE
|
|
54
|
-
| typeof CONSOLIDATION_REQUEST_TYPE
|
|
56
|
+
| typeof CONSOLIDATION_REQUEST_TYPE
|
|
57
|
+
| typeof BUILDER_DEPOSIT_REQUEST_TYPE
|
|
58
|
+
| typeof BUILDER_EXIT_REQUEST_TYPE;
|
|
55
59
|
|
|
56
60
|
export function isExecutionRequestType(type: number): type is ExecutionRequestType {
|
|
57
|
-
return
|
|
61
|
+
return (
|
|
62
|
+
type === DEPOSIT_REQUEST_TYPE ||
|
|
63
|
+
type === WITHDRAWAL_REQUEST_TYPE ||
|
|
64
|
+
type === CONSOLIDATION_REQUEST_TYPE ||
|
|
65
|
+
type === BUILDER_DEPOSIT_REQUEST_TYPE ||
|
|
66
|
+
type === BUILDER_EXIT_REQUEST_TYPE
|
|
67
|
+
);
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
export type ExecutePayloadResponse =
|
|
@@ -393,11 +393,12 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
|
|
|
393
393
|
blobs,
|
|
394
394
|
proofs,
|
|
395
395
|
}),
|
|
396
|
-
executionRequests: serializeExecutionRequests(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
396
|
+
executionRequests: serializeExecutionRequests(
|
|
397
|
+
fork,
|
|
398
|
+
ForkSeq[fork] >= ForkSeq.gloas
|
|
399
|
+
? ssz.gloas.ExecutionRequests.defaultValue()
|
|
400
|
+
: ssz.electra.ExecutionRequests.defaultValue()
|
|
401
|
+
),
|
|
401
402
|
});
|
|
402
403
|
|
|
403
404
|
// IF the payload is deemed VALID and the build process has begun
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BUILDER_DEPOSIT_REQUEST_TYPE,
|
|
3
|
+
BUILDER_EXIT_REQUEST_TYPE,
|
|
2
4
|
BYTES_PER_FIELD_ELEMENT,
|
|
3
5
|
BYTES_PER_LOGS_BLOOM,
|
|
4
6
|
CELLS_PER_EXT_BLOB,
|
|
@@ -205,16 +207,27 @@ export type WithdrawalRpc = {
|
|
|
205
207
|
};
|
|
206
208
|
|
|
207
209
|
/**
|
|
208
|
-
* ExecutionRequestsRpc
|
|
209
|
-
* -
|
|
210
|
-
* - ssz'ed
|
|
211
|
-
* - ssz'ed
|
|
210
|
+
* ExecutionRequestsRpc holds at most 5 elements and no repeated type, ordered by
|
|
211
|
+
* the single-byte request type prefix:
|
|
212
|
+
* - ssz'ed DepositRequests (0x00)
|
|
213
|
+
* - ssz'ed WithdrawalRequests (0x01)
|
|
214
|
+
* - ssz'ed ConsolidationRequests (0x02)
|
|
215
|
+
* - ssz'ed BuilderDepositRequests (0x03)
|
|
216
|
+
* - ssz'ed BuilderExitRequests (0x04)
|
|
212
217
|
*/
|
|
213
|
-
export type ExecutionRequestsRpc = (
|
|
218
|
+
export type ExecutionRequestsRpc = (
|
|
219
|
+
| DepositRequestsRpc
|
|
220
|
+
| WithdrawalRequestsRpc
|
|
221
|
+
| ConsolidationRequestsRpc
|
|
222
|
+
| BuilderDepositRequestsRpc
|
|
223
|
+
| BuilderExitRequestsRpc
|
|
224
|
+
)[];
|
|
214
225
|
|
|
215
226
|
export type DepositRequestsRpc = DATA;
|
|
216
227
|
export type WithdrawalRequestsRpc = DATA;
|
|
217
228
|
export type ConsolidationRequestsRpc = DATA;
|
|
229
|
+
export type BuilderDepositRequestsRpc = DATA;
|
|
230
|
+
export type BuilderExitRequestsRpc = DATA;
|
|
218
231
|
|
|
219
232
|
export type BlobAndProofRpc = {
|
|
220
233
|
blob: DATA;
|
|
@@ -339,7 +352,7 @@ export function parseExecutionPayload(
|
|
|
339
352
|
data = response.executionPayload;
|
|
340
353
|
blobsBundle = response.blobsBundle ? parseBlobsBundle(response.blobsBundle) : undefined;
|
|
341
354
|
executionRequests = response.executionRequests
|
|
342
|
-
? deserializeExecutionRequests(response.executionRequests)
|
|
355
|
+
? deserializeExecutionRequests(fork, response.executionRequests)
|
|
343
356
|
: undefined;
|
|
344
357
|
shouldOverrideBuilder = response.shouldOverrideBuilder ?? false;
|
|
345
358
|
} else {
|
|
@@ -526,13 +539,36 @@ function deserializeConsolidationRequests(serialized: ConsolidationRequestsRpc):
|
|
|
526
539
|
return ssz.electra.ConsolidationRequests.deserialize(dataToBytes(serialized, null));
|
|
527
540
|
}
|
|
528
541
|
|
|
542
|
+
function serializeBuilderDepositRequests(
|
|
543
|
+
builderDepositRequests: gloas.BuilderDepositRequests
|
|
544
|
+
): BuilderDepositRequestsRpc {
|
|
545
|
+
const requestsBytes = ssz.gloas.BuilderDepositRequests.serialize(builderDepositRequests);
|
|
546
|
+
return bytesToData(prefixRequests(requestsBytes, BUILDER_DEPOSIT_REQUEST_TYPE));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function deserializeBuilderDepositRequests(serialized: BuilderDepositRequestsRpc): gloas.BuilderDepositRequests {
|
|
550
|
+
return ssz.gloas.BuilderDepositRequests.deserialize(dataToBytes(serialized, null));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function serializeBuilderExitRequests(builderExitRequests: gloas.BuilderExitRequests): BuilderExitRequestsRpc {
|
|
554
|
+
const requestsBytes = ssz.gloas.BuilderExitRequests.serialize(builderExitRequests);
|
|
555
|
+
return bytesToData(prefixRequests(requestsBytes, BUILDER_EXIT_REQUEST_TYPE));
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function deserializeBuilderExitRequests(serialized: BuilderExitRequestsRpc): gloas.BuilderExitRequests {
|
|
559
|
+
return ssz.gloas.BuilderExitRequests.deserialize(dataToBytes(serialized, null));
|
|
560
|
+
}
|
|
561
|
+
|
|
529
562
|
/**
|
|
530
563
|
* This is identical to get_execution_requests_list in
|
|
531
564
|
* https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/electra/beacon-chain.md#new-get_execution_requests_list
|
|
565
|
+
*
|
|
566
|
+
* Gloas extends the list with builder deposits (0x03) and builder exits (0x04) per
|
|
567
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.11/specs/gloas/beacon-chain.md#modified-get_execution_requests_list
|
|
532
568
|
*/
|
|
533
|
-
export function serializeExecutionRequests(executionRequests: ExecutionRequests): ExecutionRequestsRpc {
|
|
569
|
+
export function serializeExecutionRequests(fork: ForkName, executionRequests: ExecutionRequests): ExecutionRequestsRpc {
|
|
534
570
|
const {deposits, withdrawals, consolidations} = executionRequests;
|
|
535
|
-
const result = [];
|
|
571
|
+
const result: ExecutionRequestsRpc = [];
|
|
536
572
|
|
|
537
573
|
if (deposits.length !== 0) {
|
|
538
574
|
result.push(serializeDepositRequests(deposits));
|
|
@@ -546,15 +582,26 @@ export function serializeExecutionRequests(executionRequests: ExecutionRequests)
|
|
|
546
582
|
result.push(serializeConsolidationRequests(consolidations));
|
|
547
583
|
}
|
|
548
584
|
|
|
585
|
+
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
586
|
+
const {builderDeposits, builderExits} = executionRequests as gloas.ExecutionRequests;
|
|
587
|
+
|
|
588
|
+
if (builderDeposits.length !== 0) {
|
|
589
|
+
result.push(serializeBuilderDepositRequests(builderDeposits));
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (builderExits.length !== 0) {
|
|
593
|
+
result.push(serializeBuilderExitRequests(builderExits));
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
549
597
|
return result;
|
|
550
598
|
}
|
|
551
599
|
|
|
552
|
-
export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc): ExecutionRequests {
|
|
553
|
-
const result: ExecutionRequests =
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
};
|
|
600
|
+
export function deserializeExecutionRequests(fork: ForkName, serialized: ExecutionRequestsRpc): ExecutionRequests {
|
|
601
|
+
const result: ExecutionRequests =
|
|
602
|
+
ForkSeq[fork] >= ForkSeq.gloas
|
|
603
|
+
? {deposits: [], withdrawals: [], consolidations: [], builderDeposits: [], builderExits: []}
|
|
604
|
+
: {deposits: [], withdrawals: [], consolidations: []};
|
|
558
605
|
|
|
559
606
|
if (serialized.length === 0) {
|
|
560
607
|
return result;
|
|
@@ -601,6 +648,20 @@ export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc):
|
|
|
601
648
|
result.consolidations = deserializeConsolidationRequests(requests);
|
|
602
649
|
break;
|
|
603
650
|
}
|
|
651
|
+
case BUILDER_DEPOSIT_REQUEST_TYPE: {
|
|
652
|
+
if (ForkSeq[fork] < ForkSeq.gloas) {
|
|
653
|
+
throw Error(`Builder deposit request is not supported pre-gloas fork=${fork}`);
|
|
654
|
+
}
|
|
655
|
+
(result as gloas.ExecutionRequests).builderDeposits = deserializeBuilderDepositRequests(requests);
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
case BUILDER_EXIT_REQUEST_TYPE: {
|
|
659
|
+
if (ForkSeq[fork] < ForkSeq.gloas) {
|
|
660
|
+
throw Error(`Builder exit request is not supported pre-gloas fork=${fork}`);
|
|
661
|
+
}
|
|
662
|
+
(result as gloas.ExecutionRequests).builderExits = deserializeBuilderExitRequests(requests);
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
604
665
|
}
|
|
605
666
|
prevRequestType = currentRequestType;
|
|
606
667
|
}
|
|
@@ -610,6 +610,11 @@ export function createLodestarMetrics(
|
|
|
610
610
|
help: "The origination source of one of the BlockInputSync triggers",
|
|
611
611
|
labelNames: ["source"],
|
|
612
612
|
}),
|
|
613
|
+
payloadSource: register.counter<{source: BlockInputSource}>({
|
|
614
|
+
name: "lodestar_payload_input_sync_source_total",
|
|
615
|
+
help: "Count of payload (execution payload envelope) sync triggers, labeled by their source",
|
|
616
|
+
labelNames: ["source"],
|
|
617
|
+
}),
|
|
613
618
|
pendingBlocks: register.gauge({
|
|
614
619
|
name: "lodestar_sync_unknown_block_pending_blocks_size",
|
|
615
620
|
help: "Current size of UnknownBlockSync pending blocks cache",
|
|
@@ -861,6 +866,12 @@ export function createLodestarMetrics(
|
|
|
861
866
|
labelNames: ["numBlobs"],
|
|
862
867
|
}),
|
|
863
868
|
|
|
869
|
+
skippedSlots: register.histogram({
|
|
870
|
+
name: "lodestar_gossip_block_skipped_slots",
|
|
871
|
+
help: "Number of skipped slots between a gossip block and its parent (blockSlot - parentSlot - 1)",
|
|
872
|
+
buckets: [0, 1, 2, 4, 8, 16, 32],
|
|
873
|
+
}),
|
|
874
|
+
|
|
864
875
|
processBlockErrors: register.gauge<{error: BlockErrorCode | "NOT_BLOCK_ERROR"}>({
|
|
865
876
|
name: "lodestar_gossip_block_process_block_errors",
|
|
866
877
|
help: "Count of errors, by error type, while processing blocks",
|
|
@@ -185,7 +185,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
185
185
|
peerIdStr,
|
|
186
186
|
});
|
|
187
187
|
try {
|
|
188
|
-
await validateGossipBlock(config, chain, signedBlock, fork);
|
|
188
|
+
const {skippedSlots} = await validateGossipBlock(config, chain, signedBlock, fork);
|
|
189
189
|
|
|
190
190
|
if (isForkPostGloas(fork)) {
|
|
191
191
|
chain.seenPayloadEnvelopeInputCache.add({
|
|
@@ -205,8 +205,15 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
205
205
|
|
|
206
206
|
metrics?.gossipBlock.gossipValidation.recvToValidation.observe(recvToValidation);
|
|
207
207
|
metrics?.gossipBlock.gossipValidation.validationTime.observe(validationTime);
|
|
208
|
+
metrics?.gossipBlock.skippedSlots.observe(skippedSlots);
|
|
208
209
|
|
|
209
|
-
logger.debug("Validated gossip block", {
|
|
210
|
+
logger.debug("Validated gossip block", {
|
|
211
|
+
...blockInputMeta,
|
|
212
|
+
...logCtx,
|
|
213
|
+
recvToValidation,
|
|
214
|
+
validationTime,
|
|
215
|
+
skippedSlots,
|
|
216
|
+
});
|
|
210
217
|
|
|
211
218
|
chain.emitter.emit(routes.events.EventType.blockGossip, {slot, block: blockRootHex});
|
|
212
219
|
|
|
@@ -1214,6 +1221,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1214
1221
|
}
|
|
1215
1222
|
chain.forkChoice.notifyPtcMessages(
|
|
1216
1223
|
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
1224
|
+
payloadAttestationMessage.data.slot,
|
|
1217
1225
|
validationResult.validatorCommitteeIndices,
|
|
1218
1226
|
payloadAttestationMessage.data.payloadPresent,
|
|
1219
1227
|
payloadAttestationMessage.data.blobDataAvailable
|
|
@@ -186,9 +186,11 @@ export class NetworkProcessor {
|
|
|
186
186
|
// we may not receive the block for messages like Attestation and SignedAggregateAndProof messages, in that case PendingGossipsubMessage needs
|
|
187
187
|
// to be stored in this Map and reprocessed once the block comes
|
|
188
188
|
private readonly awaitingMessagesByBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
|
|
189
|
+
private awaitingBlockMessageCount = 0;
|
|
189
190
|
// we may not receive the payload for messages that require the FULL payload variant to be processed,
|
|
190
191
|
// in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
|
|
191
192
|
private readonly awaitingMessagesByPayloadBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
|
|
193
|
+
private awaitingPayloadMessageCount = 0;
|
|
192
194
|
private unknownBlocksBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
193
195
|
private unknownEnvelopesBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
194
196
|
|
|
@@ -228,8 +230,8 @@ export class NetworkProcessor {
|
|
|
228
230
|
metrics.gossipValidationQueue.keySize.set({topic}, this.gossipQueues[topic].keySize);
|
|
229
231
|
metrics.gossipValidationQueue.concurrency.set({topic}, this.gossipTopicConcurrency[topic]);
|
|
230
232
|
}
|
|
231
|
-
metrics.awaitingBlockGossipMessages.countPerSlot.set(this.
|
|
232
|
-
metrics.awaitingPayloadGossipMessages.countPerSlot.set(this.
|
|
233
|
+
metrics.awaitingBlockGossipMessages.countPerSlot.set(this.awaitingBlockMessageCount);
|
|
234
|
+
metrics.awaitingPayloadGossipMessages.countPerSlot.set(this.awaitingPayloadMessageCount);
|
|
233
235
|
// specific metric for beacon_attestation topic
|
|
234
236
|
metrics.gossipValidationQueue.keyAge.reset();
|
|
235
237
|
for (const ageMs of this.gossipQueues.beacon_attestation.getDataAgeMs()) {
|
|
@@ -297,7 +299,7 @@ export class NetworkProcessor {
|
|
|
297
299
|
return;
|
|
298
300
|
}
|
|
299
301
|
this.unknownEnvelopesBySlot.getOrDefault(slot).add(root);
|
|
300
|
-
this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, peer, source});
|
|
302
|
+
this.chain.emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, {rootHex: root, slot, peer, source});
|
|
301
303
|
}
|
|
302
304
|
|
|
303
305
|
private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
|
|
@@ -497,7 +499,7 @@ export class NetworkProcessor {
|
|
|
497
499
|
this.pushPendingGossipsubMessageToQueue(message);
|
|
498
500
|
break;
|
|
499
501
|
case PreprocessAction.AwaitBlock: {
|
|
500
|
-
if (this.
|
|
502
|
+
if (this.awaitingBlockMessageCount > MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS) {
|
|
501
503
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
502
504
|
this.metrics?.awaitingBlockGossipMessages.reject.inc({
|
|
503
505
|
reason: ReprocessRejectReason.reached_limit,
|
|
@@ -509,10 +511,11 @@ export class NetworkProcessor {
|
|
|
509
511
|
this.metrics?.awaitingBlockGossipMessages.queue.inc({topic: topicType});
|
|
510
512
|
const awaitingGossipsubMessages = this.awaitingMessagesByBlockRoot.getOrDefault(preprocessResult.root);
|
|
511
513
|
awaitingGossipsubMessages.add(message);
|
|
514
|
+
this.awaitingBlockMessageCount++;
|
|
512
515
|
break;
|
|
513
516
|
}
|
|
514
517
|
case PreprocessAction.AwaitEnvelope: {
|
|
515
|
-
if (this.
|
|
518
|
+
if (this.awaitingPayloadMessageCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
|
|
516
519
|
this.metrics?.awaitingPayloadGossipMessages.reject.inc({
|
|
517
520
|
reason: ReprocessRejectReason.reached_limit,
|
|
518
521
|
topic: topicType,
|
|
@@ -525,6 +528,7 @@ export class NetworkProcessor {
|
|
|
525
528
|
preprocessResult.root
|
|
526
529
|
);
|
|
527
530
|
awaitingPayloadGossipsubMessages.add(message);
|
|
531
|
+
this.awaitingPayloadMessageCount++;
|
|
528
532
|
break;
|
|
529
533
|
}
|
|
530
534
|
}
|
|
@@ -548,6 +552,12 @@ export class NetworkProcessor {
|
|
|
548
552
|
return;
|
|
549
553
|
}
|
|
550
554
|
|
|
555
|
+
// Atomically remove from map and update counter before async iteration to
|
|
556
|
+
// prevent double-decrement race with onClockSlot during yield points below
|
|
557
|
+
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
558
|
+
this.awaitingBlockMessageCount -= waitingGossipsubMessages.size;
|
|
559
|
+
}
|
|
560
|
+
|
|
551
561
|
const nowSec = Date.now() / 1000;
|
|
552
562
|
let count = 0;
|
|
553
563
|
// TODO: we can group attestations to process in batches but since we have the SeenAttestationDatas
|
|
@@ -567,8 +577,6 @@ export class NetworkProcessor {
|
|
|
567
577
|
await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
|
|
568
578
|
}
|
|
569
579
|
}
|
|
570
|
-
|
|
571
|
-
this.awaitingMessagesByBlockRoot.delete(rootHex);
|
|
572
580
|
};
|
|
573
581
|
|
|
574
582
|
private onPayloadEnvelopeProcessed = async ({blockRoot: rootHex}: {blockRoot: RootHex}): Promise<void> => {
|
|
@@ -577,6 +585,12 @@ export class NetworkProcessor {
|
|
|
577
585
|
return;
|
|
578
586
|
}
|
|
579
587
|
|
|
588
|
+
// Atomically remove from map and update counter before async iteration to
|
|
589
|
+
// prevent double-decrement race with onClockSlot during yield points below
|
|
590
|
+
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
591
|
+
this.awaitingPayloadMessageCount -= waitingGossipsubMessages.size;
|
|
592
|
+
}
|
|
593
|
+
|
|
580
594
|
const nowSec = Date.now() / 1000;
|
|
581
595
|
let count = 0;
|
|
582
596
|
for (const message of waitingGossipsubMessages) {
|
|
@@ -593,8 +607,6 @@ export class NetworkProcessor {
|
|
|
593
607
|
await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
|
|
594
608
|
}
|
|
595
609
|
}
|
|
596
|
-
|
|
597
|
-
this.awaitingMessagesByPayloadBlockRoot.delete(rootHex);
|
|
598
610
|
};
|
|
599
611
|
|
|
600
612
|
private onClockSlot = (clockSlot: Slot): void => {
|
|
@@ -618,7 +630,9 @@ export class NetworkProcessor {
|
|
|
618
630
|
);
|
|
619
631
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
620
632
|
}
|
|
621
|
-
this.awaitingMessagesByBlockRoot.delete(rootHex)
|
|
633
|
+
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
634
|
+
this.awaitingBlockMessageCount -= gossipMessages.size;
|
|
635
|
+
}
|
|
622
636
|
}
|
|
623
637
|
}
|
|
624
638
|
this.unknownBlocksBySlot.delete(slot);
|
|
@@ -641,7 +655,9 @@ export class NetworkProcessor {
|
|
|
641
655
|
);
|
|
642
656
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
643
657
|
}
|
|
644
|
-
this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)
|
|
658
|
+
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
659
|
+
this.awaitingPayloadMessageCount -= gossipMessages.size;
|
|
660
|
+
}
|
|
645
661
|
}
|
|
646
662
|
}
|
|
647
663
|
this.unknownEnvelopesBySlot.delete(slot);
|
|
@@ -784,20 +800,4 @@ export class NetworkProcessor {
|
|
|
784
800
|
|
|
785
801
|
return null;
|
|
786
802
|
}
|
|
787
|
-
|
|
788
|
-
private get unknownBlockGossipsubMessagesCount(): number {
|
|
789
|
-
let count = 0;
|
|
790
|
-
for (const messages of this.awaitingMessagesByBlockRoot.values()) {
|
|
791
|
-
count += messages.size;
|
|
792
|
-
}
|
|
793
|
-
return count;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
private get unknownPayloadGossipsubMessagesCount(): number {
|
|
797
|
-
let count = 0;
|
|
798
|
-
for (const messages of this.awaitingMessagesByPayloadBlockRoot.values()) {
|
|
799
|
-
count += messages.size;
|
|
800
|
-
}
|
|
801
|
-
return count;
|
|
802
|
-
}
|
|
803
803
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {PeerId} from "@libp2p/interface";
|
|
2
2
|
import {BeaconConfig} from "@lodestar/config";
|
|
3
|
-
import {GENESIS_SLOT, isForkPostDeneb
|
|
3
|
+
import {GENESIS_SLOT, isForkPostDeneb} from "@lodestar/params";
|
|
4
4
|
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
|
|
5
5
|
import {computeEpochAtSlot} from "@lodestar/state-transition";
|
|
6
6
|
import {deneb, phase0} from "@lodestar/types";
|
|
@@ -29,13 +29,20 @@ export async function* onBeaconBlocksByRange(
|
|
|
29
29
|
// starts above it to avoid duplicate yields. See archiveBlocks.ts for the migration logic.
|
|
30
30
|
const archiveMaxSlot = finalizedSlot;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// endSlot is exclusive, so highest served slot is endSlot - 1.
|
|
33
|
+
// Throw only when the entire requested range is below earliestAvailableSlot.
|
|
34
|
+
if (endSlot - 1 < chain.earliestAvailableSlot) {
|
|
35
|
+
chain.logger.verbose("Peer requested range before earliestAvailableSlot for BeaconBlocksByRange", {
|
|
35
36
|
peer: prettyPrintPeerId(peerId),
|
|
36
37
|
client: peerClient,
|
|
38
|
+
startSlot,
|
|
39
|
+
count,
|
|
40
|
+
earliestAvailableSlot: chain.earliestAvailableSlot,
|
|
37
41
|
});
|
|
38
|
-
|
|
42
|
+
throw new ResponseError(
|
|
43
|
+
RespStatus.RESOURCE_UNAVAILABLE,
|
|
44
|
+
`Requested range is before earliestAvailableSlot startSlot=${startSlot} count=${count} earliestAvailableSlot=${chain.earliestAvailableSlot}`
|
|
45
|
+
);
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
// Finalized range of blocks
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {PeerId} from "@libp2p/interface";
|
|
2
2
|
import {ChainConfig} from "@lodestar/config";
|
|
3
|
+
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
3
4
|
import {ForkSeq, GENESIS_SLOT} from "@lodestar/params";
|
|
4
5
|
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
|
|
5
6
|
import {computeEpochAtSlot} from "@lodestar/state-transition";
|
|
@@ -33,12 +34,20 @@ export async function* onDataColumnSidecarsByRange(
|
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
// endSlot is exclusive, so highest served slot is endSlot - 1.
|
|
38
|
+
// Throw only when the entire requested range is below earliestAvailableSlot.
|
|
39
|
+
if (endSlot - 1 < chain.earliestAvailableSlot) {
|
|
40
|
+
chain.logger.verbose("Peer requested range before earliestAvailableSlot for DataColumnSidecarsByRange", {
|
|
38
41
|
peer: prettyPrintPeerId(peerId),
|
|
39
42
|
client: peerClient,
|
|
43
|
+
startSlot,
|
|
44
|
+
count,
|
|
45
|
+
earliestAvailableSlot: chain.earliestAvailableSlot,
|
|
40
46
|
});
|
|
41
|
-
|
|
47
|
+
throw new ResponseError(
|
|
48
|
+
RespStatus.RESOURCE_UNAVAILABLE,
|
|
49
|
+
`Requested range is before earliestAvailableSlot startSlot=${startSlot} count=${count} earliestAvailableSlot=${chain.earliestAvailableSlot}`
|
|
50
|
+
);
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
const finalized = db.dataColumnSidecarArchive;
|
|
@@ -104,6 +113,11 @@ export async function* onDataColumnSidecarsByRange(
|
|
|
104
113
|
|
|
105
114
|
// Must include only columns in the range requested
|
|
106
115
|
if (block.slot > archiveMaxSlot && block.slot >= startSlot && block.slot < endSlot) {
|
|
116
|
+
// Post-gloas, columns exist only for FULL blocks (pre-gloas blocks are always FULL)
|
|
117
|
+
if (block.payloadStatus !== PayloadStatus.FULL) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
107
121
|
// Note: Here the forkChoice head may change due to a re-org, so the headChain reflects the canonical chain
|
|
108
122
|
// at the time of the start of the request. Spec is clear the chain of columns must be consistent, but on
|
|
109
123
|
// re-org there's no need to abort the request
|
|
@@ -30,7 +30,7 @@ export async function* onDataColumnSidecarsByRoot(
|
|
|
30
30
|
const {blockRoot, columns: requestedColumns} = dataColumnsByRootIdentifier;
|
|
31
31
|
const availableColumns = validateRequestedDataColumns(chain, requestedColumns);
|
|
32
32
|
if (availableColumns.length === 0) {
|
|
33
|
-
|
|
33
|
+
continue;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const blockRootHex = toRootHex(blockRoot);
|